mirror of
https://github.com/ioquake/ioq3.git
synced 2024-11-10 07:11:46 +00:00
Add protocol handler support
This lets the user click a link in a web browser to very easily join a Quake 3 multiplayer game. As browser-based matchmaking websites become more popular, this makes it a lot more convenient and simple to play Quake 3 with others. The links have the following URI format: quake3://connect/example.com:27950. The format has been designed to be flexible to allow more types of links in the future and avoiding having to make a breaking change. At the moment, "connect" is the only supported command.
This commit is contained in:
parent
23a85089c2
commit
31c6d2f9d5
7 changed files with 217 additions and 8 deletions
|
@ -40,6 +40,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
#define CINEMATICS_LOGO "foologo.roq"
|
#define CINEMATICS_LOGO "foologo.roq"
|
||||||
#define CINEMATICS_INTRO "intro.roq"
|
#define CINEMATICS_INTRO "intro.roq"
|
||||||
// #define LEGACY_PROTOCOL // You probably don't need this for your standalone game
|
// #define LEGACY_PROTOCOL // You probably don't need this for your standalone game
|
||||||
|
// #define PROTOCOL_HANDLER "foobar"
|
||||||
#else
|
#else
|
||||||
#define PRODUCT_NAME "ioq3"
|
#define PRODUCT_NAME "ioq3"
|
||||||
#define BASEGAME "baseq3"
|
#define BASEGAME "baseq3"
|
||||||
|
@ -56,6 +57,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
#define CINEMATICS_LOGO "idlogo.RoQ"
|
#define CINEMATICS_LOGO "idlogo.RoQ"
|
||||||
#define CINEMATICS_INTRO "intro.RoQ"
|
#define CINEMATICS_INTRO "intro.RoQ"
|
||||||
#define LEGACY_PROTOCOL
|
#define LEGACY_PROTOCOL
|
||||||
|
#define PROTOCOL_HANDLER "quake3"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Heartbeat for dpmaster protocol. You shouldn't change this unless you know what you're doing
|
// Heartbeat for dpmaster protocol. You shouldn't change this unless you know what you're doing
|
||||||
|
|
|
@ -64,3 +64,8 @@ void Sys_AnsiColorPrint( const char *msg );
|
||||||
|
|
||||||
int Sys_PID( void );
|
int Sys_PID( void );
|
||||||
qboolean Sys_PIDIsRunning( int pid );
|
qboolean Sys_PIDIsRunning( int pid );
|
||||||
|
|
||||||
|
#ifdef PROTOCOL_HANDLER
|
||||||
|
char *Sys_InitProtocolHandler( void );
|
||||||
|
char *Sys_ParseProtocolUri( const char *uri );
|
||||||
|
#endif
|
||||||
|
|
|
@ -642,6 +642,94 @@ void Sys_ParseArgs( int argc, char **argv )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef PROTOCOL_HANDLER
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
Sys_InitProtocolHandler
|
||||||
|
|
||||||
|
See sys_osx.m for macOS implementation.
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
#ifndef __APPLE__
|
||||||
|
char *Sys_InitProtocolHandler( void )
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
Sys_ParseProtocolUri
|
||||||
|
|
||||||
|
This parses a protocol URI, e.g. "quake3://connect/example.com:27950"
|
||||||
|
to a string that can be run in the console, or a null pointer if the
|
||||||
|
operation is invalid or unsupported.
|
||||||
|
At the moment only the "connect" command is supported.
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
char *Sys_ParseProtocolUri( const char *uri )
|
||||||
|
{
|
||||||
|
// Both "quake3://" and "quake3:" can be used
|
||||||
|
if ( Q_strncmp( uri, PROTOCOL_HANDLER ":", strlen( PROTOCOL_HANDLER ":" ) ) )
|
||||||
|
{
|
||||||
|
Com_Printf( "Sys_ParseProtocolUri: unsupported protocol.\n" );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
uri += strlen( PROTOCOL_HANDLER ":" );
|
||||||
|
if ( !Q_strncmp( uri, "//", strlen( "//" ) ) )
|
||||||
|
{
|
||||||
|
uri += strlen( "//" );
|
||||||
|
}
|
||||||
|
Com_Printf( "Sys_ParseProtocolUri: %s\n", uri );
|
||||||
|
|
||||||
|
// At the moment, only "connect/hostname:port" is supported
|
||||||
|
if ( !Q_strncmp( uri, "connect/", strlen( "connect/" ) ) )
|
||||||
|
{
|
||||||
|
int i, bufsize;
|
||||||
|
char *out;
|
||||||
|
|
||||||
|
uri += strlen( "connect/" );
|
||||||
|
if ( *uri == '\0' || *uri == '?' )
|
||||||
|
{
|
||||||
|
Com_Printf( "Sys_ParseProtocolUri: missing argument.\n" );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for any unsupported characters
|
||||||
|
// For safety reasons, the "hostname:port" part can only
|
||||||
|
// contain characters from: a-zA-Z0-9.:-[]
|
||||||
|
for ( i=0; uri[i] != '\0'; i++ )
|
||||||
|
{
|
||||||
|
if ( uri[i] == '?' )
|
||||||
|
{
|
||||||
|
// For forwards compatibility, any query string parameters are ignored (e.g. "?password=abcd")
|
||||||
|
// However, these are not passed on macOS, so it may be a bad idea to add them.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isalpha( uri[i] ) == 0 && isdigit( uri[i] ) == 0
|
||||||
|
&& uri[i] != '.' && uri[i] != ':' && uri[i] != '-'
|
||||||
|
&& uri[i] != '[' && uri[i] != ']' )
|
||||||
|
{
|
||||||
|
Com_Printf( "Sys_ParseProtocolUri: hostname contains unsupported character.\n" );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bufsize = strlen( "+connect " ) + i + 1;
|
||||||
|
out = malloc( bufsize );
|
||||||
|
strcpy( out, "+connect " );
|
||||||
|
strncat( out, uri, i );
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Com_Printf( "Sys_ParseProtocolUri: unsupported command.\n" );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef DEFAULT_BASEDIR
|
#ifndef DEFAULT_BASEDIR
|
||||||
# ifdef __APPLE__
|
# ifdef __APPLE__
|
||||||
# define DEFAULT_BASEDIR Sys_StripAppBundle(Sys_BinaryPath())
|
# define DEFAULT_BASEDIR Sys_StripAppBundle(Sys_BinaryPath())
|
||||||
|
@ -690,6 +778,9 @@ int main( int argc, char **argv )
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
char commandLine[ MAX_STRING_CHARS ] = { 0 };
|
char commandLine[ MAX_STRING_CHARS ] = { 0 };
|
||||||
|
#ifdef PROTOCOL_HANDLER
|
||||||
|
char *protocolCommand = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
extern void Sys_LaunchAutoupdater(int argc, char **argv);
|
extern void Sys_LaunchAutoupdater(int argc, char **argv);
|
||||||
Sys_LaunchAutoupdater(argc, argv);
|
Sys_LaunchAutoupdater(argc, argv);
|
||||||
|
@ -724,6 +815,10 @@ int main( int argc, char **argv )
|
||||||
|
|
||||||
Sys_PlatformInit( );
|
Sys_PlatformInit( );
|
||||||
|
|
||||||
|
#ifdef PROTOCOL_HANDLER
|
||||||
|
protocolCommand = Sys_InitProtocolHandler( );
|
||||||
|
#endif
|
||||||
|
|
||||||
// Set the initial time base
|
// Set the initial time base
|
||||||
Sys_Milliseconds( );
|
Sys_Milliseconds( );
|
||||||
|
|
||||||
|
@ -740,7 +835,22 @@ int main( int argc, char **argv )
|
||||||
// Concatenate the command line for passing to Com_Init
|
// Concatenate the command line for passing to Com_Init
|
||||||
for( i = 1; i < argc; i++ )
|
for( i = 1; i < argc; i++ )
|
||||||
{
|
{
|
||||||
const qboolean containsSpaces = strchr(argv[i], ' ') != NULL;
|
qboolean containsSpaces;
|
||||||
|
|
||||||
|
// For security reasons we always detect --uri, even when PROTOCOL_HANDLER is undefined
|
||||||
|
// Any arguments after "--uri quake3://..." is ignored
|
||||||
|
if ( !strcmp( argv[i], "--uri" ) )
|
||||||
|
{
|
||||||
|
#ifdef PROTOCOL_HANDLER
|
||||||
|
if ( argc > i+1 )
|
||||||
|
{
|
||||||
|
protocolCommand = Sys_ParseProtocolUri( argv[i+1] );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
containsSpaces = strchr(argv[i], ' ') != NULL;
|
||||||
if (containsSpaces)
|
if (containsSpaces)
|
||||||
Q_strcat( commandLine, sizeof( commandLine ), "\"" );
|
Q_strcat( commandLine, sizeof( commandLine ), "\"" );
|
||||||
|
|
||||||
|
@ -752,6 +862,14 @@ int main( int argc, char **argv )
|
||||||
Q_strcat( commandLine, sizeof( commandLine ), " " );
|
Q_strcat( commandLine, sizeof( commandLine ), " " );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef PROTOCOL_HANDLER
|
||||||
|
if ( protocolCommand != NULL )
|
||||||
|
{
|
||||||
|
Q_strcat( commandLine, sizeof( commandLine ), protocolCommand );
|
||||||
|
free( protocolCommand );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
CON_Init( );
|
CON_Init( );
|
||||||
Com_Init( commandLine );
|
Com_Init( commandLine );
|
||||||
NET_Init( );
|
NET_Init( );
|
||||||
|
|
|
@ -34,6 +34,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
#import <Carbon/Carbon.h>
|
#import <Carbon/Carbon.h>
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
#ifdef PROTOCOL_HANDLER
|
||||||
|
char *protocolCommand = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
==============
|
==============
|
||||||
Sys_Dialog
|
Sys_Dialog
|
||||||
|
@ -115,3 +119,42 @@ char *Sys_StripAppBundle( char *dir )
|
||||||
Q_strncpyz(cwd, Sys_Dirname(cwd), sizeof(cwd));
|
Q_strncpyz(cwd, Sys_Dirname(cwd), sizeof(cwd));
|
||||||
return cwd;
|
return cwd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef PROTOCOL_HANDLER
|
||||||
|
|
||||||
|
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation AppDelegate
|
||||||
|
|
||||||
|
- (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent
|
||||||
|
{
|
||||||
|
NSString *input = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
|
||||||
|
protocolCommand = Sys_ParseProtocolUri( input.UTF8String );
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)applicationDidFinishLaunching:(NSNotification *)notification
|
||||||
|
{
|
||||||
|
[NSApp stop:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
char *Sys_InitProtocolHandler( void )
|
||||||
|
{
|
||||||
|
[NSApplication sharedApplication];
|
||||||
|
|
||||||
|
AppDelegate *appDelegate = [AppDelegate new];
|
||||||
|
NSAppleEventManager *sharedAppleEventManager = [NSAppleEventManager new];
|
||||||
|
[sharedAppleEventManager setEventHandler:appDelegate
|
||||||
|
andSelector:@selector(handleAppleEvent:withReplyEvent:)
|
||||||
|
forEventClass:kInternetEventClass
|
||||||
|
andEventID:kAEGetURL];
|
||||||
|
|
||||||
|
[NSApp setDelegate:appDelegate];
|
||||||
|
[NSApp run];
|
||||||
|
|
||||||
|
return protocolCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -196,6 +196,7 @@ CONTENTS_FOLDER_PATH="${WRAPPER_NAME}/Contents"
|
||||||
UNLOCALIZED_RESOURCES_FOLDER_PATH="${CONTENTS_FOLDER_PATH}/Resources"
|
UNLOCALIZED_RESOURCES_FOLDER_PATH="${CONTENTS_FOLDER_PATH}/Resources"
|
||||||
EXECUTABLE_FOLDER_PATH="${CONTENTS_FOLDER_PATH}/MacOS"
|
EXECUTABLE_FOLDER_PATH="${CONTENTS_FOLDER_PATH}/MacOS"
|
||||||
EXECUTABLE_NAME="${PRODUCT_NAME}"
|
EXECUTABLE_NAME="${PRODUCT_NAME}"
|
||||||
|
PROTOCOL_HANDLER="quake3"
|
||||||
|
|
||||||
# loop through the architectures to build string lists for each universal binary
|
# loop through the architectures to build string lists for each universal binary
|
||||||
for ARCH in $SEARCH_ARCHS; do
|
for ARCH in $SEARCH_ARCHS; do
|
||||||
|
@ -379,6 +380,21 @@ if [ -n "${MACOSX_DEPLOYMENT_TARGET_PPC}" ] || [ -n "${MACOSX_DEPLOYMENT_TARGET_
|
||||||
</dict>"
|
</dict>"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -n "${PROTOCOL_HANDLER}" ]; then
|
||||||
|
PLIST="${PLIST}
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>${PRODUCT_NAME}</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>${PROTOCOL_HANDLER}</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>"
|
||||||
|
fi
|
||||||
|
|
||||||
PLIST="${PLIST}
|
PLIST="${PLIST}
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>QUAKE III ARENA Copyright © 1999-2000 id Software, Inc. All rights reserved.</string>
|
<string>QUAKE III ARENA Copyright © 1999-2000 id Software, Inc. All rights reserved.</string>
|
||||||
|
|
|
@ -45,7 +45,7 @@ OutFile "ioquake3-XXXVERSIONXXX-XXXRELEASEXXX.x86.exe"
|
||||||
|
|
||||||
!insertmacro MULTIUSER_PAGE_INSTALLMODE
|
!insertmacro MULTIUSER_PAGE_INSTALLMODE
|
||||||
;!insertmacro MUI_PAGE_LICENSE "../../COPYING.txt"
|
;!insertmacro MUI_PAGE_LICENSE "../../COPYING.txt"
|
||||||
!define MUI_COMPONENTSPAGE_NODESC
|
!define MUI_COMPONENTSPAGE_SMALLDESC
|
||||||
!insertmacro MUI_PAGE_COMPONENTS
|
!insertmacro MUI_PAGE_COMPONENTS
|
||||||
!insertmacro MUI_PAGE_DIRECTORY
|
!insertmacro MUI_PAGE_DIRECTORY
|
||||||
!insertmacro MUI_PAGE_INSTFILES
|
!insertmacro MUI_PAGE_INSTFILES
|
||||||
|
@ -71,7 +71,7 @@ Function un.onInit
|
||||||
FunctionEnd
|
FunctionEnd
|
||||||
|
|
||||||
; The stuff to install
|
; The stuff to install
|
||||||
Section "ioquake3 (required)"
|
Section "ioquake3 (required)" ioquake3
|
||||||
|
|
||||||
SectionIn RO
|
SectionIn RO
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ Section "ioquake3 (required)"
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
||||||
; Optional section (can be disabled by the user)
|
; Optional section (can be disabled by the user)
|
||||||
Section "Start Menu Shortcuts"
|
Section "Start Menu Shortcuts" StartMenuShortcuts
|
||||||
|
|
||||||
CreateDirectory "$SMPROGRAMS\ioquake3"
|
CreateDirectory "$SMPROGRAMS\ioquake3"
|
||||||
CreateShortCut "$SMPROGRAMS\ioquake3\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0
|
CreateShortCut "$SMPROGRAMS\ioquake3\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0
|
||||||
|
@ -132,7 +132,17 @@ Section "Start Menu Shortcuts"
|
||||||
|
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
||||||
Section "SDL2.dll"
|
Section "Protocol Handler" ProtocolHandler
|
||||||
|
|
||||||
|
WriteRegStr SHCTX "Software\Classes\quake3" "CustomUrlApplication" "$INSTDIR\ioquake3.x86.exe"
|
||||||
|
WriteRegStr SHCTX "Software\Classes\quake3" "CustomUrlArguments" '"%1"'
|
||||||
|
WriteRegStr SHCTX "Software\Classes\quake3" "URL Protocol" ""
|
||||||
|
WriteRegStr SHCTX "Software\Classes\quake3\DefaultIcon" "" "$INSTDIR\ioquake3.x86.exe,0"
|
||||||
|
WriteRegStr SHCTX "Software\Classes\quake3\shell\open\command" "" '"$INSTDIR\ioquake3.x86.exe" --uri "%1"'
|
||||||
|
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
Section "SDL2.dll" SDL
|
||||||
|
|
||||||
SetOutPath $INSTDIR
|
SetOutPath $INSTDIR
|
||||||
|
|
||||||
|
@ -141,7 +151,7 @@ Section "SDL2.dll"
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
||||||
!ifdef USE_OPENAL_DLOPEN
|
!ifdef USE_OPENAL_DLOPEN
|
||||||
Section "OpenAL-Soft library"
|
Section "OpenAL-Soft library" OpenAL
|
||||||
|
|
||||||
SetOutPath $INSTDIR
|
SetOutPath $INSTDIR
|
||||||
|
|
||||||
|
@ -151,7 +161,7 @@ SectionEnd
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
!ifdef USE_CURL_DLOPEN
|
!ifdef USE_CURL_DLOPEN
|
||||||
Section "libcurl"
|
Section "libcurl" libcurl
|
||||||
|
|
||||||
SetOutPath $INSTDIR
|
SetOutPath $INSTDIR
|
||||||
|
|
||||||
|
@ -169,6 +179,7 @@ Section "Uninstall"
|
||||||
; Remove registry keys
|
; Remove registry keys
|
||||||
DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\ioquake3"
|
DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\ioquake3"
|
||||||
DeleteRegKey SHCTX "Software\ioquake3"
|
DeleteRegKey SHCTX "Software\ioquake3"
|
||||||
|
DeleteRegKey SHCTX "Software\Classes\quake3"
|
||||||
|
|
||||||
; Remove files and uninstaller
|
; Remove files and uninstaller
|
||||||
Delete $INSTDIR\baseq3\cgamex86.dll
|
Delete $INSTDIR\baseq3\cgamex86.dll
|
||||||
|
@ -220,3 +231,16 @@ Section "Uninstall"
|
||||||
RMDir "$INSTDIR"
|
RMDir "$INSTDIR"
|
||||||
|
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
||||||
|
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
|
||||||
|
!insertmacro MUI_DESCRIPTION_TEXT ${ioquake3} "The game executables."
|
||||||
|
!insertmacro MUI_DESCRIPTION_TEXT ${StartMenuShortcuts} "Create shortcuts in the start menu."
|
||||||
|
!insertmacro MUI_DESCRIPTION_TEXT ${ProtocolHandler} "The protocol handler lets you connect to a game by clicking a link in a web browser."
|
||||||
|
!insertmacro MUI_DESCRIPTION_TEXT ${SDL} "SDL files."
|
||||||
|
!ifdef USE_OPENAL_DLOPEN
|
||||||
|
!insertmacro MUI_DESCRIPTION_TEXT ${OpenAL} "OpenAL files."
|
||||||
|
!endif
|
||||||
|
!ifdef USE_CURL_DLOPEN
|
||||||
|
!insertmacro MUI_DESCRIPTION_TEXT ${libcurl} "libcurl files."
|
||||||
|
!endif
|
||||||
|
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Name=ioquake3
|
Name=ioquake3
|
||||||
Exec=ioquake3
|
Exec=ioquake3 --uri %u
|
||||||
Icon=quake3
|
Icon=quake3
|
||||||
Type=Application
|
Type=Application
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Encoding=UTF-8
|
Encoding=UTF-8
|
||||||
Categories=Game;ActionGame;
|
Categories=Game;ActionGame;
|
||||||
|
MimeType=x-scheme-handler/quake3;
|
||||||
X-SuSE-translate=false
|
X-SuSE-translate=false
|
||||||
|
|
Loading…
Reference in a new issue