//========= 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 #ifdef POSIX #include #endif #ifndef SWDS #include #include #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 ) && false ) { 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 ) && false ) { 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( 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 \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 [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 \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