From 3a58ad52e63635c93e95955f984fd6052156e56b Mon Sep 17 00:00:00 2001 From: bones_was_here Date: Tue, 18 Jul 2023 01:26:12 +1000 Subject: [PATCH] dedicated server: don't register client-only cvars Fixes random client cvars like r_trippy and cl_netpacketloss_send appearing in the server. Adds a warn (instead of crashing) for if we try to set an unregistered engine cvar somewhere. --- cmd.h | 3 ++- cvar.c | 24 +++++++++++++++++++----- fs.c | 17 ++++++++++------- host.c | 4 ++++ sv_main.c | 4 ++-- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/cmd.h b/cmd.h index bee0a1f3..fddd2a08 100644 --- a/cmd.h +++ b/cmd.h @@ -59,8 +59,9 @@ struct cmd_state_s; #define CF_PRIVATE (1<<11) // cvar should not be $ expanded or sent to the server under any circumstances (rcon_password, etc) #define CF_MAXFLAGSVAL ((1<<12) - 1) // used to determine if flags is valid // for internal use only! +#define CF_REGISTERED (1<<29) // created by Cvar_RegisterVariable() #define CF_DEFAULTSET (1<<30) -#define CF_ALLOCATED (1<<31) +#define CF_ALLOCATED (1<<31) // created by Cvar_Get() (console or QC) #define CF_SHARED 3 diff --git a/cvar.c b/cvar.c index 7936c923..98f11e39 100644 --- a/cvar.c +++ b/cvar.c @@ -437,6 +437,12 @@ void Cvar_SetQuick (cvar_t *var, const char *value) return; } + if (!(var->flags & CF_REGISTERED) && !(var->flags & CF_ALLOCATED)) + { + Con_Printf(CON_WARN "Warning: Cvar_SetQuick() cannot set unregistered cvar \"%s\"\n", var->name); + return; // setting an unregistered engine cvar crashes + } + if (developer_extra.integer) Con_DPrintf("Cvar_SetQuick({\"%s\", \"%s\", %i, \"%s\"}, \"%s\");\n", var->name, var->string, var->flags, var->defstring, value); @@ -498,22 +504,25 @@ void Cvar_RegisterVirtual(cvar_t *variable, const char *name ) cvar_hash_t *hash; int hashindex; + if (cls.state == ca_dedicated && !(variable->flags & CF_SERVER)) + return; + if(!*name) { - Con_Printf("Cvar_RegisterVirtual: invalid virtual cvar name\n"); + Con_Printf(CON_WARN "Cvar_RegisterVirtual: invalid virtual cvar name\n"); return; } // check for overlap with a command if (Cmd_Exists(cmd_local, name)) { - Con_Printf("Cvar_RegisterVirtual: %s is a command\n", name); + Con_Printf(CON_WARN "Cvar_RegisterVirtual: %s is a command\n", name); return; } if(Cvar_FindVar(&cvars_all, name, 0)) { - Con_Printf("Cvar_RegisterVirtual: %s is a cvar\n", name); + Con_Printf(CON_WARN "Cvar_RegisterVirtual: %s is a cvar\n", name); return; } @@ -579,7 +588,9 @@ void Cvar_RegisterVariable (cvar_t *variable) switch (variable->flags & (CF_CLIENT | CF_SERVER)) { - case CF_CLIENT: + case CF_CLIENT: // client-only cvar + if (cls.state == ca_dedicated) + return; case CF_SERVER: case CF_CLIENT | CF_SERVER: cvars = &cvars_all; @@ -607,7 +618,7 @@ void Cvar_RegisterVariable (cvar_t *variable) // (because the engine directly accesses fixed variables) // NOTE: this isn't actually used currently // (all cvars are registered before config parsing) - variable->flags |= (cvar->flags & ~CF_ALLOCATED); + variable->flags &= ~CF_ALLOCATED; // cvar->string is now owned by variable instead variable->string = cvar->string; variable->defstring = cvar->defstring; @@ -662,6 +673,9 @@ void Cvar_RegisterVariable (cvar_t *variable) for (i = 0;i < PRVM_PROG_MAX;i++) variable->globaldefindex[i] = -1; + // Safe for Cvar_SetQuick() + variable->flags |= CF_REGISTERED; + Cvar_Link(variable, cvars); } diff --git a/fs.c b/fs.c index 25789e02..56ce8cac 100644 --- a/fs.c +++ b/fs.c @@ -1513,13 +1513,16 @@ void FS_Rescan (void) // add back the selfpack as new first item FS_AddSelfPack(); - // set the default screenshot name to either the mod name or the - // gamemode screenshot name - if (strcmp(com_modname, gamedirname1)) - Cvar_SetQuick (&scr_screenshot_name, com_modname); - else - Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname); - + if (cls.state != ca_dedicated) + { + // set the default screenshot name to either the mod name or the + // gamemode screenshot name + if (strcmp(com_modname, gamedirname1)) + Cvar_SetQuick (&scr_screenshot_name, com_modname); + else + Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname); + } + if((i = Sys_CheckParm("-modname")) && i < sys.argc - 1) strlcpy(com_modname, sys.argv[i+1], sizeof(com_modname)); diff --git a/host.c b/host.c index e34f712b..8b64f9df 100644 --- a/host.c +++ b/host.c @@ -424,6 +424,10 @@ static void Host_Init (void) if (Sys_CheckParm("-nostdout")) sys_nostdout = 1; + // -dedicated is checked in SV_ServerOptions() but that's too late for Cvar_RegisterVariable() to skip all the client-only cvars + if (Sys_CheckParm ("-dedicated") || !cl_available) + cls.state = ca_dedicated; + // initialize console command/cvar/alias/command execution systems Cmd_Init(); diff --git a/sv_main.c b/sv_main.c index 16fd612b..ac35d949 100644 --- a/sv_main.c +++ b/sv_main.c @@ -452,7 +452,6 @@ static void SV_ServerOptions (void) i = Sys_CheckParm ("-dedicated"); if (i || !cl_available) { - cls.state = ca_dedicated; // check for -dedicated specifying how many players if (i && i + 1 < sys.argc && atoi (sys.argv[i+1]) >= 1) svs.maxclients = atoi (sys.argv[i+1]); @@ -2604,7 +2603,8 @@ double SV_Frame(double time) { advancetime = sys_ticrate.value; // listen servers can run multiple server frames per client frame - framelimit = cl_maxphysicsframesperserverframe.integer; + if (cl_maxphysicsframesperserverframe.integer > 0) + framelimit = cl_maxphysicsframesperserverframe.integer; aborttime = Sys_DirtyTime() + 0.1; } -- 2.39.2