Counter Strike : Global Offensive Source Code
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.

1311 lines
37 KiB

  1. //===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include "vstdlib/cvar.h"
  9. #include <ctype.h>
  10. #include "tier0/icommandline.h"
  11. #include "tier1/utlrbtree.h"
  12. #include "tier1/strtools.h"
  13. #include "tier1/keyvalues.h"
  14. #include "tier1/convar.h"
  15. #include "tier0/vprof.h"
  16. #include "tier1/tier1.h"
  17. #include "tier1/utlbuffer.h"
  18. #include "tier1/utlmap.h"
  19. #include "tier1/fmtstr.h"
  20. #ifdef _X360
  21. #include "xbox/xbox_console.h"
  22. #elif defined( _PS3 )
  23. #include "ps3/ps3_console.h"
  24. #endif
  25. #ifdef POSIX
  26. #include <wctype.h>
  27. #include <wchar.h>
  28. #define VPROJ_INCREMENT_COUNTER(a, b) /* */
  29. #define VPROJ(a) /* */
  30. #endif
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include "tier0/memdbgon.h"
  33. //-----------------------------------------------------------------------------
  34. // Default implementation of CvarQuery
  35. //-----------------------------------------------------------------------------
  36. class CDefaultCvarQuery : public CBaseAppSystem< ICvarQuery >
  37. {
  38. public:
  39. virtual void *QueryInterface( const char *pInterfaceName )
  40. {
  41. if ( !Q_stricmp( pInterfaceName, CVAR_QUERY_INTERFACE_VERSION ) )
  42. return (ICvarQuery*)this;
  43. return NULL;
  44. }
  45. virtual bool AreConVarsLinkable( const ConVar *child, const ConVar *parent )
  46. {
  47. return true;
  48. }
  49. };
  50. static CDefaultCvarQuery s_DefaultCvarQuery;
  51. static ICvarQuery *s_pCVarQuery = NULL;
  52. #include "concommandhash.h"
  53. //-----------------------------------------------------------------------------
  54. // Default implementation
  55. //-----------------------------------------------------------------------------
  56. class CCvar : public CBaseAppSystem< ICvar >
  57. {
  58. public:
  59. CCvar();
  60. // Methods of IAppSystem
  61. virtual bool Connect( CreateInterfaceFn factory );
  62. virtual void Disconnect();
  63. virtual void *QueryInterface( const char *pInterfaceName );
  64. virtual InitReturnVal_t Init();
  65. virtual void Shutdown();
  66. // Inherited from ICVar
  67. virtual CVarDLLIdentifier_t AllocateDLLIdentifier();
  68. virtual void RegisterConCommand( ConCommandBase *pCommandBase );
  69. virtual void UnregisterConCommand( ConCommandBase *pCommandBase );
  70. virtual void UnregisterConCommands( CVarDLLIdentifier_t id );
  71. virtual const char* GetCommandLineValue( const char *pVariableName );
  72. virtual ConCommandBase *FindCommandBase( const char *name );
  73. virtual const ConCommandBase *FindCommandBase( const char *name ) const;
  74. virtual ConVar *FindVar ( const char *var_name );
  75. virtual const ConVar *FindVar ( const char *var_name ) const;
  76. virtual ConCommand *FindCommand( const char *name );
  77. virtual const ConCommand *FindCommand( const char *name ) const;
  78. virtual void InstallGlobalChangeCallback( FnChangeCallback_t callback );
  79. virtual void RemoveGlobalChangeCallback( FnChangeCallback_t callback );
  80. virtual void CallGlobalChangeCallbacks( ConVar *var, const char *pOldString, float flOldValue );
  81. virtual void InstallConsoleDisplayFunc( IConsoleDisplayFunc* pDisplayFunc );
  82. virtual void RemoveConsoleDisplayFunc( IConsoleDisplayFunc* pDisplayFunc );
  83. virtual void ConsoleColorPrintf( const Color& clr, const char *pFormat, ... ) const;
  84. virtual void ConsolePrintf( const char *pFormat, ... ) const;
  85. virtual void ConsoleDPrintf( const char *pFormat, ... ) const;
  86. virtual void RevertFlaggedConVars( int nFlag );
  87. virtual void InstallCVarQuery( ICvarQuery *pQuery );
  88. #if defined( USE_VXCONSOLE )
  89. virtual void PublishToVXConsole( );
  90. #endif
  91. virtual void SetMaxSplitScreenSlots( int nSlots );
  92. virtual int GetMaxSplitScreenSlots() const;
  93. virtual void AddSplitScreenConVars();
  94. virtual void RemoveSplitScreenConVars( CVarDLLIdentifier_t id );
  95. virtual int GetConsoleDisplayFuncCount() const;
  96. virtual void GetConsoleText( int nDisplayFuncIndex, char *pchText, size_t bufSize ) const;
  97. virtual bool IsMaterialThreadSetAllowed( ) const;
  98. virtual void QueueMaterialThreadSetValue( ConVar *pConVar, const char *pValue );
  99. virtual void QueueMaterialThreadSetValue( ConVar *pConVar, int nValue );
  100. virtual void QueueMaterialThreadSetValue( ConVar *pConVar, float flValue );
  101. virtual bool HasQueuedMaterialThreadConVarSets() const;
  102. virtual int ProcessQueuedMaterialThreadConVarSets();
  103. private:
  104. enum
  105. {
  106. CONSOLE_COLOR_PRINT = 0,
  107. CONSOLE_PRINT,
  108. CONSOLE_DPRINT,
  109. };
  110. void DisplayQueuedMessages( );
  111. CUtlVector< FnChangeCallback_t > m_GlobalChangeCallbacks;
  112. CUtlVector< IConsoleDisplayFunc* > m_DisplayFuncs;
  113. int m_nNextDLLIdentifier;
  114. ConCommandBase *m_pConCommandList;
  115. CConCommandHash m_CommandHash;
  116. // temporary console area so we can store prints before console display funs are installed
  117. mutable CUtlBuffer m_TempConsoleBuffer;
  118. int m_nMaxSplitScreenSlots;
  119. protected:
  120. // internals for ICVarIterator
  121. class CCVarIteratorInternal : public ICVarIteratorInternal
  122. {
  123. public:
  124. CCVarIteratorInternal( CCvar *outer )
  125. : m_pOuter( outer ), m_pHash( &outer->m_CommandHash ), // remember my CCvar,
  126. m_hashIter( -1, -1 ) // and invalid iterator
  127. {}
  128. virtual void SetFirst( void ) RESTRICT;
  129. virtual void Next( void ) RESTRICT;
  130. virtual bool IsValid( void ) RESTRICT;
  131. virtual ConCommandBase *Get( void ) RESTRICT;
  132. protected:
  133. CCvar * const m_pOuter;
  134. CConCommandHash * const m_pHash;
  135. CConCommandHash::CCommandHashIterator_t m_hashIter;
  136. };
  137. virtual ICVarIteratorInternal *FactoryInternalIterator( void );
  138. friend class CCVarIteratorInternal;
  139. enum ConVarSetType_t
  140. {
  141. CONVAR_SET_STRING = 0,
  142. CONVAR_SET_INT,
  143. CONVAR_SET_FLOAT,
  144. };
  145. struct QueuedConVarSet_t
  146. {
  147. ConVar *m_pConVar;
  148. ConVarSetType_t m_nType;
  149. int m_nInt;
  150. float m_flFloat;
  151. CUtlString m_String;
  152. };
  153. struct SplitScreenConVar_t
  154. {
  155. SplitScreenConVar_t()
  156. {
  157. Reset();
  158. }
  159. void Reset()
  160. {
  161. m_VarName = "";
  162. m_pVar = NULL;
  163. }
  164. CUtlString m_VarName;
  165. CSplitScreenAddedConVar *m_pVar;
  166. };
  167. struct SplitScreenAddedConVars_t
  168. {
  169. SplitScreenAddedConVars_t()
  170. {
  171. for ( int i = 0; i < MAX_SPLITSCREEN_CLIENTS - 1; ++i )
  172. {
  173. m_Vars[ i ].Reset();
  174. }
  175. }
  176. // Var names need "static" buffers...
  177. SplitScreenConVar_t m_Vars[ MAX_SPLITSCREEN_CLIENTS - 1 ];
  178. };
  179. CUtlMap< ConVar *, SplitScreenAddedConVars_t > m_SplitScreenAddedConVarsMap;
  180. CUtlVector< QueuedConVarSet_t > m_QueuedConVarSets;
  181. bool m_bMaterialSystemThreadSetAllowed;
  182. private:
  183. // Standard console commands -- DO NOT PLACE ANY HIGHER THAN HERE BECAUSE THESE MUST BE THE FIRST TO DESTRUCT
  184. CON_COMMAND_MEMBER_F( CCvar, "find", Find, "Find concommands with the specified string in their name/help text.", 0 )
  185. #ifdef _DEBUG
  186. CON_COMMAND_MEMBER_F( CCvar, "ccvar_hash_report", HashReport, "report info on bucket distribution of internal hash.", 0 )
  187. #endif
  188. };
  189. void CCvar::CCVarIteratorInternal::SetFirst( void ) RESTRICT
  190. {
  191. m_hashIter = m_pHash->First();
  192. }
  193. void CCvar::CCVarIteratorInternal::Next( void ) RESTRICT
  194. {
  195. m_hashIter = m_pHash->Next( m_hashIter );
  196. }
  197. bool CCvar::CCVarIteratorInternal::IsValid( void ) RESTRICT
  198. {
  199. return m_pHash->IsValidIterator( m_hashIter );
  200. }
  201. ConCommandBase *CCvar::CCVarIteratorInternal::Get( void ) RESTRICT
  202. {
  203. Assert( IsValid( ) );
  204. return (*m_pHash)[m_hashIter];
  205. }
  206. ICvar::ICVarIteratorInternal *CCvar::FactoryInternalIterator( void )
  207. {
  208. return new CCVarIteratorInternal( this );
  209. }
  210. //-----------------------------------------------------------------------------
  211. // Factor for CVars
  212. //-----------------------------------------------------------------------------
  213. static CCvar s_Cvar;
  214. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CCvar, ICvar, CVAR_INTERFACE_VERSION, s_Cvar );
  215. //-----------------------------------------------------------------------------
  216. // Returns a CVar dictionary for tool usage
  217. //-----------------------------------------------------------------------------
  218. CreateInterfaceFn VStdLib_GetICVarFactory()
  219. {
  220. return Sys_GetFactoryThis();
  221. }
  222. //-----------------------------------------------------------------------------
  223. // Constructor
  224. //-----------------------------------------------------------------------------
  225. CCvar::CCvar() : m_TempConsoleBuffer( 0, 1024 ), m_SplitScreenAddedConVarsMap( 0, 0, DefLessFunc( ConVar * ) )
  226. {
  227. m_nNextDLLIdentifier = 0;
  228. m_pConCommandList = NULL;
  229. m_nMaxSplitScreenSlots = 1;
  230. m_bMaterialSystemThreadSetAllowed = false;
  231. m_CommandHash.Init();
  232. }
  233. //-----------------------------------------------------------------------------
  234. // Methods of IAppSystem
  235. //-----------------------------------------------------------------------------
  236. bool CCvar::Connect( CreateInterfaceFn factory )
  237. {
  238. ConnectTier1Libraries( &factory, 1 );
  239. s_pCVarQuery = (ICvarQuery*)factory( CVAR_QUERY_INTERFACE_VERSION, NULL );
  240. if ( !s_pCVarQuery )
  241. {
  242. s_pCVarQuery = &s_DefaultCvarQuery;
  243. }
  244. ConVar_Register();
  245. return true;
  246. }
  247. void CCvar::Disconnect()
  248. {
  249. ConVar_Unregister();
  250. s_pCVarQuery = NULL;
  251. DisconnectTier1Libraries();
  252. Assert( m_SplitScreenAddedConVarsMap.Count() == 0 );
  253. }
  254. InitReturnVal_t CCvar::Init()
  255. {
  256. return INIT_OK;
  257. }
  258. void CCvar::Shutdown()
  259. {
  260. }
  261. void *CCvar::QueryInterface( const char *pInterfaceName )
  262. {
  263. // We implement the ICvar interface
  264. if ( !V_strcmp( pInterfaceName, CVAR_INTERFACE_VERSION ) )
  265. return (ICvar*)this;
  266. return NULL;
  267. }
  268. //-----------------------------------------------------------------------------
  269. // Method allowing the engine ICvarQuery interface to take over
  270. //-----------------------------------------------------------------------------
  271. void CCvar::InstallCVarQuery( ICvarQuery *pQuery )
  272. {
  273. Assert( s_pCVarQuery == &s_DefaultCvarQuery );
  274. s_pCVarQuery = pQuery ? pQuery : &s_DefaultCvarQuery;
  275. }
  276. //-----------------------------------------------------------------------------
  277. // Used by DLLs to be able to unregister all their commands + convars
  278. //-----------------------------------------------------------------------------
  279. CVarDLLIdentifier_t CCvar::AllocateDLLIdentifier()
  280. {
  281. return m_nNextDLLIdentifier++;
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Purpose:
  285. // Input : *variable -
  286. //-----------------------------------------------------------------------------
  287. void CCvar::RegisterConCommand( ConCommandBase *variable )
  288. {
  289. // Already registered
  290. if ( variable->IsRegistered() )
  291. return;
  292. variable->m_bRegistered = true;
  293. const char *pName = variable->GetName();
  294. if ( !pName || !pName[0] )
  295. {
  296. variable->m_pNext = NULL;
  297. return;
  298. }
  299. // If the variable is already defined, then setup the new variable as a proxy to it.
  300. const ConCommandBase *pOther = FindCommandBase( variable->GetName() );
  301. if ( pOther )
  302. {
  303. if ( variable->IsCommand() || pOther->IsCommand() )
  304. {
  305. #ifdef _DEBUG
  306. // Don't warn if the commands are the same - this happens with some debug only commands
  307. if( Q_stricmp(variable->GetName(), pOther->GetName() ) != 0 )
  308. #endif
  309. {
  310. Warning( "WARNING: unable to link %s and %s because one or more is a ConCommand.\n", variable->GetName(), pOther->GetName() );
  311. }
  312. }
  313. else
  314. {
  315. // This cast is ok because we make sure they're ConVars above.
  316. ConVar *pChildVar = const_cast< ConVar* >( static_cast< const ConVar* >( variable ) );
  317. ConVar *pParentVar = const_cast< ConVar* >( static_cast< const ConVar* >( pOther ) );
  318. // See if it's a valid linkage
  319. if ( s_pCVarQuery->AreConVarsLinkable( pChildVar, pParentVar ) )
  320. {
  321. // Make sure the default values are the same (but only spew about this for FCVAR_REPLICATED)
  322. if( pChildVar->m_pszDefaultValue && pParentVar->m_pszDefaultValue &&
  323. pChildVar->IsFlagSet( FCVAR_REPLICATED ) && pParentVar->IsFlagSet( FCVAR_REPLICATED ) )
  324. {
  325. if( Q_stricmp( pChildVar->m_pszDefaultValue, pParentVar->m_pszDefaultValue ) != 0 )
  326. {
  327. Warning( "Parent and child ConVars with different default values! %s child: %s parent: %s (parent wins)\n",
  328. variable->GetName(), pChildVar->m_pszDefaultValue, pParentVar->m_pszDefaultValue );
  329. }
  330. }
  331. pChildVar->m_pParent = pParentVar->m_pParent;
  332. // Absorb material thread related convar flags
  333. pParentVar->m_nFlags |= pChildVar->m_nFlags & ( FCVAR_MATERIAL_THREAD_MASK | FCVAR_ACCESSIBLE_FROM_THREADS );
  334. // Transfer children's callbacks to parent
  335. if ( pChildVar->m_fnChangeCallbacks.Count() )
  336. {
  337. for ( int i = 0; i < pChildVar->m_fnChangeCallbacks.Count(); ++i )
  338. {
  339. pParentVar->m_fnChangeCallbacks.AddToTail( pChildVar->m_fnChangeCallbacks[ i ] );
  340. }
  341. // Wipe child callbacks
  342. pChildVar->m_fnChangeCallbacks.RemoveAll();
  343. }
  344. // make sure we don't have conflicting help strings.
  345. if ( pChildVar->m_pszHelpString && Q_strlen( pChildVar->m_pszHelpString ) != 0 )
  346. {
  347. if ( pParentVar->m_pszHelpString && Q_strlen( pParentVar->m_pszHelpString ) != 0 )
  348. {
  349. if ( Q_stricmp( pParentVar->m_pszHelpString, pChildVar->m_pszHelpString ) != 0 )
  350. {
  351. Warning( "Convar %s has multiple help strings:\n\tparent (wins): \"%s\"\n\tchild: \"%s\"\n",
  352. variable->GetName(), pParentVar->m_pszHelpString, pChildVar->m_pszHelpString );
  353. }
  354. }
  355. else
  356. {
  357. pParentVar->m_pszHelpString = pChildVar->m_pszHelpString;
  358. }
  359. }
  360. // make sure we don't have conflicting FCVAR_*** flags.
  361. static int const nFlags[] =
  362. { FCVAR_CHEAT, FCVAR_REPLICATED, FCVAR_DONTRECORD, FCVAR_ARCHIVE, FCVAR_ARCHIVE_GAMECONSOLE };
  363. static char const * const szFlags[] =
  364. { "FCVAR_CHEAT", "FCVAR_REPLICATED", "FCVAR_DONTRECORD", "FCVAR_ARCHIVE", "FCVAR_ARCHIVE_GAMECONSOLE" };
  365. COMPILE_TIME_ASSERT( ARRAYSIZE( nFlags ) == ARRAYSIZE( szFlags ) );
  366. for ( int k = 0; k < ARRAYSIZE( nFlags ); ++ k )
  367. {
  368. if ( ( pChildVar->m_nFlags & nFlags[k] ) != ( pParentVar->m_nFlags & nFlags[k] ) )
  369. {
  370. Warning( "Convar %s has conflicting %s flags (child: %s%s, parent: %s%s, parent wins)\n",
  371. variable->GetName(), szFlags[k],
  372. ( pChildVar->m_nFlags & nFlags[k] ) ? "has " : "no ", szFlags[k],
  373. ( pParentVar->m_nFlags & nFlags[k] ) ? "has " : "no ", szFlags[k] );
  374. }
  375. }
  376. }
  377. }
  378. variable->m_pNext = NULL;
  379. return;
  380. }
  381. // link the variable in
  382. variable->m_pNext = m_pConCommandList;
  383. m_pConCommandList = variable;
  384. AssertMsg1(FindCommandBase(variable->GetName()) == NULL, "Console command %s added twice!",
  385. variable->GetName());
  386. m_CommandHash.Insert(variable);
  387. }
  388. void CCvar::AddSplitScreenConVars()
  389. {
  390. if ( m_nMaxSplitScreenSlots == 1 )
  391. return;
  392. for( ConCommandBase *pCommand = m_pConCommandList; pCommand; pCommand = pCommand->m_pNext )
  393. {
  394. if ( pCommand->IsCommand() )
  395. continue;
  396. ConVar *pConVar = static_cast< ConVar * >( pCommand );
  397. if ( !pConVar->IsFlagSet( FCVAR_SS ) )
  398. continue;
  399. // See if it's already mapped in
  400. int idx = m_SplitScreenAddedConVarsMap.Find( pConVar );
  401. if ( idx == m_SplitScreenAddedConVarsMap.InvalidIndex() )
  402. {
  403. idx = m_SplitScreenAddedConVarsMap.Insert( pConVar );
  404. }
  405. SplitScreenAddedConVars_t &info = m_SplitScreenAddedConVarsMap[ idx ];
  406. for ( int i = 1 ; i < m_nMaxSplitScreenSlots; ++i )
  407. {
  408. // Already registered it
  409. if ( info.m_Vars[ i - 1 ].m_pVar )
  410. continue;
  411. // start at name2, etc.
  412. info.m_Vars[ i - 1 ].m_VarName = CFmtStr( "%s%d", pConVar->GetName(), i + 1 );
  413. CSplitScreenAddedConVar *pVar = new CSplitScreenAddedConVar( i, info.m_Vars[ i - 1 ].m_VarName.Get(), pConVar );
  414. info.m_Vars[ i - 1 ].m_pVar = pVar;
  415. pVar->SetSplitScreenPlayerSlot( i );
  416. RegisterConCommand( pVar );
  417. }
  418. }
  419. ConCommandBase::s_pConCommandBases = NULL;
  420. }
  421. void CCvar::RemoveSplitScreenConVars( CVarDLLIdentifier_t id )
  422. {
  423. if ( m_nMaxSplitScreenSlots == 1 )
  424. {
  425. Assert( m_SplitScreenAddedConVarsMap.Count() == 0 );
  426. return;
  427. }
  428. CUtlVector< ConVar * > deleted;
  429. FOR_EACH_MAP( m_SplitScreenAddedConVarsMap, i )
  430. {
  431. ConVar *key = m_SplitScreenAddedConVarsMap.Key( i );
  432. if ( key->GetDLLIdentifier() != id )
  433. {
  434. continue;
  435. }
  436. SplitScreenAddedConVars_t &info = m_SplitScreenAddedConVarsMap[ i ];
  437. for ( int i = 1 ; i < m_nMaxSplitScreenSlots; ++i )
  438. {
  439. if ( info.m_Vars[ i - 1 ].m_pVar )
  440. {
  441. UnregisterConCommand( info.m_Vars[ i - 1 ].m_pVar );
  442. delete info.m_Vars[ i - 1 ].m_pVar;
  443. info.m_Vars[ i - 1 ].m_pVar = NULL;
  444. }
  445. }
  446. deleted.AddToTail( key );
  447. }
  448. for ( int i = 0; i < deleted.Count(); ++i )
  449. {
  450. m_SplitScreenAddedConVarsMap.Remove( deleted[ i ] );
  451. }
  452. }
  453. void CCvar::UnregisterConCommand( ConCommandBase *pCommandToRemove )
  454. {
  455. // Not registered? Don't bother
  456. if ( !pCommandToRemove->IsRegistered() )
  457. return;
  458. pCommandToRemove->m_bRegistered = false;
  459. // FIXME: Should we make this a doubly-linked list? Would remove faster
  460. ConCommandBase *pPrev = NULL;
  461. for( ConCommandBase *pCommand = m_pConCommandList; pCommand; pCommand = pCommand->m_pNext )
  462. {
  463. if ( pCommand != pCommandToRemove )
  464. {
  465. pPrev = pCommand;
  466. continue;
  467. }
  468. if ( pPrev == NULL )
  469. {
  470. m_pConCommandList = pCommand->m_pNext;
  471. }
  472. else
  473. {
  474. pPrev->m_pNext = pCommand->m_pNext;
  475. }
  476. pCommand->m_pNext = NULL;
  477. m_CommandHash.Remove(m_CommandHash.Find(pCommand));
  478. break;
  479. }
  480. }
  481. void CCvar::UnregisterConCommands( CVarDLLIdentifier_t id )
  482. {
  483. ConCommandBase *pNewList;
  484. ConCommandBase *pCommand, *pNext;
  485. pNewList = NULL;
  486. m_CommandHash.Purge( true );
  487. pCommand = m_pConCommandList;
  488. while ( pCommand )
  489. {
  490. pNext = pCommand->m_pNext;
  491. if ( pCommand->GetDLLIdentifier() != id )
  492. {
  493. pCommand->m_pNext = pNewList;
  494. pNewList = pCommand;
  495. m_CommandHash.Insert( pCommand );
  496. }
  497. else
  498. {
  499. // Unlink
  500. pCommand->m_bRegistered = false;
  501. pCommand->m_pNext = NULL;
  502. }
  503. pCommand = pNext;
  504. }
  505. m_pConCommandList = pNewList;
  506. }
  507. //-----------------------------------------------------------------------------
  508. // Finds base commands
  509. //-----------------------------------------------------------------------------
  510. const ConCommandBase *CCvar::FindCommandBase( const char *name ) const
  511. {
  512. VPROF_INCREMENT_COUNTER( "CCvar::FindCommandBase", 1 );
  513. VPROF_BUDGET( "CCvar::FindCommandBase", VPROF_BUDGETGROUP_CVAR_FIND );
  514. return m_CommandHash.FindPtr( name );
  515. }
  516. ConCommandBase *CCvar::FindCommandBase( const char *name )
  517. {
  518. VPROF_INCREMENT_COUNTER( "CCvar::FindCommandBase", 1 );
  519. VPROF_BUDGET( "CCvar::FindCommandBase", VPROF_BUDGETGROUP_CVAR_FIND );
  520. return m_CommandHash.FindPtr( name );
  521. }
  522. //-----------------------------------------------------------------------------
  523. // Purpose Finds ConVars
  524. //-----------------------------------------------------------------------------
  525. const ConVar *CCvar::FindVar( const char *var_name ) const
  526. {
  527. const ConCommandBase *var = FindCommandBase( var_name );
  528. if ( !var )
  529. {
  530. return NULL;
  531. }
  532. else
  533. {
  534. if (var->IsCommand())
  535. {
  536. Warning("Tried to look up command %s as if it were a variable.\n", var_name );
  537. return NULL;
  538. }
  539. }
  540. return static_cast<const ConVar*>(var);
  541. }
  542. ConVar *CCvar::FindVar( const char *var_name )
  543. {
  544. ConCommandBase *var = FindCommandBase( var_name );
  545. if ( !var )
  546. {
  547. return NULL;
  548. }
  549. else
  550. {
  551. if (var->IsCommand())
  552. {
  553. Warning("Tried to look up command %s as if it were a variable.\n", var_name );
  554. return NULL;
  555. }
  556. }
  557. return static_cast<ConVar*>( var );
  558. }
  559. //-----------------------------------------------------------------------------
  560. // Purpose Finds ConCommands
  561. //-----------------------------------------------------------------------------
  562. const ConCommand *CCvar::FindCommand( const char *pCommandName ) const
  563. {
  564. const ConCommandBase *var = FindCommandBase( pCommandName );
  565. if ( !var || !var->IsCommand() )
  566. return NULL;
  567. return static_cast<const ConCommand*>(var);
  568. }
  569. ConCommand *CCvar::FindCommand( const char *pCommandName )
  570. {
  571. ConCommandBase *var = FindCommandBase( pCommandName );
  572. if ( !var || !var->IsCommand() )
  573. return NULL;
  574. return static_cast<ConCommand*>( var );
  575. }
  576. const char* CCvar::GetCommandLineValue( const char *pVariableName )
  577. {
  578. int nLen = Q_strlen(pVariableName);
  579. char *pSearch = (char*)stackalloc( nLen + 2 );
  580. pSearch[0] = '+';
  581. memcpy( &pSearch[1], pVariableName, nLen + 1 );
  582. return CommandLine()->ParmValue( pSearch );
  583. }
  584. //-----------------------------------------------------------------------------
  585. // Install, remove global callbacks
  586. //-----------------------------------------------------------------------------
  587. void CCvar::InstallGlobalChangeCallback( FnChangeCallback_t callback )
  588. {
  589. Assert( callback && m_GlobalChangeCallbacks.Find( callback ) < 0 );
  590. m_GlobalChangeCallbacks.AddToTail( callback );
  591. }
  592. void CCvar::RemoveGlobalChangeCallback( FnChangeCallback_t callback )
  593. {
  594. Assert( callback );
  595. m_GlobalChangeCallbacks.FindAndRemove( callback );
  596. }
  597. //-----------------------------------------------------------------------------
  598. // Purpose:
  599. //-----------------------------------------------------------------------------
  600. void CCvar::CallGlobalChangeCallbacks( ConVar *var, const char *pOldString, float flOldValue )
  601. {
  602. int nCallbackCount = m_GlobalChangeCallbacks.Count();
  603. for ( int i = 0; i < nCallbackCount; ++i )
  604. {
  605. (*m_GlobalChangeCallbacks[i])( var, pOldString, flOldValue );
  606. }
  607. }
  608. //-----------------------------------------------------------------------------
  609. // Sets convars containing the flags to their default value
  610. //-----------------------------------------------------------------------------
  611. void CCvar::RevertFlaggedConVars( int nFlag )
  612. {
  613. for ( CConCommandHash::CCommandHashIterator_t i = m_CommandHash.First() ;
  614. m_CommandHash.IsValidIterator( i ) ;
  615. i = m_CommandHash.Next( i ) )
  616. {
  617. ConCommandBase *var = m_CommandHash[ i ];
  618. if ( var->IsCommand() )
  619. continue;
  620. ConVar *cvar = ( ConVar * )var;
  621. if ( !cvar->IsFlagSet( nFlag ) )
  622. continue;
  623. // It's == to the default value, don't count
  624. if ( !Q_stricmp( cvar->GetDefault(), cvar->GetString() ) )
  625. continue;
  626. cvar->Revert();
  627. // DevMsg( "%s = \"%s\" (reverted)\n", cvar->GetName(), cvar->GetString() );
  628. }
  629. }
  630. //-----------------------------------------------------------------------------
  631. // Deal with queued material system convars
  632. //-----------------------------------------------------------------------------
  633. bool CCvar::IsMaterialThreadSetAllowed( ) const
  634. {
  635. Assert( ThreadInMainThread() );
  636. return m_bMaterialSystemThreadSetAllowed;
  637. }
  638. void CCvar::QueueMaterialThreadSetValue( ConVar *pConVar, const char *pValue )
  639. {
  640. Assert( ThreadInMainThread() );
  641. int j = m_QueuedConVarSets.AddToTail();
  642. m_QueuedConVarSets[j].m_pConVar = pConVar;
  643. m_QueuedConVarSets[j].m_nType = CONVAR_SET_STRING;
  644. m_QueuedConVarSets[j].m_String = pValue;
  645. }
  646. void CCvar::QueueMaterialThreadSetValue( ConVar *pConVar, int nValue )
  647. {
  648. Assert( ThreadInMainThread() );
  649. int j = m_QueuedConVarSets.AddToTail();
  650. m_QueuedConVarSets[j].m_pConVar = pConVar;
  651. m_QueuedConVarSets[j].m_nType = CONVAR_SET_INT;
  652. m_QueuedConVarSets[j].m_nInt = nValue;
  653. }
  654. void CCvar::QueueMaterialThreadSetValue( ConVar *pConVar, float flValue )
  655. {
  656. Assert( ThreadInMainThread() );
  657. int j = m_QueuedConVarSets.AddToTail();
  658. m_QueuedConVarSets[j].m_pConVar = pConVar;
  659. m_QueuedConVarSets[j].m_nType = CONVAR_SET_FLOAT;
  660. m_QueuedConVarSets[j].m_flFloat = flValue;
  661. }
  662. bool CCvar::HasQueuedMaterialThreadConVarSets() const
  663. {
  664. Assert( ThreadInMainThread() );
  665. return m_QueuedConVarSets.Count() > 0;
  666. }
  667. int CCvar::ProcessQueuedMaterialThreadConVarSets()
  668. {
  669. Assert( ThreadInMainThread() );
  670. m_bMaterialSystemThreadSetAllowed = true;
  671. int nUpdateFlags = 0;
  672. int nCount = m_QueuedConVarSets.Count();
  673. for ( int i = 0; i < nCount; ++i )
  674. {
  675. const QueuedConVarSet_t& set = m_QueuedConVarSets[i];
  676. switch( set.m_nType )
  677. {
  678. case CONVAR_SET_FLOAT:
  679. set.m_pConVar->SetValue( set.m_flFloat );
  680. break;
  681. case CONVAR_SET_INT:
  682. set.m_pConVar->SetValue( set.m_nInt );
  683. break;
  684. case CONVAR_SET_STRING:
  685. set.m_pConVar->SetValue( set.m_String );
  686. break;
  687. }
  688. nUpdateFlags |= set.m_pConVar->GetFlags() & FCVAR_MATERIAL_THREAD_MASK;
  689. }
  690. m_QueuedConVarSets.RemoveAll();
  691. m_bMaterialSystemThreadSetAllowed = false;
  692. return nUpdateFlags;
  693. }
  694. //-----------------------------------------------------------------------------
  695. // Display queued messages
  696. //-----------------------------------------------------------------------------
  697. void CCvar::DisplayQueuedMessages( )
  698. {
  699. // Display any queued up messages
  700. if ( m_TempConsoleBuffer.TellPut() == 0 )
  701. return;
  702. Color clr;
  703. CUtlBuffer bufStringToken;
  704. while( m_TempConsoleBuffer.IsValid() )
  705. {
  706. int nType = m_TempConsoleBuffer.GetChar();
  707. if ( nType == CONSOLE_COLOR_PRINT )
  708. {
  709. clr.SetRawColor( m_TempConsoleBuffer.GetInt() );
  710. }
  711. int nStringLength = m_TempConsoleBuffer.PeekStringLength();
  712. bufStringToken.EnsureCapacity( nStringLength + 1 );
  713. char* pTemp = (char*) bufStringToken.Base();
  714. m_TempConsoleBuffer.GetString( pTemp, nStringLength + 1 );
  715. switch( nType )
  716. {
  717. case CONSOLE_COLOR_PRINT:
  718. ConsoleColorPrintf( clr, "%s", pTemp );
  719. break;
  720. case CONSOLE_PRINT:
  721. ConsolePrintf( "%s", pTemp );
  722. break;
  723. case CONSOLE_DPRINT:
  724. ConsoleDPrintf( "%s", pTemp );
  725. break;
  726. }
  727. }
  728. m_TempConsoleBuffer.Purge();
  729. }
  730. //-----------------------------------------------------------------------------
  731. // Install a console printer
  732. //-----------------------------------------------------------------------------
  733. void CCvar::InstallConsoleDisplayFunc( IConsoleDisplayFunc* pDisplayFunc )
  734. {
  735. Assert( m_DisplayFuncs.Find( pDisplayFunc ) < 0 );
  736. m_DisplayFuncs.AddToTail( pDisplayFunc );
  737. DisplayQueuedMessages();
  738. }
  739. void CCvar::RemoveConsoleDisplayFunc( IConsoleDisplayFunc* pDisplayFunc )
  740. {
  741. m_DisplayFuncs.FindAndRemove( pDisplayFunc );
  742. }
  743. int CCvar::GetConsoleDisplayFuncCount() const
  744. {
  745. return m_DisplayFuncs.Count();
  746. }
  747. void CCvar::GetConsoleText( int nDisplayFuncIndex, char *pchText, size_t bufSize ) const
  748. {
  749. m_DisplayFuncs[ nDisplayFuncIndex ]->GetConsoleText( pchText, bufSize );
  750. }
  751. void CCvar::ConsoleColorPrintf( const Color& clr, const char *pFormat, ... ) const
  752. {
  753. char temp[ 8192 ];
  754. va_list argptr;
  755. va_start( argptr, pFormat );
  756. _vsnprintf( temp, sizeof( temp ) - 1, pFormat, argptr );
  757. va_end( argptr );
  758. temp[ sizeof( temp ) - 1 ] = 0;
  759. int c = m_DisplayFuncs.Count();
  760. if ( c == 0 )
  761. {
  762. m_TempConsoleBuffer.PutChar( CONSOLE_COLOR_PRINT );
  763. m_TempConsoleBuffer.PutInt( clr.GetRawColor() );
  764. m_TempConsoleBuffer.PutString( temp );
  765. return;
  766. }
  767. for ( int i = 0 ; i < c; ++i )
  768. {
  769. m_DisplayFuncs[ i ]->ColorPrint( clr, temp );
  770. }
  771. }
  772. void CCvar::ConsolePrintf( const char *pFormat, ... ) const
  773. {
  774. char temp[ 8192 ];
  775. va_list argptr;
  776. va_start( argptr, pFormat );
  777. _vsnprintf( temp, sizeof( temp ) - 1, pFormat, argptr );
  778. va_end( argptr );
  779. temp[ sizeof( temp ) - 1 ] = 0;
  780. int c = m_DisplayFuncs.Count();
  781. if ( c == 0 )
  782. {
  783. m_TempConsoleBuffer.PutChar( CONSOLE_PRINT );
  784. m_TempConsoleBuffer.PutString( temp );
  785. return;
  786. }
  787. for ( int i = 0 ; i < c; ++i )
  788. {
  789. m_DisplayFuncs[ i ]->Print( temp );
  790. }
  791. }
  792. void CCvar::ConsoleDPrintf( const char *pFormat, ... ) const
  793. {
  794. char temp[ 8192 ];
  795. va_list argptr;
  796. va_start( argptr, pFormat );
  797. _vsnprintf( temp, sizeof( temp ) - 1, pFormat, argptr );
  798. va_end( argptr );
  799. temp[ sizeof( temp ) - 1 ] = 0;
  800. int c = m_DisplayFuncs.Count();
  801. if ( c == 0 )
  802. {
  803. m_TempConsoleBuffer.PutChar( CONSOLE_DPRINT );
  804. m_TempConsoleBuffer.PutString( temp );
  805. return;
  806. }
  807. for ( int i = 0 ; i < c; ++i )
  808. {
  809. m_DisplayFuncs[ i ]->DPrint( temp );
  810. }
  811. }
  812. //-----------------------------------------------------------------------------
  813. // Purpose:
  814. //-----------------------------------------------------------------------------
  815. #if defined( USE_VXCONSOLE )
  816. #ifdef _PS3
  817. /*
  818. Here's a terrible hack.
  819. In porting the part of the game that speaks to VXConsole, EA chose to
  820. write it as a cluster of global functions, instead of a class interface like
  821. Aaron did with IXboxConsole. Some of these globals need access to symbols
  822. inside the engine, so they are defined there. However, CCvar is inside vstdlib.
  823. In the EA build this didn't make a difference because everything was a huge
  824. monolithic executable, and you could just access any symbol from anywhere.
  825. In our build, with its PRXes, that doesn't fly.
  826. So, the proper solution to this problem is to wrap all of the PS3 vxconsole
  827. stuff in an interface, put it inside vstlib, create the dcim connection there,
  828. and then export the interface pointer. The engine meanwhile would export the
  829. symbols the vxlib needs, and then we give that interface class inside
  830. vstlib a pointer to the engine once the engine is available.
  831. Right now however I just want to get the thing working with as little modification
  832. as possible so I can fix the vxconsole windows app itself and hopefully get
  833. bidirectional TTY to our game. So, instead of the proper solution,
  834. I'm just duct-taping everything together by simply passing a pointer to the engine
  835. symbol this function needs whenever I call it.
  836. Blech. I'll fix it later.
  837. -egr 4/29/10. (is it later than September 2010? go call egr and make fun of him.)
  838. */
  839. void CCvar::PublishToVXConsole()
  840. #else
  841. void CCvar::PublishToVXConsole()
  842. #endif
  843. {
  844. const char *commands[6*1024];
  845. const char *helptext[6*1024];
  846. int numCommands = 0;
  847. // iterate and publish commands to the remote console
  848. for ( CConCommandHash::CCommandHashIterator_t i = m_CommandHash.First() ;
  849. m_CommandHash.IsValidIterator( i ) ;
  850. i = m_CommandHash.Next( i ) )
  851. {
  852. ConCommandBase *pCur = m_CommandHash[ i ];
  853. // add unregistered commands to list
  854. if ( numCommands < sizeof(commands)/sizeof(commands[0]) )
  855. {
  856. commands[numCommands] = pCur->GetName();
  857. helptext[numCommands] = pCur->GetHelpText();
  858. numCommands++;
  859. }
  860. }
  861. if ( numCommands )
  862. {
  863. #ifdef _PS3
  864. g_pValvePS3Console->AddCommands( numCommands, commands, helptext );
  865. #else
  866. XBX_rAddCommands( numCommands, commands, helptext );
  867. #endif
  868. }
  869. }
  870. #endif
  871. static bool ConVarSortFunc( ConCommandBase * const &lhs, ConCommandBase * const &rhs )
  872. {
  873. return CaselessStringLessThan( lhs->GetName(), rhs->GetName() );
  874. }
  875. //-----------------------------------------------------------------------------
  876. // Console commands
  877. //-----------------------------------------------------------------------------
  878. void CCvar::Find( const CCommand &args )
  879. {
  880. const char *search;
  881. if ( args.ArgC() != 2 )
  882. {
  883. ConMsg( "Usage: find <string>\n" );
  884. return;
  885. }
  886. // Get substring to find
  887. search = args[1];
  888. CUtlRBTree< ConCommandBase *, int > sorted( 0, 0, ConVarSortFunc );
  889. // Loop through vars and print out findings
  890. for ( CConCommandHash::CCommandHashIterator_t i = m_CommandHash.First() ;
  891. m_CommandHash.IsValidIterator(i) ;
  892. i = m_CommandHash.Next(i) )
  893. {
  894. ConCommandBase *var = m_CommandHash[ i ];
  895. if ( var->IsFlagSet(FCVAR_DEVELOPMENTONLY) || var->IsFlagSet(FCVAR_HIDDEN) )
  896. continue;
  897. if ( !Q_stristr( var->GetName(), search ) &&
  898. !Q_stristr( var->GetHelpText(), search ) )
  899. continue;
  900. sorted.Insert( var );
  901. }
  902. for ( int i = sorted.FirstInorder(); i != sorted.InvalidIndex(); i = sorted.NextInorder( i ) )
  903. {
  904. ConVar_PrintDescription( sorted[ i ] );
  905. }
  906. }
  907. #ifdef _DEBUG
  908. void CCvar::HashReport( const CCommand &args )
  909. {
  910. m_CommandHash.Report();
  911. }
  912. #endif
  913. void CCvar::SetMaxSplitScreenSlots( int nSlots )
  914. {
  915. m_nMaxSplitScreenSlots = nSlots;
  916. AddSplitScreenConVars();
  917. }
  918. int CCvar::GetMaxSplitScreenSlots() const
  919. {
  920. return m_nMaxSplitScreenSlots;
  921. }
  922. //-----------------------------------------------------------------------------
  923. // Console command hash data structure
  924. //-----------------------------------------------------------------------------
  925. CConCommandHash::CConCommandHash()
  926. {
  927. Purge( true );
  928. }
  929. CConCommandHash::~CConCommandHash()
  930. {
  931. Purge( false );
  932. }
  933. void CConCommandHash::Purge( bool bReinitialize )
  934. {
  935. m_aBuckets.Purge();
  936. m_aDataPool.Purge();
  937. if ( bReinitialize )
  938. {
  939. Init();
  940. }
  941. }
  942. // Initialize.
  943. void CConCommandHash::Init( void )
  944. {
  945. // kNUM_BUCKETS must be a power of two.
  946. COMPILE_TIME_ASSERT((kNUM_BUCKETS & ( kNUM_BUCKETS - 1 )) == 0);
  947. // Set the bucket size.
  948. m_aBuckets.SetSize( kNUM_BUCKETS );
  949. for ( int iBucket = 0; iBucket < kNUM_BUCKETS; ++iBucket )
  950. {
  951. m_aBuckets[iBucket] = m_aDataPool.InvalidIndex();
  952. }
  953. // Calculate the grow size.
  954. int nGrowSize = 4 * kNUM_BUCKETS;
  955. m_aDataPool.SetGrowSize( nGrowSize );
  956. }
  957. //-----------------------------------------------------------------------------
  958. // Purpose: Insert data into the hash table given its key (unsigned int),
  959. // WITH a check to see if the element already exists within the hash.
  960. //-----------------------------------------------------------------------------
  961. CConCommandHash::CCommandHashHandle_t CConCommandHash::Insert( ConCommandBase *cmd )
  962. {
  963. // Check to see if that key already exists in the buckets (should be unique).
  964. CCommandHashHandle_t hHash = Find( cmd );
  965. if( hHash != InvalidHandle() )
  966. return hHash;
  967. return FastInsert( cmd );
  968. }
  969. //-----------------------------------------------------------------------------
  970. // Purpose: Insert data into the hash table given its key (unsigned int),
  971. // WITHOUT a check to see if the element already exists within the hash.
  972. //-----------------------------------------------------------------------------
  973. CConCommandHash::CCommandHashHandle_t CConCommandHash::FastInsert( ConCommandBase *cmd )
  974. {
  975. // Get a new element from the pool.
  976. intp iHashData = m_aDataPool.Alloc( true );
  977. HashEntry_t * RESTRICT pHashData = &m_aDataPool[iHashData];
  978. if ( !pHashData )
  979. return InvalidHandle();
  980. HashKey_t key = Hash(cmd);
  981. // Add data to new element.
  982. pHashData->m_uiKey = key;
  983. pHashData->m_Data = cmd;
  984. // Link element.
  985. int iBucket = key & kBUCKETMASK ; // HashFuncs::Hash( uiKey, m_uiBucketMask );
  986. m_aDataPool.LinkBefore( m_aBuckets[iBucket], iHashData );
  987. m_aBuckets[iBucket] = iHashData;
  988. return iHashData;
  989. }
  990. //-----------------------------------------------------------------------------
  991. // Purpose: Remove a given element from the hash.
  992. //-----------------------------------------------------------------------------
  993. void CConCommandHash::Remove( CCommandHashHandle_t hHash ) RESTRICT
  994. {
  995. HashEntry_t * RESTRICT entry = &m_aDataPool[hHash];
  996. HashKey_t iBucket = entry->m_uiKey & kBUCKETMASK ;
  997. if ( m_aBuckets[iBucket] == hHash )
  998. {
  999. // It is a bucket head.
  1000. m_aBuckets[iBucket] = m_aDataPool.Next( hHash );
  1001. }
  1002. else
  1003. {
  1004. // Not a bucket head.
  1005. m_aDataPool.Unlink( hHash );
  1006. }
  1007. // Remove the element.
  1008. m_aDataPool.Remove( hHash );
  1009. }
  1010. //-----------------------------------------------------------------------------
  1011. // Purpose: Remove all elements from the hash
  1012. //-----------------------------------------------------------------------------
  1013. void CConCommandHash::RemoveAll( void )
  1014. {
  1015. m_aBuckets.RemoveAll();
  1016. m_aDataPool.RemoveAll();
  1017. }
  1018. //-----------------------------------------------------------------------------
  1019. // Find hash entry corresponding to a string name
  1020. //-----------------------------------------------------------------------------
  1021. CConCommandHash::CCommandHashHandle_t CConCommandHash::Find( const char *name, HashKey_t hashkey) const RESTRICT
  1022. {
  1023. // hash the "key" - get the correct hash table "bucket"
  1024. int iBucket = hashkey & kBUCKETMASK;
  1025. for ( datapool_t::IndexLocalType_t iElement = m_aBuckets[iBucket]; iElement != m_aDataPool.InvalidIndex(); iElement = m_aDataPool.Next( iElement ) )
  1026. {
  1027. const HashEntry_t &element = m_aDataPool[iElement];
  1028. if ( element.m_uiKey == hashkey && // if hashes of strings match,
  1029. Q_stricmp( name, element.m_Data->GetName() ) == 0) // then test the actual strings
  1030. {
  1031. return iElement;
  1032. }
  1033. }
  1034. // found nuffink
  1035. return InvalidHandle();
  1036. }
  1037. //-----------------------------------------------------------------------------
  1038. // Find a command in the hash.
  1039. //-----------------------------------------------------------------------------
  1040. CConCommandHash::CCommandHashHandle_t CConCommandHash::Find( const ConCommandBase *cmd ) const RESTRICT
  1041. {
  1042. // Set this #if to 1 if the assert at bottom starts whining --
  1043. // that indicates that a console command is being double-registered,
  1044. // or something similarly nonfatally bad. With this #if 1, we'll search
  1045. // by name instead of by pointer, which is more robust in the face
  1046. // of double registered commands, but obviously slower.
  1047. #if 0
  1048. return Find(cmd->GetName());
  1049. #else
  1050. HashKey_t hashkey = Hash(cmd);
  1051. int iBucket = hashkey & kBUCKETMASK;
  1052. // hunt through all entries in that bucket
  1053. for ( datapool_t::IndexLocalType_t iElement = m_aBuckets[iBucket]; iElement != m_aDataPool.InvalidIndex(); iElement = m_aDataPool.Next( iElement ) )
  1054. {
  1055. const HashEntry_t &element = m_aDataPool[iElement];
  1056. if ( element.m_uiKey == hashkey && // if the hashes match...
  1057. element.m_Data == cmd ) // and the pointers...
  1058. {
  1059. // in debug, test to make sure we don't have commands under the same name
  1060. // or something goofy like that
  1061. AssertMsg1( iElement == Find(cmd->GetName()),
  1062. "ConCommand %s had two entries in the hash!", cmd->GetName() );
  1063. // return this element
  1064. return iElement;
  1065. }
  1066. }
  1067. // found nothing.
  1068. #ifdef DBGFLAG_ASSERT // double check against search by name
  1069. CCommandHashHandle_t dbghand = Find(cmd->GetName());
  1070. AssertMsg1( InvalidHandle() == dbghand,
  1071. "ConCommand %s couldn't be found by pointer, but was found by name!", cmd->GetName() );
  1072. #endif
  1073. return InvalidHandle();
  1074. #endif
  1075. }
  1076. #ifdef _DEBUG
  1077. // Dump a report to MSG
  1078. void CConCommandHash::Report( void )
  1079. {
  1080. Msg("Console command hash bucket load:\n");
  1081. int total = 0;
  1082. for ( int iBucket = 0 ; iBucket < kNUM_BUCKETS ; ++iBucket )
  1083. {
  1084. int count = 0;
  1085. CCommandHashHandle_t iElement = m_aBuckets[iBucket]; // get the head of the bucket
  1086. while ( iElement != m_aDataPool.InvalidIndex() )
  1087. {
  1088. ++count;
  1089. iElement = m_aDataPool.Next( iElement );
  1090. }
  1091. Msg( "%d: %d\n", iBucket, count );
  1092. total += count;
  1093. }
  1094. Msg("\tAverage: %.1f\n", total / ((float)(kNUM_BUCKETS)));
  1095. }
  1096. #endif