engine: server: refactoring, better validation of cl_updaterate and rate values. Allow sv_min/maxupdaterate to be set to 0 for unlimited powah

This commit is contained in:
Alibek Omarov
2026-01-13 08:25:42 +05:00
parent 5ed1bca57f
commit 5a0517c1b8
5 changed files with 62 additions and 27 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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" );

View File

@@ -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 ?