diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1e8755ab..393b88fd 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -4,9 +4,9 @@ on: [push, pull_request]
jobs:
linux:
name: Linux
- runs-on: ubuntu-18.04
+ runs-on: ubuntu-20.04
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Install Dependencies
run: |
sudo apt-get update
@@ -15,7 +15,7 @@ jobs:
run: make release
env:
ARCHIVE: 1
- - uses: actions/upload-artifact@v2
+ - uses: actions/upload-artifact@v3
with:
name: Linux
path: build/*.zip
@@ -23,27 +23,27 @@ jobs:
name: Windows
runs-on: windows-2019
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Compile
run: |
choco install zip
make release
env:
ARCHIVE: 1
- - uses: actions/upload-artifact@v2
+ - uses: actions/upload-artifact@v3
with:
name: Windows
path: build/*.zip
macos:
name: macOS
- runs-on: macos-10.15
+ runs-on: macos-11
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Compile
run: make release
env:
ARCHIVE: 1
- - uses: actions/upload-artifact@v2
+ - uses: actions/upload-artifact@v3
with:
name: macOS
path: build/*.zip
diff --git a/SECURITY.md b/SECURITY.md
index 449b6607..8732e8b1 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -4,4 +4,7 @@ We take security very seriously at ioquake3. We welcome any peer review of our 1
### Where should I report security issues?
In order to give the community time to respond and upgrade we strongly urge you report all security issues privately.
-Please contact zachary@ioquake.org directly to provide details and repro steps and we will respond ASAP.
+Please e-mail zachary@ioquake.org directly to provide details and repro steps and we will respond as soon as possible, but please note:
+
+### This is an entirely free software project without much in the way of external funding or sponsorships.
+### We cannot guarantee quick responses but we very much appreciate your discretion when reporting security vulnerabilities.
diff --git a/code/qcommon/q_shared.h b/code/qcommon/q_shared.h
index b1dfc5cb..ed03e96b 100644
--- a/code/qcommon/q_shared.h
+++ b/code/qcommon/q_shared.h
@@ -43,6 +43,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define CINEMATICS_LOGO "foologo.roq"
#define CINEMATICS_INTRO "intro.roq"
// #define LEGACY_PROTOCOL // You probably don't need this for your standalone game
+// #define PROTOCOL_HANDLER "foobar"
#else
#define PRODUCT_NAME "Lilium Arena Classic"
#define BASEGAME "baseq3"
@@ -62,6 +63,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define CINEMATICS_LOGO "idlogo.RoQ"
#define CINEMATICS_INTRO "intro.RoQ"
#define LEGACY_PROTOCOL
+ #define PROTOCOL_HANDLER "quake3"
#endif
// Heartbeat for dpmaster protocol. You shouldn't change this unless you know what you're doing
diff --git a/code/sdl/sdl_input.c b/code/sdl/sdl_input.c
index 390f7b3b..1660cf75 100644
--- a/code/sdl/sdl_input.c
+++ b/code/sdl/sdl_input.c
@@ -1168,6 +1168,28 @@ static void IN_ProcessEvents( void )
}
break;
+#if defined(PROTOCOL_HANDLER) && defined(__APPLE__)
+ case SDL_DROPFILE:
+ {
+ char *filename = e.drop.file;
+
+ // Handle macOS open URL event. URL protocol scheme must be set in Info.plist.
+ if( !Q_strncmp( filename, PROTOCOL_HANDLER ":", strlen( PROTOCOL_HANDLER ":" ) ) )
+ {
+ char *protocolCommand = Sys_ParseProtocolUri( filename );
+
+ if( protocolCommand )
+ {
+ Cbuf_ExecuteText( EXEC_APPEND, va( "%s\n", protocolCommand ) );
+ free( protocolCommand );
+ }
+ }
+
+ SDL_free( filename );
+ }
+ break;
+#endif
+
default:
break;
}
@@ -1250,6 +1272,10 @@ void IN_Init( void *windowData )
in_joystick = Cvar_Get( "in_joystick", "0", CVAR_ARCHIVE|CVAR_LATCH );
in_joystickThreshold = Cvar_Get( "joy_threshold", "0.15", CVAR_ARCHIVE );
+#if defined(PROTOCOL_HANDLER) && defined(__APPLE__)
+ SDL_EventState( SDL_DROPFILE, SDL_ENABLE );
+#endif
+
SDL_StartTextInput( );
mouseAvailable = ( in_mouse->value != 0 );
diff --git a/code/server/sv_client.c b/code/server/sv_client.c
index 6b8d4444..90ddcee1 100644
--- a/code/server/sv_client.c
+++ b/code/server/sv_client.c
@@ -2031,7 +2031,7 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) {
// NOTE: when the client message is fux0red the acknowledgement numbers
// can be out of range, this could cause the server to send thousands of server
// commands which the server thinks are not yet acknowledged in SV_UpdateServerCommandsToClient
- if (cl->reliableAcknowledge < cl->reliableSequence - MAX_RELIABLE_COMMANDS) {
+ if ((cl->reliableSequence - cl->reliableAcknowledge >= MAX_RELIABLE_COMMANDS) || (cl->reliableSequence - cl->reliableAcknowledge < 0)) {
// usually only hackers create messages like this
// it is more annoying for them to let them hanging
#ifndef NDEBUG
diff --git a/code/sys/sys_local.h b/code/sys/sys_local.h
index cd7fbe7f..248a51cd 100644
--- a/code/sys/sys_local.h
+++ b/code/sys/sys_local.h
@@ -64,3 +64,7 @@ void Sys_AnsiColorPrint( const char *msg );
int Sys_PID( void );
qboolean Sys_PIDIsRunning( int pid );
+
+#ifdef PROTOCOL_HANDLER
+char *Sys_ParseProtocolUri( const char *uri );
+#endif
diff --git a/code/sys/sys_main.c b/code/sys/sys_main.c
index ae46c705..0113920b 100644
--- a/code/sys/sys_main.c
+++ b/code/sys/sys_main.c
@@ -642,6 +642,80 @@ void Sys_ParseArgs( int argc, char **argv )
}
}
+#ifdef PROTOCOL_HANDLER
+/*
+=================
+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
# ifdef __APPLE__
# define DEFAULT_BASEDIR Sys_StripAppBundle(Sys_BinaryPath())
@@ -690,6 +764,9 @@ int main( int argc, char **argv )
{
int i;
char commandLine[ MAX_STRING_CHARS ] = { 0 };
+#ifdef PROTOCOL_HANDLER
+ char *protocolCommand = NULL;
+#endif
extern void Sys_LaunchAutoupdater(int argc, char **argv);
Sys_LaunchAutoupdater(argc, argv);
@@ -740,7 +817,22 @@ int main( int argc, char **argv )
// Concatenate the command line for passing to Com_Init
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)
Q_strcat( commandLine, sizeof( commandLine ), "\"" );
@@ -752,6 +844,15 @@ int main( int argc, char **argv )
Q_strcat( commandLine, sizeof( commandLine ), " " );
}
+#ifdef PROTOCOL_HANDLER
+ if ( protocolCommand != NULL )
+ {
+ Q_strcat( commandLine, sizeof( commandLine ), "+" );
+ Q_strcat( commandLine, sizeof( commandLine ), protocolCommand );
+ free( protocolCommand );
+ }
+#endif
+
CON_Init( );
Com_Init( commandLine );
NET_Init( );
diff --git a/make-macosx-app.sh b/make-macosx-app.sh
index 37eae3af..d8d8106c 100755
--- a/make-macosx-app.sh
+++ b/make-macosx-app.sh
@@ -197,6 +197,7 @@ WRAPPER_NAME="${PRODUCT_NAME}.${WRAPPER_EXTENSION}"
CONTENTS_FOLDER_PATH="${WRAPPER_NAME}/Contents"
UNLOCALIZED_RESOURCES_FOLDER_PATH="${CONTENTS_FOLDER_PATH}/Resources"
EXECUTABLE_FOLDER_PATH="${CONTENTS_FOLDER_PATH}/MacOS"
+PROTOCOL_HANDLER="quake3"
# loop through the architectures to build string lists for each universal binary
for ARCH in $SEARCH_ARCHS; do
@@ -380,6 +381,21 @@ if [ -n "${MACOSX_DEPLOYMENT_TARGET_PPC}" ] || [ -n "${MACOSX_DEPLOYMENT_TARGET_
"
fi
+ if [ -n "${PROTOCOL_HANDLER}" ]; then
+ PLIST="${PLIST}
+ CFBundleURLTypes
+
+
+ CFBundleURLName
+ ${PRODUCT_NAME}
+ CFBundleURLSchemes
+
+ ${PROTOCOL_HANDLER}
+
+
+ "
+ fi
+
PLIST="${PLIST}
NSHumanReadableCopyright
${PRODUCT_NAME} Copyright © 1999-2005 id Software, 2005-2018 ioquake3 contributors.
diff --git a/misc/nsis/ioquake3.nsi.in b/misc/nsis/ioquake3.nsi.in
index de029e64..7e2b07df 100644
--- a/misc/nsis/ioquake3.nsi.in
+++ b/misc/nsis/ioquake3.nsi.in
@@ -45,7 +45,7 @@ OutFile "ioquake3-XXXVERSIONXXX-XXXRELEASEXXX.x86.exe"
!insertmacro MULTIUSER_PAGE_INSTALLMODE
;!insertmacro MUI_PAGE_LICENSE "../../COPYING.txt"
-!define MUI_COMPONENTSPAGE_NODESC
+!define MUI_COMPONENTSPAGE_SMALLDESC
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
@@ -71,7 +71,7 @@ Function un.onInit
FunctionEnd
; The stuff to install
-Section "ioquake3 (required)"
+Section "ioquake3 (required)" ioquake3
SectionIn RO
@@ -124,7 +124,7 @@ Section "ioquake3 (required)"
SectionEnd
; Optional section (can be disabled by the user)
-Section "Start Menu Shortcuts"
+Section "Start Menu Shortcuts" StartMenuShortcuts
CreateDirectory "$SMPROGRAMS\ioquake3"
CreateShortCut "$SMPROGRAMS\ioquake3\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0
@@ -132,7 +132,17 @@ Section "Start Menu Shortcuts"
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
@@ -141,7 +151,7 @@ Section "SDL2.dll"
SectionEnd
!ifdef USE_OPENAL_DLOPEN
-Section "OpenAL-Soft library"
+Section "OpenAL-Soft library" OpenAL
SetOutPath $INSTDIR
@@ -151,7 +161,7 @@ SectionEnd
!endif
!ifdef USE_CURL_DLOPEN
-Section "libcurl"
+Section "libcurl" libcurl
SetOutPath $INSTDIR
@@ -169,6 +179,7 @@ Section "Uninstall"
; Remove registry keys
DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\ioquake3"
DeleteRegKey SHCTX "Software\ioquake3"
+ DeleteRegKey SHCTX "Software\Classes\quake3"
; Remove files and uninstaller
Delete $INSTDIR\baseq3\cgamex86.dll
@@ -220,3 +231,16 @@ Section "Uninstall"
RMDir "$INSTDIR"
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
diff --git a/misc/setup/ioquake3.desktop b/misc/setup/ioquake3.desktop
index 0ccff3ad..644d424c 100644
--- a/misc/setup/ioquake3.desktop
+++ b/misc/setup/ioquake3.desktop
@@ -1,9 +1,10 @@
[Desktop Entry]
Name=ioquake3
-Exec=ioquake3
+Exec=ioquake3 --uri %u
Icon=quake3
Type=Application
Terminal=false
Encoding=UTF-8
Categories=Game;ActionGame;
+MimeType=x-scheme-handler/quake3;
X-SuSE-translate=false