diff options
Diffstat (limited to 'external/vpc/vstdlib/cvar.cpp')
| -rw-r--r-- | external/vpc/vstdlib/cvar.cpp | 1302 |
1 files changed, 1302 insertions, 0 deletions
diff --git a/external/vpc/vstdlib/cvar.cpp b/external/vpc/vstdlib/cvar.cpp new file mode 100644 index 0000000..060b674 --- /dev/null +++ b/external/vpc/vstdlib/cvar.cpp @@ -0,0 +1,1302 @@ +//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#include "vstdlib/cvar.h" +#include <ctype.h> +#include "tier0/icommandline.h" +#include "tier1/utlrbtree.h" +#include "tier1/strtools.h" +#include "tier1/keyvalues.h" +#include "tier1/convar.h" +#include "tier0/vprof.h" +#include "tier1/tier1.h" +#include "tier1/utlbuffer.h" +#include "tier1/utlmap.h" +#include "tier1/fmtstr.h" + +#ifdef _X360 +#include "xbox/xbox_console.h" +#elif defined( _PS3 ) +#include "ps3/ps3_console.h" +#endif + +#ifdef POSIX +#include <wctype.h> +#include <wchar.h> + +#define VPROJ_INCREMENT_COUNTER(a, b) /* */ +#define VPROJ(a) /* */ +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// Default implementation of CvarQuery +//----------------------------------------------------------------------------- +class CDefaultCvarQuery : public CBaseAppSystem< ICvarQuery > +{ +public: + virtual void *QueryInterface( const char *pInterfaceName ) + { + if ( !V_stricmp( pInterfaceName, CVAR_QUERY_INTERFACE_VERSION ) ) + return (ICvarQuery*)this; + return NULL; + + } + + virtual bool AreConVarsLinkable( const ConVar *child, const ConVar *parent ) + { + return true; + } +}; + +static CDefaultCvarQuery s_DefaultCvarQuery; +static ICvarQuery *s_pCVarQuery = NULL; + +#include "concommandhash.h" + +//----------------------------------------------------------------------------- +// Default implementation +//----------------------------------------------------------------------------- +class CCvar : public CBaseAppSystem< ICvar > +{ +public: + CCvar(); + + // Methods of IAppSystem + virtual bool Connect( CreateInterfaceFn factory ); + virtual void Disconnect(); + virtual void *QueryInterface( const char *pInterfaceName ); + virtual InitReturnVal_t Init(); + virtual void Shutdown(); + + // Inherited from ICVar + virtual CVarDLLIdentifier_t AllocateDLLIdentifier(); + virtual void RegisterConCommand( ConCommandBase *pCommandBase ); + virtual void UnregisterConCommand( ConCommandBase *pCommandBase ); + virtual void UnregisterConCommands( CVarDLLIdentifier_t id ); + virtual const char* GetCommandLineValue( const char *pVariableName ); + virtual ConCommandBase *FindCommandBase( const char *name ); + virtual const ConCommandBase *FindCommandBase( const char *name ) const; + virtual ConVar *FindVar ( const char *var_name ); + virtual const ConVar *FindVar ( const char *var_name ) const; + virtual ConCommand *FindCommand( const char *name ); + virtual const ConCommand *FindCommand( const char *name ) const; + virtual void InstallGlobalChangeCallback( FnChangeCallback_t callback ); + virtual void RemoveGlobalChangeCallback( FnChangeCallback_t callback ); + virtual void CallGlobalChangeCallbacks( ConVar *var, const char *pOldString, float flOldValue ); + virtual void InstallConsoleDisplayFunc( IConsoleDisplayFunc* pDisplayFunc ); + virtual void RemoveConsoleDisplayFunc( IConsoleDisplayFunc* pDisplayFunc ); + virtual void ConsoleColorPrintf( const Color& clr, const char *pFormat, ... ) const; + virtual void ConsolePrintf( const char *pFormat, ... ) const; + virtual void ConsoleDPrintf( const char *pFormat, ... ) const; + virtual void RevertFlaggedConVars( int nFlag ); + virtual void InstallCVarQuery( ICvarQuery *pQuery ); + +#if defined( USE_VXCONSOLE ) + virtual void PublishToVXConsole( ); +#endif + + virtual void SetMaxSplitScreenSlots( int nSlots ); + virtual int GetMaxSplitScreenSlots() const; + + virtual void AddSplitScreenConVars(); + virtual void RemoveSplitScreenConVars( CVarDLLIdentifier_t id ); + + virtual int GetConsoleDisplayFuncCount() const; + virtual void GetConsoleText( int nDisplayFuncIndex, char *pchText, size_t bufSize ) const; + virtual bool IsMaterialThreadSetAllowed( ) const; + virtual void QueueMaterialThreadSetValue( ConVar *pConVar, const char *pValue ); + virtual void QueueMaterialThreadSetValue( ConVar *pConVar, int nValue ); + virtual void QueueMaterialThreadSetValue( ConVar *pConVar, float flValue ); + virtual bool HasQueuedMaterialThreadConVarSets() const; + virtual int ProcessQueuedMaterialThreadConVarSets(); + +private: + enum + { + CONSOLE_COLOR_PRINT = 0, + CONSOLE_PRINT, + CONSOLE_DPRINT, + }; + + void DisplayQueuedMessages( ); + + CUtlVector< FnChangeCallback_t > m_GlobalChangeCallbacks; + CUtlVector< IConsoleDisplayFunc* > m_DisplayFuncs; + int m_nNextDLLIdentifier; + + ConCommandBase *m_pConCommandList; + CConCommandHash m_CommandHash; + + // temporary console area so we can store prints before console display funs are installed + mutable CUtlBuffer m_TempConsoleBuffer; + int m_nMaxSplitScreenSlots; + +protected: + + // internals for ICVarIterator + class CCVarIteratorInternal : public ICVarIteratorInternal + { + public: + CCVarIteratorInternal( CCvar *outer ) + : m_pOuter( outer ), m_pHash( &outer->m_CommandHash ), // remember my CCvar, + m_hashIter( -1, -1 ) // and invalid iterator + {} + virtual void SetFirst( void ); + virtual void Next( void ); + virtual bool IsValid( void ); + virtual ConCommandBase *Get( void ); + protected: + CCvar * const m_pOuter; + CConCommandHash * const m_pHash; + CConCommandHash::CCommandHashIterator_t m_hashIter; + }; + + virtual ICVarIteratorInternal *FactoryInternalIterator( void ); + friend class CCVarIteratorInternal; + + enum ConVarSetType_t + { + CONVAR_SET_STRING = 0, + CONVAR_SET_INT, + CONVAR_SET_FLOAT, + }; + + struct QueuedConVarSet_t + { + ConVar *m_pConVar; + ConVarSetType_t m_nType; + int m_nInt; + float m_flFloat; + CUtlString m_String; + }; + + struct SplitScreenConVar_t + { + SplitScreenConVar_t() + { + Reset(); + } + + void Reset() + { + m_VarName = ""; + m_pVar = NULL; + } + + CUtlString m_VarName; + CSplitScreenAddedConVar *m_pVar; + }; + + struct SplitScreenAddedConVars_t + { + SplitScreenAddedConVars_t() + { + for ( int i = 0; i < MAX_SPLITSCREEN_CLIENTS - 1; ++i ) + { + m_Vars[ i ].Reset(); + } + } + + // Var names need "static" buffers... + SplitScreenConVar_t m_Vars[ MAX_SPLITSCREEN_CLIENTS - 1 ]; + }; + + CUtlMap< ConVar *, SplitScreenAddedConVars_t > m_SplitScreenAddedConVarsMap; + CUtlVector< QueuedConVarSet_t > m_QueuedConVarSets; + bool m_bMaterialSystemThreadSetAllowed; + +private: + // Standard console commands -- DO NOT PLACE ANY HIGHER THAN HERE BECAUSE THESE MUST BE THE FIRST TO DESTRUCT + CON_COMMAND_MEMBER_F( CCvar, "find", Find, "Find concommands with the specified string in their name/help text.", 0 ) +#ifdef _DEBUG + CON_COMMAND_MEMBER_F( CCvar, "ccvar_hash_report", HashReport, "report info on bucket distribution of internal hash.", 0 ) +#endif +}; + +void CCvar::CCVarIteratorInternal::SetFirst( void ) +{ + m_hashIter = m_pHash->First(); +} + +void CCvar::CCVarIteratorInternal::Next( void ) +{ + m_hashIter = m_pHash->Next( m_hashIter ); +} + +bool CCvar::CCVarIteratorInternal::IsValid( void ) +{ + return m_pHash->IsValidIterator( m_hashIter ); +} + +ConCommandBase *CCvar::CCVarIteratorInternal::Get( void ) +{ + Assert( IsValid( ) ); + return (*m_pHash)[m_hashIter]; +} + +ICvar::ICVarIteratorInternal *CCvar::FactoryInternalIterator( void ) +{ + return new CCVarIteratorInternal( this ); +} + +//----------------------------------------------------------------------------- +// Factor for CVars +//----------------------------------------------------------------------------- +static CCvar s_Cvar; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CCvar, ICvar, CVAR_INTERFACE_VERSION, s_Cvar ); + + +//----------------------------------------------------------------------------- +// Returns a CVar dictionary for tool usage +//----------------------------------------------------------------------------- +CreateInterfaceFn VStdLib_GetICVarFactory() +{ + return Sys_GetFactoryThis(); +} + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CCvar::CCvar() : m_TempConsoleBuffer( 0, 1024 ), m_SplitScreenAddedConVarsMap( 0, 0, DefLessFunc( ConVar * ) ) +{ + m_nNextDLLIdentifier = 0; + m_pConCommandList = NULL; + m_nMaxSplitScreenSlots = 1; + m_bMaterialSystemThreadSetAllowed = false; + m_CommandHash.Init(); +} + +//----------------------------------------------------------------------------- +// Methods of IAppSystem +//----------------------------------------------------------------------------- +bool CCvar::Connect( CreateInterfaceFn factory ) +{ + ConnectTier1Libraries( &factory, 1 ); + + s_pCVarQuery = (ICvarQuery*)factory( CVAR_QUERY_INTERFACE_VERSION, NULL ); + if ( !s_pCVarQuery ) + { + s_pCVarQuery = &s_DefaultCvarQuery; + } + + ConVar_Register(); + return true; +} + +void CCvar::Disconnect() +{ + ConVar_Unregister(); + s_pCVarQuery = NULL; + DisconnectTier1Libraries(); + + Assert( m_SplitScreenAddedConVarsMap.Count() == 0 ); +} + +InitReturnVal_t CCvar::Init() +{ + return INIT_OK; +} + +void CCvar::Shutdown() +{ +} + +void *CCvar::QueryInterface( const char *pInterfaceName ) +{ + // We implement the ICvar interface + if ( !V_strcmp( pInterfaceName, CVAR_INTERFACE_VERSION ) ) + return (ICvar*)this; + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Method allowing the engine ICvarQuery interface to take over +//----------------------------------------------------------------------------- +void CCvar::InstallCVarQuery( ICvarQuery *pQuery ) +{ + Assert( s_pCVarQuery == &s_DefaultCvarQuery ); + s_pCVarQuery = pQuery ? pQuery : &s_DefaultCvarQuery; +} + + +//----------------------------------------------------------------------------- +// Used by DLLs to be able to unregister all their commands + convars +//----------------------------------------------------------------------------- +CVarDLLIdentifier_t CCvar::AllocateDLLIdentifier() +{ + return m_nNextDLLIdentifier++; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *variable - +//----------------------------------------------------------------------------- +void CCvar::RegisterConCommand( ConCommandBase *variable ) +{ + // Already registered + if ( variable->IsRegistered() ) + return; + + variable->m_bRegistered = true; + + const char *pName = variable->GetName(); + if ( !pName || !pName[0] ) + { + variable->m_pNext = NULL; + return; + } + + // If the variable is already defined, then setup the new variable as a proxy to it. + const ConCommandBase *pOther = FindCommandBase( variable->GetName() ); + if ( pOther ) + { + if ( variable->IsCommand() || pOther->IsCommand() ) + { + Warning( "WARNING: unable to link %s and %s because one or more is a ConCommand.\n", variable->GetName(), pOther->GetName() ); + } + else + { + // This cast is ok because we make sure they're ConVars above. + ConVar *pChildVar = const_cast< ConVar* >( static_cast< const ConVar* >( variable ) ); + ConVar *pParentVar = const_cast< ConVar* >( static_cast< const ConVar* >( pOther ) ); + + // See if it's a valid linkage + if ( s_pCVarQuery->AreConVarsLinkable( pChildVar, pParentVar ) ) + { + // Make sure the default values are the same (but only spew about this for FCVAR_REPLICATED) + if( pChildVar->m_pszDefaultValue && pParentVar->m_pszDefaultValue && + pChildVar->IsFlagSet( FCVAR_REPLICATED ) && pParentVar->IsFlagSet( FCVAR_REPLICATED ) ) + { + if( V_stricmp( pChildVar->m_pszDefaultValue, pParentVar->m_pszDefaultValue ) != 0 ) + { + Warning( "Parent and child ConVars with different default values! %s child: %s parent: %s (parent wins)\n", + variable->GetName(), pChildVar->m_pszDefaultValue, pParentVar->m_pszDefaultValue ); + } + } + + pChildVar->m_pParent = pParentVar->m_pParent; + + // Absorb material thread related convar flags + pParentVar->m_nFlags |= pChildVar->m_nFlags & ( FCVAR_MATERIAL_THREAD_MASK | FCVAR_ACCESSIBLE_FROM_THREADS ); + + // Transfer children's callbacks to parent + if ( pChildVar->m_fnChangeCallbacks.Count() ) + { + for ( int i = 0; i < pChildVar->m_fnChangeCallbacks.Count(); ++i ) + { + pParentVar->m_fnChangeCallbacks.AddToTail( pChildVar->m_fnChangeCallbacks[ i ] ); + } + // Wipe child callbacks + pChildVar->m_fnChangeCallbacks.RemoveAll(); + } + + // make sure we don't have conflicting help strings. + if ( pChildVar->m_pszHelpString && V_strlen( pChildVar->m_pszHelpString ) != 0 ) + { + if ( pParentVar->m_pszHelpString && V_strlen( pParentVar->m_pszHelpString ) != 0 ) + { + if ( V_stricmp( pParentVar->m_pszHelpString, pChildVar->m_pszHelpString ) != 0 ) + { + Warning( "Convar %s has multiple help strings:\n\tparent (wins): \"%s\"\n\tchild: \"%s\"\n", + variable->GetName(), pParentVar->m_pszHelpString, pChildVar->m_pszHelpString ); + } + } + else + { + pParentVar->m_pszHelpString = pChildVar->m_pszHelpString; + } + } + + // make sure we don't have conflicting FCVAR_*** flags. + static int const nFlags[] = + { FCVAR_CHEAT, FCVAR_REPLICATED, FCVAR_DONTRECORD, FCVAR_ARCHIVE, FCVAR_ARCHIVE_GAMECONSOLE }; + static char const * const szFlags[] = + { "FCVAR_CHEAT", "FCVAR_REPLICATED", "FCVAR_DONTRECORD", "FCVAR_ARCHIVE", "FCVAR_ARCHIVE_GAMECONSOLE" }; + + COMPILE_TIME_ASSERT( ARRAYSIZE( nFlags ) == ARRAYSIZE( szFlags ) ); + + for ( int k = 0; k < ARRAYSIZE( nFlags ); ++ k ) + { + if ( ( pChildVar->m_nFlags & nFlags[k] ) != ( pParentVar->m_nFlags & nFlags[k] ) ) + { + Warning( "Convar %s has conflicting %s flags (child: %s%s, parent: %s%s, parent wins)\n", + variable->GetName(), szFlags[k], + ( pChildVar->m_nFlags & nFlags[k] ) ? "has " : "no ", szFlags[k], + ( pParentVar->m_nFlags & nFlags[k] ) ? "has " : "no ", szFlags[k] ); + } + } + } + } + + variable->m_pNext = NULL; + return; + } + + // link the variable in + variable->m_pNext = m_pConCommandList; + m_pConCommandList = variable; + + AssertMsg1(FindCommandBase(variable->GetName()) == NULL, "Console command %s added twice!", + variable->GetName()); + m_CommandHash.Insert(variable); +} + +void CCvar::AddSplitScreenConVars() +{ + if ( m_nMaxSplitScreenSlots == 1 ) + return; + + for( ConCommandBase *pCommand = m_pConCommandList; pCommand; pCommand = pCommand->m_pNext ) + { + if ( pCommand->IsCommand() ) + continue; + + ConVar *pConVar = static_cast< ConVar * >( pCommand ); + + if ( !pConVar->IsFlagSet( FCVAR_SS ) ) + continue; + + // See if it's already mapped in + int idx = m_SplitScreenAddedConVarsMap.Find( pConVar ); + if ( idx == m_SplitScreenAddedConVarsMap.InvalidIndex() ) + { + idx = m_SplitScreenAddedConVarsMap.Insert( pConVar ); + } + + SplitScreenAddedConVars_t &info = m_SplitScreenAddedConVarsMap[ idx ]; + for ( int i = 1 ; i < m_nMaxSplitScreenSlots; ++i ) + { + // Already registered it + if ( info.m_Vars[ i - 1 ].m_pVar ) + continue; + + // start at name2, etc. + info.m_Vars[ i - 1 ].m_VarName = CFmtStr( "%s%d", pConVar->GetName(), i + 1 ); + + CSplitScreenAddedConVar *pVar = new CSplitScreenAddedConVar( i, info.m_Vars[ i - 1 ].m_VarName.Get(), pConVar ); + info.m_Vars[ i - 1 ].m_pVar = pVar; + pVar->SetSplitScreenPlayerSlot( i ); + + RegisterConCommand( pVar ); + } + } + + ConCommandBase::s_pConCommandBases = NULL; +} + +void CCvar::RemoveSplitScreenConVars( CVarDLLIdentifier_t id ) + +{ + if ( m_nMaxSplitScreenSlots == 1 ) + { + Assert( m_SplitScreenAddedConVarsMap.Count() == 0 ); + return; + } + + CUtlVector< ConVar * > deleted; + + FOR_EACH_MAP( m_SplitScreenAddedConVarsMap, i ) + { + ConVar *key = m_SplitScreenAddedConVarsMap.Key( i ); + + if ( key->GetDLLIdentifier() != id ) + { + continue; + } + + SplitScreenAddedConVars_t &info = m_SplitScreenAddedConVarsMap[ i ]; + + + + for ( int i = 1 ; i < m_nMaxSplitScreenSlots; ++i ) + { + + if ( info.m_Vars[ i - 1 ].m_pVar ) + { + UnregisterConCommand( info.m_Vars[ i - 1 ].m_pVar ); + delete info.m_Vars[ i - 1 ].m_pVar; + info.m_Vars[ i - 1 ].m_pVar = NULL; + } + } + deleted.AddToTail( key ); + } + + for ( int i = 0; i < deleted.Count(); ++i ) + { + m_SplitScreenAddedConVarsMap.Remove( deleted[ i ] ); + } +} + +void CCvar::UnregisterConCommand( ConCommandBase *pCommandToRemove ) +{ + // Not registered? Don't bother + if ( !pCommandToRemove->IsRegistered() ) + return; + + pCommandToRemove->m_bRegistered = false; + + // FIXME: Should we make this a doubly-linked list? Would remove faster + ConCommandBase *pPrev = NULL; + for( ConCommandBase *pCommand = m_pConCommandList; pCommand; pCommand = pCommand->m_pNext ) + { + if ( pCommand != pCommandToRemove ) + { + pPrev = pCommand; + continue; + } + + if ( pPrev == NULL ) + { + m_pConCommandList = pCommand->m_pNext; + } + else + { + pPrev->m_pNext = pCommand->m_pNext; + } + pCommand->m_pNext = NULL; + m_CommandHash.Remove(m_CommandHash.Find(pCommand)); + break; + } +} + +void CCvar::UnregisterConCommands( CVarDLLIdentifier_t id ) +{ + ConCommandBase *pNewList; + ConCommandBase *pCommand, *pNext; + + pNewList = NULL; + + m_CommandHash.Purge( true ); + pCommand = m_pConCommandList; + while ( pCommand ) + { + pNext = pCommand->m_pNext; + if ( pCommand->GetDLLIdentifier() != id ) + { + pCommand->m_pNext = pNewList; + pNewList = pCommand; + + m_CommandHash.Insert( pCommand ); + } + else + { + // Unlink + pCommand->m_bRegistered = false; + pCommand->m_pNext = NULL; + + } + + pCommand = pNext; + } + + m_pConCommandList = pNewList; +} + + +//----------------------------------------------------------------------------- +// Finds base commands +//----------------------------------------------------------------------------- +const ConCommandBase *CCvar::FindCommandBase( const char *name ) const +{ + VPROF_INCREMENT_COUNTER( "CCvar::FindCommandBase", 1 ); + VPROF_BUDGET( "CCvar::FindCommandBase", VPROF_BUDGETGROUP_CVAR_FIND ); + + return m_CommandHash.FindPtr( name ); +} + +ConCommandBase *CCvar::FindCommandBase( const char *name ) +{ + VPROF_INCREMENT_COUNTER( "CCvar::FindCommandBase", 1 ); + VPROF_BUDGET( "CCvar::FindCommandBase", VPROF_BUDGETGROUP_CVAR_FIND ); + + return m_CommandHash.FindPtr( name ); +} + + +//----------------------------------------------------------------------------- +// Purpose Finds ConVars +//----------------------------------------------------------------------------- +const ConVar *CCvar::FindVar( const char *var_name ) const +{ + const ConCommandBase *var = FindCommandBase( var_name ); + if ( !var ) + { + return NULL; + } + else + { + if (var->IsCommand()) + { + AssertMsg1( false , "Tried to look up command %s as if it were a variable.\n", var_name ); + return NULL; + } + } + + return static_cast<const ConVar*>(var); +} + +ConVar *CCvar::FindVar( const char *var_name ) +{ + ConCommandBase *var = FindCommandBase( var_name ); + if ( !var ) + { + return NULL; + } + else + { + if (var->IsCommand()) + { + AssertMsg1( false , "Tried to look up command %s as if it were a variable.\n", var_name ); + return NULL; + } + } + + return static_cast<ConVar*>( var ); +} + + +//----------------------------------------------------------------------------- +// Purpose Finds ConCommands +//----------------------------------------------------------------------------- +const ConCommand *CCvar::FindCommand( const char *pCommandName ) const +{ + const ConCommandBase *var = FindCommandBase( pCommandName ); + if ( !var || !var->IsCommand() ) + return NULL; + + return static_cast<const ConCommand*>(var); +} + +ConCommand *CCvar::FindCommand( const char *pCommandName ) +{ + ConCommandBase *var = FindCommandBase( pCommandName ); + if ( !var || !var->IsCommand() ) + return NULL; + + return static_cast<ConCommand*>( var ); +} + + +const char* CCvar::GetCommandLineValue( const char *pVariableName ) +{ + int nLen = V_strlen(pVariableName); + char *pSearch = (char*)stackalloc( nLen + 2 ); + pSearch[0] = '+'; + memcpy( &pSearch[1], pVariableName, nLen + 1 ); + return CommandLine()->ParmValue( pSearch ); +} + +//----------------------------------------------------------------------------- +// Install, remove global callbacks +//----------------------------------------------------------------------------- +void CCvar::InstallGlobalChangeCallback( FnChangeCallback_t callback ) +{ + Assert( callback && m_GlobalChangeCallbacks.Find( callback ) < 0 ); + m_GlobalChangeCallbacks.AddToTail( callback ); +} + +void CCvar::RemoveGlobalChangeCallback( FnChangeCallback_t callback ) +{ + Assert( callback ); + m_GlobalChangeCallbacks.FindAndRemove( callback ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCvar::CallGlobalChangeCallbacks( ConVar *var, const char *pOldString, float flOldValue ) +{ + int nCallbackCount = m_GlobalChangeCallbacks.Count(); + for ( int i = 0; i < nCallbackCount; ++i ) + { + (*m_GlobalChangeCallbacks[i])( var, pOldString, flOldValue ); + } +} + + +//----------------------------------------------------------------------------- +// Sets convars containing the flags to their default value +//----------------------------------------------------------------------------- +void CCvar::RevertFlaggedConVars( int nFlag ) +{ + for ( CConCommandHash::CCommandHashIterator_t i = m_CommandHash.First() ; + m_CommandHash.IsValidIterator( i ) ; + i = m_CommandHash.Next( i ) ) + { + ConCommandBase *var = m_CommandHash[ i ]; + if ( var->IsCommand() ) + continue; + + ConVar *cvar = ( ConVar * )var; + + if ( !cvar->IsFlagSet( nFlag ) ) + continue; + + // It's == to the default value, don't count + if ( !V_stricmp( cvar->GetDefault(), cvar->GetString() ) ) + continue; + + cvar->Revert(); + // DevMsg( "%s = \"%s\" (reverted)\n", cvar->GetName(), cvar->GetString() ); + } +} + + +//----------------------------------------------------------------------------- +// Deal with queued material system convars +//----------------------------------------------------------------------------- +bool CCvar::IsMaterialThreadSetAllowed( ) const +{ + Assert( ThreadInMainThread() ); + return m_bMaterialSystemThreadSetAllowed; +} + +void CCvar::QueueMaterialThreadSetValue( ConVar *pConVar, const char *pValue ) +{ + Assert( ThreadInMainThread() ); + int j = m_QueuedConVarSets.AddToTail(); + m_QueuedConVarSets[j].m_pConVar = pConVar; + m_QueuedConVarSets[j].m_nType = CONVAR_SET_STRING; + m_QueuedConVarSets[j].m_String = pValue; +} + +void CCvar::QueueMaterialThreadSetValue( ConVar *pConVar, int nValue ) +{ + Assert( ThreadInMainThread() ); + int j = m_QueuedConVarSets.AddToTail(); + m_QueuedConVarSets[j].m_pConVar = pConVar; + m_QueuedConVarSets[j].m_nType = CONVAR_SET_INT; + m_QueuedConVarSets[j].m_nInt = nValue; +} + +void CCvar::QueueMaterialThreadSetValue( ConVar *pConVar, float flValue ) +{ + Assert( ThreadInMainThread() ); + int j = m_QueuedConVarSets.AddToTail(); + m_QueuedConVarSets[j].m_pConVar = pConVar; + m_QueuedConVarSets[j].m_nType = CONVAR_SET_FLOAT; + m_QueuedConVarSets[j].m_flFloat = flValue; +} + +bool CCvar::HasQueuedMaterialThreadConVarSets() const +{ + Assert( ThreadInMainThread() ); + return m_QueuedConVarSets.Count() > 0; +} + +int CCvar::ProcessQueuedMaterialThreadConVarSets() +{ + Assert( ThreadInMainThread() ); + m_bMaterialSystemThreadSetAllowed = true; + + int nUpdateFlags = 0; + int nCount = m_QueuedConVarSets.Count(); + for ( int i = 0; i < nCount; ++i ) + { + const QueuedConVarSet_t& set = m_QueuedConVarSets[i]; + switch( set.m_nType ) + { + case CONVAR_SET_FLOAT: + set.m_pConVar->SetValue( set.m_flFloat ); + break; + case CONVAR_SET_INT: + set.m_pConVar->SetValue( set.m_nInt ); + break; + case CONVAR_SET_STRING: + set.m_pConVar->SetValue( set.m_String ); + break; + } + + nUpdateFlags |= set.m_pConVar->GetFlags() & FCVAR_MATERIAL_THREAD_MASK; + } + + m_QueuedConVarSets.RemoveAll(); + m_bMaterialSystemThreadSetAllowed = false; + return nUpdateFlags; +} + + +//----------------------------------------------------------------------------- +// Display queued messages +//----------------------------------------------------------------------------- +void CCvar::DisplayQueuedMessages( ) +{ + // Display any queued up messages + if ( m_TempConsoleBuffer.TellPut() == 0 ) + return; + + Color clr; + int nStringLength; + while( m_TempConsoleBuffer.IsValid() ) + { + int nType = m_TempConsoleBuffer.GetChar(); + if ( nType == CONSOLE_COLOR_PRINT ) + { + clr.SetRawColor( m_TempConsoleBuffer.GetInt() ); + } + nStringLength = m_TempConsoleBuffer.PeekStringLength(); + char* pTemp = (char*)stackalloc( nStringLength + 1 ); + m_TempConsoleBuffer.GetString( pTemp ); + + switch( nType ) + { + case CONSOLE_COLOR_PRINT: + ConsoleColorPrintf( clr, pTemp ); + break; + + case CONSOLE_PRINT: + ConsolePrintf( pTemp ); + break; + + case CONSOLE_DPRINT: + ConsoleDPrintf( pTemp ); + break; + } + } + + m_TempConsoleBuffer.Purge(); +} + + +//----------------------------------------------------------------------------- +// Install a console printer +//----------------------------------------------------------------------------- +void CCvar::InstallConsoleDisplayFunc( IConsoleDisplayFunc* pDisplayFunc ) +{ + Assert( m_DisplayFuncs.Find( pDisplayFunc ) < 0 ); + m_DisplayFuncs.AddToTail( pDisplayFunc ); + DisplayQueuedMessages(); +} + +void CCvar::RemoveConsoleDisplayFunc( IConsoleDisplayFunc* pDisplayFunc ) +{ + m_DisplayFuncs.FindAndRemove( pDisplayFunc ); +} + +int CCvar::GetConsoleDisplayFuncCount() const +{ + return m_DisplayFuncs.Count(); +} + +void CCvar::GetConsoleText( int nDisplayFuncIndex, char *pchText, size_t bufSize ) const +{ + m_DisplayFuncs[ nDisplayFuncIndex ]->GetConsoleText( pchText, bufSize ); +} + +void CCvar::ConsoleColorPrintf( const Color& clr, const char *pFormat, ... ) const +{ + char temp[ 8192 ]; + va_list argptr; + va_start( argptr, pFormat ); + _vsnprintf( temp, sizeof( temp ) - 1, pFormat, argptr ); + va_end( argptr ); + temp[ sizeof( temp ) - 1 ] = 0; + + int c = m_DisplayFuncs.Count(); + if ( c == 0 ) + { + m_TempConsoleBuffer.PutChar( CONSOLE_COLOR_PRINT ); + m_TempConsoleBuffer.PutInt( clr.GetRawColor() ); + m_TempConsoleBuffer.PutString( temp ); + return; + } + + for ( int i = 0 ; i < c; ++i ) + { + m_DisplayFuncs[ i ]->ColorPrint( clr, temp ); + } +} + +void CCvar::ConsolePrintf( const char *pFormat, ... ) const +{ + char temp[ 8192 ]; + va_list argptr; + va_start( argptr, pFormat ); + _vsnprintf( temp, sizeof( temp ) - 1, pFormat, argptr ); + va_end( argptr ); + temp[ sizeof( temp ) - 1 ] = 0; + + int c = m_DisplayFuncs.Count(); + if ( c == 0 ) + { + m_TempConsoleBuffer.PutChar( CONSOLE_PRINT ); + m_TempConsoleBuffer.PutString( temp ); + return; + } + + for ( int i = 0 ; i < c; ++i ) + { + m_DisplayFuncs[ i ]->Print( temp ); + } +} + +void CCvar::ConsoleDPrintf( const char *pFormat, ... ) const +{ + char temp[ 8192 ]; + va_list argptr; + va_start( argptr, pFormat ); + _vsnprintf( temp, sizeof( temp ) - 1, pFormat, argptr ); + va_end( argptr ); + temp[ sizeof( temp ) - 1 ] = 0; + + int c = m_DisplayFuncs.Count(); + if ( c == 0 ) + { + m_TempConsoleBuffer.PutChar( CONSOLE_DPRINT ); + m_TempConsoleBuffer.PutString( temp ); + return; + } + + for ( int i = 0 ; i < c; ++i ) + { + m_DisplayFuncs[ i ]->DPrint( temp ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +#if defined( USE_VXCONSOLE ) +#ifdef _PS3 +/* +Here's a terrible hack. +In porting the part of the game that speaks to VXConsole, EA chose to +write it as a cluster of global functions, instead of a class interface like +Aaron did with IXboxConsole. Some of these globals need access to symbols +inside the engine, so they are defined there. However, CCvar is inside vstdlib. +In the EA build this didn't make a difference because everything was a huge +monolithic executable, and you could just access any symbol from anywhere. +In our build, with its PRXes, that doesn't fly. +So, the proper solution to this problem is to wrap all of the PS3 vxconsole +stuff in an interface, put it inside vstlib, create the dcim connection there, +and then export the interface pointer. The engine meanwhile would export the +symbols the vxlib needs, and then we give that interface class inside +vstlib a pointer to the engine once the engine is available. +Right now however I just want to get the thing working with as little modification +as possible so I can fix the vxconsole windows app itself and hopefully get +bidirectional TTY to our game. So, instead of the proper solution, +I'm just duct-taping everything together by simply passing a pointer to the engine +symbol this function needs whenever I call it. +Blech. I'll fix it later. +-egr 4/29/10. (is it later than September 2010? go call egr and make fun of him.) +*/ +void CCvar::PublishToVXConsole() +#else +void CCvar::PublishToVXConsole() +#endif +{ + const char *commands[6*1024]; + const char *helptext[6*1024]; + int numCommands = 0; + + // iterate and publish commands to the remote console + for ( CConCommandHash::CCommandHashIterator_t i = m_CommandHash.First() ; + m_CommandHash.IsValidIterator( i ) ; + i = m_CommandHash.Next( i ) ) + { + ConCommandBase *pCur = m_CommandHash[ i ]; + // add unregistered commands to list + if ( numCommands < sizeof(commands)/sizeof(commands[0]) ) + { + commands[numCommands] = pCur->GetName(); + helptext[numCommands] = pCur->GetHelpText(); + numCommands++; + } + } + + if ( numCommands ) + { +#ifdef _PS3 + g_pValvePS3Console->AddCommands( numCommands, commands, helptext ); +#else + XBX_rAddCommands( numCommands, commands, helptext ); +#endif + } +} + +#endif + + +static bool ConVarSortFunc( ConCommandBase * const &lhs, ConCommandBase * const &rhs ) +{ + return CaselessStringLessThan( lhs->GetName(), rhs->GetName() ); +} + +//----------------------------------------------------------------------------- +// Console commands +//----------------------------------------------------------------------------- +void CCvar::Find( const CCommand &args ) +{ + const char *search; + + if ( args.ArgC() != 2 ) + { + ConMsg( "Usage: find <string>\n" ); + return; + } + + // Get substring to find + search = args[1]; + + CUtlRBTree< ConCommandBase *, int > sorted( 0, 0, ConVarSortFunc ); + + // Loop through vars and print out findings + for ( CConCommandHash::CCommandHashIterator_t i = m_CommandHash.First() ; + m_CommandHash.IsValidIterator(i) ; + i = m_CommandHash.Next(i) ) + { + ConCommandBase *var = m_CommandHash[ i ]; + if ( var->IsFlagSet(FCVAR_DEVELOPMENTONLY) || var->IsFlagSet(FCVAR_HIDDEN) ) + continue; + + if ( !V_stristr( var->GetName(), search ) && + !V_stristr( var->GetHelpText(), search ) ) + continue; + + sorted.Insert( var ); + } + + for ( int i = sorted.FirstInorder(); i != sorted.InvalidIndex(); i = sorted.NextInorder( i ) ) + { + ConVar_PrintDescription( sorted[ i ] ); + } +} + +#ifdef _DEBUG +void CCvar::HashReport( const CCommand &args ) +{ + m_CommandHash.Report(); +} +#endif + + +void CCvar::SetMaxSplitScreenSlots( int nSlots ) +{ + m_nMaxSplitScreenSlots = nSlots; + + AddSplitScreenConVars(); +} + +int CCvar::GetMaxSplitScreenSlots() const +{ + return m_nMaxSplitScreenSlots; +} + + + +//----------------------------------------------------------------------------- +// Console command hash data structure +//----------------------------------------------------------------------------- +CConCommandHash::CConCommandHash() +{ + Purge( true ); +} + +CConCommandHash::~CConCommandHash() +{ + Purge( false ); +} + +void CConCommandHash::Purge( bool bReinitialize ) +{ + m_aBuckets.Purge(); + m_aDataPool.Purge(); + if ( bReinitialize ) + { + Init(); + } +} + +// Initialize. +void CConCommandHash::Init( void ) +{ + // kNUM_BUCKETS must be a power of two. + COMPILE_TIME_ASSERT((kNUM_BUCKETS & ( kNUM_BUCKETS - 1 )) == 0); + + // Set the bucket size. + m_aBuckets.SetSize( kNUM_BUCKETS ); + for ( int iBucket = 0; iBucket < kNUM_BUCKETS; ++iBucket ) + { + m_aBuckets[iBucket] = m_aDataPool.InvalidIndex(); + } + + // Calculate the grow size. + int nGrowSize = 4 * kNUM_BUCKETS; + m_aDataPool.SetGrowSize( nGrowSize ); +} + +//----------------------------------------------------------------------------- +// Purpose: Insert data into the hash table given its key (unsigned int), +// WITH a check to see if the element already exists within the hash. +//----------------------------------------------------------------------------- +CConCommandHash::CCommandHashHandle_t CConCommandHash::Insert( ConCommandBase *cmd ) +{ + // Check to see if that key already exists in the buckets (should be unique). + CCommandHashHandle_t hHash = Find( cmd ); + if( hHash != InvalidHandle() ) + return hHash; + + return FastInsert( cmd ); +} +//----------------------------------------------------------------------------- +// Purpose: Insert data into the hash table given its key (unsigned int), +// WITHOUT a check to see if the element already exists within the hash. +//----------------------------------------------------------------------------- +CConCommandHash::CCommandHashHandle_t CConCommandHash::FastInsert( ConCommandBase *cmd ) +{ + // Get a new element from the pool. + int iHashData = m_aDataPool.Alloc( true ); + HashEntry_t * pHashData = &m_aDataPool[iHashData]; + if ( !pHashData ) + return InvalidHandle(); + + HashKey_t key = Hash(cmd); + + // Add data to new element. + pHashData->m_uiKey = key; + pHashData->m_Data = cmd; + + // Link element. + int iBucket = key & kBUCKETMASK ; // HashFuncs::Hash( uiKey, m_uiBucketMask ); + m_aDataPool.LinkBefore( m_aBuckets[iBucket], iHashData ); + m_aBuckets[iBucket] = iHashData; + + return iHashData; +} + +//----------------------------------------------------------------------------- +// Purpose: Remove a given element from the hash. +//----------------------------------------------------------------------------- +void CConCommandHash::Remove( CCommandHashHandle_t hHash ) +{ + HashEntry_t * entry = &m_aDataPool[hHash]; + HashKey_t iBucket = entry->m_uiKey & kBUCKETMASK ; + if ( m_aBuckets[iBucket] == hHash ) + { + // It is a bucket head. + m_aBuckets[iBucket] = m_aDataPool.Next( hHash ); + } + else + { + // Not a bucket head. + m_aDataPool.Unlink( hHash ); + } + + // Remove the element. + m_aDataPool.Remove( hHash ); +} + +//----------------------------------------------------------------------------- +// Purpose: Remove all elements from the hash +//----------------------------------------------------------------------------- +void CConCommandHash::RemoveAll( void ) +{ + m_aBuckets.RemoveAll(); + m_aDataPool.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Find hash entry corresponding to a string name +//----------------------------------------------------------------------------- +CConCommandHash::CCommandHashHandle_t CConCommandHash::Find( const char *name, HashKey_t hashkey) const +{ + // hash the "key" - get the correct hash table "bucket" + int iBucket = hashkey & kBUCKETMASK; + + for ( datapool_t::IndexLocalType_t iElement = m_aBuckets[iBucket]; iElement != m_aDataPool.InvalidIndex(); iElement = m_aDataPool.Next( iElement ) ) + { + const HashEntry_t &element = m_aDataPool[iElement]; + if ( element.m_uiKey == hashkey && // if hashes of strings match, + V_stricmp( name, element.m_Data->GetName() ) == 0) // then test the actual strings + { + return iElement; + } + } + + // found nuffink + return InvalidHandle(); +} + +//----------------------------------------------------------------------------- +// Find a command in the hash. +//----------------------------------------------------------------------------- +CConCommandHash::CCommandHashHandle_t CConCommandHash::Find( const ConCommandBase *cmd ) const +{ + // Set this #if to 1 if the assert at bottom starts whining -- + // that indicates that a console command is being double-registered, + // or something similarly nonfatally bad. With this #if 1, we'll search + // by name instead of by pointer, which is more robust in the face + // of double registered commands, but obviously slower. +#if 0 + return Find(cmd->GetName()); +#else + HashKey_t hashkey = Hash(cmd); + int iBucket = hashkey & kBUCKETMASK; + + // hunt through all entries in that bucket + for ( datapool_t::IndexLocalType_t iElement = m_aBuckets[iBucket]; iElement != m_aDataPool.InvalidIndex(); iElement = m_aDataPool.Next( iElement ) ) + { + const HashEntry_t &element = m_aDataPool[iElement]; + if ( element.m_uiKey == hashkey && // if the hashes match... + element.m_Data == cmd ) // and the pointers... + { + // in debug, test to make sure we don't have commands under the same name + // or something goofy like that + AssertMsg1( iElement == Find(cmd->GetName()), + "ConCommand %s had two entries in the hash!", cmd->GetName() ); + + // return this element + return iElement; + } + } + + // found nothing. +#ifdef DBGFLAG_ASSERT // double check against search by name + CCommandHashHandle_t dbghand = Find(cmd->GetName()); + + AssertMsg1( InvalidHandle() == dbghand, + "ConCommand %s couldn't be found by pointer, but was found by name!", cmd->GetName() ); +#endif + return InvalidHandle(); +#endif +} + + +#ifdef _DEBUG +// Dump a report to MSG +void CConCommandHash::Report( void ) +{ + Msg("Console command hash bucket load:\n"); + int total = 0; + for ( int iBucket = 0 ; iBucket < kNUM_BUCKETS ; ++iBucket ) + { + int count = 0; + CCommandHashHandle_t iElement = m_aBuckets[iBucket]; // get the head of the bucket + while ( iElement != m_aDataPool.InvalidIndex() ) + { + ++count; + iElement = m_aDataPool.Next( iElement ); + } + + Msg( "%d: %d\n", iBucket, count ); + total += count; + } + + Msg("\tAverage: %.1f\n", total / ((float)(kNUM_BUCKETS))); +} +#endif |