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.

1137 lines
27 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include "cvar.h"
  9. #include "gl_cvars.h"
  10. #include "tier1/convar.h"
  11. #include "filesystem.h"
  12. #include "filesystem_engine.h"
  13. #include "client.h"
  14. #include "server.h"
  15. #include "GameEventManager.h"
  16. #include "netmessages.h"
  17. #include "sv_main.h"
  18. #include "demo.h"
  19. #include <ctype.h>
  20. #ifdef POSIX
  21. #include <wctype.h>
  22. #endif
  23. #ifndef SWDS
  24. #include <vgui_controls/Controls.h>
  25. #include <vgui/ILocalize.h>
  26. #endif
  27. // memdbgon must be the last include file in a .cpp file!!!
  28. #include "tier0/memdbgon.h"
  29. //-----------------------------------------------------------------------------
  30. // Singleton CCvarUtilities
  31. //-----------------------------------------------------------------------------
  32. static CCvarUtilities g_CvarUtilities;
  33. CCvarUtilities *cv = &g_CvarUtilities;
  34. //-----------------------------------------------------------------------------
  35. // Purpose: Update clients/server when FCVAR_REPLICATED etc vars change
  36. //-----------------------------------------------------------------------------
  37. static void ConVarNetworkChangeCallback( IConVar *pConVar, const char *pOldValue, float flOldValue )
  38. {
  39. ConVarRef var( pConVar );
  40. if ( !pOldValue )
  41. {
  42. if ( var.GetFloat() == flOldValue )
  43. return;
  44. }
  45. else
  46. {
  47. if ( !Q_strcmp( var.GetString(), pOldValue ) )
  48. return;
  49. }
  50. if ( var.IsFlagSet( FCVAR_USERINFO ) )
  51. {
  52. // Are we not a server, but a client and have a change?
  53. if ( cl.IsConnected() )
  54. {
  55. // send changed cvar to server
  56. NET_SetConVar convar( var.GetName(), var.GetString() );
  57. cl.m_NetChannel->SendNetMsg( convar );
  58. }
  59. }
  60. // Log changes to server variables
  61. // Print to clients
  62. if ( var.IsFlagSet( FCVAR_NOTIFY ) )
  63. {
  64. IGameEvent *event = g_GameEventManager.CreateEvent( "server_cvar" );
  65. if ( event )
  66. {
  67. event->SetString( "cvarname", var.GetName() );
  68. if ( var.IsFlagSet( FCVAR_PROTECTED ) )
  69. {
  70. event->SetString("cvarvalue", "***PROTECTED***" );
  71. }
  72. else
  73. {
  74. event->SetString("cvarvalue", var.GetString() );
  75. }
  76. g_GameEventManager.FireEvent( event );
  77. }
  78. }
  79. // Force changes down to clients (if running server)
  80. if ( var.IsFlagSet( FCVAR_REPLICATED ) && sv.IsActive() )
  81. {
  82. SV_ReplicateConVarChange( static_cast< ConVar* >( pConVar ), var.GetString() );
  83. }
  84. }
  85. //-----------------------------------------------------------------------------
  86. // Implementation of the ICvarQuery interface
  87. //-----------------------------------------------------------------------------
  88. class CCvarQuery : public CBaseAppSystem< ICvarQuery >
  89. {
  90. public:
  91. virtual bool Connect( CreateInterfaceFn factory )
  92. {
  93. ICvar *pCVar = (ICvar*)factory( CVAR_INTERFACE_VERSION, 0 );
  94. if ( !pCVar )
  95. return false;
  96. pCVar->InstallCVarQuery( this );
  97. return true;
  98. }
  99. virtual InitReturnVal_t Init()
  100. {
  101. // If the value has changed, notify clients/server based on ConVar flags.
  102. // NOTE: this will only happen for non-FCVAR_NEVER_AS_STRING vars.
  103. // Also, this happened in SetDirect for older clients that don't have the
  104. // callback interface.
  105. g_pCVar->InstallGlobalChangeCallback( ConVarNetworkChangeCallback );
  106. return INIT_OK;
  107. }
  108. virtual void Shutdown()
  109. {
  110. g_pCVar->RemoveGlobalChangeCallback( ConVarNetworkChangeCallback );
  111. }
  112. virtual void *QueryInterface( const char *pInterfaceName )
  113. {
  114. if ( !Q_stricmp( pInterfaceName, CVAR_QUERY_INTERFACE_VERSION ) )
  115. return (ICvarQuery*)this;
  116. return NULL;
  117. }
  118. // Purpose: Returns true if the commands can be aliased to one another
  119. // Either game/client .dll shared with engine,
  120. // or game and client dll shared and marked FCVAR_REPLICATED
  121. virtual bool AreConVarsLinkable( const ConVar *child, const ConVar *parent )
  122. {
  123. // Both parent and child must be marked replicated for this to work
  124. bool repchild = child->IsFlagSet( FCVAR_REPLICATED );
  125. bool repparent = parent->IsFlagSet( FCVAR_REPLICATED );
  126. if ( repchild && repparent )
  127. {
  128. // Never on protected vars
  129. if ( child->IsFlagSet( FCVAR_PROTECTED ) || parent->IsFlagSet( FCVAR_PROTECTED ) )
  130. {
  131. ConMsg( "FCVAR_REPLICATED can't also be FCVAR_PROTECTED (%s)\n", child->GetName() );
  132. return false;
  133. }
  134. // Only on ConVars
  135. if ( child->IsCommand() || parent->IsCommand() )
  136. {
  137. ConMsg( "FCVAR_REPLICATED not valid on ConCommands (%s)\n", child->GetName() );
  138. return false;
  139. }
  140. // One must be in client .dll and the other in the game .dll, or both in the engine
  141. if ( child->IsFlagSet( FCVAR_GAMEDLL ) && !parent->IsFlagSet( FCVAR_CLIENTDLL ) )
  142. {
  143. ConMsg( "For FCVAR_REPLICATED, ConVar must be defined in client and game .dlls (%s)\n", child->GetName() );
  144. return false;
  145. }
  146. if ( child->IsFlagSet( FCVAR_CLIENTDLL ) && !parent->IsFlagSet( FCVAR_GAMEDLL ) )
  147. {
  148. ConMsg( "For FCVAR_REPLICATED, ConVar must be defined in client and game .dlls (%s)\n", child->GetName() );
  149. return false;
  150. }
  151. // Allowable
  152. return true;
  153. }
  154. // Otherwise need both to allow linkage
  155. if ( repchild || repparent )
  156. {
  157. ConMsg( "Both ConVars must be marked FCVAR_REPLICATED for linkage to work (%s)\n", child->GetName() );
  158. return false;
  159. }
  160. if ( parent->IsFlagSet( FCVAR_CLIENTDLL ) )
  161. {
  162. ConMsg( "Parent cvar in client.dll not allowed (%s)\n", child->GetName() );
  163. return false;
  164. }
  165. if ( parent->IsFlagSet( FCVAR_GAMEDLL ) )
  166. {
  167. ConMsg( "Parent cvar in server.dll not allowed (%s)\n", child->GetName() );
  168. return false;
  169. }
  170. return true;
  171. }
  172. };
  173. //-----------------------------------------------------------------------------
  174. // Singleton
  175. //-----------------------------------------------------------------------------
  176. static CCvarQuery s_CvarQuery;
  177. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CCvarQuery, ICvarQuery, CVAR_QUERY_INTERFACE_VERSION, s_CvarQuery );
  178. //-----------------------------------------------------------------------------
  179. //
  180. // CVar utilities begins here
  181. //
  182. //-----------------------------------------------------------------------------
  183. static bool IsAllSpaces( const wchar_t *str )
  184. {
  185. const wchar_t *p = str;
  186. while ( p && *p )
  187. {
  188. if ( !iswspace( *p ) )
  189. return false;
  190. ++p;
  191. }
  192. return true;
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Purpose:
  196. // Input : *var -
  197. // *value -
  198. //-----------------------------------------------------------------------------
  199. void CCvarUtilities::SetDirect( ConVar *var, const char *value )
  200. {
  201. const char *pszValue;
  202. char szNew[ 1024 ];
  203. // Bail early if we're trying to set a FCVAR_USERINFO cvar on a dedicated server
  204. if ( var->IsFlagSet( FCVAR_USERINFO ) )
  205. {
  206. if ( sv.IsDedicated() )
  207. {
  208. return;
  209. }
  210. }
  211. pszValue = value;
  212. // This cvar's string must only contain printable characters.
  213. // Strip out any other crap.
  214. // We'll fill in "empty" if nothing is left
  215. if ( var->IsFlagSet( FCVAR_PRINTABLEONLY ) )
  216. {
  217. wchar_t unicode[ 512 ];
  218. #ifndef SWDS
  219. if ( sv.IsDedicated() )
  220. {
  221. // Dedicated servers don't have g_pVGuiLocalize, so fall back
  222. V_UTF8ToUnicode( pszValue, unicode, sizeof( unicode ) );
  223. }
  224. else
  225. {
  226. g_pVGuiLocalize->ConvertANSIToUnicode( pszValue, unicode, sizeof( unicode ) );
  227. }
  228. #else
  229. V_UTF8ToUnicode( pszValue, unicode, sizeof( unicode ) );
  230. #endif
  231. wchar_t newUnicode[ 512 ];
  232. const wchar_t *pS;
  233. wchar_t *pD;
  234. // Clear out new string
  235. newUnicode[0] = L'\0';
  236. pS = unicode;
  237. pD = newUnicode;
  238. // Step through the string, only copying back in characters that are printable
  239. while ( *pS )
  240. {
  241. if ( iswcntrl( *pS ) || *pS == '~' )
  242. {
  243. pS++;
  244. continue;
  245. }
  246. *pD++ = *pS++;
  247. }
  248. // Terminate the new string
  249. *pD = L'\0';
  250. // If it's empty or all spaces, then insert a marker string
  251. if ( !wcslen( newUnicode ) || IsAllSpaces( newUnicode ) )
  252. {
  253. wcsncpy( newUnicode, L"#empty", ( sizeof( newUnicode ) / sizeof( wchar_t ) ) - 1 );
  254. newUnicode[ ( sizeof( newUnicode ) / sizeof( wchar_t ) ) - 1 ] = L'\0';
  255. }
  256. #ifndef SWDS
  257. if ( sv.IsDedicated() )
  258. {
  259. V_UnicodeToUTF8( newUnicode, szNew, sizeof( szNew ) );
  260. }
  261. else
  262. {
  263. g_pVGuiLocalize->ConvertUnicodeToANSI( newUnicode, szNew, sizeof( szNew ) );
  264. }
  265. #else
  266. V_UnicodeToUTF8( newUnicode, szNew, sizeof( szNew ) );
  267. #endif
  268. // Point the value here.
  269. pszValue = szNew;
  270. }
  271. if ( var->IsFlagSet( FCVAR_NEVER_AS_STRING ) )
  272. {
  273. var->SetValue( (float)atof( pszValue ) );
  274. }
  275. else
  276. {
  277. var->SetValue( pszValue );
  278. }
  279. }
  280. //-----------------------------------------------------------------------------
  281. // Purpose:
  282. // Output : Returns true on success, false on failure.
  283. //-----------------------------------------------------------------------------
  284. // If you are changing this, please take a look at IsValidToggleCommand()
  285. bool CCvarUtilities::IsCommand( const CCommand &args )
  286. {
  287. int c = args.ArgC();
  288. if ( c == 0 )
  289. return false;
  290. ConVar *v;
  291. // check variables
  292. v = g_pCVar->FindVar( args[0] );
  293. if ( !v )
  294. return false;
  295. // NOTE: Not checking for 'HIDDEN' here so we can actually set hidden convars
  296. if ( v->IsFlagSet(FCVAR_DEVELOPMENTONLY) )
  297. return false;
  298. // perform a variable print or set
  299. if ( c == 1 )
  300. {
  301. ConVar_PrintDescription( v );
  302. return true;
  303. }
  304. if ( v->IsFlagSet( FCVAR_SPONLY ) )
  305. {
  306. #ifndef SWDS
  307. // Connected to server?
  308. if ( cl.IsConnected() )
  309. {
  310. // Is it not a single player game?
  311. if ( cl.m_nMaxClients > 1 )
  312. {
  313. ConMsg( "Can't set %s in multiplayer\n", v->GetName() );
  314. return true;
  315. }
  316. }
  317. #endif
  318. }
  319. if ( v->IsFlagSet( FCVAR_NOT_CONNECTED ) )
  320. {
  321. #ifndef SWDS
  322. // Connected to server?
  323. if ( cl.IsConnected() )
  324. {
  325. extern IBaseClientDLL *g_ClientDLL;
  326. if ( v->IsFlagSet( FCVAR_USERINFO ) && g_ClientDLL && g_ClientDLL->IsConnectedUserInfoChangeAllowed( v ) )
  327. {
  328. // Client.dll is allowing the convar change
  329. }
  330. else
  331. {
  332. ConMsg( "Can't change %s when playing, disconnect from the server or switch team to spectators\n", v->GetName() );
  333. return true;
  334. }
  335. }
  336. #endif
  337. }
  338. // Allow cheat commands in singleplayer, debug, or multiplayer with sv_cheats on
  339. if ( v->IsFlagSet( FCVAR_CHEAT ) )
  340. {
  341. if ( !Host_IsSinglePlayerGame() && !CanCheat()
  342. #if !defined(SWDS)
  343. && !cl.ishltv
  344. #if defined( REPLAY_ENABLED )
  345. && !cl.isreplay
  346. #endif
  347. && !demoplayer->IsPlayingBack()
  348. #endif
  349. )
  350. {
  351. ConMsg( "Can't use cheat cvar %s in multiplayer, unless the server has sv_cheats set to 1.\n", v->GetName() );
  352. return true;
  353. }
  354. }
  355. // Text invoking the command was typed into the console, decide what to do with it
  356. // if this is a replicated ConVar, except don't worry about restrictions if playing a .dem file
  357. if ( v->IsFlagSet( FCVAR_REPLICATED )
  358. #if !defined(SWDS)
  359. && !demoplayer->IsPlayingBack()
  360. #endif
  361. )
  362. {
  363. // If not running a server but possibly connected as a client, then
  364. // if the message came from console, don't process the command
  365. if ( !sv.IsActive() &&
  366. !sv.IsLoading() &&
  367. (cmd_source == src_command) &&
  368. cl.IsConnected() )
  369. {
  370. ConMsg( "Can't change replicated ConVar %s from console of client, only server operator can change its value\n", v->GetName() );
  371. return true;
  372. }
  373. // FIXME: Do we need a case where cmd_source == src_client?
  374. Assert( cmd_source != src_client );
  375. }
  376. // Note that we don't want the tokenized list, send down the entire string
  377. // except for surrounding quotes
  378. char remaining[1024];
  379. const char *pArgS = args.ArgS();
  380. int nLen = Q_strlen( pArgS );
  381. bool bIsQuoted = pArgS[0] == '\"';
  382. if ( !bIsQuoted )
  383. {
  384. Q_strncpy( remaining, args.ArgS(), sizeof(remaining) );
  385. }
  386. else
  387. {
  388. --nLen;
  389. Q_strncpy( remaining, &pArgS[1], sizeof(remaining) );
  390. }
  391. // Now strip off any trailing spaces
  392. char *p = remaining + nLen - 1;
  393. while ( p >= remaining )
  394. {
  395. if ( *p > ' ' )
  396. break;
  397. *p-- = 0;
  398. }
  399. // Strip off ending quote
  400. if ( bIsQuoted && p >= remaining )
  401. {
  402. if ( *p == '\"' )
  403. {
  404. *p = 0;
  405. }
  406. }
  407. SetDirect( v, remaining );
  408. return true;
  409. }
  410. // This is a band-aid copied directly from IsCommand().
  411. bool CCvarUtilities::IsValidToggleCommand( const char *cmd )
  412. {
  413. ConVar *v;
  414. // check variables
  415. v = g_pCVar->FindVar ( cmd );
  416. if (!v)
  417. {
  418. ConMsg( "%s is not a valid cvar\n", cmd );
  419. return false;
  420. }
  421. if ( v->IsFlagSet(FCVAR_DEVELOPMENTONLY) || v->IsFlagSet(FCVAR_HIDDEN) )
  422. return false;
  423. if ( v->IsFlagSet( FCVAR_SPONLY ) )
  424. {
  425. #ifndef SWDS
  426. // Connected to server?
  427. if ( cl.IsConnected() )
  428. {
  429. // Is it not a single player game?
  430. if ( cl.m_nMaxClients > 1 )
  431. {
  432. ConMsg( "Can't set %s in multiplayer\n", v->GetName() );
  433. return false;
  434. }
  435. }
  436. #endif
  437. }
  438. if ( v->IsFlagSet( FCVAR_NOT_CONNECTED ) )
  439. {
  440. #ifndef SWDS
  441. // Connected to server?
  442. if ( cl.IsConnected() )
  443. {
  444. extern IBaseClientDLL *g_ClientDLL;
  445. if ( v->IsFlagSet( FCVAR_USERINFO ) && g_ClientDLL && g_ClientDLL->IsConnectedUserInfoChangeAllowed( v ) )
  446. {
  447. // Client.dll is allowing the convar change
  448. }
  449. else
  450. {
  451. ConMsg( "Can't change %s when playing, disconnect from the server or switch team to spectators\n", v->GetName() );
  452. return false;
  453. }
  454. }
  455. #endif
  456. }
  457. // Allow cheat commands in singleplayer, debug, or multiplayer with sv_cheats on
  458. if ( v->IsFlagSet( FCVAR_CHEAT ) )
  459. {
  460. if ( !Host_IsSinglePlayerGame() && !CanCheat()
  461. #if !defined(SWDS) && !defined(_XBOX)
  462. && !demoplayer->IsPlayingBack()
  463. #endif
  464. )
  465. {
  466. ConMsg( "Can't use cheat cvar %s in multiplayer, unless the server has sv_cheats set to 1.\n", v->GetName() );
  467. return false;
  468. }
  469. }
  470. // Text invoking the command was typed into the console, decide what to do with it
  471. // if this is a replicated ConVar, except don't worry about restrictions if playing a .dem file
  472. if ( v->IsFlagSet( FCVAR_REPLICATED )
  473. #if !defined(SWDS) && !defined(_XBOX)
  474. && !demoplayer->IsPlayingBack()
  475. #endif
  476. )
  477. {
  478. // If not running a server but possibly connected as a client, then
  479. // if the message came from console, don't process the command
  480. if ( !sv.IsActive() &&
  481. !sv.IsLoading() &&
  482. (cmd_source == src_command) &&
  483. cl.IsConnected() )
  484. {
  485. ConMsg( "Can't change replicated ConVar %s from console of client, only server operator can change its value\n", v->GetName() );
  486. return false;
  487. }
  488. }
  489. // FIXME: Do we need a case where cmd_source == src_client?
  490. Assert( cmd_source != src_client );
  491. return true;
  492. }
  493. //-----------------------------------------------------------------------------
  494. // Purpose:
  495. // Input : *f -
  496. //-----------------------------------------------------------------------------
  497. void CCvarUtilities::WriteVariables( CUtlBuffer &buff, bool bAllVars )
  498. {
  499. const ConCommandBase *var;
  500. for (var = g_pCVar->GetCommands() ; var ; var = var->GetNext())
  501. {
  502. if ( var->IsCommand() )
  503. continue;
  504. bool archive = var->IsFlagSet( IsX360() ? FCVAR_ARCHIVE_XBOX : FCVAR_ARCHIVE );
  505. if ( archive )
  506. {
  507. const ConVar *pConvar = assert_cast<const ConVar *>( var );
  508. // Only write out values that differ from the defaults.
  509. if ( bAllVars || Q_strcmp( pConvar->GetString(), pConvar->GetDefault() ) != 0 )
  510. {
  511. buff.Printf( "%s \"%s\"\n", var->GetName(), ((ConVar *)var)->GetString() );
  512. }
  513. }
  514. }
  515. }
  516. static char *StripTabsAndReturns( const char *inbuffer, char *outbuffer, int outbufferSize )
  517. {
  518. char *out = outbuffer;
  519. const char *i = inbuffer;
  520. char *o = out;
  521. out[ 0 ] = 0;
  522. while ( *i && o - out < outbufferSize - 1 )
  523. {
  524. if ( *i == '\n' ||
  525. *i == '\r' ||
  526. *i == '\t' )
  527. {
  528. *o++ = ' ';
  529. i++;
  530. continue;
  531. }
  532. if ( *i == '\"' )
  533. {
  534. *o++ = '\'';
  535. i++;
  536. continue;
  537. }
  538. *o++ = *i++;
  539. }
  540. *o = '\0';
  541. return out;
  542. }
  543. static char *StripQuotes( const char *inbuffer, char *outbuffer, int outbufferSize )
  544. {
  545. char *out = outbuffer;
  546. const char *i = inbuffer;
  547. char *o = out;
  548. out[ 0 ] = 0;
  549. while ( *i && o - out < outbufferSize - 1 )
  550. {
  551. if ( *i == '\"' )
  552. {
  553. *o++ = '\'';
  554. i++;
  555. continue;
  556. }
  557. *o++ = *i++;
  558. }
  559. *o = '\0';
  560. return out;
  561. }
  562. struct ConVarFlags_t
  563. {
  564. int bit;
  565. const char *desc;
  566. const char *shortdesc;
  567. };
  568. #define CONVARFLAG( x, y ) { FCVAR_##x, #x, #y }
  569. static ConVarFlags_t g_ConVarFlags[]=
  570. {
  571. // CONVARFLAG( UNREGISTERED, "u" ),
  572. CONVARFLAG( ARCHIVE, "a" ),
  573. CONVARFLAG( SPONLY, "sp" ),
  574. CONVARFLAG( GAMEDLL, "sv" ),
  575. CONVARFLAG( CHEAT, "cheat" ),
  576. CONVARFLAG( USERINFO, "user" ),
  577. CONVARFLAG( NOTIFY, "nf" ),
  578. CONVARFLAG( PROTECTED, "prot" ),
  579. CONVARFLAG( PRINTABLEONLY, "print" ),
  580. CONVARFLAG( UNLOGGED, "log" ),
  581. CONVARFLAG( NEVER_AS_STRING, "numeric" ),
  582. CONVARFLAG( REPLICATED, "rep" ),
  583. CONVARFLAG( DEMO, "demo" ),
  584. CONVARFLAG( DONTRECORD, "norecord" ),
  585. CONVARFLAG( SERVER_CAN_EXECUTE, "server_can_execute" ),
  586. CONVARFLAG( CLIENTCMD_CAN_EXECUTE, "clientcmd_can_execute" ),
  587. CONVARFLAG( CLIENTDLL, "cl" ),
  588. };
  589. static void PrintListHeader( FileHandle_t& f )
  590. {
  591. char csvflagstr[ 1024 ];
  592. csvflagstr[ 0 ] = 0;
  593. int c = ARRAYSIZE( g_ConVarFlags );
  594. for ( int i = 0 ; i < c; ++i )
  595. {
  596. char csvf[ 64 ];
  597. ConVarFlags_t & entry = g_ConVarFlags[ i ];
  598. Q_snprintf( csvf, sizeof( csvf ), "\"%s\",", entry.desc );
  599. Q_strncat( csvflagstr, csvf, sizeof( csvflagstr ), COPY_ALL_CHARACTERS );
  600. }
  601. g_pFileSystem->FPrintf( f,"\"%s\",\"%s\",%s,\"%s\"\n", "Name", "Value", csvflagstr, "Help Text" );
  602. }
  603. //-----------------------------------------------------------------------------
  604. // Purpose:
  605. // Input : *var -
  606. // *f -
  607. //-----------------------------------------------------------------------------
  608. static void PrintCvar( const ConVar *var, bool logging, FileHandle_t& fh )
  609. {
  610. char flagstr[ 128 ];
  611. char csvflagstr[ 1024 ];
  612. flagstr[ 0 ] = 0;
  613. csvflagstr[ 0 ] = 0;
  614. int c = ARRAYSIZE( g_ConVarFlags );
  615. for ( int i = 0 ; i < c; ++i )
  616. {
  617. char f[ 32 ];
  618. char csvf[ 64 ];
  619. ConVarFlags_t & entry = g_ConVarFlags[ i ];
  620. if ( var->IsFlagSet( entry.bit ) )
  621. {
  622. Q_snprintf( f, sizeof( f ), ", %s", entry.shortdesc );
  623. Q_strncat( flagstr, f, sizeof( flagstr ), COPY_ALL_CHARACTERS );
  624. Q_snprintf( csvf, sizeof( csvf ), "\"%s\",", entry.desc );
  625. }
  626. else
  627. {
  628. Q_snprintf( csvf, sizeof( csvf ), "," );
  629. }
  630. Q_strncat( csvflagstr, csvf, sizeof( csvflagstr ), COPY_ALL_CHARACTERS );
  631. }
  632. char valstr[ 32 ];
  633. char tempbuff[512] = { 0 };
  634. // Clean up integers
  635. if ( var->GetInt() == (int)var->GetFloat() )
  636. {
  637. Q_snprintf(valstr, sizeof( valstr ), "%-8i", var->GetInt() );
  638. }
  639. else
  640. {
  641. Q_snprintf(valstr, sizeof( valstr ), "%-8.3f", var->GetFloat() );
  642. }
  643. // Print to console
  644. ConMsg( "%-40s : %-8s : %-16s : %s\n", var->GetName(), valstr, flagstr, StripTabsAndReturns( var->GetHelpText(), tempbuff, sizeof(tempbuff) ) );
  645. if ( logging )
  646. {
  647. g_pFileSystem->FPrintf( fh,"\"%s\",\"%s\",%s,\"%s\"\n", var->GetName(), valstr, csvflagstr, StripQuotes( var->GetHelpText(), tempbuff, sizeof(tempbuff) ) );
  648. }
  649. }
  650. static void PrintCommand( const ConCommand *cmd, bool logging, FileHandle_t& f )
  651. {
  652. // Print to console
  653. char tempbuff[512] = { 0 };
  654. ConMsg ("%-40s : %-8s : %-16s : %s\n",cmd->GetName(), "cmd", "", StripTabsAndReturns( cmd->GetHelpText(), tempbuff, sizeof(tempbuff) ) );
  655. if ( logging )
  656. {
  657. char emptyflags[ 256 ];
  658. emptyflags[ 0 ] = 0;
  659. int c = ARRAYSIZE( g_ConVarFlags );
  660. for ( int i = 0; i < c; ++i )
  661. {
  662. char csvf[ 64 ];
  663. Q_snprintf( csvf, sizeof( csvf ), "," );
  664. Q_strncat( emptyflags, csvf, sizeof( emptyflags ), COPY_ALL_CHARACTERS );
  665. }
  666. // Names staring with +/- need to be wrapped in single quotes
  667. char name[ 256 ];
  668. Q_snprintf( name, sizeof( name ), "%s", cmd->GetName() );
  669. if ( name[ 0 ] == '+' || name[ 0 ] == '-' )
  670. {
  671. Q_snprintf( name, sizeof( name ), "'%s'", cmd->GetName() );
  672. }
  673. g_pFileSystem->FPrintf( f, "\"%s\",\"%s\",%s,\"%s\"\n", name, "cmd", emptyflags, StripQuotes( cmd->GetHelpText(), tempbuff, sizeof(tempbuff) ) );
  674. }
  675. }
  676. static bool ConCommandBaseLessFunc( const ConCommandBase * const &lhs, const ConCommandBase * const &rhs )
  677. {
  678. const char *left = lhs->GetName();
  679. const char *right = rhs->GetName();
  680. if ( *left == '-' || *left == '+' )
  681. left++;
  682. if ( *right == '-' || *right == '+' )
  683. right++;
  684. return ( Q_stricmp( left, right ) < 0 );
  685. }
  686. //-----------------------------------------------------------------------------
  687. // Purpose:
  688. // Output : void CCvar::CvarList_f
  689. //-----------------------------------------------------------------------------
  690. void CCvarUtilities::CvarList( const CCommand &args )
  691. {
  692. const ConCommandBase *var; // Temporary Pointer to cvars
  693. int iArgs; // Argument count
  694. const char *partial = NULL; // Partial cvar to search for...
  695. // E.eg
  696. int ipLen = 0; // Length of the partial cvar
  697. FileHandle_t f = FILESYSTEM_INVALID_HANDLE; // FilePointer for logging
  698. bool bLogging = false;
  699. // Are we logging?
  700. iArgs = args.ArgC(); // Get count
  701. // Print usage?
  702. if ( iArgs == 2 && !Q_strcasecmp( args[1],"?" ) )
  703. {
  704. ConMsg( "cvarlist: [log logfile] [ partial ]\n" );
  705. return;
  706. }
  707. if ( !Q_strcasecmp( args[1],"log" ) && iArgs >= 3 )
  708. {
  709. char fn[256];
  710. Q_snprintf( fn, sizeof( fn ), "%s", args[2] );
  711. f = g_pFileSystem->Open( fn,"wb" );
  712. if ( f )
  713. {
  714. bLogging = true;
  715. }
  716. else
  717. {
  718. ConMsg( "Couldn't open '%s' for writing!\n", fn );
  719. return;
  720. }
  721. if ( iArgs == 4 )
  722. {
  723. partial = args[ 3 ];
  724. ipLen = Q_strlen( partial );
  725. }
  726. }
  727. else
  728. {
  729. partial = args[ 1 ];
  730. ipLen = Q_strlen( partial );
  731. }
  732. // Banner
  733. ConMsg( "cvar list\n--------------\n" );
  734. CUtlRBTree< const ConCommandBase * > sorted( 0, 0, ConCommandBaseLessFunc );
  735. // Loop through cvars...
  736. for ( var= g_pCVar->GetCommands(); var; var=var->GetNext() )
  737. {
  738. bool print = false;
  739. if ( var->IsFlagSet(FCVAR_DEVELOPMENTONLY) || var->IsFlagSet(FCVAR_HIDDEN) )
  740. continue;
  741. if (partial) // Partial string searching?
  742. {
  743. if ( !Q_strncasecmp( var->GetName(), partial, ipLen ) )
  744. {
  745. print = true;
  746. }
  747. }
  748. else
  749. {
  750. print = true;
  751. }
  752. if ( !print )
  753. continue;
  754. sorted.Insert( var );
  755. }
  756. if ( bLogging )
  757. {
  758. PrintListHeader( f );
  759. }
  760. for ( int i = sorted.FirstInorder(); i != sorted.InvalidIndex(); i = sorted.NextInorder( i ) )
  761. {
  762. var = sorted[ i ];
  763. if ( var->IsCommand() )
  764. {
  765. PrintCommand( (ConCommand *)var, bLogging, f );
  766. }
  767. else
  768. {
  769. PrintCvar( (ConVar *)var, bLogging, f );
  770. }
  771. }
  772. // Show total and syntax help...
  773. if ( partial && partial[0] )
  774. {
  775. ConMsg("--------------\n%3i convars/concommands for [%s]\n", sorted.Count(), partial );
  776. }
  777. else
  778. {
  779. ConMsg("--------------\n%3i total convars/concommands\n", sorted.Count() );
  780. }
  781. if ( bLogging )
  782. {
  783. g_pFileSystem->Close( f );
  784. }
  785. }
  786. //-----------------------------------------------------------------------------
  787. // Purpose:
  788. // Input :
  789. // Output : int
  790. //-----------------------------------------------------------------------------
  791. int CCvarUtilities::CountVariablesWithFlags( int flags )
  792. {
  793. int i = 0;
  794. const ConCommandBase *var;
  795. for ( var = g_pCVar->GetCommands(); var; var = var->GetNext() )
  796. {
  797. if ( var->IsCommand() )
  798. continue;
  799. if ( var->IsFlagSet( flags ) )
  800. {
  801. i++;
  802. }
  803. }
  804. return i;
  805. }
  806. //-----------------------------------------------------------------------------
  807. // Purpose:
  808. //-----------------------------------------------------------------------------
  809. void CCvarUtilities::CvarHelp( const CCommand &args )
  810. {
  811. const char *search;
  812. const ConCommandBase *var;
  813. if ( args.ArgC() != 2 )
  814. {
  815. ConMsg( "Usage: help <cvarname>\n" );
  816. return;
  817. }
  818. // Get name of var to find
  819. search = args[1];
  820. // Search for it
  821. var = g_pCVar->FindCommandBase( search );
  822. if ( !var )
  823. {
  824. ConMsg( "help: no cvar or command named %s\n", search );
  825. return;
  826. }
  827. // Show info
  828. ConVar_PrintDescription( var );
  829. }
  830. //-----------------------------------------------------------------------------
  831. // Purpose:
  832. //-----------------------------------------------------------------------------
  833. void CCvarUtilities::CvarDifferences( const CCommand &args )
  834. {
  835. const ConCommandBase *var;
  836. // Loop through vars and print out findings
  837. for ( var = g_pCVar->GetCommands(); var; var=var->GetNext() )
  838. {
  839. if ( var->IsCommand( ) )
  840. continue;
  841. if ( var->IsFlagSet(FCVAR_DEVELOPMENTONLY) || var->IsFlagSet(FCVAR_HIDDEN) )
  842. continue;
  843. if ( !Q_stricmp( ((ConVar *)var)->GetDefault(), ((ConVar *)var)->GetString() ) )
  844. continue;
  845. ConVar_PrintDescription( (ConVar *)var );
  846. }
  847. }
  848. //-----------------------------------------------------------------------------
  849. // Purpose: Toggles a cvar on/off, or cycles through a set of values
  850. //-----------------------------------------------------------------------------
  851. void CCvarUtilities::CvarToggle( const CCommand &args )
  852. {
  853. int i;
  854. int c = args.ArgC();
  855. if ( c < 2 )
  856. {
  857. ConMsg( "Usage: toggle <cvarname> [value1] [value2] [value3]...\n" );
  858. return;
  859. }
  860. ConVar *var = g_pCVar->FindVar( args[1] );
  861. if ( !IsValidToggleCommand( args[1] ) )
  862. {
  863. return;
  864. }
  865. if ( c == 2 )
  866. {
  867. // just toggle it on and off
  868. var->SetValue( !var->GetBool() );
  869. ConVar_PrintDescription( var );
  870. }
  871. else
  872. {
  873. // look for the current value in the command arguments
  874. for( i = 2; i < c; i++ )
  875. {
  876. if ( !Q_strcmp( var->GetString(), args[ i ] ) )
  877. break;
  878. }
  879. // choose the next one
  880. i++;
  881. // if we didn't find it, or were at the last value in the command arguments, use the 1st argument
  882. if ( i >= c )
  883. {
  884. i = 2;
  885. }
  886. var->SetValue( args[ i ] );
  887. ConVar_PrintDescription( var );
  888. }
  889. }
  890. void CCvarUtilities::CvarFindFlags_f( const CCommand &args )
  891. {
  892. if ( args.ArgC() < 2 )
  893. {
  894. ConMsg( "Usage: findflags <string>\n" );
  895. ConMsg( "Available flags to search for: \n" );
  896. for ( int i=0; i < ARRAYSIZE( g_ConVarFlags ); i++ )
  897. {
  898. ConMsg( " - %s\n", g_ConVarFlags[i].desc );
  899. }
  900. return;
  901. }
  902. // Get substring to find
  903. const char *search = args[1];
  904. const ConCommandBase *var;
  905. // Loop through vars and print out findings
  906. for (var=g_pCVar->GetCommands() ; var ; var=var->GetNext())
  907. {
  908. if ( var->IsFlagSet(FCVAR_DEVELOPMENTONLY) || var->IsFlagSet(FCVAR_HIDDEN) )
  909. continue;
  910. for ( int i=0; i < ARRAYSIZE( g_ConVarFlags ); i++ )
  911. {
  912. if ( !var->IsFlagSet( g_ConVarFlags[i].bit ) )
  913. continue;
  914. if ( !V_stristr( g_ConVarFlags[i].desc, search ) )
  915. continue;
  916. ConVar_PrintDescription( var );
  917. }
  918. }
  919. }
  920. //-----------------------------------------------------------------------------
  921. // Purpose: Hook to command
  922. //-----------------------------------------------------------------------------
  923. CON_COMMAND( findflags, "Find concommands by flags." )
  924. {
  925. cv->CvarFindFlags_f( args );
  926. }
  927. //-----------------------------------------------------------------------------
  928. // Purpose: Hook to command
  929. //-----------------------------------------------------------------------------
  930. CON_COMMAND( cvarlist, "Show the list of convars/concommands." )
  931. {
  932. cv->CvarList( args );
  933. }
  934. //-----------------------------------------------------------------------------
  935. // Purpose: Print help text for cvar
  936. //-----------------------------------------------------------------------------
  937. CON_COMMAND( help, "Find help about a convar/concommand." )
  938. {
  939. cv->CvarHelp( args );
  940. }
  941. //-----------------------------------------------------------------------------
  942. // Purpose:
  943. //-----------------------------------------------------------------------------
  944. CON_COMMAND( differences, "Show all convars which are not at their default values." )
  945. {
  946. cv->CvarDifferences( args );
  947. }
  948. //-----------------------------------------------------------------------------
  949. // Purpose:
  950. //-----------------------------------------------------------------------------
  951. CON_COMMAND( toggle, "Toggles a convar on or off, or cycles through a set of values." )
  952. {
  953. cv->CvarToggle( args );
  954. }
  955. //-----------------------------------------------------------------------------
  956. // Purpose: Send the cvars to VXConsole
  957. //-----------------------------------------------------------------------------
  958. #if defined( _X360 )
  959. CON_COMMAND( getcvars, "" )
  960. {
  961. g_pCVar->PublishToVXConsole();
  962. }
  963. #endif