From 69204aec07fb1e03fd6bf976d1d6511a691fbc86 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 28 Sep 2025 22:31:46 +0500 Subject: [PATCH] engine: client: synchronize sv_cheats state with the server --- engine/client/cl_main.c | 46 +++++++++++++++++++++++++------------ engine/client/cl_parse.c | 4 ++++ engine/client/cl_parse_gs.c | 11 ++------- engine/client/client.h | 5 ++++ 4 files changed, 42 insertions(+), 24 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index fac7ff7c..1ec33bc6 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -174,6 +174,22 @@ connprotocol_t CL_Protocol( void ) return cls.legacymode; } +void CL_SetCheatState( qboolean multiplayer, qboolean allow_cheats ) +{ + if( NET_NetadrType( &cls.netchan.remote_address ) == NA_LOOPBACK ) + return; + + if( allow_cheats ) + { + Cvar_FullSet( "sv_cheats", "1", FCVAR_READ_ONLY | FCVAR_SERVER ); + } + else + { + Cvar_FullSet( "sv_cheats", "0", FCVAR_READ_ONLY | FCVAR_SERVER ); + Cvar_SetCheatState(); + } +} + /* =============== CL_CheckClientState @@ -1072,13 +1088,9 @@ static void CL_SendConnectPacket( connprotocol_t proto, int challenge ) input_devices = IN_CollectInputDevices(); IN_LockInputDevices( adrtype != NA_LOOPBACK ? true : false ); - // GoldSrc doesn't need sv_cheats set to 0, it's handled by svc_goldsrc_sendextrainfo - // it also doesn't need useragent string + // GoldSrc doesn't need useragent string if( adrtype != NA_LOOPBACK && proto != PROTO_GOLDSRC ) { - Cvar_SetCheatState(); - Cvar_FullSet( "sv_cheats", "0", FCVAR_READ_ONLY | FCVAR_SERVER ); - Info_SetValueForKeyf( protinfo, "d", sizeof( protinfo ), "%d", input_devices ); Info_SetValueForKey( protinfo, "v", XASH_VERSION, sizeof( protinfo ) ); Info_SetValueForKeyf( protinfo, "b", sizeof( protinfo ), "%d", Q_buildnum( )); @@ -2338,16 +2350,24 @@ static void CL_ClientConnect( connprotocol_t proto, const char *c, netadr_t from if( Q_strcmp( c, S2C_GOLDSRC_CONNECTION )) { Con_DPrintf( S_ERROR "GoldSrc client connect expected but wasn't received, ignored\n"); + CL_Disconnect_f(); return; } - if( Cmd_Argc() > 4 ) - cls.build_num = Q_atoi( Cmd_Argv( 4 )); + cls.build_num = Q_atoi( Cmd_Argv( 4 )); + cls.allow_cheats = false; // set by svc_goldsrc_sendextrainfo } - else if( !Q_strcmp( c, S2C_GOLDSRC_CONNECTION )) + else { - Con_DPrintf( S_ERROR "GoldSrc client connect received but wasn't expected, ignored\n"); - return; + if( Q_strcmp( c, S2C_CONNECTION )) + { + Con_DPrintf( S_ERROR "Xash3D client connect expected but wasn't received, ignored\n"); + CL_Disconnect_f(); + return; + } + + cls.build_num = 0; // not used in Xash3D protocols + cls.allow_cheats = Q_atoi( Info_ValueForKey( Cmd_Argv( 1 ), "cheats" )); } CL_Reconnect( true ); @@ -2753,11 +2773,7 @@ static void CL_ReadPackets( void ) CL_ReadNetMessage(); CL_ApplyAddAngle(); -#if 0 - // keep cheat cvars are unchanged - if( cl.maxclients > 1 && cls.state == ca_active && !host_developer.value ) - Cvar_SetCheatState(); -#endif + // hot precache and downloading resources if( cls.signon == SIGNONS && cl.lastresourcecheck < host.realtime ) { diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 1997dd93..755cf1ac 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -1003,6 +1003,10 @@ void CL_ParseServerData( sizebuf_t *msg, connprotocol_t proto ) } else Cvar_DirectSet( &r_decals, NULL ); + // for GoldSrc, it's handled by svc_goldsrc_sendextrainfo + if( proto != PROTO_GOLDSRC ) + CL_SetCheatState( cl.maxclients > 1, cls.allow_cheats ); + // set the background state if( cls.demoplayback && ( cls.demonum != -1 )) cl.background = true; diff --git a/engine/client/cl_parse_gs.c b/engine/client/cl_parse_gs.c index 5bfce813..4d9903ae 100644 --- a/engine/client/cl_parse_gs.c +++ b/engine/client/cl_parse_gs.c @@ -31,15 +31,8 @@ static void CL_ParseExtraInfo( sizebuf_t *msg ) if( COM_CheckStringEmpty( clientfallback )) Con_Reportf( S_ERROR "%s: TODO: add fallback directory %s!\n", __func__, clientfallback ); - if( MSG_ReadByte( msg )) - { - Cvar_FullSet( "sv_cheats", "1", FCVAR_READ_ONLY | FCVAR_SERVER ); - } - else - { - Cvar_SetCheatState(); - Cvar_FullSet( "sv_cheats", "0", FCVAR_READ_ONLY | FCVAR_SERVER ); - } + cls.allow_cheats = MSG_ReadByte( msg ) ? true : false; + CL_SetCheatState( cl.maxclients > 1, cls.allow_cheats ); } static void CL_ParseNewMovevars( sizebuf_t *msg ) diff --git a/engine/client/client.h b/engine/client/client.h index 4ba3a969..5750aea4 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -640,6 +640,10 @@ typedef struct // server's build number (might be zero) int build_num; uint8_t steamid[8]; + + // whether server allows cheats or not + // set differently depending on protocol and extensions + qboolean allow_cheats; } client_static_t; #ifdef __cplusplus @@ -763,6 +767,7 @@ void CL_UpdateFrameLerp( void ); int CL_IsDevOverviewMode( void ); void CL_SignonReply( connprotocol_t proto ); void CL_ClearState( void ); +void CL_SetCheatState( qboolean multiplayer, qboolean allow_cheats ); // // cl_demo.c