Team Fortress 2 Source Code as on 22/4/2020
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.

1521 lines
39 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include "basetypes.h"
  12. #include "tier1/convar.h"
  13. #include "tier1/strtools.h"
  14. #include "tier1/characterset.h"
  15. #include "tier1/utlbuffer.h"
  16. #include "tier1/tier1.h"
  17. #include "tier1/convar_serverbounded.h"
  18. #include "icvar.h"
  19. #include "tier0/dbg.h"
  20. #if defined( _X360 )
  21. #include "xbox/xbox_console.h"
  22. #endif
  23. #include "tier0/memdbgon.h"
  24. // Comment this out when we release.
  25. //#define ALLOW_DEVELOPMENT_CVARS
  26. // This enables the l4d style of culling all cvars that are not marked FCVAR_RELEASE :
  27. // #define CULL_ALL_CVARS_NOT_FCVAR_RELEASE
  28. //-----------------------------------------------------------------------------
  29. // Statically constructed list of ConCommandBases,
  30. // used for registering them with the ICVar interface
  31. //-----------------------------------------------------------------------------
  32. ConCommandBase *ConCommandBase::s_pConCommandBases = NULL;
  33. IConCommandBaseAccessor *ConCommandBase::s_pAccessor = NULL;
  34. static int s_nCVarFlag = 0;
  35. static int s_nDLLIdentifier = -1; // A unique identifier indicating which DLL this convar came from
  36. static bool s_bRegistered = false;
  37. class CDefaultAccessor : public IConCommandBaseAccessor
  38. {
  39. public:
  40. virtual bool RegisterConCommandBase( ConCommandBase *pVar )
  41. {
  42. // Link to engine's list instead
  43. g_pCVar->RegisterConCommand( pVar );
  44. return true;
  45. }
  46. };
  47. static CDefaultAccessor s_DefaultAccessor;
  48. //-----------------------------------------------------------------------------
  49. // Called by the framework to register ConCommandBases with the ICVar
  50. //-----------------------------------------------------------------------------
  51. void ConVar_Register( int nCVarFlag, IConCommandBaseAccessor *pAccessor )
  52. {
  53. if ( !g_pCVar || s_bRegistered )
  54. return;
  55. Assert( s_nDLLIdentifier < 0 );
  56. s_bRegistered = true;
  57. s_nCVarFlag = nCVarFlag;
  58. s_nDLLIdentifier = g_pCVar->AllocateDLLIdentifier();
  59. ConCommandBase *pCur, *pNext;
  60. ConCommandBase::s_pAccessor = pAccessor ? pAccessor : &s_DefaultAccessor;
  61. pCur = ConCommandBase::s_pConCommandBases;
  62. while ( pCur )
  63. {
  64. pNext = pCur->m_pNext;
  65. pCur->AddFlags( s_nCVarFlag );
  66. pCur->Init();
  67. pCur = pNext;
  68. }
  69. g_pCVar->AddSplitScreenConVars();
  70. g_pCVar->ProcessQueuedMaterialThreadConVarSets();
  71. ConCommandBase::s_pConCommandBases = NULL;
  72. }
  73. void ConVar_Unregister( )
  74. {
  75. if ( !g_pCVar || !s_bRegistered )
  76. return;
  77. Assert( s_nDLLIdentifier >= 0 );
  78. // Do this after unregister!!!
  79. g_pCVar->RemoveSplitScreenConVars( s_nDLLIdentifier );
  80. g_pCVar->UnregisterConCommands( s_nDLLIdentifier );
  81. s_nDLLIdentifier = -1;
  82. s_bRegistered = false;
  83. }
  84. //-----------------------------------------------------------------------------
  85. // Purpose: Default constructor
  86. //-----------------------------------------------------------------------------
  87. ConCommandBase::ConCommandBase( void )
  88. {
  89. m_bRegistered = false;
  90. m_pszName = NULL;
  91. m_pszHelpString = NULL;
  92. m_nFlags = 0;
  93. m_pNext = NULL;
  94. }
  95. //-----------------------------------------------------------------------------
  96. // Purpose: The base console invoked command/cvar interface
  97. // Input : *pName - name of variable/command
  98. // *pHelpString - help text
  99. // flags - flags
  100. //-----------------------------------------------------------------------------
  101. ConCommandBase::ConCommandBase( const char *pName, const char *pHelpString /*=0*/, int flags /*= 0*/ )
  102. {
  103. Create( pName, pHelpString, flags );
  104. }
  105. //-----------------------------------------------------------------------------
  106. // Purpose:
  107. //-----------------------------------------------------------------------------
  108. ConCommandBase::~ConCommandBase( void )
  109. {
  110. }
  111. //-----------------------------------------------------------------------------
  112. // Purpose:
  113. // Output : Returns true on success, false on failure.
  114. //-----------------------------------------------------------------------------
  115. bool ConCommandBase::IsCommand( void ) const
  116. {
  117. // Assert( 0 ); This can't assert. . causes a recursive assert in Sys_Printf, etc.
  118. return true;
  119. }
  120. //-----------------------------------------------------------------------------
  121. // Returns the DLL identifier
  122. //-----------------------------------------------------------------------------
  123. CVarDLLIdentifier_t ConCommandBase::GetDLLIdentifier() const
  124. {
  125. return s_nDLLIdentifier;
  126. }
  127. //-----------------------------------------------------------------------------
  128. // Purpose:
  129. // Input : *pName -
  130. // callback -
  131. // *pHelpString -
  132. // flags -
  133. //-----------------------------------------------------------------------------
  134. void ConCommandBase::Create( const char *pName, const char *pHelpString /*= 0*/, int flags /*= 0*/ )
  135. {
  136. static char *empty_string = "";
  137. m_bRegistered = false;
  138. // Name should be static data
  139. Assert( pName );
  140. m_pszName = pName;
  141. m_pszHelpString = pHelpString ? pHelpString : empty_string;
  142. m_nFlags = flags;
  143. #ifdef ALLOW_DEVELOPMENT_CVARS
  144. m_nFlags &= ~FCVAR_DEVELOPMENTONLY;
  145. #endif
  146. if ( !( m_nFlags & FCVAR_UNREGISTERED ) )
  147. {
  148. m_pNext = s_pConCommandBases;
  149. s_pConCommandBases = this;
  150. }
  151. else
  152. {
  153. // It's unregistered
  154. m_pNext = NULL;
  155. }
  156. // If s_pAccessor is already set (this ConVar is not a global variable),
  157. // register it.
  158. if ( s_pAccessor )
  159. {
  160. Init();
  161. }
  162. }
  163. //-----------------------------------------------------------------------------
  164. // Purpose: Used internally by OneTimeInit to initialize.
  165. //-----------------------------------------------------------------------------
  166. void ConCommandBase::Init()
  167. {
  168. if ( s_pAccessor )
  169. {
  170. s_pAccessor->RegisterConCommandBase( this );
  171. }
  172. }
  173. void ConCommandBase::Shutdown()
  174. {
  175. if ( g_pCVar )
  176. {
  177. g_pCVar->UnregisterConCommand( this );
  178. }
  179. }
  180. //-----------------------------------------------------------------------------
  181. // Purpose: Return name of the command/var
  182. // Output : const char
  183. //-----------------------------------------------------------------------------
  184. const char *ConCommandBase::GetName( void ) const
  185. {
  186. return m_pszName;
  187. }
  188. //-----------------------------------------------------------------------------
  189. // Purpose:
  190. // Input : flag -
  191. // Output : Returns true on success, false on failure.
  192. //-----------------------------------------------------------------------------
  193. bool ConCommandBase::IsFlagSet( int flag ) const
  194. {
  195. return ( flag & m_nFlags ) ? true : false;
  196. }
  197. //-----------------------------------------------------------------------------
  198. // Purpose:
  199. // Input : flags -
  200. //-----------------------------------------------------------------------------
  201. void ConCommandBase::AddFlags( int flags )
  202. {
  203. m_nFlags |= flags;
  204. #ifdef ALLOW_DEVELOPMENT_CVARS
  205. m_nFlags &= ~FCVAR_DEVELOPMENTONLY;
  206. #endif
  207. }
  208. //-----------------------------------------------------------------------------
  209. // Purpose: removes specified flags
  210. //-----------------------------------------------------------------------------
  211. void ConCommandBase::RemoveFlags( int flags )
  212. {
  213. m_nFlags &= ~flags;
  214. }
  215. // Returns current flags
  216. int ConCommandBase::GetFlags() const
  217. {
  218. return m_nFlags;
  219. }
  220. //-----------------------------------------------------------------------------
  221. // Purpose:
  222. // Output : const ConCommandBase
  223. //-----------------------------------------------------------------------------
  224. const ConCommandBase *ConCommandBase::GetNext( void ) const
  225. {
  226. return m_pNext;
  227. }
  228. ConCommandBase *ConCommandBase::GetNext( void )
  229. {
  230. return m_pNext;
  231. }
  232. //-----------------------------------------------------------------------------
  233. // Purpose: Copies string using local new/delete operators
  234. // Input : *from -
  235. // Output : char
  236. //-----------------------------------------------------------------------------
  237. char *ConCommandBase::CopyString( const char *from )
  238. {
  239. int len;
  240. char *to;
  241. len = strlen( from );
  242. if ( len <= 0 )
  243. {
  244. to = new char[1];
  245. to[0] = 0;
  246. }
  247. else
  248. {
  249. to = new char[len+1];
  250. V_strncpy( to, from, len+1 );
  251. }
  252. return to;
  253. }
  254. //-----------------------------------------------------------------------------
  255. // Purpose:
  256. // Output : const char
  257. //-----------------------------------------------------------------------------
  258. const char *ConCommandBase::GetHelpText( void ) const
  259. {
  260. return m_pszHelpString;
  261. }
  262. //-----------------------------------------------------------------------------
  263. // Purpose: Has this cvar been registered
  264. // Output : Returns true on success, false on failure.
  265. //-----------------------------------------------------------------------------
  266. bool ConCommandBase::IsRegistered( void ) const
  267. {
  268. return m_bRegistered;
  269. }
  270. //-----------------------------------------------------------------------------
  271. //
  272. // Con Commands start here
  273. //
  274. //-----------------------------------------------------------------------------
  275. //-----------------------------------------------------------------------------
  276. // Global methods
  277. //-----------------------------------------------------------------------------
  278. static characterset_t s_BreakSet;
  279. static bool s_bBuiltBreakSet = false;
  280. //-----------------------------------------------------------------------------
  281. // Tokenizer class
  282. //-----------------------------------------------------------------------------
  283. CCommand::CCommand()
  284. {
  285. if ( !s_bBuiltBreakSet )
  286. {
  287. s_bBuiltBreakSet = true;
  288. CharacterSetBuild( &s_BreakSet, "{}()':" );
  289. }
  290. Reset();
  291. }
  292. CCommand::CCommand( int nArgC, const char **ppArgV )
  293. {
  294. Assert( nArgC > 0 );
  295. if ( !s_bBuiltBreakSet )
  296. {
  297. s_bBuiltBreakSet = true;
  298. CharacterSetBuild( &s_BreakSet, "{}()':" );
  299. }
  300. Reset();
  301. char *pBuf = m_pArgvBuffer;
  302. char *pSBuf = m_pArgSBuffer;
  303. m_nArgc = nArgC;
  304. for ( int i = 0; i < nArgC; ++i )
  305. {
  306. m_ppArgv[i] = pBuf;
  307. int nLen = V_strlen( ppArgV[i] );
  308. memcpy( pBuf, ppArgV[i], nLen+1 );
  309. if ( i == 0 )
  310. {
  311. m_nArgv0Size = nLen;
  312. }
  313. pBuf += nLen+1;
  314. bool bContainsSpace = strchr( ppArgV[i], ' ' ) != NULL;
  315. if ( bContainsSpace )
  316. {
  317. *pSBuf++ = '\"';
  318. }
  319. memcpy( pSBuf, ppArgV[i], nLen );
  320. pSBuf += nLen;
  321. if ( bContainsSpace )
  322. {
  323. *pSBuf++ = '\"';
  324. }
  325. if ( i != nArgC - 1 )
  326. {
  327. *pSBuf++ = ' ';
  328. }
  329. }
  330. }
  331. void CCommand::Reset()
  332. {
  333. m_nArgc = 0;
  334. m_nArgv0Size = 0;
  335. m_pArgSBuffer[0] = 0;
  336. }
  337. characterset_t* CCommand::DefaultBreakSet()
  338. {
  339. return &s_BreakSet;
  340. }
  341. bool CCommand::Tokenize( const char *pCommand, characterset_t *pBreakSet )
  342. {
  343. Reset();
  344. if ( !pCommand )
  345. return false;
  346. // Use default break set
  347. if ( !pBreakSet )
  348. {
  349. pBreakSet = &s_BreakSet;
  350. }
  351. // Copy the current command into a temp buffer
  352. // NOTE: This is here to avoid the pointers returned by DequeueNextCommand
  353. // to become invalid by calling AddText. Is there a way we can avoid the memcpy?
  354. int nLen = V_strlen( pCommand );
  355. if ( nLen >= COMMAND_MAX_LENGTH - 1 )
  356. {
  357. Warning( "CCommand::Tokenize: Encountered command which overflows the tokenizer buffer.. Skipping!\n" );
  358. return false;
  359. }
  360. memcpy( m_pArgSBuffer, pCommand, nLen + 1 );
  361. // Parse the current command into the current command buffer
  362. CUtlBuffer bufParse( m_pArgSBuffer, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
  363. int nArgvBufferSize = 0;
  364. while ( bufParse.IsValid() && ( m_nArgc < COMMAND_MAX_ARGC ) )
  365. {
  366. char *pArgvBuf = &m_pArgvBuffer[nArgvBufferSize];
  367. int nMaxLen = COMMAND_MAX_LENGTH - nArgvBufferSize;
  368. int nStartGet = bufParse.TellGet();
  369. int nSize = bufParse.ParseToken( pBreakSet, pArgvBuf, nMaxLen );
  370. if ( nSize < 0 )
  371. break;
  372. // Check for overflow condition
  373. if ( nMaxLen == nSize )
  374. {
  375. Reset();
  376. return false;
  377. }
  378. if ( m_nArgc == 1 )
  379. {
  380. // Deal with the case where the arguments were quoted
  381. m_nArgv0Size = bufParse.TellGet();
  382. bool bFoundEndQuote = m_pArgSBuffer[m_nArgv0Size-1] == '\"';
  383. if ( bFoundEndQuote )
  384. {
  385. --m_nArgv0Size;
  386. }
  387. m_nArgv0Size -= nSize;
  388. Assert( m_nArgv0Size != 0 );
  389. // The StartGet check is to handle this case: "foo"bar
  390. // which will parse into 2 different args. ArgS should point to bar.
  391. bool bFoundStartQuote = ( m_nArgv0Size > nStartGet ) && ( m_pArgSBuffer[m_nArgv0Size-1] == '\"' );
  392. Assert( bFoundEndQuote == bFoundStartQuote );
  393. if ( bFoundStartQuote )
  394. {
  395. --m_nArgv0Size;
  396. }
  397. }
  398. m_ppArgv[ m_nArgc++ ] = pArgvBuf;
  399. if( m_nArgc >= COMMAND_MAX_ARGC )
  400. {
  401. Warning( "CCommand::Tokenize: Encountered command which overflows the argument buffer.. Clamped!\n" );
  402. }
  403. nArgvBufferSize += nSize + 1;
  404. Assert( nArgvBufferSize <= COMMAND_MAX_LENGTH );
  405. }
  406. return true;
  407. }
  408. //-----------------------------------------------------------------------------
  409. // Helper function to parse arguments to commands.
  410. //-----------------------------------------------------------------------------
  411. const char* CCommand::FindArg( const char *pName ) const
  412. {
  413. int nArgC = ArgC();
  414. for ( int i = 1; i < nArgC; i++ )
  415. {
  416. if ( !V_stricmp( Arg(i), pName ) )
  417. return (i+1) < nArgC ? Arg( i+1 ) : "";
  418. }
  419. return 0;
  420. }
  421. int CCommand::FindArgInt( const char *pName, int nDefaultVal ) const
  422. {
  423. const char *pVal = FindArg( pName );
  424. if ( pVal )
  425. return atoi( pVal );
  426. else
  427. return nDefaultVal;
  428. }
  429. //-----------------------------------------------------------------------------
  430. // Default console command autocompletion function
  431. //-----------------------------------------------------------------------------
  432. int DefaultCompletionFunc( const char *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
  433. {
  434. return 0;
  435. }
  436. //-----------------------------------------------------------------------------
  437. // Purpose: Constructs a console command
  438. //-----------------------------------------------------------------------------
  439. //ConCommand::ConCommand()
  440. //{
  441. // m_bIsNewConCommand = true;
  442. //}
  443. ConCommand::ConCommand( const char *pName, FnCommandCallbackV1_t callback, const char *pHelpString /*= 0*/, int flags /*= 0*/, FnCommandCompletionCallback completionFunc /*= 0*/ )
  444. {
  445. // Set the callback
  446. m_fnCommandCallbackV1 = callback;
  447. m_bUsingNewCommandCallback = false;
  448. m_bUsingCommandCallbackInterface = false;
  449. m_fnCompletionCallback = completionFunc ? completionFunc : DefaultCompletionFunc;
  450. m_bHasCompletionCallback = completionFunc != 0 ? true : false;
  451. // Setup the rest
  452. BaseClass::Create( pName, pHelpString, flags );
  453. }
  454. ConCommand::ConCommand( const char *pName, FnCommandCallback_t callback, const char *pHelpString /*= 0*/, int flags /*= 0*/, FnCommandCompletionCallback completionFunc /*= 0*/ )
  455. {
  456. // Set the callback
  457. m_fnCommandCallback = callback;
  458. m_bUsingNewCommandCallback = true;
  459. m_fnCompletionCallback = completionFunc ? completionFunc : DefaultCompletionFunc;
  460. m_bHasCompletionCallback = completionFunc != 0 ? true : false;
  461. m_bUsingCommandCallbackInterface = false;
  462. // Setup the rest
  463. BaseClass::Create( pName, pHelpString, flags );
  464. }
  465. ConCommand::ConCommand( const char *pName, ICommandCallback *pCallback, const char *pHelpString /*= 0*/, int flags /*= 0*/, ICommandCompletionCallback *pCompletionCallback /*= 0*/ )
  466. {
  467. // Set the callback
  468. m_pCommandCallback = pCallback;
  469. m_bUsingNewCommandCallback = false;
  470. m_pCommandCompletionCallback = pCompletionCallback;
  471. m_bHasCompletionCallback = ( pCompletionCallback != 0 );
  472. m_bUsingCommandCallbackInterface = true;
  473. // Setup the rest
  474. BaseClass::Create( pName, pHelpString, flags );
  475. }
  476. //-----------------------------------------------------------------------------
  477. // Destructor
  478. //-----------------------------------------------------------------------------
  479. ConCommand::~ConCommand( void )
  480. {
  481. }
  482. //-----------------------------------------------------------------------------
  483. // Purpose: Returns true if this is a command
  484. //-----------------------------------------------------------------------------
  485. bool ConCommand::IsCommand( void ) const
  486. {
  487. return true;
  488. }
  489. //-----------------------------------------------------------------------------
  490. // Purpose: Invoke the function if there is one
  491. //-----------------------------------------------------------------------------
  492. void ConCommand::Dispatch( const CCommand &command )
  493. {
  494. if ( m_bUsingNewCommandCallback )
  495. {
  496. if ( m_fnCommandCallback )
  497. {
  498. ( *m_fnCommandCallback )( command );
  499. return;
  500. }
  501. }
  502. else if ( m_bUsingCommandCallbackInterface )
  503. {
  504. if ( m_pCommandCallback )
  505. {
  506. m_pCommandCallback->CommandCallback( command );
  507. return;
  508. }
  509. }
  510. else
  511. {
  512. if ( m_fnCommandCallbackV1 )
  513. {
  514. ( *m_fnCommandCallbackV1 )();
  515. return;
  516. }
  517. }
  518. // Command without callback!!!
  519. AssertMsg1( 0, "Encountered ConCommand '%s' without a callback!\n", GetName() );
  520. }
  521. //-----------------------------------------------------------------------------
  522. // Purpose: Calls the autocompletion method to get autocompletion suggestions
  523. //-----------------------------------------------------------------------------
  524. int ConCommand::AutoCompleteSuggest( const char *partial, CUtlVector< CUtlString > &commands )
  525. {
  526. if ( m_bUsingCommandCallbackInterface )
  527. {
  528. if ( !m_pCommandCompletionCallback )
  529. return 0;
  530. return m_pCommandCompletionCallback->CommandCompletionCallback( partial, commands );
  531. }
  532. Assert( m_fnCompletionCallback );
  533. if ( !m_fnCompletionCallback )
  534. return 0;
  535. char rgpchCommands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ];
  536. int iret = ( m_fnCompletionCallback )( partial, rgpchCommands );
  537. for ( int i = 0 ; i < iret; ++i )
  538. {
  539. CUtlString str = rgpchCommands[ i ];
  540. commands.AddToTail( str );
  541. }
  542. return iret;
  543. }
  544. //-----------------------------------------------------------------------------
  545. // Returns true if the console command can autocomplete
  546. //-----------------------------------------------------------------------------
  547. bool ConCommand::CanAutoComplete( void )
  548. {
  549. return m_bHasCompletionCallback;
  550. }
  551. //-----------------------------------------------------------------------------
  552. //
  553. // Console Variables
  554. //
  555. //-----------------------------------------------------------------------------
  556. //-----------------------------------------------------------------------------
  557. // Various constructors
  558. //-----------------------------------------------------------------------------
  559. ConVar::ConVar( const char *pName, const char *pDefaultValue, int flags /* = 0 */ )
  560. {
  561. Create( pName, pDefaultValue, flags );
  562. }
  563. ConVar::ConVar( const char *pName, const char *pDefaultValue, int flags, const char *pHelpString )
  564. {
  565. Create( pName, pDefaultValue, flags, pHelpString );
  566. }
  567. ConVar::ConVar( const char *pName, const char *pDefaultValue, int flags, const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax )
  568. {
  569. Create( pName, pDefaultValue, flags, pHelpString, bMin, fMin, bMax, fMax );
  570. }
  571. ConVar::ConVar( const char *pName, const char *pDefaultValue, int flags, const char *pHelpString, FnChangeCallback_t callback )
  572. {
  573. Create( pName, pDefaultValue, flags, pHelpString, false, 0.0, false, 0.0, callback );
  574. }
  575. ConVar::ConVar( const char *pName, const char *pDefaultValue, int flags, const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax, FnChangeCallback_t callback )
  576. {
  577. Create( pName, pDefaultValue, flags, pHelpString, bMin, fMin, bMax, fMax, callback );
  578. }
  579. //-----------------------------------------------------------------------------
  580. // Destructor
  581. //-----------------------------------------------------------------------------
  582. ConVar::~ConVar( void )
  583. {
  584. if ( m_Value.m_pszString )
  585. {
  586. delete[] m_Value.m_pszString;
  587. m_Value.m_pszString = NULL;
  588. }
  589. }
  590. //-----------------------------------------------------------------------------
  591. // Install a change callback (there shouldn't already be one....)
  592. //-----------------------------------------------------------------------------
  593. void ConVar::InstallChangeCallback( FnChangeCallback_t callback, bool bInvoke /*=true*/ )
  594. {
  595. if ( !callback )
  596. {
  597. Warning( "InstallChangeCallback called with NULL callback, ignoring!!!\n" );
  598. return;
  599. }
  600. if ( m_pParent->m_fnChangeCallbacks.Find( callback ) != m_pParent->m_fnChangeCallbacks.InvalidIndex() )
  601. {
  602. // Same ptr added twice, sigh...
  603. Warning( "InstallChangeCallback ignoring duplicate change callback!!!\n" );
  604. return;
  605. }
  606. m_pParent->m_fnChangeCallbacks.AddToTail( callback );
  607. // Call it immediately to set the initial value...
  608. if ( bInvoke )
  609. {
  610. callback( this, m_Value.m_pszString, m_Value.m_fValue );
  611. }
  612. }
  613. void ConVar::RemoveChangeCallback( FnChangeCallback_t callback )
  614. {
  615. m_pParent->m_fnChangeCallbacks.FindAndRemove( callback );
  616. }
  617. bool ConVar::IsFlagSet( int flag ) const
  618. {
  619. return ( flag & m_pParent->m_nFlags ) ? true : false;
  620. }
  621. int ConVar::GetFlags() const
  622. {
  623. return m_pParent->m_nFlags;
  624. }
  625. const char *ConVar::GetHelpText( void ) const
  626. {
  627. return m_pParent->m_pszHelpString;
  628. }
  629. void ConVar::AddFlags( int flags )
  630. {
  631. m_pParent->m_nFlags |= flags;
  632. #ifdef ALLOW_DEVELOPMENT_CVARS
  633. m_pParent->m_nFlags &= ~FCVAR_DEVELOPMENTONLY;
  634. #endif
  635. }
  636. bool ConVar::IsRegistered( void ) const
  637. {
  638. return m_pParent->m_bRegistered;
  639. }
  640. const char *ConVar::GetName( void ) const
  641. {
  642. return m_pParent->m_pszName;
  643. }
  644. const char *ConVar::GetBaseName( void ) const
  645. {
  646. return m_pParent->m_pszName;
  647. }
  648. int ConVar::GetSplitScreenPlayerSlot( void ) const
  649. {
  650. // Default implementation (certain FCVAR_USERINFO derive a new type of convar and set this)
  651. return 0;
  652. }
  653. //-----------------------------------------------------------------------------
  654. // Purpose:
  655. // Output : Returns true on success, false on failure.
  656. //-----------------------------------------------------------------------------
  657. bool ConVar::IsCommand( void ) const
  658. {
  659. return false;
  660. }
  661. //-----------------------------------------------------------------------------
  662. // Purpose:
  663. // Input :
  664. //-----------------------------------------------------------------------------
  665. void ConVar::Init()
  666. {
  667. BaseClass::Init();
  668. }
  669. bool ConVar::InternalSetColorFromString( const char *value )
  670. {
  671. bool bColor = false;
  672. // Try pulling RGBA color values out of the string
  673. int nRGBA[4];
  674. int nParamsRead = sscanf( value, "%i %i %i %i", &(nRGBA[0]), &(nRGBA[1]), &(nRGBA[2]), &(nRGBA[3]) );
  675. if ( nParamsRead >= 3 )
  676. {
  677. // This is probably a color!
  678. if ( nParamsRead == 3 )
  679. {
  680. // Assume they wanted full alpha
  681. nRGBA[3] = 255;
  682. }
  683. if ( nRGBA[0] >= 0 && nRGBA[0] <= 255 &&
  684. nRGBA[1] >= 0 && nRGBA[1] <= 255 &&
  685. nRGBA[2] >= 0 && nRGBA[2] <= 255 &&
  686. nRGBA[3] >= 0 && nRGBA[3] <= 255 )
  687. {
  688. // This is definitely a color!
  689. bColor = true;
  690. // Stuff all the values into each byte of our int
  691. unsigned char *pColorElement = ((unsigned char*)&m_Value.m_nValue);
  692. pColorElement[0] = nRGBA[0];
  693. pColorElement[1] = nRGBA[1];
  694. pColorElement[2] = nRGBA[2];
  695. pColorElement[3] = nRGBA[3];
  696. // Copy that value into a float (even though this has little meaning)
  697. m_Value.m_fValue = ( float )( m_Value.m_nValue );
  698. }
  699. }
  700. return bColor;
  701. }
  702. //-----------------------------------------------------------------------------
  703. // Purpose:
  704. // Input : *value -
  705. //-----------------------------------------------------------------------------
  706. void ConVar::InternalSetValue( const char *value )
  707. {
  708. if ( IsFlagSet( FCVAR_MATERIAL_THREAD_MASK ) )
  709. {
  710. if ( g_pCVar && !g_pCVar->IsMaterialThreadSetAllowed() )
  711. {
  712. g_pCVar->QueueMaterialThreadSetValue( this, value );
  713. return;
  714. }
  715. }
  716. char tempVal[ 32 ];
  717. char *val;
  718. Assert(m_pParent == this); // Only valid for root convars.
  719. float flOldValue = m_Value.m_fValue;
  720. val = (char *)value;
  721. if ( !val )
  722. val = "";
  723. if ( !InternalSetColorFromString( value ) )
  724. {
  725. // Not a color, do the standard thing
  726. float fNewValue = ( float )atof( value );
  727. if ( !IsFinite( fNewValue ) )
  728. {
  729. Warning( "Warning: %s = '%s' is infinite, clamping value.\n", GetName(), value );
  730. fNewValue = FLT_MAX;
  731. }
  732. if ( ClampValue( fNewValue ) )
  733. {
  734. V_snprintf( tempVal,sizeof(tempVal), "%f", fNewValue );
  735. val = tempVal;
  736. }
  737. // Redetermine value
  738. m_Value.m_fValue = fNewValue;
  739. m_Value.m_nValue = ( int )( m_Value.m_fValue );
  740. }
  741. if ( !( m_nFlags & FCVAR_NEVER_AS_STRING ) )
  742. {
  743. ChangeStringValue( val, flOldValue );
  744. }
  745. }
  746. //-----------------------------------------------------------------------------
  747. // Purpose:
  748. // Input : *tempVal -
  749. //-----------------------------------------------------------------------------
  750. void ConVar::ChangeStringValue( const char *tempVal, float flOldValue )
  751. {
  752. Assert( !( m_nFlags & FCVAR_NEVER_AS_STRING ) );
  753. char* pszOldValue = (char*)stackalloc( m_Value.m_StringLength );
  754. memcpy( pszOldValue, m_Value.m_pszString, m_Value.m_StringLength );
  755. int len = V_strlen(tempVal) + 1;
  756. if ( len > m_Value.m_StringLength)
  757. {
  758. if (m_Value.m_pszString)
  759. {
  760. delete[] m_Value.m_pszString;
  761. }
  762. m_Value.m_pszString = new char[len];
  763. m_Value.m_StringLength = len;
  764. }
  765. memcpy( m_Value.m_pszString, tempVal, len );
  766. // Invoke any necessary callback function
  767. for ( int i = 0; i < m_fnChangeCallbacks.Count(); ++i )
  768. {
  769. m_fnChangeCallbacks[ i ]( this, pszOldValue, flOldValue );
  770. }
  771. if ( g_pCVar )
  772. {
  773. g_pCVar->CallGlobalChangeCallbacks( this, pszOldValue, flOldValue );
  774. }
  775. stackfree( pszOldValue );
  776. }
  777. //-----------------------------------------------------------------------------
  778. // Purpose: Check whether to clamp and then perform clamp
  779. // Input : value -
  780. // Output : Returns true if value changed
  781. //-----------------------------------------------------------------------------
  782. bool ConVar::ClampValue( float& value )
  783. {
  784. if ( m_bHasMin && ( value < m_fMinVal ) )
  785. {
  786. value = m_fMinVal;
  787. return true;
  788. }
  789. if ( m_bHasMax && ( value > m_fMaxVal ) )
  790. {
  791. value = m_fMaxVal;
  792. return true;
  793. }
  794. return false;
  795. }
  796. //-----------------------------------------------------------------------------
  797. // Purpose:
  798. // Input : *value -
  799. //-----------------------------------------------------------------------------
  800. void ConVar::InternalSetFloatValue( float fNewValue )
  801. {
  802. if ( fNewValue == m_Value.m_fValue )
  803. return;
  804. if ( IsFlagSet( FCVAR_MATERIAL_THREAD_MASK ) )
  805. {
  806. if ( g_pCVar && !g_pCVar->IsMaterialThreadSetAllowed() )
  807. {
  808. g_pCVar->QueueMaterialThreadSetValue( this, fNewValue );
  809. return;
  810. }
  811. }
  812. Assert( m_pParent == this ); // Only valid for root convars.
  813. // Check bounds
  814. ClampValue( fNewValue );
  815. // Redetermine value
  816. float flOldValue = m_Value.m_fValue;
  817. m_Value.m_fValue = fNewValue;
  818. m_Value.m_nValue = ( int )m_Value.m_fValue;
  819. if ( !( m_nFlags & FCVAR_NEVER_AS_STRING ) )
  820. {
  821. char tempVal[ 32 ];
  822. V_snprintf( tempVal, sizeof( tempVal), "%f", m_Value.m_fValue );
  823. ChangeStringValue( tempVal, flOldValue );
  824. }
  825. else
  826. {
  827. Assert( m_fnChangeCallbacks.Count() == 0 );
  828. }
  829. }
  830. //-----------------------------------------------------------------------------
  831. // Purpose:
  832. // Input : *value -
  833. //-----------------------------------------------------------------------------
  834. void ConVar::InternalSetIntValue( int nValue )
  835. {
  836. if ( nValue == m_Value.m_nValue )
  837. return;
  838. if ( IsFlagSet( FCVAR_MATERIAL_THREAD_MASK ) )
  839. {
  840. if ( g_pCVar && !g_pCVar->IsMaterialThreadSetAllowed() )
  841. {
  842. g_pCVar->QueueMaterialThreadSetValue( this, nValue );
  843. return;
  844. }
  845. }
  846. Assert( m_pParent == this ); // Only valid for root convars.
  847. float fValue = (float)nValue;
  848. if ( ClampValue( fValue ) )
  849. {
  850. nValue = ( int )( fValue );
  851. }
  852. // Redetermine value
  853. float flOldValue = m_Value.m_fValue;
  854. m_Value.m_fValue = fValue;
  855. m_Value.m_nValue = nValue;
  856. if ( !( m_nFlags & FCVAR_NEVER_AS_STRING ) )
  857. {
  858. char tempVal[ 32 ];
  859. V_snprintf( tempVal, sizeof( tempVal ), "%d", m_Value.m_nValue );
  860. ChangeStringValue( tempVal, flOldValue );
  861. }
  862. else
  863. {
  864. Assert( m_fnChangeCallbacks.Count() == 0 );
  865. }
  866. }
  867. //-----------------------------------------------------------------------------
  868. // Purpose:
  869. // Input : *value -
  870. //-----------------------------------------------------------------------------
  871. void ConVar::InternalSetColorValue( Color value )
  872. {
  873. // Stuff color values into an int
  874. int nValue;
  875. unsigned char *pColorElement = ((unsigned char*)&nValue);
  876. pColorElement[0] = value[0];
  877. pColorElement[1] = value[1];
  878. pColorElement[2] = value[2];
  879. pColorElement[3] = value[3];
  880. // Call the int internal set
  881. InternalSetIntValue( nValue );
  882. }
  883. //-----------------------------------------------------------------------------
  884. // Purpose: Private creation
  885. //-----------------------------------------------------------------------------
  886. void ConVar::Create( const char *pName, const char *pDefaultValue, int flags /*= 0*/,
  887. const char *pHelpString /*= NULL*/, bool bMin /*= false*/, float fMin /*= 0.0*/,
  888. bool bMax /*= false*/, float fMax /*= false*/, FnChangeCallback_t callback /*= NULL*/ )
  889. {
  890. static char *empty_string = "";
  891. m_pParent = this;
  892. // Name should be static data
  893. m_pszDefaultValue = pDefaultValue ? pDefaultValue : empty_string;
  894. Assert( m_pszDefaultValue );
  895. m_bHasMin = bMin;
  896. m_fMinVal = fMin;
  897. m_bHasMax = bMax;
  898. m_fMaxVal = fMax;
  899. if ( callback )
  900. {
  901. m_fnChangeCallbacks.AddToTail( callback );
  902. }
  903. m_Value.m_StringLength = strlen( m_pszDefaultValue ) + 1;
  904. m_Value.m_pszString = new char[m_Value.m_StringLength];
  905. memcpy( m_Value.m_pszString, m_pszDefaultValue, m_Value.m_StringLength );
  906. if ( !InternalSetColorFromString( m_Value.m_pszString ) )
  907. {
  908. m_Value.m_fValue = ( float )atof( m_Value.m_pszString );
  909. if ( !IsFinite( m_Value.m_fValue ) )
  910. {
  911. Warning( "ConVar(%s) defined with infinite float value (%s)\n", pName, m_Value.m_pszString );
  912. m_Value.m_fValue = FLT_MAX;
  913. Assert( 0 );
  914. }
  915. // Bounds Check, should never happen, if it does, no big deal
  916. if ( m_bHasMin && ( m_Value.m_fValue < m_fMinVal ) )
  917. {
  918. Assert( 0 );
  919. }
  920. if ( m_bHasMax && ( m_Value.m_fValue > m_fMaxVal ) )
  921. {
  922. Assert( 0 );
  923. }
  924. m_Value.m_nValue = ( int )m_Value.m_fValue;
  925. }
  926. //If we're not tagged as cheat, archive or release then hide us.
  927. #if CULL_ALL_CVARS_NOT_FCVAR_RELEASE
  928. // FIXMEL4DTOMAINMERGE: will need to assess if this hides too many convars for TF and other projects in main
  929. if ( !( flags & ( FCVAR_CHEAT | FCVAR_ARCHIVE | FCVAR_RELEASE | FCVAR_USERINFO ) ) )
  930. {
  931. flags |= FCVAR_DEVELOPMENTONLY;
  932. }
  933. #else
  934. #endif
  935. BaseClass::Create( pName, pHelpString, flags );
  936. }
  937. //-----------------------------------------------------------------------------
  938. // Purpose:
  939. // Input : *value -
  940. //-----------------------------------------------------------------------------
  941. void ConVar::SetValue(const char *value)
  942. {
  943. ConVar *var = ( ConVar * )m_pParent;
  944. var->InternalSetValue( value );
  945. }
  946. //-----------------------------------------------------------------------------
  947. // Purpose:
  948. // Input : value -
  949. //-----------------------------------------------------------------------------
  950. void ConVar::SetValue( float value )
  951. {
  952. ConVar *var = ( ConVar * )m_pParent;
  953. var->InternalSetFloatValue( value );
  954. }
  955. //-----------------------------------------------------------------------------
  956. // Purpose:
  957. // Input : value -
  958. //-----------------------------------------------------------------------------
  959. void ConVar::SetValue( int value )
  960. {
  961. ConVar *var = ( ConVar * )m_pParent;
  962. var->InternalSetIntValue( value );
  963. }
  964. //-----------------------------------------------------------------------------
  965. // Purpose:
  966. // Input : value -
  967. //-----------------------------------------------------------------------------
  968. void ConVar::SetValue( Color value )
  969. {
  970. ConVar *var = ( ConVar * )m_pParent;
  971. var->InternalSetColorValue( value );
  972. }
  973. //-----------------------------------------------------------------------------
  974. // Purpose: Reset to default value
  975. //-----------------------------------------------------------------------------
  976. void ConVar::Revert( void )
  977. {
  978. // Force default value again
  979. ConVar *var = ( ConVar * )m_pParent;
  980. var->SetValue( var->m_pszDefaultValue );
  981. }
  982. //-----------------------------------------------------------------------------
  983. // Purpose:
  984. // Input : minVal -
  985. // Output : true if there is a min set
  986. //-----------------------------------------------------------------------------
  987. bool ConVar::GetMin( float& minVal ) const
  988. {
  989. minVal = m_pParent->m_fMinVal;
  990. return m_pParent->m_bHasMin;
  991. }
  992. //-----------------------------------------------------------------------------
  993. // Purpose:
  994. // Input : maxVal -
  995. //-----------------------------------------------------------------------------
  996. bool ConVar::GetMax( float& maxVal ) const
  997. {
  998. maxVal = m_pParent->m_fMaxVal;
  999. return m_pParent->m_bHasMax;
  1000. }
  1001. float ConVar::GetMinValue() const
  1002. {
  1003. return m_pParent->m_fMinVal;
  1004. }
  1005. float ConVar::GetMaxValue() const
  1006. {
  1007. return m_pParent->m_fMaxVal;;
  1008. }
  1009. bool ConVar::HasMin() const
  1010. {
  1011. return m_pParent->m_bHasMin;
  1012. }
  1013. bool ConVar::HasMax() const
  1014. {
  1015. return m_pParent->m_bHasMax;
  1016. }
  1017. //-----------------------------------------------------------------------------
  1018. // Purpose:
  1019. // Output : const char
  1020. //-----------------------------------------------------------------------------
  1021. const char *ConVar::GetDefault( void ) const
  1022. {
  1023. return m_pParent->m_pszDefaultValue;
  1024. }
  1025. void ConVar::SetDefault( const char *pszDefault )
  1026. {
  1027. static char *empty_string = "";
  1028. m_pszDefaultValue = pszDefault ? pszDefault : empty_string;
  1029. Assert( m_pszDefaultValue );
  1030. }
  1031. //-----------------------------------------------------------------------------
  1032. // This version is simply used to make reading convars simpler.
  1033. // Writing convars isn't allowed in this mode
  1034. //-----------------------------------------------------------------------------
  1035. class CEmptyConVar : public ConVar
  1036. {
  1037. public:
  1038. CEmptyConVar() : ConVar( "", "0" ) {}
  1039. // Used for optimal read access
  1040. virtual void SetValue( const char *pValue ) {}
  1041. virtual void SetValue( float flValue ) {}
  1042. virtual void SetValue( int nValue ) {}
  1043. virtual const char *GetName( void ) const { return ""; }
  1044. virtual bool IsFlagSet( int nFlags ) const { return false; }
  1045. };
  1046. static CEmptyConVar s_EmptyConVar;
  1047. ConVarRef::ConVarRef( const char *pName )
  1048. {
  1049. Init( pName, false );
  1050. }
  1051. ConVarRef::ConVarRef( const char *pName, bool bIgnoreMissing )
  1052. {
  1053. Init( pName, bIgnoreMissing );
  1054. }
  1055. void ConVarRef::Init( const char *pName, bool bIgnoreMissing )
  1056. {
  1057. m_pConVar = g_pCVar ? g_pCVar->FindVar( pName ) : &s_EmptyConVar;
  1058. if ( !m_pConVar )
  1059. {
  1060. m_pConVar = &s_EmptyConVar;
  1061. }
  1062. m_pConVarState = static_cast< ConVar * >( m_pConVar );
  1063. if( !IsValid() )
  1064. {
  1065. static bool bFirst = true;
  1066. if ( g_pCVar || bFirst )
  1067. {
  1068. if ( !bIgnoreMissing )
  1069. {
  1070. Warning( "ConVarRef %s doesn't point to an existing ConVar\n", pName );
  1071. }
  1072. bFirst = false;
  1073. }
  1074. }
  1075. }
  1076. ConVarRef::ConVarRef( IConVar *pConVar )
  1077. {
  1078. m_pConVar = pConVar ? pConVar : &s_EmptyConVar;
  1079. m_pConVarState = static_cast< ConVar * >( m_pConVar );
  1080. }
  1081. bool ConVarRef::IsValid() const
  1082. {
  1083. return m_pConVar != &s_EmptyConVar;
  1084. }
  1085. // Helper for splitscreen ConVars
  1086. SplitScreenConVarRef::SplitScreenConVarRef( const char *pName )
  1087. {
  1088. Init( pName, false );
  1089. }
  1090. SplitScreenConVarRef::SplitScreenConVarRef( const char *pName, bool bIgnoreMissing )
  1091. {
  1092. Init( pName, bIgnoreMissing );
  1093. }
  1094. void SplitScreenConVarRef::Init( const char *pName, bool bIgnoreMissing )
  1095. {
  1096. for ( int i = 0; i < MAX_SPLITSCREEN_CLIENTS; ++i )
  1097. {
  1098. cv_t &info = m_Info[ i ];
  1099. char pchName[ 256 ];
  1100. if ( i != 0 )
  1101. {
  1102. V_snprintf( pchName, sizeof( pchName ), "%s%d", pName, i + 1 );
  1103. }
  1104. else
  1105. {
  1106. V_strncpy( pchName, pName, sizeof( pchName ) );
  1107. }
  1108. info.m_pConVar = g_pCVar ? g_pCVar->FindVar( pchName ) : &s_EmptyConVar;
  1109. if ( !info.m_pConVar )
  1110. {
  1111. info.m_pConVar = &s_EmptyConVar;
  1112. if ( i > 0 )
  1113. {
  1114. // Point at slot zero instead, in case we got in here with a non FCVAR_SS var...
  1115. info.m_pConVar = m_Info[ 0 ].m_pConVar;
  1116. }
  1117. }
  1118. info.m_pConVarState = static_cast< ConVar * >( info.m_pConVar );
  1119. }
  1120. if ( !IsValid() )
  1121. {
  1122. static bool bFirst = true;
  1123. if ( g_pCVar || bFirst )
  1124. {
  1125. if ( !bIgnoreMissing )
  1126. {
  1127. Warning( "ConVarRef %s doesn't point to an existing ConVar\n", pName );
  1128. }
  1129. bFirst = false;
  1130. }
  1131. }
  1132. }
  1133. SplitScreenConVarRef::SplitScreenConVarRef( IConVar *pConVar )
  1134. {
  1135. cv_t &info = m_Info[ 0 ];
  1136. info.m_pConVar = pConVar ? pConVar : &s_EmptyConVar;
  1137. info.m_pConVarState = static_cast< ConVar * >( info.m_pConVar );
  1138. for ( int i = 1; i < MAX_SPLITSCREEN_CLIENTS; ++i )
  1139. {
  1140. info = m_Info[ i ];
  1141. char pchName[ 256 ];
  1142. V_snprintf( pchName, sizeof( pchName ), "%s%d", pConVar->GetName(), i + 1 );
  1143. info.m_pConVar = g_pCVar ? g_pCVar->FindVar( pchName ) : &s_EmptyConVar;
  1144. if ( !info.m_pConVar )
  1145. {
  1146. info.m_pConVar = &s_EmptyConVar;
  1147. if ( i > 0 )
  1148. {
  1149. // Point at slot zero instead, in case we got in here with a non FCVAR_SS var...
  1150. info.m_pConVar = m_Info[ 0 ].m_pConVar;
  1151. }
  1152. }
  1153. info.m_pConVarState = static_cast< ConVar * >( info.m_pConVar );
  1154. }
  1155. }
  1156. bool SplitScreenConVarRef::IsValid() const
  1157. {
  1158. return m_Info[ 0 ].m_pConVar != &s_EmptyConVar;
  1159. }
  1160. struct PrintConVarFlags_t
  1161. {
  1162. int flag;
  1163. const char *desc;
  1164. };
  1165. static PrintConVarFlags_t g_PrintConVarFlags[] =
  1166. {
  1167. { FCVAR_GAMEDLL, "game" },
  1168. { FCVAR_CLIENTDLL, "client" },
  1169. { FCVAR_ARCHIVE, "archive" },
  1170. { FCVAR_NOTIFY, "notify" },
  1171. { FCVAR_SPONLY, "singleplayer" },
  1172. { FCVAR_NOT_CONNECTED, "notconnected" },
  1173. { FCVAR_CHEAT, "cheat" },
  1174. { FCVAR_REPLICATED, "replicated" },
  1175. { FCVAR_SERVER_CAN_EXECUTE, "server_can_execute" },
  1176. { FCVAR_CLIENTCMD_CAN_EXECUTE, "clientcmd_can_execute" },
  1177. { FCVAR_USERINFO, "user" },
  1178. { FCVAR_SS, "ss" },
  1179. { FCVAR_SS_ADDED, "ss_added" },
  1180. };
  1181. //-----------------------------------------------------------------------------
  1182. // Purpose:
  1183. //-----------------------------------------------------------------------------
  1184. void ConVar_AppendFlags( const ConCommandBase *var, char *buf, size_t bufsize )
  1185. {
  1186. for ( int i = 0; i < ARRAYSIZE( g_PrintConVarFlags ) ; ++i )
  1187. {
  1188. const PrintConVarFlags_t &info = g_PrintConVarFlags[ i ];
  1189. if ( var->IsFlagSet( info.flag ) )
  1190. {
  1191. char append[ 128 ];
  1192. V_snprintf( append, sizeof( append ), " %s", info.desc );
  1193. V_strncat( buf, append, bufsize, COPY_ALL_CHARACTERS );
  1194. }
  1195. }
  1196. }
  1197. static void AppendPrintf( char *buf, size_t bufsize, char const *fmt, ... )
  1198. {
  1199. char scratch[ 1024 ];
  1200. va_list argptr;
  1201. va_start( argptr, fmt );
  1202. _vsnprintf( scratch, sizeof( scratch ) - 1, fmt, argptr );
  1203. va_end( argptr );
  1204. scratch[ sizeof( scratch ) - 1 ] = 0;
  1205. V_strncat( buf, scratch, bufsize, COPY_ALL_CHARACTERS );
  1206. }
  1207. //-----------------------------------------------------------------------------
  1208. // Purpose:
  1209. //-----------------------------------------------------------------------------
  1210. void ConVar_PrintDescription( const ConCommandBase *pVar )
  1211. {
  1212. bool bMin, bMax;
  1213. float fMin, fMax;
  1214. const char *pStr;
  1215. Assert( pVar );
  1216. Color clr;
  1217. clr.SetColor( 255, 100, 100, 255 );
  1218. char outstr[ 4096 ];
  1219. outstr[ 0 ] = 0;
  1220. if ( !pVar->IsCommand() )
  1221. {
  1222. ConVar *var = ( ConVar * )pVar;
  1223. const ConVar_ServerBounded *pBounded = dynamic_cast<const ConVar_ServerBounded*>( var );
  1224. bMin = var->GetMin( fMin );
  1225. bMax = var->GetMax( fMax );
  1226. const char *value = NULL;
  1227. char tempVal[ 32 ];
  1228. if ( pBounded || var->IsFlagSet( FCVAR_NEVER_AS_STRING ) )
  1229. {
  1230. value = tempVal;
  1231. int intVal = pBounded ? pBounded->GetInt() : var->GetInt();
  1232. float floatVal = pBounded ? pBounded->GetFloat() : var->GetFloat();
  1233. if ( fabs( (float)intVal - floatVal ) < 0.000001 )
  1234. {
  1235. V_snprintf( tempVal, sizeof( tempVal ), "%d", intVal );
  1236. }
  1237. else
  1238. {
  1239. V_snprintf( tempVal, sizeof( tempVal ), "%f", floatVal );
  1240. }
  1241. }
  1242. else
  1243. {
  1244. value = var->GetString();
  1245. }
  1246. if ( value )
  1247. {
  1248. AppendPrintf( outstr, sizeof( outstr ), "\"%s\" = \"%s\"", var->GetName(), value );
  1249. if ( V_stricmp( value, var->GetDefault() ) )
  1250. {
  1251. AppendPrintf( outstr, sizeof( outstr ), " ( def. \"%s\" )", var->GetDefault() );
  1252. }
  1253. }
  1254. if ( bMin )
  1255. {
  1256. AppendPrintf( outstr, sizeof( outstr ), " min. %f", fMin );
  1257. }
  1258. if ( bMax )
  1259. {
  1260. AppendPrintf( outstr, sizeof( outstr ), " max. %f", fMax );
  1261. }
  1262. // Handle virtualized cvars.
  1263. if ( pBounded && fabs( pBounded->GetFloat() - var->GetFloat() ) > 0.0001f )
  1264. {
  1265. AppendPrintf( outstr, sizeof( outstr ), " [%.3f server clamped to %.3f]",
  1266. var->GetFloat(), pBounded->GetFloat() );
  1267. }
  1268. }
  1269. else
  1270. {
  1271. ConCommand *var = ( ConCommand * )pVar;
  1272. AppendPrintf( outstr, sizeof( outstr ), "\"%s\" ", var->GetName() );
  1273. }
  1274. ConVar_AppendFlags( pVar, outstr, sizeof( outstr ) );
  1275. pStr = pVar->GetHelpText();
  1276. if ( pStr && *pStr )
  1277. {
  1278. ConMsg( "%-80s - %.80s\n", outstr, pStr );
  1279. }
  1280. else
  1281. {
  1282. ConMsg( "%-80s\n", outstr );
  1283. }
  1284. }