diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /engine/cvar.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'engine/cvar.cpp')
| -rw-r--r-- | engine/cvar.cpp | 1137 |
1 files changed, 1137 insertions, 0 deletions
diff --git a/engine/cvar.cpp b/engine/cvar.cpp new file mode 100644 index 0000000..ccb8fc3 --- /dev/null +++ b/engine/cvar.cpp @@ -0,0 +1,1137 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#include "cvar.h" +#include "gl_cvars.h" + +#include "tier1/convar.h" +#include "filesystem.h" +#include "filesystem_engine.h" +#include "client.h" +#include "server.h" +#include "GameEventManager.h" +#include "netmessages.h" +#include "sv_main.h" +#include "demo.h" +#include <ctype.h> +#ifdef POSIX +#include <wctype.h> +#endif + +#ifndef SWDS +#include <vgui_controls/Controls.h> +#include <vgui/ILocalize.h> +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Singleton CCvarUtilities +//----------------------------------------------------------------------------- +static CCvarUtilities g_CvarUtilities; +CCvarUtilities *cv = &g_CvarUtilities; + + +//----------------------------------------------------------------------------- +// Purpose: Update clients/server when FCVAR_REPLICATED etc vars change +//----------------------------------------------------------------------------- +static void ConVarNetworkChangeCallback( IConVar *pConVar, const char *pOldValue, float flOldValue ) +{ + ConVarRef var( pConVar ); + if ( !pOldValue ) + { + if ( var.GetFloat() == flOldValue ) + return; + } + else + { + if ( !Q_strcmp( var.GetString(), pOldValue ) ) + return; + } + + if ( var.IsFlagSet( FCVAR_USERINFO ) ) + { + // Are we not a server, but a client and have a change? + if ( cl.IsConnected() ) + { + // send changed cvar to server + NET_SetConVar convar( var.GetName(), var.GetString() ); + cl.m_NetChannel->SendNetMsg( convar ); + } + } + + // Log changes to server variables + + // Print to clients + if ( var.IsFlagSet( FCVAR_NOTIFY ) ) + { + IGameEvent *event = g_GameEventManager.CreateEvent( "server_cvar" ); + + if ( event ) + { + event->SetString( "cvarname", var.GetName() ); + + if ( var.IsFlagSet( FCVAR_PROTECTED ) ) + { + event->SetString("cvarvalue", "***PROTECTED***" ); + } + else + { + event->SetString("cvarvalue", var.GetString() ); + } + + g_GameEventManager.FireEvent( event ); + } + } + + // Force changes down to clients (if running server) + if ( var.IsFlagSet( FCVAR_REPLICATED ) && sv.IsActive() ) + { + SV_ReplicateConVarChange( static_cast< ConVar* >( pConVar ), var.GetString() ); + } +} + + +//----------------------------------------------------------------------------- +// Implementation of the ICvarQuery interface +//----------------------------------------------------------------------------- +class CCvarQuery : public CBaseAppSystem< ICvarQuery > +{ +public: + virtual bool Connect( CreateInterfaceFn factory ) + { + ICvar *pCVar = (ICvar*)factory( CVAR_INTERFACE_VERSION, 0 ); + if ( !pCVar ) + return false; + + pCVar->InstallCVarQuery( this ); + return true; + } + + virtual InitReturnVal_t Init() + { + // If the value has changed, notify clients/server based on ConVar flags. + // NOTE: this will only happen for non-FCVAR_NEVER_AS_STRING vars. + // Also, this happened in SetDirect for older clients that don't have the + // callback interface. + g_pCVar->InstallGlobalChangeCallback( ConVarNetworkChangeCallback ); + return INIT_OK; + } + + virtual void Shutdown() + { + g_pCVar->RemoveGlobalChangeCallback( ConVarNetworkChangeCallback ); + } + + virtual void *QueryInterface( const char *pInterfaceName ) + { + if ( !Q_stricmp( pInterfaceName, CVAR_QUERY_INTERFACE_VERSION ) ) + return (ICvarQuery*)this; + return NULL; + + } + + // Purpose: Returns true if the commands can be aliased to one another + // Either game/client .dll shared with engine, + // or game and client dll shared and marked FCVAR_REPLICATED + virtual bool AreConVarsLinkable( const ConVar *child, const ConVar *parent ) + { + // Both parent and child must be marked replicated for this to work + bool repchild = child->IsFlagSet( FCVAR_REPLICATED ); + bool repparent = parent->IsFlagSet( FCVAR_REPLICATED ); + + if ( repchild && repparent ) + { + // Never on protected vars + if ( child->IsFlagSet( FCVAR_PROTECTED ) || parent->IsFlagSet( FCVAR_PROTECTED ) ) + { + ConMsg( "FCVAR_REPLICATED can't also be FCVAR_PROTECTED (%s)\n", child->GetName() ); + return false; + } + + // Only on ConVars + if ( child->IsCommand() || parent->IsCommand() ) + { + ConMsg( "FCVAR_REPLICATED not valid on ConCommands (%s)\n", child->GetName() ); + return false; + } + + // One must be in client .dll and the other in the game .dll, or both in the engine + if ( child->IsFlagSet( FCVAR_GAMEDLL ) && !parent->IsFlagSet( FCVAR_CLIENTDLL ) ) + { + ConMsg( "For FCVAR_REPLICATED, ConVar must be defined in client and game .dlls (%s)\n", child->GetName() ); + return false; + } + + if ( child->IsFlagSet( FCVAR_CLIENTDLL ) && !parent->IsFlagSet( FCVAR_GAMEDLL ) ) + { + ConMsg( "For FCVAR_REPLICATED, ConVar must be defined in client and game .dlls (%s)\n", child->GetName() ); + return false; + } + + // Allowable + return true; + } + + // Otherwise need both to allow linkage + if ( repchild || repparent ) + { + ConMsg( "Both ConVars must be marked FCVAR_REPLICATED for linkage to work (%s)\n", child->GetName() ); + return false; + } + + if ( parent->IsFlagSet( FCVAR_CLIENTDLL ) ) + { + ConMsg( "Parent cvar in client.dll not allowed (%s)\n", child->GetName() ); + return false; + } + + if ( parent->IsFlagSet( FCVAR_GAMEDLL ) ) + { + ConMsg( "Parent cvar in server.dll not allowed (%s)\n", child->GetName() ); + return false; + } + + return true; + } +}; + + +//----------------------------------------------------------------------------- +// Singleton +//----------------------------------------------------------------------------- +static CCvarQuery s_CvarQuery; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CCvarQuery, ICvarQuery, CVAR_QUERY_INTERFACE_VERSION, s_CvarQuery ); + + +//----------------------------------------------------------------------------- +// +// CVar utilities begins here +// +//----------------------------------------------------------------------------- +static bool IsAllSpaces( const wchar_t *str ) +{ + const wchar_t *p = str; + while ( p && *p ) + { + if ( !iswspace( *p ) ) + return false; + + ++p; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *var - +// *value - +//----------------------------------------------------------------------------- +void CCvarUtilities::SetDirect( ConVar *var, const char *value ) +{ + const char *pszValue; + char szNew[ 1024 ]; + + // Bail early if we're trying to set a FCVAR_USERINFO cvar on a dedicated server + if ( var->IsFlagSet( FCVAR_USERINFO ) ) + { + if ( sv.IsDedicated() ) + { + return; + } + } + + pszValue = value; + // This cvar's string must only contain printable characters. + // Strip out any other crap. + // We'll fill in "empty" if nothing is left + if ( var->IsFlagSet( FCVAR_PRINTABLEONLY ) ) + { + wchar_t unicode[ 512 ]; +#ifndef SWDS + if ( sv.IsDedicated() ) + { + // Dedicated servers don't have g_pVGuiLocalize, so fall back + V_UTF8ToUnicode( pszValue, unicode, sizeof( unicode ) ); + } + else + { + g_pVGuiLocalize->ConvertANSIToUnicode( pszValue, unicode, sizeof( unicode ) ); + } +#else + V_UTF8ToUnicode( pszValue, unicode, sizeof( unicode ) ); +#endif + wchar_t newUnicode[ 512 ]; + + const wchar_t *pS; + wchar_t *pD; + + // Clear out new string + newUnicode[0] = L'\0'; + + pS = unicode; + pD = newUnicode; + + // Step through the string, only copying back in characters that are printable + while ( *pS ) + { + if ( iswcntrl( *pS ) || *pS == '~' ) + { + pS++; + continue; + } + + *pD++ = *pS++; + } + + // Terminate the new string + *pD = L'\0'; + + // If it's empty or all spaces, then insert a marker string + if ( !wcslen( newUnicode ) || IsAllSpaces( newUnicode ) ) + { + wcsncpy( newUnicode, L"#empty", ( sizeof( newUnicode ) / sizeof( wchar_t ) ) - 1 ); + newUnicode[ ( sizeof( newUnicode ) / sizeof( wchar_t ) ) - 1 ] = L'\0'; + } + +#ifndef SWDS + if ( sv.IsDedicated() ) + { + V_UnicodeToUTF8( newUnicode, szNew, sizeof( szNew ) ); + } + else + { + g_pVGuiLocalize->ConvertUnicodeToANSI( newUnicode, szNew, sizeof( szNew ) ); + } +#else + V_UnicodeToUTF8( newUnicode, szNew, sizeof( szNew ) ); +#endif + // Point the value here. + pszValue = szNew; + } + + if ( var->IsFlagSet( FCVAR_NEVER_AS_STRING ) ) + { + var->SetValue( (float)atof( pszValue ) ); + } + else + { + var->SetValue( pszValue ); + } +} + + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- + +// If you are changing this, please take a look at IsValidToggleCommand() +bool CCvarUtilities::IsCommand( const CCommand &args ) +{ + int c = args.ArgC(); + if ( c == 0 ) + return false; + + ConVar *v; + + // check variables + v = g_pCVar->FindVar( args[0] ); + if ( !v ) + return false; + + // NOTE: Not checking for 'HIDDEN' here so we can actually set hidden convars + if ( v->IsFlagSet(FCVAR_DEVELOPMENTONLY) ) + return false; + + // perform a variable print or set + if ( c == 1 ) + { + ConVar_PrintDescription( v ); + return true; + } + + if ( v->IsFlagSet( FCVAR_SPONLY ) ) + { +#ifndef SWDS + // Connected to server? + if ( cl.IsConnected() ) + { + // Is it not a single player game? + if ( cl.m_nMaxClients > 1 ) + { + ConMsg( "Can't set %s in multiplayer\n", v->GetName() ); + return true; + } + } +#endif + } + + if ( v->IsFlagSet( FCVAR_NOT_CONNECTED ) ) + { +#ifndef SWDS + // Connected to server? + if ( cl.IsConnected() ) + { + extern IBaseClientDLL *g_ClientDLL; + if ( v->IsFlagSet( FCVAR_USERINFO ) && g_ClientDLL && g_ClientDLL->IsConnectedUserInfoChangeAllowed( v ) ) + { + // Client.dll is allowing the convar change + } + else + { + ConMsg( "Can't change %s when playing, disconnect from the server or switch team to spectators\n", v->GetName() ); + return true; + } + } +#endif + } + + // Allow cheat commands in singleplayer, debug, or multiplayer with sv_cheats on + if ( v->IsFlagSet( FCVAR_CHEAT ) ) + { + if ( !Host_IsSinglePlayerGame() && !CanCheat() +#if !defined(SWDS) + && !cl.ishltv +#if defined( REPLAY_ENABLED ) + && !cl.isreplay +#endif + && !demoplayer->IsPlayingBack() +#endif + ) + { + ConMsg( "Can't use cheat cvar %s in multiplayer, unless the server has sv_cheats set to 1.\n", v->GetName() ); + return true; + } + } + + // Text invoking the command was typed into the console, decide what to do with it + // if this is a replicated ConVar, except don't worry about restrictions if playing a .dem file + if ( v->IsFlagSet( FCVAR_REPLICATED ) +#if !defined(SWDS) + && !demoplayer->IsPlayingBack() +#endif + ) + { + // If not running a server but possibly connected as a client, then + // if the message came from console, don't process the command + if ( !sv.IsActive() && + !sv.IsLoading() && + (cmd_source == src_command) && + cl.IsConnected() ) + { + ConMsg( "Can't change replicated ConVar %s from console of client, only server operator can change its value\n", v->GetName() ); + return true; + } + + // FIXME: Do we need a case where cmd_source == src_client? + Assert( cmd_source != src_client ); + } + + // Note that we don't want the tokenized list, send down the entire string + // except for surrounding quotes + char remaining[1024]; + const char *pArgS = args.ArgS(); + int nLen = Q_strlen( pArgS ); + bool bIsQuoted = pArgS[0] == '\"'; + if ( !bIsQuoted ) + { + Q_strncpy( remaining, args.ArgS(), sizeof(remaining) ); + } + else + { + --nLen; + Q_strncpy( remaining, &pArgS[1], sizeof(remaining) ); + } + + // Now strip off any trailing spaces + char *p = remaining + nLen - 1; + while ( p >= remaining ) + { + if ( *p > ' ' ) + break; + + *p-- = 0; + } + + // Strip off ending quote + if ( bIsQuoted && p >= remaining ) + { + if ( *p == '\"' ) + { + *p = 0; + } + } + + SetDirect( v, remaining ); + return true; +} + +// This is a band-aid copied directly from IsCommand(). +bool CCvarUtilities::IsValidToggleCommand( const char *cmd ) +{ + ConVar *v; + + // check variables + v = g_pCVar->FindVar ( cmd ); + if (!v) + { + ConMsg( "%s is not a valid cvar\n", cmd ); + return false; + } + + if ( v->IsFlagSet(FCVAR_DEVELOPMENTONLY) || v->IsFlagSet(FCVAR_HIDDEN) ) + return false; + + if ( v->IsFlagSet( FCVAR_SPONLY ) ) + { +#ifndef SWDS + // Connected to server? + if ( cl.IsConnected() ) + { + // Is it not a single player game? + if ( cl.m_nMaxClients > 1 ) + { + ConMsg( "Can't set %s in multiplayer\n", v->GetName() ); + return false; + } + } +#endif + } + + if ( v->IsFlagSet( FCVAR_NOT_CONNECTED ) ) + { +#ifndef SWDS + // Connected to server? + if ( cl.IsConnected() ) + { + extern IBaseClientDLL *g_ClientDLL; + if ( v->IsFlagSet( FCVAR_USERINFO ) && g_ClientDLL && g_ClientDLL->IsConnectedUserInfoChangeAllowed( v ) ) + { + // Client.dll is allowing the convar change + } + else + { + ConMsg( "Can't change %s when playing, disconnect from the server or switch team to spectators\n", v->GetName() ); + return false; + } + } +#endif + } + + // Allow cheat commands in singleplayer, debug, or multiplayer with sv_cheats on + if ( v->IsFlagSet( FCVAR_CHEAT ) ) + { + if ( !Host_IsSinglePlayerGame() && !CanCheat() +#if !defined(SWDS) && !defined(_XBOX) + && !demoplayer->IsPlayingBack() +#endif + ) + { + ConMsg( "Can't use cheat cvar %s in multiplayer, unless the server has sv_cheats set to 1.\n", v->GetName() ); + return false; + } + } + + // Text invoking the command was typed into the console, decide what to do with it + // if this is a replicated ConVar, except don't worry about restrictions if playing a .dem file + if ( v->IsFlagSet( FCVAR_REPLICATED ) +#if !defined(SWDS) && !defined(_XBOX) + && !demoplayer->IsPlayingBack() +#endif + ) + { + // If not running a server but possibly connected as a client, then + // if the message came from console, don't process the command + if ( !sv.IsActive() && + !sv.IsLoading() && + (cmd_source == src_command) && + cl.IsConnected() ) + { + ConMsg( "Can't change replicated ConVar %s from console of client, only server operator can change its value\n", v->GetName() ); + return false; + } + } + + // FIXME: Do we need a case where cmd_source == src_client? + Assert( cmd_source != src_client ); + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *f - +//----------------------------------------------------------------------------- +void CCvarUtilities::WriteVariables( CUtlBuffer &buff, bool bAllVars ) +{ + const ConCommandBase *var; + + for (var = g_pCVar->GetCommands() ; var ; var = var->GetNext()) + { + if ( var->IsCommand() ) + continue; + + bool archive = var->IsFlagSet( IsX360() ? FCVAR_ARCHIVE_XBOX : FCVAR_ARCHIVE ); + if ( archive ) + { + const ConVar *pConvar = assert_cast<const ConVar *>( var ); + // Only write out values that differ from the defaults. + if ( bAllVars || Q_strcmp( pConvar->GetString(), pConvar->GetDefault() ) != 0 ) + { + buff.Printf( "%s \"%s\"\n", var->GetName(), ((ConVar *)var)->GetString() ); + } + } + } +} + +static char *StripTabsAndReturns( const char *inbuffer, char *outbuffer, int outbufferSize ) +{ + char *out = outbuffer; + const char *i = inbuffer; + char *o = out; + + out[ 0 ] = 0; + + while ( *i && o - out < outbufferSize - 1 ) + { + if ( *i == '\n' || + *i == '\r' || + *i == '\t' ) + { + *o++ = ' '; + i++; + continue; + } + if ( *i == '\"' ) + { + *o++ = '\''; + i++; + continue; + } + + *o++ = *i++; + } + + *o = '\0'; + + return out; +} + +static char *StripQuotes( const char *inbuffer, char *outbuffer, int outbufferSize ) +{ + char *out = outbuffer; + const char *i = inbuffer; + char *o = out; + + out[ 0 ] = 0; + + while ( *i && o - out < outbufferSize - 1 ) + { + if ( *i == '\"' ) + { + *o++ = '\''; + i++; + continue; + } + + *o++ = *i++; + } + + *o = '\0'; + + return out; +} + +struct ConVarFlags_t +{ + int bit; + const char *desc; + const char *shortdesc; +}; + +#define CONVARFLAG( x, y ) { FCVAR_##x, #x, #y } + +static ConVarFlags_t g_ConVarFlags[]= +{ + // CONVARFLAG( UNREGISTERED, "u" ), + CONVARFLAG( ARCHIVE, "a" ), + CONVARFLAG( SPONLY, "sp" ), + CONVARFLAG( GAMEDLL, "sv" ), + CONVARFLAG( CHEAT, "cheat" ), + CONVARFLAG( USERINFO, "user" ), + CONVARFLAG( NOTIFY, "nf" ), + CONVARFLAG( PROTECTED, "prot" ), + CONVARFLAG( PRINTABLEONLY, "print" ), + CONVARFLAG( UNLOGGED, "log" ), + CONVARFLAG( NEVER_AS_STRING, "numeric" ), + CONVARFLAG( REPLICATED, "rep" ), + CONVARFLAG( DEMO, "demo" ), + CONVARFLAG( DONTRECORD, "norecord" ), + CONVARFLAG( SERVER_CAN_EXECUTE, "server_can_execute" ), + CONVARFLAG( CLIENTCMD_CAN_EXECUTE, "clientcmd_can_execute" ), + CONVARFLAG( CLIENTDLL, "cl" ), +}; + +static void PrintListHeader( FileHandle_t& f ) +{ + char csvflagstr[ 1024 ]; + + csvflagstr[ 0 ] = 0; + + int c = ARRAYSIZE( g_ConVarFlags ); + for ( int i = 0 ; i < c; ++i ) + { + char csvf[ 64 ]; + + ConVarFlags_t & entry = g_ConVarFlags[ i ]; + Q_snprintf( csvf, sizeof( csvf ), "\"%s\",", entry.desc ); + Q_strncat( csvflagstr, csvf, sizeof( csvflagstr ), COPY_ALL_CHARACTERS ); + } + + g_pFileSystem->FPrintf( f,"\"%s\",\"%s\",%s,\"%s\"\n", "Name", "Value", csvflagstr, "Help Text" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *var - +// *f - +//----------------------------------------------------------------------------- +static void PrintCvar( const ConVar *var, bool logging, FileHandle_t& fh ) +{ + char flagstr[ 128 ]; + char csvflagstr[ 1024 ]; + + flagstr[ 0 ] = 0; + csvflagstr[ 0 ] = 0; + + int c = ARRAYSIZE( g_ConVarFlags ); + for ( int i = 0 ; i < c; ++i ) + { + char f[ 32 ]; + char csvf[ 64 ]; + + ConVarFlags_t & entry = g_ConVarFlags[ i ]; + if ( var->IsFlagSet( entry.bit ) ) + { + Q_snprintf( f, sizeof( f ), ", %s", entry.shortdesc ); + + Q_strncat( flagstr, f, sizeof( flagstr ), COPY_ALL_CHARACTERS ); + + Q_snprintf( csvf, sizeof( csvf ), "\"%s\",", entry.desc ); + } + else + { + Q_snprintf( csvf, sizeof( csvf ), "," ); + } + + Q_strncat( csvflagstr, csvf, sizeof( csvflagstr ), COPY_ALL_CHARACTERS ); + } + + + char valstr[ 32 ]; + char tempbuff[512] = { 0 }; + + // Clean up integers + if ( var->GetInt() == (int)var->GetFloat() ) + { + Q_snprintf(valstr, sizeof( valstr ), "%-8i", var->GetInt() ); + } + else + { + Q_snprintf(valstr, sizeof( valstr ), "%-8.3f", var->GetFloat() ); + } + + // Print to console + ConMsg( "%-40s : %-8s : %-16s : %s\n", var->GetName(), valstr, flagstr, StripTabsAndReturns( var->GetHelpText(), tempbuff, sizeof(tempbuff) ) ); + if ( logging ) + { + g_pFileSystem->FPrintf( fh,"\"%s\",\"%s\",%s,\"%s\"\n", var->GetName(), valstr, csvflagstr, StripQuotes( var->GetHelpText(), tempbuff, sizeof(tempbuff) ) ); + } +} + +static void PrintCommand( const ConCommand *cmd, bool logging, FileHandle_t& f ) +{ + // Print to console + char tempbuff[512] = { 0 }; + ConMsg ("%-40s : %-8s : %-16s : %s\n",cmd->GetName(), "cmd", "", StripTabsAndReturns( cmd->GetHelpText(), tempbuff, sizeof(tempbuff) ) ); + if ( logging ) + { + char emptyflags[ 256 ]; + + emptyflags[ 0 ] = 0; + + int c = ARRAYSIZE( g_ConVarFlags ); + for ( int i = 0; i < c; ++i ) + { + char csvf[ 64 ]; + Q_snprintf( csvf, sizeof( csvf ), "," ); + Q_strncat( emptyflags, csvf, sizeof( emptyflags ), COPY_ALL_CHARACTERS ); + } + // Names staring with +/- need to be wrapped in single quotes + char name[ 256 ]; + Q_snprintf( name, sizeof( name ), "%s", cmd->GetName() ); + if ( name[ 0 ] == '+' || name[ 0 ] == '-' ) + { + Q_snprintf( name, sizeof( name ), "'%s'", cmd->GetName() ); + } + g_pFileSystem->FPrintf( f, "\"%s\",\"%s\",%s,\"%s\"\n", name, "cmd", emptyflags, StripQuotes( cmd->GetHelpText(), tempbuff, sizeof(tempbuff) ) ); + } +} + +static bool ConCommandBaseLessFunc( const ConCommandBase * const &lhs, const ConCommandBase * const &rhs ) +{ + const char *left = lhs->GetName(); + const char *right = rhs->GetName(); + + if ( *left == '-' || *left == '+' ) + left++; + if ( *right == '-' || *right == '+' ) + right++; + + return ( Q_stricmp( left, right ) < 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : void CCvar::CvarList_f +//----------------------------------------------------------------------------- +void CCvarUtilities::CvarList( const CCommand &args ) +{ + const ConCommandBase *var; // Temporary Pointer to cvars + int iArgs; // Argument count + const char *partial = NULL; // Partial cvar to search for... + // E.eg + int ipLen = 0; // Length of the partial cvar + + FileHandle_t f = FILESYSTEM_INVALID_HANDLE; // FilePointer for logging + bool bLogging = false; + // Are we logging? + iArgs = args.ArgC(); // Get count + + // Print usage? + if ( iArgs == 2 && !Q_strcasecmp( args[1],"?" ) ) + { + ConMsg( "cvarlist: [log logfile] [ partial ]\n" ); + return; + } + + if ( !Q_strcasecmp( args[1],"log" ) && iArgs >= 3 ) + { + char fn[256]; + Q_snprintf( fn, sizeof( fn ), "%s", args[2] ); + f = g_pFileSystem->Open( fn,"wb" ); + if ( f ) + { + bLogging = true; + } + else + { + ConMsg( "Couldn't open '%s' for writing!\n", fn ); + return; + } + + if ( iArgs == 4 ) + { + partial = args[ 3 ]; + ipLen = Q_strlen( partial ); + } + } + else + { + partial = args[ 1 ]; + ipLen = Q_strlen( partial ); + } + + // Banner + ConMsg( "cvar list\n--------------\n" ); + + CUtlRBTree< const ConCommandBase * > sorted( 0, 0, ConCommandBaseLessFunc ); + + // Loop through cvars... + for ( var= g_pCVar->GetCommands(); var; var=var->GetNext() ) + { + bool print = false; + + if ( var->IsFlagSet(FCVAR_DEVELOPMENTONLY) || var->IsFlagSet(FCVAR_HIDDEN) ) + continue; + + if (partial) // Partial string searching? + { + if ( !Q_strncasecmp( var->GetName(), partial, ipLen ) ) + { + print = true; + } + } + else + { + print = true; + } + + if ( !print ) + continue; + + sorted.Insert( var ); + } + + if ( bLogging ) + { + PrintListHeader( f ); + } + for ( int i = sorted.FirstInorder(); i != sorted.InvalidIndex(); i = sorted.NextInorder( i ) ) + { + var = sorted[ i ]; + if ( var->IsCommand() ) + { + PrintCommand( (ConCommand *)var, bLogging, f ); + } + else + { + PrintCvar( (ConVar *)var, bLogging, f ); + } + } + + + // Show total and syntax help... + if ( partial && partial[0] ) + { + ConMsg("--------------\n%3i convars/concommands for [%s]\n", sorted.Count(), partial ); + } + else + { + ConMsg("--------------\n%3i total convars/concommands\n", sorted.Count() ); + } + + if ( bLogging ) + { + g_pFileSystem->Close( f ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : int +//----------------------------------------------------------------------------- +int CCvarUtilities::CountVariablesWithFlags( int flags ) +{ + int i = 0; + const ConCommandBase *var; + + for ( var = g_pCVar->GetCommands(); var; var = var->GetNext() ) + { + if ( var->IsCommand() ) + continue; + + if ( var->IsFlagSet( flags ) ) + { + i++; + } + } + + return i; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCvarUtilities::CvarHelp( const CCommand &args ) +{ + const char *search; + const ConCommandBase *var; + + if ( args.ArgC() != 2 ) + { + ConMsg( "Usage: help <cvarname>\n" ); + return; + } + + // Get name of var to find + search = args[1]; + + // Search for it + var = g_pCVar->FindCommandBase( search ); + if ( !var ) + { + ConMsg( "help: no cvar or command named %s\n", search ); + return; + } + + // Show info + ConVar_PrintDescription( var ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCvarUtilities::CvarDifferences( const CCommand &args ) +{ + const ConCommandBase *var; + + // Loop through vars and print out findings + for ( var = g_pCVar->GetCommands(); var; var=var->GetNext() ) + { + if ( var->IsCommand( ) ) + continue; + if ( var->IsFlagSet(FCVAR_DEVELOPMENTONLY) || var->IsFlagSet(FCVAR_HIDDEN) ) + continue; + + if ( !Q_stricmp( ((ConVar *)var)->GetDefault(), ((ConVar *)var)->GetString() ) ) + continue; + + ConVar_PrintDescription( (ConVar *)var ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Toggles a cvar on/off, or cycles through a set of values +//----------------------------------------------------------------------------- +void CCvarUtilities::CvarToggle( const CCommand &args ) +{ + int i; + + int c = args.ArgC(); + if ( c < 2 ) + { + ConMsg( "Usage: toggle <cvarname> [value1] [value2] [value3]...\n" ); + return; + } + + ConVar *var = g_pCVar->FindVar( args[1] ); + + if ( !IsValidToggleCommand( args[1] ) ) + { + return; + } + + if ( c == 2 ) + { + // just toggle it on and off + var->SetValue( !var->GetBool() ); + ConVar_PrintDescription( var ); + } + else + { + // look for the current value in the command arguments + for( i = 2; i < c; i++ ) + { + if ( !Q_strcmp( var->GetString(), args[ i ] ) ) + break; + } + + // choose the next one + i++; + + // if we didn't find it, or were at the last value in the command arguments, use the 1st argument + if ( i >= c ) + { + i = 2; + } + + var->SetValue( args[ i ] ); + ConVar_PrintDescription( var ); + } +} + +void CCvarUtilities::CvarFindFlags_f( const CCommand &args ) +{ + if ( args.ArgC() < 2 ) + { + ConMsg( "Usage: findflags <string>\n" ); + ConMsg( "Available flags to search for: \n" ); + + for ( int i=0; i < ARRAYSIZE( g_ConVarFlags ); i++ ) + { + ConMsg( " - %s\n", g_ConVarFlags[i].desc ); + } + return; + } + + // Get substring to find + const char *search = args[1]; + const ConCommandBase *var; + + // Loop through vars and print out findings + for (var=g_pCVar->GetCommands() ; var ; var=var->GetNext()) + { + if ( var->IsFlagSet(FCVAR_DEVELOPMENTONLY) || var->IsFlagSet(FCVAR_HIDDEN) ) + continue; + + for ( int i=0; i < ARRAYSIZE( g_ConVarFlags ); i++ ) + { + if ( !var->IsFlagSet( g_ConVarFlags[i].bit ) ) + continue; + + if ( !V_stristr( g_ConVarFlags[i].desc, search ) ) + continue; + + ConVar_PrintDescription( var ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Hook to command +//----------------------------------------------------------------------------- +CON_COMMAND( findflags, "Find concommands by flags." ) +{ + cv->CvarFindFlags_f( args ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Hook to command +//----------------------------------------------------------------------------- +CON_COMMAND( cvarlist, "Show the list of convars/concommands." ) +{ + cv->CvarList( args ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Print help text for cvar +//----------------------------------------------------------------------------- +CON_COMMAND( help, "Find help about a convar/concommand." ) +{ + cv->CvarHelp( args ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CON_COMMAND( differences, "Show all convars which are not at their default values." ) +{ + cv->CvarDifferences( args ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CON_COMMAND( toggle, "Toggles a convar on or off, or cycles through a set of values." ) +{ + cv->CvarToggle( args ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Send the cvars to VXConsole +//----------------------------------------------------------------------------- +#if defined( _X360 ) +CON_COMMAND( getcvars, "" ) +{ + g_pCVar->PublishToVXConsole(); +} +#endif + |