From 5a0517c1b8b1fad47fae72992e599179edc7565d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 13 Jan 2026 08:25:42 +0500 Subject: [PATCH] engine: server: refactoring, better validation of cl_updaterate and rate values. Allow sv_min/maxupdaterate to be set to 0 for unlimited powah --- engine/server/server.h | 2 +- engine/server/sv_client.c | 65 +++++++++++++++++++++++++++++---------- engine/server/sv_frame.c | 3 +- engine/server/sv_main.c | 6 ++-- engine/server/sv_pmove.c | 13 +++++--- 5 files changed, 62 insertions(+), 27 deletions(-) diff --git a/engine/server/server.h b/engine/server/server.h index 80fa597a..be883051 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -233,7 +233,7 @@ typedef struct sv_client_s double next_messagetime; // time when we should send next world state update double next_checkpingtime; // time to send all players pings to client double next_sendinfotime; // time to send info about all players - double cl_updaterate; // client requested updaterate + double next_messageinterval; // update rate, clamped double timebase; // client timebase double connection_started; diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index a64e7d62..3af9f4c1 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -38,15 +38,13 @@ SV_GetPlayerCount */ void SV_GetPlayerCount( int *players, int *bots ) { - int i; - *players = 0; *bots = 0; if( !svs.clients ) return; - for( i = 0; i < svs.maxclients; i++ ) + for( int i = 0; i < svs.maxclients; i++ ) { if( svs.clients[i].state >= cs_connected ) { @@ -55,7 +53,6 @@ void SV_GetPlayerCount( int *players, int *bots ) else (*players)++; } - } } @@ -443,7 +440,7 @@ static void SV_ConnectClient( netadr_t from ) newcl->upstate = us_inactive; newcl->connection_started = host.realtime; - newcl->cl_updaterate = 0.05; // 20 fps as default + newcl->next_messageinterval = 0.05; // 20 fps as default newcl->delta_sequence = -1; // parse some info from the info strings (this can override cl_updaterate) @@ -451,7 +448,7 @@ static void SV_ConnectClient( netadr_t from ) SV_UserinfoChanged( newcl ); - newcl->next_messagetime = host.realtime + newcl->cl_updaterate; + newcl->next_messagetime = host.realtime + sv.frametime + newcl->next_messageinterval; // reset stats newcl->next_checkpingtime = -1.0; @@ -1774,6 +1771,40 @@ static qboolean SV_ShouldUpdateUserinfo( sv_client_t *cl ) return allow; } +static double SV_CheckUpdateRate( double rate ) +{ + if( sv_maxupdaterate.value ) + { + if( rate < 1.0f / sv_maxupdaterate.value ) + return 1.0f / sv_maxupdaterate.value; + } + + if( sv_minupdaterate.value ) + { + if( rate > 1.0f / sv_minupdaterate.value ) + return 1.0f / sv_minupdaterate.value; + } + + return rate; +} + +static double SV_CheckRate( double rate ) +{ + if( sv_maxrate.value ) + { + if( rate > sv_maxrate.value ) + return rate; + } + + if( sv_minrate.value ) + { + if( rate < sv_minrate.value ) + return rate; + } + + return rate; +} + /* ================= SV_UserinfoChanged @@ -1789,6 +1820,7 @@ static void SV_UserinfoChanged( sv_client_t *cl ) string name1, name2; sv_client_t *current; const char *val; + int ival; if( !COM_CheckString( cl->userinfo )) return; @@ -1852,9 +1884,10 @@ static void SV_UserinfoChanged( sv_client_t *cl ) // rate command val = Info_ValueForKey( cl->userinfo, "rate" ); - if( COM_CheckString( val ) ) - cl->netchan.rate = bound( sv_minrate.value, Q_atoi( val ), sv_maxrate.value ); - else cl->netchan.rate = DEFAULT_RATE; + cl->netchan.rate = Q_atoi( Info_ValueForKey( cl->userinfo, "rate" )); + if( cl->netchan.rate <= 0 ) + cl->netchan.rate = DEFAULT_RATE; + cl->netchan.rate = bound( MIN_RATE, cl->netchan.rate, MAX_RATE ); // movement prediction if( Q_atoi( Info_ValueForKey( cl->userinfo, "cl_nopred" ))) @@ -1871,13 +1904,13 @@ static void SV_UserinfoChanged( sv_client_t *cl ) SetBits( cl->flags, FCL_LOCAL_WEAPONS ); else ClearBits( cl->flags, FCL_LOCAL_WEAPONS ); - val = Info_ValueForKey( cl->userinfo, "cl_updaterate" ); + ival = Q_atoi( Info_ValueForKey( cl->userinfo, "cl_updaterate" )); - if( COM_CheckString( val )) - { - float rate = Q_atoi( val ); - cl->cl_updaterate = 1.0 / bound( sv_minupdaterate.value, rate, sv_maxupdaterate.value ); - } + if( ival <= 0 ) + ival = 20; // 20 fps as default + + cl->next_messageinterval = SV_CheckUpdateRate( 1.0 / ival ); + cl->netchan.rate = SV_CheckRate( cl->netchan.rate ); // call prog code to allow overrides svgame.dllFuncs.pfnClientUserInfoChanged( cl->edict, cl->userinfo ); @@ -3602,7 +3635,7 @@ void SV_ExecuteClientMessage( sv_client_t *cl, sizebuf_t *msg ) frame = &cl->frames[cl->netchan.incoming_acknowledged & SV_UPDATE_MASK]; // ping time doesn't factor in message interval, either - frame->ping_time = host.realtime - frame->senttime - cl->cl_updaterate; + frame->ping_time = host.realtime - frame->senttime - cl->next_messageinterval; // on first frame ( no senttime ) don't skew ping if( frame->senttime == 0.0f ) frame->ping_time = 0.0f; diff --git a/engine/server/sv_frame.c b/engine/server/sv_frame.c index 32c1797f..fd71183d 100644 --- a/engine/server/sv_frame.c +++ b/engine/server/sv_frame.c @@ -892,8 +892,7 @@ void SV_SendClientMessages( void ) // now that we were able to send, reset timer to point to next possible send time. // check here also because sv_max/minupdaterate could been changed in runtime - updaterate_time = bound( 1.0 / sv_maxupdaterate.value, cl->cl_updaterate, 1.0 / sv_minupdaterate.value ); - cl->next_messagetime = host.realtime + sv.frametime + updaterate_time; + cl->next_messagetime = host.realtime + sv.frametime + cl->next_messageinterval; ClearBits( cl->flags, FCL_SEND_NET_MESSAGE ); // NOTE: we should send frame even if server is not simulated to prevent overflow diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index b86a1b00..1596d2da 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -34,10 +34,10 @@ CVAR_DEFINE_AUTO( rcon_enable, "1", FCVAR_PROTECTED, "enable accepting remote co CVAR_DEFINE_AUTO( sv_cheats, "0", FCVAR_SERVER, "allow cheats on server" ); CVAR_DEFINE_AUTO( sv_instancedbaseline, "1", 0, "allow to use instanced baselines to saves network overhead" ); static CVAR_DEFINE_AUTO( sv_contact, "", FCVAR_ARCHIVE|FCVAR_SERVER, "server techincal support contact address or web-page" ); -CVAR_DEFINE_AUTO( sv_minupdaterate, "25.0", FCVAR_ARCHIVE, "minimal value for 'cl_updaterate' window" ); -CVAR_DEFINE_AUTO( sv_maxupdaterate, "60.0", FCVAR_ARCHIVE, "maximal value for 'cl_updaterate' window" ); +CVAR_DEFINE_AUTO( sv_minupdaterate, "10.0", FCVAR_ARCHIVE, "minimal value for 'cl_updaterate' window, 0 == unlimited" ); +CVAR_DEFINE_AUTO( sv_maxupdaterate, "60.0", FCVAR_ARCHIVE, "maximal value for 'cl_updaterate' window, 0 == unlimited" ); CVAR_DEFINE_AUTO( sv_minrate, "5000", FCVAR_SERVER, "min bandwidth rate allowed on server, 0 == unlimited" ); -CVAR_DEFINE_AUTO( sv_maxrate, "50000", FCVAR_SERVER, "max bandwidth rate allowed on server, 0 == unlimited" ); +CVAR_DEFINE_AUTO( sv_maxrate, "0", FCVAR_SERVER, "max bandwidth rate allowed on server, 0 == unlimited" ); // TODO: CVAR_DEFINE_AUTO( sv_logrelay, "0", FCVAR_ARCHIVE, "allow log messages from remote machines to be logged on this server" ); CVAR_DEFINE_AUTO( sv_newunit, "0", 0, "clear level-saves from previous SP game chapter to help keep .sav file size as minimum" ); CVAR_DEFINE_AUTO( sv_clienttrace, "1", FCVAR_SERVER, "0 = big box(Quake), 0.5 = halfsize, 1 = normal (100%), otherwise it's a scaling factor" ); diff --git a/engine/server/sv_pmove.c b/engine/server/sv_pmove.c index 8a0f26ef..4e1264ea 100644 --- a/engine/server/sv_pmove.c +++ b/engine/server/sv_pmove.c @@ -727,16 +727,19 @@ static void SV_SetupMoveInterpolant( sv_client_t *cl ) if( sv_maxunlag.value != 0.0f ) { - if (sv_maxunlag.value < 0.0f ) - Cvar_SetValue( "sv_maxunlag", 0.0f ); + if( sv_maxunlag.value < 0.0f ) + Cvar_DirectSetValue( &sv_maxunlag, 0.0f ); + latency = Q_min( latency, sv_maxunlag.value ); } lerp_msec = cl->lastcmd.lerp_msec * 0.001f; - if( lerp_msec > 0.1f ) lerp_msec = 0.1f; - if( lerp_msec < cl->cl_updaterate ) - lerp_msec = cl->cl_updaterate; + if( lerp_msec > 0.1f ) + lerp_msec = 0.1f; + + if( lerp_msec < cl->next_messageinterval ) + lerp_msec = cl->next_messageinterval; finalpush = ( host.realtime - latency - lerp_msec ) + sv_unlagpush.value; if( finalpush > host.realtime ) finalpush = host.realtime; // pushed too much ?