From 6abbbb7bc3fd79fd6f465699602865152f2c3855 Mon Sep 17 00:00:00 2001 From: WofWca Date: Thu, 23 Jul 2026 23:19:42 +0400 Subject: [PATCH] feat: add a way to add CVAR descriptions in `game` This is a follow-up to a6a90c7fa7d933a7ec7b098607baaa890bee0005 (https://github.com/ec-/baseq3a/pull/83). I let AI do this since this is a trivial boilerplate change (but I of course reviewed and tested it (only the QVM build though, not DLL), and adjusted some whitespace stuff). --- code/game/g_cvar.h | 124 ++++++++++++++++++++--------------------- code/game/g_local.h | 5 +- code/game/g_main.c | 16 ++++++ code/game/g_syscalls.c | 4 ++ 4 files changed, 86 insertions(+), 63 deletions(-) diff --git a/code/game/g_cvar.h b/code/game/g_cvar.h index c9a8ad9b..1b677c60 100644 --- a/code/game/g_cvar.h +++ b/code/game/g_cvar.h @@ -1,96 +1,96 @@ #ifdef EXTERN_G_CVAR - #define G_CVAR( vmCvar, cvarName, defaultString, cvarFlags, modificationCount, trackChange, teamShader ) extern vmCvar_t vmCvar; + #define G_CVAR( vmCvar, cvarName, defaultString, cvarFlags, modificationCount, trackChange, teamShader, description ) extern vmCvar_t vmCvar; #endif #ifdef DECLARE_G_CVAR - #define G_CVAR( vmCvar, cvarName, defaultString, cvarFlags, modificationCount, trackChange, teamShader ) vmCvar_t vmCvar; + #define G_CVAR( vmCvar, cvarName, defaultString, cvarFlags, modificationCount, trackChange, teamShader, description ) vmCvar_t vmCvar; #endif #ifdef G_CVAR_LIST - #define G_CVAR( vmCvar, cvarName, defaultString, cvarFlags, modificationCount, trackChange, teamShader ) { & vmCvar, cvarName, defaultString, cvarFlags, modificationCount, trackChange, teamShader }, + #define G_CVAR( vmCvar, cvarName, defaultString, cvarFlags, modificationCount, trackChange, teamShader, description ) { & vmCvar, cvarName, defaultString, cvarFlags, modificationCount, trackChange, teamShader, description }, #endif // don't override the cheat state set by the system -G_CVAR( g_cheats, "sv_cheats", "", 0, 0, qfalse, qfalse ) +G_CVAR( g_cheats, "sv_cheats", "", 0, 0, qfalse, qfalse, NULL ) -//G_CVAR( g_restarted, "g_restarted", "0", CVAR_ROM, 0, qfalse, qfalse ) -G_CVAR( g_mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse, qfalse ) -G_CVAR( sv_fps, "sv_fps", "30", CVAR_ARCHIVE, 0, qfalse, qfalse ) +//G_CVAR( g_restarted, "g_restarted", "0", CVAR_ROM, 0, qfalse, qfalse, NULL ) +G_CVAR( g_mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse, qfalse, NULL ) +G_CVAR( sv_fps, "sv_fps", "30", CVAR_ARCHIVE, 0, qfalse, qfalse, NULL ) // latched vars -G_CVAR( g_gametype, "g_gametype", "0", CVAR_SERVERINFO | CVAR_USERINFO | CVAR_LATCH, 0, qfalse, qfalse ) +G_CVAR( g_gametype, "g_gametype", "0", CVAR_SERVERINFO | CVAR_USERINFO | CVAR_LATCH, 0, qfalse, qfalse, NULL ) -G_CVAR( g_maxclients, "sv_maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qfalse, qfalse ) // allow this many total, including spectators -G_CVAR( g_maxGameClients, "g_maxGameClients", "0", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qfalse, qfalse ) // allow this many active +G_CVAR( g_maxclients, "sv_maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qfalse, qfalse, NULL ) // allow this many total, including spectators +G_CVAR( g_maxGameClients, "g_maxGameClients", "0", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qfalse, qfalse, NULL ) // allow this many active // change anytime vars -G_CVAR( g_dmflags, "dmflags", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue, qfalse ) -G_CVAR( g_fraglimit, "fraglimit", "20", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue, qfalse ) -G_CVAR( g_timelimit, "timelimit", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue, qfalse ) -G_CVAR( g_capturelimit, "capturelimit", "8", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue, qfalse ) +G_CVAR( g_dmflags, "dmflags", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue, qfalse, NULL ) +G_CVAR( g_fraglimit, "fraglimit", "20", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue, qfalse, NULL ) +G_CVAR( g_timelimit, "timelimit", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue, qfalse, NULL ) +G_CVAR( g_capturelimit, "capturelimit", "8", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue, qfalse, NULL ) -G_CVAR( g_synchronousClients, "g_synchronousClients", "0", CVAR_SYSTEMINFO, 0, qfalse, qfalse ) +G_CVAR( g_synchronousClients, "g_synchronousClients", "0", CVAR_SYSTEMINFO, 0, qfalse, qfalse, NULL ) -G_CVAR( g_friendlyFire, "g_friendlyFire", "0", CVAR_ARCHIVE, 0, qtrue, qfalse ) +G_CVAR( g_friendlyFire, "g_friendlyFire", "0", CVAR_ARCHIVE, 0, qtrue, qfalse, NULL ) -G_CVAR( g_autoJoin, "g_autoJoin", "1", CVAR_ARCHIVE, 0, qfalse, qfalse ) -G_CVAR( g_teamForceBalance, "g_teamForceBalance", "0", CVAR_ARCHIVE, 0, qfalse, qfalse ) +G_CVAR( g_autoJoin, "g_autoJoin", "1", CVAR_ARCHIVE, 0, qfalse, qfalse, NULL ) +G_CVAR( g_teamForceBalance, "g_teamForceBalance", "0", CVAR_ARCHIVE, 0, qfalse, qfalse, NULL ) -G_CVAR( g_warmup, "g_warmup", "20", CVAR_ARCHIVE, 0, qtrue, qfalse ) -G_CVAR( g_log, "g_log", "games.log", CVAR_ARCHIVE, 0, qfalse, qfalse ) -G_CVAR( g_logSync, "g_logSync", "0", CVAR_ARCHIVE, 0, qfalse, qfalse ) +G_CVAR( g_warmup, "g_warmup", "20", CVAR_ARCHIVE, 0, qtrue, qfalse, NULL ) +G_CVAR( g_log, "g_log", "games.log", CVAR_ARCHIVE, 0, qfalse, qfalse, NULL ) +G_CVAR( g_logSync, "g_logSync", "0", CVAR_ARCHIVE, 0, qfalse, qfalse, NULL ) -G_CVAR( g_password, "g_password", "", CVAR_USERINFO, 0, qfalse, qfalse ) +G_CVAR( g_password, "g_password", "", CVAR_USERINFO, 0, qfalse, qfalse, NULL ) -G_CVAR( g_banIPs, "g_banIPs", "", CVAR_ARCHIVE, 0, qfalse, qfalse ) -G_CVAR( g_filterBan, "g_filterBan", "1", CVAR_ARCHIVE, 0, qfalse, qfalse ) +G_CVAR( g_banIPs, "g_banIPs", "", CVAR_ARCHIVE, 0, qfalse, qfalse, NULL ) +G_CVAR( g_filterBan, "g_filterBan", "1", CVAR_ARCHIVE, 0, qfalse, qfalse, NULL ) -G_CVAR( g_needpass, "g_needpass", "0", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse, qfalse ) +G_CVAR( g_needpass, "g_needpass", "0", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse, qfalse, NULL ) -G_CVAR( g_dedicated, "dedicated", "0", 0, 0, qfalse, qfalse ) +G_CVAR( g_dedicated, "dedicated", "0", 0, 0, qfalse, qfalse, NULL ) -G_CVAR( g_speed, "g_speed", "320", 0, 0, qtrue, qfalse ) -G_CVAR( g_gravity, "g_gravity", "800", 0, 0, qtrue, qfalse ) -G_CVAR( g_knockback, "g_knockback", "1000", 0, 0, qtrue, qfalse ) -G_CVAR( g_quadfactor, "g_quadfactor", "3", 0, 0, qtrue, qfalse ) -G_CVAR( g_weaponRespawn, "g_weaponrespawn", "5", 0, 0, qtrue, qfalse ) -G_CVAR( g_weaponTeamRespawn, "g_weaponTeamRespawn", "30", 0, 0, qtrue, qfalse ) -G_CVAR( g_forcerespawn, "g_forcerespawn", "20", 0, 0, qtrue, qfalse ) -G_CVAR( g_inactivity, "g_inactivity", "0", 0, 0, qtrue, qfalse ) -G_CVAR( g_debugMove, "g_debugMove", "0", 0, 0, qfalse, qfalse ) -G_CVAR( g_debugDamage, "g_debugDamage", "0", 0, 0, qfalse, qfalse ) -G_CVAR( g_debugAlloc, "g_debugAlloc", "0", 0, 0, qfalse, qfalse ) -G_CVAR( g_motd, "g_motd", "", 0, 0, qfalse, qfalse ) -G_CVAR( g_blood, "com_blood", "1", 0, 0, qfalse, qfalse ) +G_CVAR( g_speed, "g_speed", "320", 0, 0, qtrue, qfalse, NULL ) +G_CVAR( g_gravity, "g_gravity", "800", 0, 0, qtrue, qfalse, NULL ) +G_CVAR( g_knockback, "g_knockback", "1000", 0, 0, qtrue, qfalse, NULL ) +G_CVAR( g_quadfactor, "g_quadfactor", "3", 0, 0, qtrue, qfalse, NULL ) +G_CVAR( g_weaponRespawn, "g_weaponrespawn", "5", 0, 0, qtrue, qfalse, NULL ) +G_CVAR( g_weaponTeamRespawn, "g_weaponTeamRespawn", "30", 0, 0, qtrue, qfalse, NULL ) +G_CVAR( g_forcerespawn, "g_forcerespawn", "20", 0, 0, qtrue, qfalse, NULL ) +G_CVAR( g_inactivity, "g_inactivity", "0", 0, 0, qtrue, qfalse, NULL ) +G_CVAR( g_debugMove, "g_debugMove", "0", 0, 0, qfalse, qfalse, NULL ) +G_CVAR( g_debugDamage, "g_debugDamage", "0", 0, 0, qfalse, qfalse, NULL ) +G_CVAR( g_debugAlloc, "g_debugAlloc", "0", 0, 0, qfalse, qfalse, NULL ) +G_CVAR( g_motd, "g_motd", "", 0, 0, qfalse, qfalse, NULL ) +G_CVAR( g_blood, "com_blood", "1", 0, 0, qfalse, qfalse, NULL ) -G_CVAR( g_podiumDist, "g_podiumDist", "80", 0, 0, qfalse, qfalse ) -G_CVAR( g_podiumDrop, "g_podiumDrop", "70", 0, 0, qfalse, qfalse ) +G_CVAR( g_podiumDist, "g_podiumDist", "80", 0, 0, qfalse, qfalse, NULL ) +G_CVAR( g_podiumDrop, "g_podiumDrop", "70", 0, 0, qfalse, qfalse, NULL ) -G_CVAR( g_allowVote, "g_allowVote", "1", CVAR_ARCHIVE, 0, qfalse, qfalse ) -G_CVAR( g_listEntity, "g_listEntity", "0", 0, 0, qfalse, qfalse ) +G_CVAR( g_allowVote, "g_allowVote", "1", CVAR_ARCHIVE, 0, qfalse, qfalse, NULL ) +G_CVAR( g_listEntity, "g_listEntity", "0", 0, 0, qfalse, qfalse, NULL ) -G_CVAR( g_unlagged, "g_unlagged", "1", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qfalse, qfalse ) -G_CVAR( g_predictPVS, "g_predictPVS", "0", CVAR_ARCHIVE, 0, qfalse, qfalse ) +G_CVAR( g_unlagged, "g_unlagged", "1", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qfalse, qfalse, NULL ) +G_CVAR( g_predictPVS, "g_predictPVS", "0", CVAR_ARCHIVE, 0, qfalse, qfalse, NULL ) #ifdef MISSIONPACK -G_CVAR( g_obeliskHealth, "g_obeliskHealth", "2500", 0, 0, qfalse, qfalse ) -G_CVAR( g_obeliskRegenPeriod, "g_obeliskRegenPeriod", "1", 0, 0, qfalse, qfalse ) -G_CVAR( g_obeliskRegenAmount, "g_obeliskRegenAmount", "15", 0, 0, qfalse, qfalse ) -G_CVAR( g_obeliskRespawnDelay, "g_obeliskRespawnDelay", "10", CVAR_SERVERINFO, 0, qfalse, qfalse ) - -G_CVAR( g_cubeTimeout, "g_cubeTimeout", "30", 0, 0, qfalse, qfalse ) -G_CVAR( g_redteam, "g_redteam", "Stroggs", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_USERINFO, 0, qtrue, qtrue ) -G_CVAR( g_blueteam, "g_blueteam", "Pagans", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_USERINFO, 0, qtrue, qtrue ) -G_CVAR( g_singlePlayer, "ui_singlePlayerActive", "", 0, 0, qfalse, qfalse ) - -G_CVAR( g_enableDust, "g_enableDust", "0", CVAR_SERVERINFO, 0, qtrue, qfalse ) -G_CVAR( g_enableBreath, "g_enableBreath", "0", CVAR_SERVERINFO, 0, qtrue, qfalse ) -G_CVAR( g_proxMineTimeout, "g_proxMineTimeout", "20000", 0, 0, qfalse, qfalse ) +G_CVAR( g_obeliskHealth, "g_obeliskHealth", "2500", 0, 0, qfalse, qfalse, NULL ) +G_CVAR( g_obeliskRegenPeriod, "g_obeliskRegenPeriod", "1", 0, 0, qfalse, qfalse, NULL ) +G_CVAR( g_obeliskRegenAmount, "g_obeliskRegenAmount", "15", 0, 0, qfalse, qfalse, NULL ) +G_CVAR( g_obeliskRespawnDelay, "g_obeliskRespawnDelay", "10", CVAR_SERVERINFO, 0, qfalse, qfalse, NULL ) + +G_CVAR( g_cubeTimeout, "g_cubeTimeout", "30", 0, 0, qfalse, qfalse, NULL ) +G_CVAR( g_redteam, "g_redteam", "Stroggs", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_USERINFO, 0, qtrue, qtrue, NULL ) +G_CVAR( g_blueteam, "g_blueteam", "Pagans", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_USERINFO, 0, qtrue, qtrue, NULL ) +G_CVAR( g_singlePlayer, "ui_singlePlayerActive", "", 0, 0, qfalse, qfalse, NULL ) + +G_CVAR( g_enableDust, "g_enableDust", "0", CVAR_SERVERINFO, 0, qtrue, qfalse, NULL ) +G_CVAR( g_enableBreath, "g_enableBreath", "0", CVAR_SERVERINFO, 0, qtrue, qfalse, NULL ) +G_CVAR( g_proxMineTimeout, "g_proxMineTimeout", "20000", 0, 0, qfalse, qfalse, NULL ) #endif -G_CVAR( g_smoothClients, "g_smoothClients", "1", 0, 0, qfalse, qfalse ) -G_CVAR( pmove_fixed, "pmove_fixed", "0", CVAR_SYSTEMINFO, 0, qfalse, qfalse ) -G_CVAR( pmove_msec, "pmove_msec", "8", CVAR_SYSTEMINFO, 0, qfalse, qfalse ) +G_CVAR( g_smoothClients, "g_smoothClients", "1", 0, 0, qfalse, qfalse, NULL ) +G_CVAR( pmove_fixed, "pmove_fixed", "0", CVAR_SYSTEMINFO, 0, qfalse, qfalse, NULL ) +G_CVAR( pmove_msec, "pmove_msec", "8", CVAR_SYSTEMINFO, 0, qfalse, qfalse, NULL ) -G_CVAR( g_rotation, "g_rotation", "0", CVAR_ARCHIVE, 0, qfalse, qfalse ) +G_CVAR( g_rotation, "g_rotation", "0", CVAR_ARCHIVE, 0, qfalse, qfalse, NULL ) #undef G_CVAR diff --git a/code/game/g_local.h b/code/game/g_local.h index d3a3b047..e3455015 100644 --- a/code/game/g_local.h +++ b/code/game/g_local.h @@ -975,12 +975,15 @@ int trap_GeneticParentsAndChildSelection(int numranks, float *ranks, int *paren void trap_SnapVector( float *v ); // extension interface +extern qboolean can_trap_Cvar_SetDescription; #ifdef Q3_VM -// +extern void (*trap_Cvar_SetDescription)( const char *var_name, const char *var_description ); #else qboolean trap_GetValue( char *value, int valueSize, const char *key ); extern int dll_com_trapGetValue; +void trap_Cvar_SetDescription( const char *var_name, const char *var_description ); +extern int dll_trap_Cvar_SetDescription; #endif extern int svf_self_portal2; diff --git a/code/game/g_main.c b/code/game/g_main.c index 34d9cf85..08c202e3 100644 --- a/code/game/g_main.c +++ b/code/game/g_main.c @@ -13,6 +13,7 @@ typedef struct { int modificationCount; // for tracking changes qboolean trackChange; // track this variable, and announce if changed qboolean teamShader; // track and if changed, update shader state + const char *description; } cvarTable_t; gentity_t g_entities[MAX_GENTITIES]; @@ -45,10 +46,14 @@ static void CheckExitRulesLater( void ); static void SendScoreboardMessageToAllClients( void ); // extension interface +qboolean can_trap_Cvar_SetDescription = qfalse; + #ifdef Q3_VM qboolean (*trap_GetValue)( char *value, int valueSize, const char *key ); +void (*trap_Cvar_SetDescription)( const char *var_name, const char *var_description ); #else int dll_com_trapGetValue; +int dll_trap_Cvar_SetDescription; #endif int svf_self_portal2; @@ -275,6 +280,9 @@ void G_RegisterCvars( void ) { for ( i = 0, cv = gameCvarTable ; i < ARRAY_LEN( gameCvarTable ) ; i++, cv++ ) { trap_Cvar_Register( cv->vmCvar, cv->cvarName, cv->defaultString, cv->cvarFlags ); + if ( can_trap_Cvar_SetDescription && cv->description ) { + trap_Cvar_SetDescription( cv->cvarName, cv->description ); + } if ( cv->vmCvar ) cv->modificationCount = cv->vmCvar->modificationCount; @@ -461,6 +469,14 @@ static void G_InitGame( int levelTime, int randomSeed, int restart ) { } else { svf_self_portal2 = 0; } + if ( trap_GetValue( value, sizeof( value ), "trap_Cvar_SetDescription_Q3E" ) ) { +#ifdef Q3_VM + trap_Cvar_SetDescription = (void*)~atoi( value ); +#else + dll_trap_Cvar_SetDescription = atoi( value ); +#endif + can_trap_Cvar_SetDescription = qtrue; + } } srand( randomSeed ); diff --git a/code/game/g_syscalls.c b/code/game/g_syscalls.c index 7a6623da..3a2e0239 100644 --- a/code/game/g_syscalls.c +++ b/code/game/g_syscalls.c @@ -772,3 +772,7 @@ int trap_PC_SourceFileAndLine( int handle, char *filename, int *line ) { qboolean trap_GetValue( char *value, int valueSize, const char *key ) { return syscall( dll_com_trapGetValue, value, valueSize, key ); } + +void trap_Cvar_SetDescription( const char *var_name, const char *var_description ) { + syscall( dll_trap_Cvar_SetDescription, var_name, var_description ); +}