1312 lines
37 KiB
C++
1312 lines
37 KiB
C++
//===== 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 ( !Q_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 ) RESTRICT;
|
|
virtual void Next( void ) RESTRICT;
|
|
virtual bool IsValid( void ) RESTRICT;
|
|
virtual ConCommandBase *Get( void ) RESTRICT;
|
|
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 ) RESTRICT
|
|
{
|
|
m_hashIter = m_pHash->First();
|
|
}
|
|
|
|
void CCvar::CCVarIteratorInternal::Next( void ) RESTRICT
|
|
{
|
|
m_hashIter = m_pHash->Next( m_hashIter );
|
|
}
|
|
|
|
bool CCvar::CCVarIteratorInternal::IsValid( void ) RESTRICT
|
|
{
|
|
return m_pHash->IsValidIterator( m_hashIter );
|
|
}
|
|
|
|
ConCommandBase *CCvar::CCVarIteratorInternal::Get( void ) RESTRICT
|
|
{
|
|
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() )
|
|
{
|
|
#ifdef _DEBUG
|
|
// Don't warn if the commands are the same - this happens with some debug only commands
|
|
if( Q_stricmp(variable->GetName(), pOther->GetName() ) != 0 )
|
|
#endif
|
|
{
|
|
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( Q_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 && Q_strlen( pChildVar->m_pszHelpString ) != 0 )
|
|
{
|
|
if ( pParentVar->m_pszHelpString && Q_strlen( pParentVar->m_pszHelpString ) != 0 )
|
|
{
|
|
if ( Q_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())
|
|
{
|
|
Warning("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())
|
|
{
|
|
Warning("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 = Q_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 ( !Q_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;
|
|
CUtlBuffer bufStringToken;
|
|
while( m_TempConsoleBuffer.IsValid() )
|
|
{
|
|
int nType = m_TempConsoleBuffer.GetChar();
|
|
if ( nType == CONSOLE_COLOR_PRINT )
|
|
{
|
|
clr.SetRawColor( m_TempConsoleBuffer.GetInt() );
|
|
}
|
|
|
|
int nStringLength = m_TempConsoleBuffer.PeekStringLength();
|
|
bufStringToken.EnsureCapacity( nStringLength + 1 );
|
|
|
|
char* pTemp = (char*) bufStringToken.Base();
|
|
m_TempConsoleBuffer.GetString( pTemp, nStringLength + 1 );
|
|
|
|
switch( nType )
|
|
{
|
|
case CONSOLE_COLOR_PRINT:
|
|
ConsoleColorPrintf( clr, "%s", pTemp );
|
|
break;
|
|
|
|
case CONSOLE_PRINT:
|
|
ConsolePrintf( "%s", pTemp );
|
|
break;
|
|
|
|
case CONSOLE_DPRINT:
|
|
ConsoleDPrintf( "%s", 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 ( !Q_stristr( var->GetName(), search ) &&
|
|
!Q_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.
|
|
intp iHashData = m_aDataPool.Alloc( true );
|
|
HashEntry_t * RESTRICT 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 ) RESTRICT
|
|
{
|
|
HashEntry_t * RESTRICT 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 RESTRICT
|
|
{
|
|
// 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,
|
|
Q_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 RESTRICT
|
|
{
|
|
// 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
|