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.

1171 lines
31 KiB

  1. //===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //===========================================================================//
  9. #include "quakedef.h"
  10. #include "zone.h"
  11. #include "demo.h"
  12. #include "filesystem.h"
  13. #include "filesystem_engine.h"
  14. #include "eiface.h"
  15. #include "server.h"
  16. #include "sys.h"
  17. #include "cl_splitscreen.h"
  18. #include "baseautocompletefilelist.h"
  19. #include "tier0/icommandline.h"
  20. #include "tier1/utlbuffer.h"
  21. #include "gl_cvars.h"
  22. #include "tier0/memalloc.h"
  23. #include "netmessages.h"
  24. #include "client.h"
  25. #include "sv_plugin.h"
  26. #include "tier1/commandbuffer.h"
  27. #include "cvar.h"
  28. #include "vstdlib/random.h"
  29. #include "tier1/utldict.h"
  30. #include "tier0/etwprof.h"
  31. #include "tier0/vprof.h"
  32. // memdbgon must be the last include file in a .cpp file!!!
  33. #include "tier0/memdbgon.h"
  34. // This denotes an execution marker in the command stream.
  35. #define CMDSTR_ADD_EXECUTION_MARKER "[$&*,`]"
  36. #ifdef _DEBUG
  37. ConVar cl_debug_respect_cheat_vars( "cl_debug_respect_cheat_vars", "0", 0, "(debug builds only) - when set to 0, the client can change cheat vars." );
  38. #endif
  39. extern ConVar sv_allow_wait_command;
  40. #define MAX_ALIAS_NAME 32
  41. #define MAX_COMMAND_LENGTH 1024
  42. struct cmdalias_t
  43. {
  44. cmdalias_t *next;
  45. char name[ MAX_ALIAS_NAME ];
  46. char *value;
  47. };
  48. static cmdalias_t *cmd_alias = NULL;
  49. static CCommandBuffer s_CommandBuffer[ CBUF_COUNT ];
  50. static CThreadFastMutex s_CommandBufferMutex;
  51. CUtlStringList m_WhitelistedConvars;
  52. #define LOCK_COMMAND_BUFFER() AUTO_LOCK(s_CommandBufferMutex)
  53. static FileAssociationInfo g_FileAssociations[] =
  54. {
  55. { ".dem", "playdemo" },
  56. { ".sav", "load" },
  57. { ".bsp", "map" },
  58. };
  59. // Client -> Server command throttling
  60. // FIXME: Perhaps kForwardedCommandQuota_nCommandsPerSecond should instead be some fraction /
  61. // amount below sv_quota_stringcmdspersecond. Right now that variable isn't networked,
  62. // so we just cap at the previous server 'throttle' value (after which commands were
  63. // discarded). The new behavior kicks you from the server if you overflow, so it's
  64. // important to be significantly below that -- we don't throttle commands issued
  65. // by client code, only via the user via console/keybind input.
  66. static const int kForwardedCommandQuota_nCommandsPerSecond = 16;
  67. static double gForwardedCommandQuota_flTimeStart = -1.0;
  68. static int gForwardedCommandQuota_nCount = 0;
  69. //=============================================================================
  70. // These functions manage a list of execution markers that we use to verify
  71. // special commands in the command buffer.
  72. //=============================================================================
  73. static CUtlVector<int> g_ExecutionMarkers;
  74. static int CreateExecutionMarker()
  75. {
  76. if ( g_ExecutionMarkers.Count() > 2048 )
  77. g_ExecutionMarkers.Remove( 0 );
  78. int i = g_ExecutionMarkers.AddToTail( RandomInt( 0, 1<<30 ) );
  79. return g_ExecutionMarkers[i];
  80. }
  81. static bool FindAndRemoveExecutionMarker( int iCode )
  82. {
  83. int i = g_ExecutionMarkers.Find( iCode );
  84. if ( i == g_ExecutionMarkers.InvalidIndex() )
  85. return false;
  86. g_ExecutionMarkers.Remove( i );
  87. return true;
  88. }
  89. //-----------------------------------------------------------------------------
  90. // Used to allow cheats even if cheats aren't theoretically allowed
  91. //-----------------------------------------------------------------------------
  92. static bool g_bRPTActive = false;
  93. void Cmd_SetRptActive( bool bActive )
  94. {
  95. g_bRPTActive = bActive;
  96. }
  97. bool Cmd_IsRptActive()
  98. {
  99. return g_bRPTActive;
  100. }
  101. //=============================================================================
  102. //-----------------------------------------------------------------------------
  103. // Just translates BindToggle <key> <cvar> into: bind <key> "increment var <cvar> 0 1 1"
  104. //-----------------------------------------------------------------------------
  105. CON_COMMAND( BindToggle, "Performs a bind <key> \"increment var <cvar> 0 1 1\"" )
  106. {
  107. if( args.ArgC() <= 2 )
  108. {
  109. ConMsg( "BindToggle <key> <cvar>: invalid syntax specified\n" );
  110. return;
  111. }
  112. char newCmd[MAX_COMMAND_LENGTH];
  113. Q_snprintf( newCmd, sizeof(newCmd), "bind %s \"incrementvar %s 0 1 1\"\n", args[1], args[2] );
  114. Cbuf_InsertText( Cbuf_GetCurrentPlayer(), newCmd, args.Source() );
  115. }
  116. //-----------------------------------------------------------------------------
  117. // Init, shutdown
  118. //-----------------------------------------------------------------------------
  119. void Cbuf_Init()
  120. {
  121. // Wait for 1 execute time
  122. for ( int i = 0; i < CBUF_COUNT; ++i )
  123. {
  124. s_CommandBuffer[ i ].SetWaitDelayTime( 1 );
  125. }
  126. }
  127. void Cbuf_Shutdown()
  128. {
  129. }
  130. ECommandTarget_t Cbuf_GetCurrentPlayer()
  131. {
  132. return ( ECommandTarget_t )( GET_ACTIVE_SPLITSCREEN_SLOT() );
  133. }
  134. //-----------------------------------------------------------------------------
  135. // Clears the command buffer
  136. //-----------------------------------------------------------------------------
  137. void Cbuf_Clear( ECommandTarget_t eTarget )
  138. {
  139. s_CommandBuffer[ eTarget ].SetWaitDelayTime( 1 );
  140. }
  141. //-----------------------------------------------------------------------------
  142. // Adds command text at the end of the buffer
  143. //-----------------------------------------------------------------------------
  144. void Cbuf_AddText( ECommandTarget_t eTarget, const char *pText, cmd_source_t cmdSource, int nTickDelay )
  145. {
  146. LOCK_COMMAND_BUFFER();
  147. if ( !s_CommandBuffer[ eTarget ].AddText( pText, cmdSource, nTickDelay ) )
  148. {
  149. ConMsg( "Cbuf_AddText: buffer overflow\n" );
  150. }
  151. }
  152. //-----------------------------------------------------------------------------
  153. // Adds command text at the beginning of the buffer
  154. //-----------------------------------------------------------------------------
  155. void Cbuf_InsertText( ECommandTarget_t eTarget, const char *pText, cmd_source_t cmdSource, int nTickDelay )
  156. {
  157. LOCK_COMMAND_BUFFER();
  158. // NOTE: This operation is only allowed when the command buffer
  159. // is in the middle of processing. If this assertion never triggers,
  160. // it's safe to eliminate Cbuf_InsertText altogether.
  161. // Otherwise, I have to add a feature to CCommandBuffer
  162. Assert( s_CommandBuffer[ eTarget ].IsProcessingCommands() );
  163. Cbuf_AddText( eTarget, pText, cmdSource, nTickDelay );
  164. }
  165. bool Cbuf_IsProcessingCommands( ECommandTarget_t eTarget )
  166. {
  167. LOCK_COMMAND_BUFFER();
  168. return s_CommandBuffer[ eTarget ].IsProcessingCommands();
  169. }
  170. void Cbuf_AddExecutionMarker( ECommandTarget_t eTarget, ECmdExecutionMarker marker )
  171. {
  172. int iMarkerCode = CreateExecutionMarker();
  173. // CMDCHAR_ADD_EXECUTION_MARKER tells us there's a special execution thing here.
  174. // (char)marker tells it what to turn on
  175. // iRandomCode is for security, so only our code can stuff this command into the buffer.
  176. char str[512];
  177. Q_snprintf( str, sizeof( str ), ";%s %c %d;", CMDSTR_ADD_EXECUTION_MARKER, (char)marker, iMarkerCode );
  178. Cbuf_AddText( eTarget, str, kCommandSrcCode );
  179. }
  180. //-----------------------------------------------------------------------------
  181. // Executes commands in the buffer
  182. //-----------------------------------------------------------------------------
  183. static void Cbuf_ExecuteCommand( ECommandTarget_t eTarget, const CCommand &args )
  184. {
  185. // Add the command text to the ETW stream to give better context to traces.
  186. ETWMark( args.GetCommandString() );
  187. // execute the command line
  188. const ConCommandBase *pCmd = Cmd_ExecuteCommand( eTarget, args );
  189. #if !defined(DEDICATED)
  190. if ( pCmd && !pCmd->IsFlagSet( FCVAR_DONTRECORD ) )
  191. {
  192. demorecorder->RecordCommand( args.GetCommandString() );
  193. }
  194. #endif
  195. }
  196. //-----------------------------------------------------------------------------
  197. // Executes commands in the buffer
  198. //-----------------------------------------------------------------------------
  199. void Cbuf_Execute()
  200. {
  201. VPROF("Cbuf_Execute");
  202. if ( !ThreadInMainThread() )
  203. {
  204. Warning( "Executing command outside main loop thread\n" );
  205. ExecuteOnce( DebuggerBreakIfDebugging() );
  206. }
  207. LOCK_COMMAND_BUFFER();
  208. #if !defined( SPLIT_SCREEN_STUBS )
  209. int nSaveIndex = GET_ACTIVE_SPLITSCREEN_SLOT();
  210. bool bSaveResolvable = SET_LOCAL_PLAYER_RESOLVABLE( __FILE__, __LINE__, false );
  211. #endif
  212. for ( int i = 0; i < CBUF_COUNT; ++i )
  213. {
  214. // If text was added with Cbuf_AddText and then Cbuf_Execute gets called from within handler, we're going
  215. // to execute the new commands anyway, so we can ignore this extra execute call here.
  216. if ( s_CommandBuffer[ i ].IsProcessingCommands() )
  217. continue;
  218. // For player slots, force the correct context
  219. if ( i >= CBUF_FIRST_PLAYER &&
  220. i < ( CBUF_FIRST_PLAYER + host_state.max_splitscreen_players ) )
  221. {
  222. SET_ACTIVE_SPLIT_SCREEN_PLAYER_SLOT( i );
  223. SET_LOCAL_PLAYER_RESOLVABLE( __FILE__, __LINE__, true );
  224. }
  225. else
  226. {
  227. SET_ACTIVE_SPLIT_SCREEN_PLAYER_SLOT( 0 );
  228. SET_LOCAL_PLAYER_RESOLVABLE( __FILE__, __LINE__, bSaveResolvable );
  229. }
  230. // NOTE: The command buffer knows about execution time related to commands,
  231. // but since HL2 doesn't, we're going to spoof the command time to simply
  232. // be the the number of times Cbuf_Execute is called.
  233. s_CommandBuffer[ i ].BeginProcessingCommands( 1 );
  234. CCommand nextCommand;
  235. while ( s_CommandBuffer[ i ].DequeueNextCommand( &nextCommand ) )
  236. {
  237. Cbuf_ExecuteCommand( ( ECommandTarget_t )i, nextCommand );
  238. }
  239. s_CommandBuffer[ i ].EndProcessingCommands( );
  240. }
  241. SET_ACTIVE_SPLIT_SCREEN_PLAYER_SLOT( nSaveIndex );
  242. SET_LOCAL_PLAYER_RESOLVABLE( __FILE__, __LINE__, bSaveResolvable );
  243. }
  244. //-----------------------------------------------------------------------------
  245. // Purpose:
  246. // Input : *param -
  247. // Output : static char const
  248. //-----------------------------------------------------------------------------
  249. static char const *Cmd_TranslateFileAssociation(char const *param )
  250. {
  251. static char sz[ 512 ];
  252. char *retval = NULL;
  253. char temp[ 512 ];
  254. V_strcpy_safe( temp, param );
  255. Q_FixSlashes( temp );
  256. Q_strlower( temp );
  257. const char *extension = V_GetFileExtension(temp);
  258. // must have an extension to map
  259. if (!extension)
  260. return retval;
  261. int c = ARRAYSIZE( g_FileAssociations );
  262. for ( int i = 0; i < c; i++ )
  263. {
  264. FileAssociationInfo& info = g_FileAssociations[ i ];
  265. if ( ! Q_strcmp( extension, info.extension+1 ) &&
  266. ! CommandLine()->FindParm(va( "+%s", info.command_to_issue ) ) )
  267. {
  268. // Translate if haven't already got one of these commands
  269. V_strcpy_safe( sz, temp );
  270. Q_FileBase( sz, temp, sizeof( sz ) );
  271. Q_snprintf( sz, sizeof( sz ), "%s %s", info.command_to_issue, temp );
  272. retval = sz;
  273. break;
  274. }
  275. }
  276. // return null if no translation, otherwise return commands
  277. return retval;
  278. }
  279. //-----------------------------------------------------------------------------
  280. // Purpose: Adds command line parameters as script statements
  281. // Commands lead with a +, and continue until a - or another +
  282. // Also automatically converts .dem, .bsp, and .sav files to +playdemo etc command line options
  283. // hl2 +cmd amlev1
  284. // hl2 -nosound +cmd amlev1
  285. // Output : void Cmd_StuffCmds_f
  286. //-----------------------------------------------------------------------------
  287. CON_COMMAND( stuffcmds, "Parses and stuffs command line + commands to command buffer." )
  288. {
  289. if ( args.ArgC() != 1 )
  290. {
  291. ConMsg( "stuffcmds : execute command line parameters\n" );
  292. return;
  293. }
  294. MEM_ALLOC_CREDIT();
  295. CUtlBuffer build( 0, 0, CUtlBuffer::TEXT_BUFFER );
  296. // arg[0] is the executable name
  297. for ( int i=1; i < CommandLine()->ParmCount(); i++ )
  298. {
  299. const char *szParm = CommandLine()->GetParm(i);
  300. if (!szParm) continue;
  301. if (szParm[0] == '-')
  302. {
  303. // skip -XXX options and eat their args
  304. const char *szValue = CommandLine()->ParmValue(szParm);
  305. if ( szValue ) i++;
  306. continue;
  307. }
  308. if (szParm[0] == '+')
  309. {
  310. // convert +XXX options and stuff them into the build buffer
  311. const char *szValue = CommandLine()->ParmValue(szParm);
  312. if (szValue)
  313. {
  314. // Special case for +map parameter on the command line to support a second argument
  315. char const *szSecondParameterUsed = NULL;
  316. if ( !Q_stricmp( "+map", szParm ) &&
  317. ( CommandLine()->ParmCount() > ( i + 2 ) ) &&
  318. CommandLine()->GetParm( i + 2 ) )
  319. {
  320. char const *szAppendParameter = CommandLine()->GetParm( i + 2 );
  321. if ( ( szAppendParameter[0] != '+' ) &&
  322. ( szAppendParameter[0] != '-' ) )
  323. {
  324. szSecondParameterUsed = szAppendParameter;
  325. build.PutString( va("%s %s %s\n", szParm+1, szValue, szSecondParameterUsed ) );
  326. ++ i; // eat one parameter we used for map name
  327. ++ i; // eat another parameter that was second appended parameter
  328. }
  329. }
  330. if ( !szSecondParameterUsed )
  331. { // If we didn't use the second parameter, then just append command value to execution buffer
  332. build.PutString( va("%s %s\n", szParm+1, szValue ) );
  333. i++;
  334. }
  335. }
  336. else
  337. {
  338. build.PutString(szParm+1);
  339. build.PutChar('\n');
  340. }
  341. }
  342. else
  343. {
  344. // singleton values, convert to command
  345. char const *translated = Cmd_TranslateFileAssociation( CommandLine()->GetParm( i ) );
  346. if (translated)
  347. {
  348. build.PutString(translated);
  349. build.PutChar('\n');
  350. }
  351. }
  352. }
  353. build.PutChar( '\0' );
  354. if ( build.TellPut() > 1 )
  355. {
  356. Cbuf_InsertText( Cbuf_GetCurrentPlayer(), (char *)build.Base(), args.Source() );
  357. }
  358. }
  359. bool IsValidFileExtension( const char *pszFilename )
  360. {
  361. if ( !pszFilename )
  362. {
  363. return false;
  364. }
  365. if ( Q_strstr( pszFilename, ".exe" ) ||
  366. Q_strstr( pszFilename, ".vbs" ) ||
  367. Q_strstr( pszFilename, ".com" ) ||
  368. Q_strstr( pszFilename, ".bat" ) ||
  369. Q_strstr( pszFilename, ".dll" ) ||
  370. Q_strstr( pszFilename, ".ini" ) ||
  371. Q_strstr( pszFilename, ".gcf" ) ||
  372. Q_strstr( pszFilename, ".sys" ) ||
  373. Q_strstr( pszFilename, ".blob" ) )
  374. {
  375. return false;
  376. }
  377. return true;
  378. }
  379. bool IsWhiteListedCmd( const char *pszCmd )
  380. {
  381. if ( m_WhitelistedConvars.Count() == 0 )
  382. {
  383. const char *svfileName = "bspconvar_whitelist.txt";
  384. KeyValues *pKV_wl = new KeyValues( "convars" );
  385. if ( pKV_wl->LoadFromFile( g_pFullFileSystem, svfileName, "GAME" ) )
  386. {
  387. KeyValuesDumpAsDevMsg( pKV_wl );
  388. for ( KeyValues *sub = pKV_wl->GetFirstSubKey(); sub; sub = sub->GetNextKey() )
  389. {
  390. m_WhitelistedConvars.CopyAndAddToTail( sub->GetName() );
  391. }
  392. }
  393. else
  394. {
  395. DevMsg( "Failed to cache %s\n", svfileName );
  396. return false;
  397. }
  398. }
  399. for ( int i = 0; i < m_WhitelistedConvars.Count(); ++i )
  400. {
  401. if ( !Q_stricmp(m_WhitelistedConvars[i], pszCmd) )
  402. {
  403. return true;
  404. }
  405. }
  406. return false;
  407. }
  408. /*
  409. ===============
  410. Cmd_Exec_f
  411. ===============
  412. */
  413. void _Cmd_Exec_f( const CCommand &args, bool bOnlyIfExists, bool bUseWhitelist = false )
  414. {
  415. LOCK_COMMAND_BUFFER();
  416. char *f;
  417. const char *s;
  418. char fileName[MAX_OSPATH];
  419. int argc = args.ArgC();
  420. if ( argc < 2 )
  421. {
  422. ConMsg( "%s <filename> [path id]: execute a script file\n", args[ 0 ] );
  423. return;
  424. }
  425. s = args[ 1 ];
  426. DevMsg( "Execing config: %s\n", s );
  427. // Optional path ID. * means no path ID.
  428. const char *pPathID = NULL;
  429. if ( argc >= 3 )
  430. {
  431. pPathID = args[ 2 ];
  432. }
  433. else
  434. {
  435. pPathID = "*";
  436. }
  437. if ( !Q_stricmp( pPathID, "T" ) )
  438. {
  439. // Has an extension already?
  440. Q_snprintf( fileName, sizeof( fileName ), "T:/cfg/%s", s );
  441. }
  442. else
  443. {
  444. // Ensure it has an extension
  445. Q_snprintf( fileName, sizeof( fileName ), "//%s/cfg/%s", pPathID, s );
  446. Q_DefaultExtension( fileName, ".cfg", sizeof( fileName ) );
  447. // check path validity
  448. if ( !COM_IsValidPath( fileName ) )
  449. {
  450. ConMsg( "%s %s: invalid path.\n", args[ 0 ], fileName );
  451. return;
  452. }
  453. }
  454. // check for invalid file extensions
  455. if ( !IsValidFileExtension( fileName ) )
  456. {
  457. ConMsg( "%s %s: invalid file type.\n", args[ 0 ], fileName );
  458. return;
  459. }
  460. // 360 doesn't need to do costly existence checks
  461. if ( IsPC() && g_pFileSystem->FileExists( fileName ) )
  462. {
  463. // don't want to exec files larger than 1 MB
  464. // probably not a valid file to exec
  465. unsigned int size = g_pFileSystem->Size( fileName );
  466. if ( size > 1*1024*1024 )
  467. {
  468. ConMsg( "%s %s: file size larger than 1 MB!\n", args[ 0 ], s );
  469. return;
  470. }
  471. }
  472. char buf[16384];
  473. int len;
  474. f = (char *)COM_LoadStackFile( fileName, buf, sizeof( buf ), len );
  475. if ( !f )
  476. {
  477. if ( !V_stristr( s, "autoexec.cfg" ) && !V_stristr( s, "joystick.cfg" ) && !V_stristr( s, "game.cfg" ))
  478. {
  479. // File doesn't exist, fail silently?
  480. if ( !bOnlyIfExists )
  481. {
  482. ConMsg( "%s: couldn't exec %s\n", args[ 0 ], s );
  483. }
  484. }
  485. return;
  486. }
  487. char *original_f = f;
  488. ConDMsg( "execing %s\n", s );
  489. ECommandTarget_t eTarget = CBUF_FIRST_PLAYER;
  490. // A bit of hack, but find the context (probably CBUF_SERVER) who is executing commands!
  491. for ( int i = 0; i < CBUF_COUNT; ++i )
  492. {
  493. if ( s_CommandBuffer[ i ].IsProcessingCommands() )
  494. {
  495. eTarget = (ECommandTarget_t)i;
  496. break;
  497. }
  498. }
  499. CCommandBuffer &rCommandBuffer = s_CommandBuffer[ eTarget ];
  500. // check to make sure we're not going to overflow the cmd_text buffer
  501. int hCommand = rCommandBuffer.GetNextCommandHandle();
  502. KeyValues *pKV_wl = new KeyValues( "convars" );
  503. // Execute each command immediately
  504. const char *pszDataPtr = f;
  505. while( pszDataPtr )
  506. {
  507. // parse a line out of the source
  508. pszDataPtr = COM_ParseLine( pszDataPtr );
  509. // no more tokens
  510. if ( Q_strlen( com_token ) <= 0 )
  511. continue;
  512. Cbuf_InsertText( eTarget, com_token, args.Source() );
  513. // Execute all commands provoked by the current line read from the file
  514. while ( rCommandBuffer.GetNextCommandHandle() != hCommand )
  515. {
  516. CCommand execCommand;
  517. if( rCommandBuffer.DequeueNextCommand( &execCommand ) )
  518. {
  519. bool bFoundConvar = true;
  520. if ( bUseWhitelist )
  521. {
  522. bFoundConvar = IsWhiteListedCmd( *execCommand.ArgV() );
  523. }
  524. if ( bFoundConvar )
  525. Cbuf_ExecuteCommand( eTarget, execCommand );
  526. }
  527. else
  528. {
  529. Assert( 0 );
  530. break;
  531. }
  532. }
  533. }
  534. if ( pKV_wl )
  535. {
  536. pKV_wl->deleteThis();
  537. pKV_wl = NULL;
  538. }
  539. if ( f != buf )
  540. {
  541. // Hack for VCR playback. vcrmode allocates the memory but doesn't use the debug memory allocator,
  542. // so we don't want to free what it allocated.
  543. if ( f == original_f )
  544. {
  545. free( f );
  546. }
  547. }
  548. }
  549. void Cmd_Exec_f( const CCommand &args )
  550. {
  551. _Cmd_Exec_f( args, false );
  552. }
  553. void Cmd_ExecIfExists_f( const CCommand &args )
  554. {
  555. _Cmd_Exec_f( args, true );
  556. }
  557. void Cmd_ExecWithWhiteList_f( const CCommand &args )
  558. {
  559. _Cmd_Exec_f( args, false, true );
  560. }
  561. /*
  562. ===============
  563. Cmd_Echo_f
  564. Just prints the rest of the line to the console
  565. ===============
  566. */
  567. CON_COMMAND_F( echo, "Echo text to console.", FCVAR_SERVER_CAN_EXECUTE )
  568. {
  569. int argc = args.ArgC();
  570. for ( int i=1; i<argc; i++ )
  571. {
  572. ConMsg ("%s ", args[i] );
  573. }
  574. ConMsg ("\n");
  575. }
  576. /*
  577. ===============
  578. Cmd_Alias_f
  579. Creates a new command that executes a command string (possibly ; seperated)
  580. ===============
  581. */
  582. CON_COMMAND( alias, "Alias a command." )
  583. {
  584. cmdalias_t *a;
  585. char cmd[MAX_COMMAND_LENGTH];
  586. int i, c;
  587. const char *s;
  588. int argc = args.ArgC();
  589. if ( argc == 1 )
  590. {
  591. ConMsg ("Current alias commands:\n");
  592. for (a = cmd_alias ; a ; a=a->next)
  593. {
  594. ConMsg ("%s : %s\n", a->name, a->value);
  595. }
  596. return;
  597. }
  598. s = args[1];
  599. if ( Q_strlen(s) >= MAX_ALIAS_NAME )
  600. {
  601. ConMsg ("Alias name is too long\n");
  602. return;
  603. }
  604. // copy the rest of the command line
  605. cmd[0] = 0; // start out with a null string
  606. c = argc;
  607. for (i=2 ; i< c ; i++)
  608. {
  609. V_strcat_safe( cmd, args[i] );
  610. if (i != c)
  611. {
  612. V_strcat_safe( cmd, " " );
  613. }
  614. }
  615. V_strcat_safe( cmd, "\n" );
  616. // if the alias already exists, reuse it
  617. for (a = cmd_alias ; a ; a=a->next)
  618. {
  619. if (!Q_strcmp(s, a->name))
  620. {
  621. if ( !Q_strcmp( a->value, cmd ) ) // Re-alias the same thing
  622. return;
  623. delete[] a->value;
  624. break;
  625. }
  626. }
  627. if (!a)
  628. {
  629. ConCommandBase *pCommandExisting = g_pCVar->FindCommandBase( s );
  630. if ( pCommandExisting )
  631. {
  632. ConMsg( "Cannot alias an existing %s\n", pCommandExisting->IsCommand() ? "concommand" : "convar" );
  633. return;
  634. }
  635. a = (cmdalias_t *)new cmdalias_t;
  636. a->next = cmd_alias;
  637. cmd_alias = a;
  638. }
  639. V_strcpy_safe ( a->name, s );
  640. a->value = COM_StringCopy(cmd);
  641. }
  642. /*
  643. ===============
  644. Runs a command only if that command exits in the bspwhitelist
  645. ===============
  646. */
  647. CON_COMMAND( whitelistcmd, "Runs a whitelisted command." )
  648. {
  649. Cmd_ForwardToServerWithWhitelist( args );
  650. }
  651. /*
  652. =============================================================================
  653. COMMAND EXECUTION
  654. =============================================================================
  655. */
  656. int cmd_clientslot = -1;
  657. //-----------------------------------------------------------------------------
  658. // Purpose:
  659. // Output : void Cmd_Init
  660. //-----------------------------------------------------------------------------
  661. CON_COMMAND( cmd, "Forward command to server." )
  662. {
  663. Cmd_ForwardToServer( args );
  664. }
  665. CON_COMMAND_AUTOCOMPLETEFILE( exec, Cmd_Exec_f, "Execute script file.", "cfg", cfg );
  666. CON_COMMAND_AUTOCOMPLETEFILE( execifexists, Cmd_ExecIfExists_f, "Execute script file if file exists.", "cfg", cfg );
  667. CON_COMMAND_AUTOCOMPLETEFILE( execwithwhitelist, Cmd_ExecWithWhiteList_f, "Execute script file, only execing convars on a whitelist.", "cfg", cfg );
  668. void Cmd_Init( void )
  669. {
  670. Sys_CreateFileAssociations( ARRAYSIZE( g_FileAssociations ), g_FileAssociations );
  671. }
  672. //-----------------------------------------------------------------------------
  673. // Purpose:
  674. //-----------------------------------------------------------------------------
  675. void Cmd_Shutdown( void )
  676. {
  677. // TODO, cleanup
  678. while ( cmd_alias )
  679. {
  680. cmdalias_t *next = cmd_alias->next;
  681. delete cmd_alias->value; // created by StringCopy()
  682. delete cmd_alias;
  683. cmd_alias = next;
  684. }
  685. }
  686. //-----------------------------------------------------------------------------
  687. // FIXME: Remove this! This is a temporary hack to deal with backward compat
  688. //-----------------------------------------------------------------------------
  689. void Cmd_Dispatch( const ConCommandBase *pCommand, const CCommand &command )
  690. {
  691. ConCommand *pConCommand = const_cast<ConCommand*>( static_cast<const ConCommand*>( pCommand ) );
  692. pConCommand->Dispatch( command );
  693. }
  694. static void HandleExecutionMarker( const char *pCommand, const char *pMarkerCode )
  695. {
  696. int iMarkerCode = atoi( pMarkerCode );
  697. // Validate..
  698. if ( FindAndRemoveExecutionMarker( iMarkerCode ) )
  699. {
  700. // Ok, now it's validated, so do the command.
  701. // REI CSGO: We no longer use execution markers, but I'm leaving this mechanism in here
  702. ECmdExecutionMarker command = (ECmdExecutionMarker)(pCommand[0]);
  703. #ifdef _WIN32
  704. #pragma warning(push)
  705. #pragma warning(disable: 4065) // switch statement contains 'default' but no 'case' labels
  706. #endif // _WIN32
  707. switch(command)
  708. {
  709. default:
  710. Warning( "Unrecognized execution marker '%c'\n", pCommand[0] );
  711. }
  712. #ifdef _WIN32
  713. #pragma warning(pop)
  714. #endif
  715. }
  716. else
  717. {
  718. static int cnt = 0;
  719. if ( ++cnt < 3 )
  720. Warning( "Invalid execution marker code.\n" );
  721. }
  722. }
  723. static bool ShouldPreventServerCommand( const CCommand& args, const ConCommandBase *pCommand )
  724. {
  725. // If the command didn't come from the server, then we aren't filtering it here.
  726. if ( args.Source() != kCommandSrcNetServer )
  727. return false;
  728. // Server can execute any command on client if this is a single player game.
  729. if ( Host_IsSinglePlayerGame() )
  730. return false;
  731. // If we don't understand the command, and it came from a server command, then forward it back to the server.
  732. // Lots of server plugins use this. They use engine->ClientCommand() to have a client execute a command that
  733. // they have hooked on the server. We disabled it once and they freaked. It IS redundant since they could just
  734. // code the call to their command on the server, but they complained loudly enough that we're adding it back in
  735. // since there's no exploit that we know of by allowing it.
  736. if ( !pCommand )
  737. return false;
  738. // If the command is marked to be executable by the server, allow it.
  739. if ( pCommand->IsFlagSet( FCVAR_SERVER_CAN_EXECUTE ) )
  740. return false;
  741. // Otherwise we are filtering the command. Print a warning to the client console (the server shouldn't be
  742. // sending commands that it isn't allowed to execute on the client)
  743. Warning( "FCVAR_SERVER_CAN_EXECUTE prevented server running command: %s\n", args.GetCommandString() );
  744. return true;
  745. }
  746. static bool ShouldPreventClientCommand( const CCommand& args, const ConCommandBase *pCommand )
  747. {
  748. // If the command didn't come from ClientCmd(), we don't filter it here.
  749. if ( args.Source() != kCommandSrcClientCmd )
  750. return false;
  751. // Commands we don't recognize aren't prevented here (they will get forwarded to the server)
  752. if ( !pCommand )
  753. return false;
  754. // Commands that are explicitly marked as executable by ClientCmd() aren't filtered
  755. if ( pCommand->IsFlagSet( FCVAR_CLIENTCMD_CAN_EXECUTE ) )
  756. return false;
  757. // Otherwise we are going to filter the command. Check if we should warn the user about this:
  758. // If this command is in the game DLL, don't mention it because we're going to forward this
  759. // request to the server and let the server handle it.
  760. if ( !pCommand->IsFlagSet( FCVAR_GAMEDLL ) )
  761. {
  762. Warning( "FCVAR_CLIENTCMD_CAN_EXECUTE prevented running command: %s\n", args.GetCommandString() );
  763. }
  764. return true;
  765. }
  766. //-----------------------------------------------------------------------------
  767. // A complete command line has been parsed, so try to execute it
  768. // FIXME: lookupnoadd the token to speed search?
  769. //-----------------------------------------------------------------------------
  770. const ConCommandBase *Cmd_ExecuteCommand( ECommandTarget_t eTarget, const CCommand &command, int nClientSlot )
  771. {
  772. // execute the command line
  773. if ( !command.ArgC() )
  774. return NULL; // no tokens
  775. // First, check for execution markers.
  776. if ( Q_strcmp( command[0], CMDSTR_ADD_EXECUTION_MARKER ) == 0 )
  777. {
  778. if ( command.ArgC() == 3 )
  779. {
  780. HandleExecutionMarker( command[1], command[2] );
  781. }
  782. else
  783. {
  784. Warning( "WARNING: INVALID EXECUTION MARKER.\n" );
  785. }
  786. return NULL;
  787. }
  788. // check alias
  789. cmdalias_t *a;
  790. for ( a=cmd_alias; a; a=a->next )
  791. {
  792. if ( !Q_strcasecmp( command[0], a->name ) )
  793. {
  794. Cbuf_InsertText( Cbuf_GetCurrentPlayer(), a->value, command.Source() );
  795. return NULL;
  796. }
  797. }
  798. cmd_clientslot = nClientSlot;
  799. // check ConCommands
  800. const ConCommandBase *pCommand = g_pCVar->FindCommandBase( command[0] );
  801. // If we prevent a server command due to FCVAR_SERVER_CAN_EXECUTE not being set, then we get out immediately.
  802. if ( ShouldPreventServerCommand( command, pCommand ) )
  803. return NULL;
  804. // FIXME: Why do we treat convars differently than commands here?
  805. if ( pCommand && pCommand->IsCommand() )
  806. {
  807. if ( !ShouldPreventClientCommand( command, pCommand ) && pCommand->IsCommand() )
  808. {
  809. bool isServerCommand =
  810. // Command is marked for execution on the server.
  811. pCommand->IsFlagSet( FCVAR_GAMEDLL )
  812. // Not received over the network
  813. && ( command.Source() != kCommandSrcNetClient && command.Source() != kCommandSrcNetServer )
  814. // Not HLDS
  815. && !sv.IsDedicated();
  816. // Hook to allow game .dll to figure out who type the message on a listen server
  817. if ( serverGameClients )
  818. {
  819. // We're actually the server, so set it up locally
  820. if ( sv.IsActive() )
  821. {
  822. g_pServerPluginHandler->SetCommandClient( -1 );
  823. #ifndef DEDICATED
  824. // Special processing for listen server player
  825. if ( isServerCommand )
  826. {
  827. if ( splitscreen->IsLocalPlayerResolvable() )
  828. {
  829. g_pServerPluginHandler->SetCommandClient( GetLocalClient().m_nPlayerSlot );
  830. }
  831. else
  832. {
  833. g_pServerPluginHandler->SetCommandClient( GetBaseLocalClient().m_nPlayerSlot );
  834. }
  835. }
  836. #endif
  837. }
  838. // We're not the server, but we've been a listen server (game .dll loaded)
  839. // forward this command tot he server instead of running it locally if we're still
  840. // connected
  841. // Otherwise, things like "say" won't work unless you quit and restart
  842. else if ( isServerCommand )
  843. {
  844. #ifndef DEDICATED
  845. if ( GetBaseLocalClient().IsConnected() )
  846. {
  847. Cmd_ForwardToServer( command );
  848. return NULL;
  849. }
  850. #endif
  851. // It's a server command, but we're not connected to a server. Don't try to execute it.
  852. return NULL;
  853. }
  854. }
  855. // Allow cheat commands in debug, or multiplayer with sv_cheats on
  856. if ( pCommand->IsFlagSet( FCVAR_CHEAT ) )
  857. {
  858. if ( !CanCheat() )
  859. {
  860. // But.. if the server is allowed to run this command and the server DID run this command, then let it through.
  861. // (used by soundscape_flush)
  862. if ( command.Source() != kCommandSrcNetServer || !pCommand->IsFlagSet( FCVAR_SERVER_CAN_EXECUTE ) )
  863. {
  864. if ( Host_IsSinglePlayerGame() )
  865. {
  866. Msg( "This game doesn't allow cheat command %s in single player, unless you have sv_cheats set to 1.\n", pCommand->GetName() );
  867. }
  868. else
  869. {
  870. Msg( "Can't use cheat command %s in multiplayer, unless the server has sv_cheats set to 1.\n", pCommand->GetName() );
  871. }
  872. return NULL;
  873. }
  874. }
  875. }
  876. if ( pCommand->IsFlagSet( FCVAR_SPONLY ) )
  877. {
  878. if ( !Host_IsSinglePlayerGame() )
  879. {
  880. Msg( "Can't use command %s in multiplayer.\n", pCommand->GetName() );
  881. return NULL;
  882. }
  883. }
  884. if ( pCommand->IsFlagSet( FCVAR_DEVELOPMENTONLY ) )
  885. {
  886. Msg( "Unknown command \"%s\"\n", pCommand->GetName() );
  887. return NULL;
  888. }
  889. Cmd_Dispatch( pCommand, command );
  890. return pCommand;
  891. }
  892. }
  893. // check cvars
  894. if ( ConVarUtilities->IsCommand( command, ( int )eTarget ) )
  895. return pCommand;
  896. #ifndef DEDICATED
  897. // forward the command line to the server, so the entity DLL can parse it
  898. if ( command.Source() != kCommandSrcNetClient )
  899. {
  900. if ( GetBaseLocalClient().IsConnected() )
  901. {
  902. Cmd_ForwardToServer( command );
  903. return NULL;
  904. }
  905. }
  906. #endif
  907. Msg( "Unknown command \"%s\"\n", command[0] );
  908. return NULL;
  909. }
  910. const char* Cmd_AliasToCommandString( const char* szAliasName )
  911. {
  912. if ( !szAliasName )
  913. return NULL;
  914. for ( cmdalias_t* a = cmd_alias; a; a = a->next )
  915. {
  916. if ( !Q_strcasecmp( szAliasName, a->name ) )
  917. {
  918. return a->value;
  919. }
  920. }
  921. return NULL;
  922. }
  923. //-----------------------------------------------------------------------------
  924. // Sends the entire command line over to the server
  925. //-----------------------------------------------------------------------------
  926. void Cmd_ForwardToServer( const CCommand &args, bool bReliable )
  927. {
  928. // YWB 6/3/98 Don't forward if this is a dedicated server
  929. #ifndef DEDICATED
  930. char str[1024];
  931. // no command to forward
  932. if ( args.ArgC() == 0 )
  933. return;
  934. // Special case: "cmd whatever args..." is forwarded as "whatever args...";
  935. // in this case we strip "cmd" from the input.
  936. if ( Q_strcasecmp( args[0], "cmd" ) == 0 )
  937. V_strcpy_safe( str, args.ArgS() );
  938. else
  939. V_strcpy_safe( str, args.GetCommandString() );
  940. extern IBaseClientDLL *g_ClientDLL;
  941. if ( demoplayer->IsPlayingBack() && g_ClientDLL )
  942. {
  943. // Not really connected, but can let client dll trap it
  944. g_ClientDLL->OnCommandDuringPlayback( str );
  945. }
  946. else
  947. {
  948. // Throttle user-input commands but not commands issued by code
  949. if ( args.Source() == kCommandSrcUserInput )
  950. {
  951. if(realtime - gForwardedCommandQuota_flTimeStart >= 1.0)
  952. {
  953. // reset quota
  954. gForwardedCommandQuota_flTimeStart = realtime;
  955. gForwardedCommandQuota_nCount = 0;
  956. }
  957. // Add 1 to quota used
  958. gForwardedCommandQuota_nCount++;
  959. // If we are over quota commands per second, dump this on the floor.
  960. // If we spam the server with too many commands, it will kick us.
  961. if ( gForwardedCommandQuota_nCount > kForwardedCommandQuota_nCommandsPerSecond )
  962. {
  963. ConMsg( "Ignoring command '%s': too many server commands issued per second\n", str );
  964. return;
  965. }
  966. }
  967. GetLocalClient().SendStringCmd( str );
  968. }
  969. #endif
  970. }
  971. //-----------------------------------------------------------------------------
  972. // Sends the entire command line over to the server only if it is whitelisted
  973. //-----------------------------------------------------------------------------
  974. void Cmd_ForwardToServerWithWhitelist( const CCommand &args, bool bReliable )
  975. {
  976. int argc = args.ArgC();
  977. char str[1024];
  978. str[0] = 0;
  979. if ( argc > 1 && args[1] && IsWhiteListedCmd( args[1] ) )
  980. {
  981. V_strcat_safe( str, args.ArgS() );
  982. Cbuf_AddText( CBUF_SERVER, str );
  983. }
  984. }