diff --git a/build/msvc_2019/game.vcxproj b/build/msvc_2019/game.vcxproj index b545c9a..8eed06d 100644 --- a/build/msvc_2019/game.vcxproj +++ b/build/msvc_2019/game.vcxproj @@ -201,12 +201,16 @@ + + + + @@ -249,6 +253,11 @@ + + + + + diff --git a/build/msvc_2019/game.vcxproj.filters b/build/msvc_2019/game.vcxproj.filters index 2c327f2..a2506f3 100644 --- a/build/msvc_2019/game.vcxproj.filters +++ b/build/msvc_2019/game.vcxproj.filters @@ -7,6 +7,12 @@ {9F35977C-8B6C-980D-3459-7E10206F140F} + + {81BBD40A-ED25-8981-3664-3A27A20D67D6} + + + {CF5F3109-BB43-F25E-24A4-ECB110A7DCE0} + @@ -78,6 +84,9 @@ game + + game + game @@ -96,6 +105,15 @@ game + + game\mods + + + game\mods + + + game\mods + game @@ -218,5 +236,20 @@ game + + game\mods\features + + + game\mods\features + + + game\mods + + + game\mods + + + game\mods + \ No newline at end of file diff --git a/build/qvm_build/build_game.bat b/build/qvm_build/build_game.bat index aca0e76..ded0c15 100644 --- a/build/qvm_build/build_game.bat +++ b/build/qvm_build/build_game.bat @@ -63,6 +63,7 @@ set src=game&set name=g_weapon&call :compile set src=game\mods&set name=g_mod_main&call :compile set src=game\mods&set name=g_mod_stubs&call :compile set src=game\mods&set name=g_mod_utils&call :compile +set src=game\mods\features&set name=feature_altswap_handler&call :compile set src=game\mods\features&set name=feature_player_move&call :compile set src=common&set name=logging&call :compile set src=common&set name=vm_extensions&call :compile diff --git a/code/game/g_active.c b/code/game/g_active.c index 17997df..23fefe0 100644 --- a/code/game/g_active.c +++ b/code/game/g_active.c @@ -1593,12 +1593,6 @@ LOGFUNCTION_VOID( ModFNDefault_PmoveInit, ( int clientNum, pmove_t *pmove ), ( c pmove->pointcontents = trap_PointContents; pmove->debugLevel = g_debugMove.integer; pmove->noFootsteps = ( g_dmflags.integer & DF_NO_FOOTSTEPS ) > 0; - - if ( client->ps.weapon >= 1 && client->ps.weapon < WP_NUM_WEAPONS ) { - if ( client->sess.altSwapFlags & ( 1 << ( client->ps.weapon - 1 ) ) ) { - pmove->altFireMode = ALTMODE_SWAPPED; - } - } } /* diff --git a/code/game/g_cmds.c b/code/game/g_cmds.c index 2a112e6..16452f7 100644 --- a/code/game/g_cmds.c +++ b/code/game/g_cmds.c @@ -1643,17 +1643,6 @@ void Cmd_SetViewpos_f( gentity_t *ent ) { TeleportPlayer( ent, origin, angles, TP_NORMAL ); } -/* -================= -Cmd_SetAltSwap_f -================= -*/ -void Cmd_SetAltSwap_f( gentity_t *ent ) { - char buffer[MAX_TOKEN_CHARS]; - trap_Argv( 1, buffer, sizeof( buffer ) ); - ent->client->sess.altSwapFlags = atoi( buffer ); -} - /* ================= @@ -1672,6 +1661,11 @@ void ClientCommand( int clientNum ) { trap_Argv( 0, cmd, sizeof( cmd ) ); + // Check if any mods have special handling of this command + if ( modfn.ModClientCommand( clientNum, cmd ) ) { + return; + } + if (Q_stricmp (cmd, "say") == 0) { Cmd_Say_f (ent, SAY_ALL, qfalse); return; @@ -1732,8 +1726,6 @@ void ClientCommand( int clientNum ) { Cmd_GameCommand_f( ent ); else if (Q_stricmp (cmd, "setviewpos") == 0) Cmd_SetViewpos_f( ent ); - else if (Q_stricmp (cmd, "setaltswap") == 0) - Cmd_SetAltSwap_f( ent ); else trap_SendServerCommand( clientNum, va("print \"unknown cmd %s\n\"", cmd ) ); } diff --git a/code/game/g_cvar_defs.h b/code/game/g_cvar_defs.h index f0314cc..533635b 100644 --- a/code/game/g_cvar_defs.h +++ b/code/game/g_cvar_defs.h @@ -84,5 +84,3 @@ CVAR_DEF( g_team_group_blue, "g_team_group_blue", "", CVAR_LATCH, qfalse ) // U CVAR_DEF( g_random_skin_limit, "g_random_skin_limit", "4", CVAR_ARCHIVE, qfalse ) CVAR_DEF( g_noJoinTimeout, "g_noJoinTimeout", "120", CVAR_ARCHIVE, qfalse ) CVAR_DEF( g_classChangeDebounceTime, "g_classChangeDebounceTime", "180", CVAR_ARCHIVE, qfalse ) - -CVAR_DEF( g_altSwapSupport, "g_altSwapSupport", "1", CVAR_ARCHIVE, qfalse ) diff --git a/code/game/g_local.h b/code/game/g_local.h index 40a9bc9..697fc8e 100644 --- a/code/game/g_local.h +++ b/code/game/g_local.h @@ -249,7 +249,6 @@ typedef struct { spectatorState_t spectatorState; int spectatorClient; // for chasecam and follow mode int wins, losses; // tournament stats - int altSwapFlags; // for alt-fire swapping } clientSession_t; #define MAX_VOTE_COUNT 3 @@ -362,6 +361,7 @@ typedef struct { // we changed gametype qboolean restarted; // waiting for a map_restart to fire + qboolean hasRestarted; // whether any map_restart has been done since map was loaded int numConnectedClients; int numNonSpectatorClients; // includes connecting clients diff --git a/code/game/g_main.c b/code/game/g_main.c index 03a03fd..e595071 100644 --- a/code/game/g_main.c +++ b/code/game/g_main.c @@ -277,10 +277,6 @@ void G_UpdateModConfigInfo( void ) { modfn.AddModConfigInfo( info ); - if ( g_altSwapSupport.integer ) { - Info_SetValueForKey( info, "altSwapSupport", "1" ); - } - if ( *info ) { trap_SetConfigstring( CS_MOD_CONFIG, buffer ); } else { @@ -289,17 +285,6 @@ void G_UpdateModConfigInfo( void ) { } } -/* -================= -G_UpdateModConfigCvar - -Called when a cvar linked to a mod config value has changed. -================= -*/ -static void G_UpdateModConfigCvar( trackedCvar_t *cv ) { - G_UpdateModConfigInfo(); -} - /* ================= G_UpdateNeedPass @@ -399,9 +384,6 @@ static void G_RegisterCvars( void ) { // configure g_needpass auto update G_RegisterCvarCallback( &g_password, G_UpdateNeedPass, qtrue ); - - // handle mod config cvar changes - G_RegisterCvarCallback( &g_altSwapSupport, G_UpdateModConfigCvar, qfalse ); } /* @@ -476,6 +458,7 @@ void G_InitGame( int levelTime, int randomSeed, int restart ) { level.time = levelTime; level.startTime = levelTime; level.restarted = restart; + level.hasRestarted = restart; level.snd_fry = G_SoundIndex("sound/player/fry.wav"); // FIXME standing in lava / slime diff --git a/code/game/g_session.c b/code/game/g_session.c index 9c7c19e..70219b6 100644 --- a/code/game/g_session.c +++ b/code/game/g_session.c @@ -71,15 +71,14 @@ void G_WriteClientSessionData( gclient_t *client ) { const char *var; info_string_t info; - s = va("%i %i %i %i %i %i %i %i", + s = va("%i %i %i %i %i %i %i", client->sess.sessionTeam, client->sess.sessionClass, client->sess.spectatorTime, client->sess.spectatorState, client->sess.spectatorClient, client->sess.wins, - client->sess.losses, - client->sess.altSwapFlags + client->sess.losses ); var = va( "session%i", clientNum ); @@ -128,15 +127,14 @@ void G_ReadSessionData( gclient_t *client ) { var = va( "session%i", clientNum ); trap_Cvar_VariableStringBuffer( var, s, sizeof(s) ); - sscanf( s, "%i %i %i %i %i %i %i %i", + sscanf( s, "%i %i %i %i %i %i %i", &client->sess.sessionTeam, &client->sess.sessionClass, &client->sess.spectatorTime, &client->sess.spectatorState, &client->sess.spectatorClient, &client->sess.wins, - &client->sess.losses, - &client->sess.altSwapFlags + &client->sess.losses ); // Call mod initialization diff --git a/code/game/mods/features/feature_altswap_handler.c b/code/game/mods/features/feature_altswap_handler.c new file mode 100644 index 0000000..c73bd5e --- /dev/null +++ b/code/game/mods/features/feature_altswap_handler.c @@ -0,0 +1,186 @@ +/* +* Alt Fire Swap Handler +* +* This module implements the "setAltSwap" command which is used by the cgame-based +* alt fire swapping system. +* +* This is mainly intended as a backup option for cases where neither server or client +* engine support for alt fire swapping is available. It is only enabled in local games +* or if sv_floodProtect is disabled on the server, because otherwise the flood protection +* could cause the command to be dropped leading to an inconsistent state. +*/ + +#include "mods/g_mod_local.h" + +#define PREFIX( x ) ModAltSwapHandler_##x +#define MOD_STATE PREFIX( state ) + +typedef struct { + int swapFlags; +} AltFireSwap_client_t; + +static struct { + qboolean enabled; + AltFireSwap_client_t clients[MAX_CLIENTS]; + + // For mod function stacking + ModFNType_InitClientSession Prev_InitClientSession; + ModFNType_GenerateClientSessionInfo Prev_GenerateClientSessionInfo; + ModFNType_ModClientCommand Prev_ModClientCommand; + ModFNType_PmoveInit Prev_PmoveInit; + ModFNType_AddModConfigInfo Prev_AddModConfigInfo; + ModFNType_PostRunFrame Prev_PostRunFrame; +} *MOD_STATE; + +#define DEDICATED_SERVER ( trap_Cvar_VariableIntegerValue( "dedicated" ) || !trap_Cvar_VariableIntegerValue( "cl_running" ) ) + +/* +================ +ModAltSwapHandler_CheckEnabled +================ +*/ +static void ModAltSwapHandler_CheckEnabled( void ) { + qboolean enabled = qtrue; + + // Disable swap support if flood protection is enabled because it can cause commands to be dropped + if ( trap_Cvar_VariableIntegerValue( "sv_floodProtect" ) && DEDICATED_SERVER ) { + enabled = qfalse; + } + + if ( enabled != MOD_STATE->enabled ) { + MOD_STATE->enabled = enabled; + G_UpdateModConfigInfo(); + if ( !enabled ) { + memset( MOD_STATE->clients, 0, sizeof( MOD_STATE->clients ) ); + } + } +} + +/* +================ +(ModFN) InitClientSession +================ +*/ +LOGFUNCTION_SVOID( PREFIX(InitClientSession), ( int clientNum, qboolean initialConnect, const info_string_t *info ), + ( clientNum, initialConnect, info ), "G_MODFN_INITCLIENTSESSION" ) { + AltFireSwap_client_t *modclient = &MOD_STATE->clients[clientNum]; + + MOD_STATE->Prev_InitClientSession( clientNum, initialConnect, info ); + memset( modclient, 0, sizeof( *modclient ) ); + + if ( !initialConnect && level.hasRestarted ) { + // Restore flags when coming back from a map restart + modclient->swapFlags = atoi ( Info_ValueForKey( info->s, "altSwapFlags" ) ); + } +} + +/* +================ +(ModFN) GenerateClientSessionInfo +================ +*/ +LOGFUNCTION_SVOID( PREFIX(GenerateClientSessionInfo), ( int clientNum, info_string_t *info ), + ( clientNum, info ), "G_MODFN_GENERATECLIENTSESSIONINFO" ) { + AltFireSwap_client_t *modclient = &MOD_STATE->clients[clientNum]; + + MOD_STATE->Prev_GenerateClientSessionInfo( clientNum, info ); + + if ( modclient->swapFlags ) { + Info_SetValueForKey_Big( info->s, "altSwapFlags", va( "%i", modclient->swapFlags ) ); + } +} + +/* +================ +(ModFN) ModClientCommand + +Handle setAltSwap command. +================ +*/ +LOGFUNCTION_SRET( qboolean, PREFIX(ModClientCommand), ( int clientNum, const char *cmd ), + ( clientNum, cmd ), "G_MODFN_MODCLIENTCOMMAND" ) { + if ( MOD_STATE->enabled && !Q_stricmp( cmd, "setAltSwap" ) ) { + AltFireSwap_client_t *modclient = &MOD_STATE->clients[clientNum]; + char buffer[32]; + trap_Argv( 1, buffer, sizeof( buffer ) ); + modclient->swapFlags = atoi( buffer ); + return qtrue; + } + + return MOD_STATE->Prev_ModClientCommand( clientNum, cmd ); +} + +/* +================ +(ModFN) PmoveInit +================ +*/ +LOGFUNCTION_SVOID( PREFIX(PmoveInit), ( int clientNum, pmove_t *pmove ), + ( clientNum, pmove ), "G_MODFN_PMOVEINIT" ) { + playerState_t *ps = &level.clients[clientNum].ps; + + MOD_STATE->Prev_PmoveInit( clientNum, pmove ); + + if ( MOD_STATE->enabled && ps->weapon >= 1 && ps->weapon < WP_NUM_WEAPONS ) { + AltFireSwap_client_t *modclient = &MOD_STATE->clients[clientNum]; + if ( modclient->swapFlags & ( 1 << ( ps->weapon - 1 ) ) ) { + pmove->altFireMode = ALTMODE_SWAPPED; + } + } +} + +/* +============== +(ModFN) AddModConfigInfo +============== +*/ +LOGFUNCTION_SVOID( PREFIX(AddModConfigInfo), ( char *info ), ( info ), "G_MODFN_ADDMODCONFIGINFO" ) { + if ( MOD_STATE->enabled ) { + Info_SetValueForKey( info, "altSwapSupport", "1" ); + } + + MOD_STATE->Prev_AddModConfigInfo( info ); +} + +/* +================ +(ModFN) PostRunFrame +================ +*/ +LOGFUNCTION_SVOID( PREFIX(PostRunFrame), ( void ), (), "G_MODFN_POSTRUNFRAME" ) { + MOD_STATE->Prev_PostRunFrame(); + ModAltSwapHandler_CheckEnabled(); +} + +/* +================ +ModAltSwapHandler_Init +================ +*/ + +#define INIT_FN_STACKABLE( name ) \ + MOD_STATE->Prev_##name = modfn.name; \ + modfn.name = PREFIX(name); + +#define INIT_FN_OVERRIDE( name ) \ + modfn.name = PREFIX(name); + +LOGFUNCTION_VOID( ModAltSwapHandler_Init, ( void ), (), "G_MOD_INIT" ) { + // Don't enable if server engine has its own alt swap handler + if ( VMExt_GVCommandInt( "sv_support_setAltSwap", 0 ) ) { + return; + } + + if ( !MOD_STATE ) { + MOD_STATE = G_Alloc( sizeof( *MOD_STATE ) ); + + INIT_FN_STACKABLE( InitClientSession ); + INIT_FN_STACKABLE( GenerateClientSessionInfo ); + INIT_FN_STACKABLE( ModClientCommand ); + INIT_FN_STACKABLE( PmoveInit ); + INIT_FN_STACKABLE( AddModConfigInfo ); + INIT_FN_STACKABLE( PostRunFrame ); + + ModAltSwapHandler_CheckEnabled(); + } +} diff --git a/code/game/mods/g_mod_defs.h b/code/game/mods/g_mod_defs.h index 7abde15..fa9763c 100644 --- a/code/game/mods/g_mod_defs.h +++ b/code/game/mods/g_mod_defs.h @@ -55,5 +55,8 @@ MOD_FUNCTION_DEF( PostPmoveActions, void, ( pmove_t *pmove, int clientNum, int o // misc ////////////////////////// +// Allows mods to handle client commands. Returns qtrue to suspend normal handling of command. +MOD_FUNCTION_DEF( ModClientCommand, qboolean, ( int clientNum, const char *cmd ) ) + // Allows mods to add values to the mod config configstring. MOD_FUNCTION_DEF( AddModConfigInfo, void, ( char *info ) ) diff --git a/code/game/mods/g_mod_local.h b/code/game/mods/g_mod_local.h index bf04de5..825cfb3 100644 --- a/code/game/mods/g_mod_local.h +++ b/code/game/mods/g_mod_local.h @@ -11,3 +11,4 @@ int G_ModUtils_GetLatchedValue( const char *cvar_name, const char *default_value // Feature Initialization void ModPlayerMove_Init( void ); +void ModAltSwapHandler_Init( void ); diff --git a/code/game/mods/g_mod_main.c b/code/game/mods/g_mod_main.c index 4233add..4198967 100644 --- a/code/game/mods/g_mod_main.c +++ b/code/game/mods/g_mod_main.c @@ -30,5 +30,6 @@ LOGFUNCTION_VOID( G_ModsInit, ( void ), (), "G_MOD_INIT" ) { if ( modsEnabled >= 2 ) { // Default mods ModPlayerMove_Init(); + ModAltSwapHandler_Init(); } } diff --git a/code/game/mods/g_mod_stubs.c b/code/game/mods/g_mod_stubs.c index 74bcdf3..7512865 100644 --- a/code/game/mods/g_mod_stubs.c +++ b/code/game/mods/g_mod_stubs.c @@ -20,6 +20,11 @@ LOGFUNCTION_RET( int, ModFNDefault_AdjustPmoveConstant, ( pmoveConstant_t pmcTyp return defaultValue; } +LOGFUNCTION_RET( qboolean, ModFNDefault_ModClientCommand, ( int clientNum, const char *cmd ), + ( clientNum, cmd ), "G_MODFN_MODCLIENTCOMMAND" ) { + return qfalse; +} + LOGFUNCTION_VOID( ModFNDefault_AddModConfigInfo, ( char *info ), ( info ), "G_MODFN_ADDMODCONFIGINFO" ) { }