You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1137 lines
27 KiB
1137 lines
27 KiB
//========= 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 |
|
|
|
|