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.

527 lines
13 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "tier0/platform.h"
  8. #include "sys.h"
  9. #include "testscriptmgr.h"
  10. #include "tier0/dbg.h"
  11. #include "filesystem_engine.h"
  12. #include "tier1/strtools.h"
  13. #include "cmd.h"
  14. #include "convar.h"
  15. #include "vstdlib/random.h"
  16. #include "host.h"
  17. #include <stdlib.h>
  18. #if defined( _X360 )
  19. #include "xbox/xbox_win32stubs.h"
  20. #endif
  21. // memdbgon must be the last include file in a .cpp file!!!
  22. #include "tier0/memdbgon.h"
  23. #define TESTSCRIPT_COLOR Color( 0, 255, 255 )
  24. CTestScriptMgr g_TestScriptMgr;
  25. ConVar testscript_debug( "testscript_debug", "0", 0, "Debug test scripts." );
  26. ConVar testscript_running( "testscript_running", "0", 0, "Set to true when test scripts are running" );
  27. // --------------------------------------------------------------------------------------------------- //
  28. // Global console commands the test script manager implements.
  29. // --------------------------------------------------------------------------------------------------- //
  30. CON_COMMAND_EXTERN_F( Test_Wait, Test_Wait, "", FCVAR_CHEAT )
  31. {
  32. if ( !CommandLine()->FindParm("-insecure") )
  33. {
  34. Warning( "Tests require that you launch the game with -insecure flag." );
  35. return;
  36. }
  37. if ( args.ArgC() < 2 )
  38. {
  39. Warning( "Test_Wait: requires seconds parameter." );
  40. return;
  41. }
  42. float flSeconds = atof( args[ 1 ] );
  43. GetTestScriptMgr()->SetWaitTime( flSeconds );
  44. }
  45. CON_COMMAND_EXTERN_F( Test_RunFrame, Test_RunFrame, "", FCVAR_CHEAT )
  46. {
  47. if ( !CommandLine()->FindParm( "-insecure" ) )
  48. {
  49. Warning( "Tests require that you launch the game with -insecure flag." );
  50. return;
  51. }
  52. GetTestScriptMgr()->SetWaitCheckPoint( "frame_end" );
  53. }
  54. CON_COMMAND_EXTERN_F( Test_WaitForCheckPoint, Test_WaitForCheckPoint, "", FCVAR_CHEAT )
  55. {
  56. if ( !CommandLine()->FindParm( "-insecure" ) )
  57. {
  58. Warning( "Tests require that you launch the game with -insecure flag." );
  59. return;
  60. }
  61. if ( args.ArgC() < 2 )
  62. {
  63. Warning( "Test_WaitForCheckPoint <checkpoint name> [once]: requires checkpoint name." );
  64. return;
  65. }
  66. bool bOnce = ( args.ArgC() >= 3 && Q_stricmp( args[2], "once" ) == 0 );
  67. GetTestScriptMgr()->SetWaitCheckPoint( args[ 1 ], bOnce );
  68. }
  69. CON_COMMAND_EXTERN_F( Test_StartLoop, Test_StartLoop, "Test_StartLoop <loop name> - Denote the start of a loop. Really just defines a named point you can jump to.", FCVAR_CHEAT )
  70. {
  71. if ( !CommandLine()->FindParm( "-insecure" ) )
  72. {
  73. Warning( "Tests require that you launch the game with -insecure flag." );
  74. return;
  75. }
  76. if ( args.ArgC() < 2 )
  77. {
  78. Warning( "Test_StartLoop: requires a loop name." );
  79. return;
  80. }
  81. GetTestScriptMgr()->StartLoop( args[ 1 ] );
  82. }
  83. CON_COMMAND_EXTERN_F( Test_LoopCount, Test_LoopCount, "Test_LoopCount <loop name> <count> - loop back to the specified loop start point the specified # of times.", FCVAR_CHEAT )
  84. {
  85. if ( !CommandLine()->FindParm( "-insecure" ) )
  86. {
  87. Warning( "Tests require that you launch the game with -insecure flag." );
  88. return;
  89. }
  90. if ( args.ArgC() < 3 )
  91. {
  92. Warning( "Test_LoopCount: requires a loop name and number of times to loop." );
  93. return;
  94. }
  95. GetTestScriptMgr()->LoopCount( args[ 1 ], atoi( args[ 2 ] ) );
  96. }
  97. CON_COMMAND_EXTERN_F( Test_Loop, Test_Loop, "Test_Loop <loop name> - loop back to the specified loop start point unconditionally.", FCVAR_CHEAT )
  98. {
  99. if ( !CommandLine()->FindParm( "-insecure" ) )
  100. {
  101. Warning( "Tests require that you launch the game with -insecure flag." );
  102. return;
  103. }
  104. if ( args.ArgC() < 2 )
  105. {
  106. Warning( "Test_Loop: requires a loop name." );
  107. return;
  108. }
  109. GetTestScriptMgr()->LoopCount( args[ 1 ], -1 );
  110. }
  111. CON_COMMAND_EXTERN_F( Test_LoopForNumSeconds, Test_LoopForNumSeconds, "Test_LoopForNumSeconds <loop name> <time> - loop back to the specified start point for the specified # of seconds.", FCVAR_CHEAT )
  112. {
  113. if ( !CommandLine()->FindParm( "-insecure" ) )
  114. {
  115. Warning( "Tests require that you launch the game with -insecure flag." );
  116. return;
  117. }
  118. if ( args.ArgC() < 3 )
  119. {
  120. Warning( "Test_LoopLoopForNumSeconds: requires a loop name and number of seconds to loop." );
  121. return;
  122. }
  123. GetTestScriptMgr()->LoopForNumSeconds( args[ 1 ], atof( args[ 2 ] ) );
  124. }
  125. CON_COMMAND_F( Test_RandomChance, "Test_RandomChance <percent chance, 0-100> <token1> <token2...> - Roll the dice and maybe run the command following the percentage chance.", FCVAR_CHEAT )
  126. {
  127. if ( !CommandLine()->FindParm( "-insecure" ) )
  128. {
  129. Warning( "Tests require that you launch the game with -insecure flag." );
  130. return;
  131. }
  132. if ( args.ArgC() < 3 )
  133. {
  134. Warning( "Test_RandomChance: requires percentage chance parameter (0-100) followed by command to execute." );
  135. return;
  136. }
  137. float flPercent = atof( args[ 1 ] );
  138. if ( RandomFloat( 0, 100 ) < flPercent )
  139. {
  140. char newString[1024];
  141. newString[0] = 0;
  142. for ( int i=2; i < args.ArgC(); i++ )
  143. {
  144. Q_strncat( newString, args[ i ], sizeof( newString ), COPY_ALL_CHARACTERS );
  145. Q_strncat( newString, " ", sizeof( newString ), COPY_ALL_CHARACTERS );
  146. }
  147. Cbuf_InsertText( Cbuf_GetCurrentPlayer(), newString, args.Source() );
  148. }
  149. }
  150. CON_COMMAND_F( Test_SendKey, "", FCVAR_CHEAT )
  151. {
  152. if ( !CommandLine()->FindParm( "-insecure" ) )
  153. {
  154. Warning( "Tests require that you launch the game with -insecure flag." );
  155. return;
  156. }
  157. if ( args.ArgC() < 2 )
  158. {
  159. Warning( "Test_SendKey: requires key to send.\n" );
  160. return;
  161. }
  162. Sys_TestSendKey( args[1] );
  163. }
  164. CON_COMMAND_F( Test_StartScript, "Start a test script running..", FCVAR_CHEAT )
  165. {
  166. if ( !CommandLine()->FindParm( "-insecure" ) )
  167. {
  168. Warning( "Tests require that you launch the game with -insecure flag." );
  169. return;
  170. }
  171. if ( args.ArgC() < 2 )
  172. {
  173. Warning( "Test_StartScript: requires filename of script to start (file must be under testscripts directory).\n" );
  174. return;
  175. }
  176. GetTestScriptMgr()->StartTestScript( args[1] );
  177. }
  178. // --------------------------------------------------------------------------------------------------- //
  179. // CTestScriptMgr implementation.
  180. // --------------------------------------------------------------------------------------------------- //
  181. CTestScriptMgr::CTestScriptMgr()
  182. {
  183. m_hFile = FILESYSTEM_INVALID_HANDLE;
  184. m_NextCheckPoint[0] = 0;
  185. m_WaitUntil = Sys_FloatTime();
  186. }
  187. CTestScriptMgr::~CTestScriptMgr()
  188. {
  189. Term();
  190. }
  191. bool CTestScriptMgr::StartTestScript( const char *pFilename )
  192. {
  193. StopTestScript();
  194. char fullName[MAX_PATH];
  195. Q_snprintf( fullName, sizeof( fullName ), "testscripts\\%s", pFilename );
  196. V_DefaultExtension( fullName, ".vtest", sizeof( fullName ) );
  197. m_hFile = g_pFileSystem->Open( fullName, "rt", "GAME" );
  198. if ( m_hFile == FILESYSTEM_INVALID_HANDLE )
  199. {
  200. Warning( "StartTestScript( %s ) failed.\n", fullName );
  201. return false;
  202. }
  203. testscript_running.SetValue( 1 );
  204. RunCommands();
  205. return true;
  206. }
  207. void CTestScriptMgr::StopTestScript( void )
  208. {
  209. testscript_running.SetValue( 0 );
  210. Term();
  211. }
  212. void CTestScriptMgr::Term()
  213. {
  214. if ( m_hFile != FILESYSTEM_INVALID_HANDLE )
  215. {
  216. g_pFileSystem->Close( m_hFile );
  217. m_hFile = FILESYSTEM_INVALID_HANDLE;
  218. }
  219. }
  220. bool CTestScriptMgr::IsInitted() const
  221. {
  222. return m_hFile != FILESYSTEM_INVALID_HANDLE;
  223. }
  224. void CTestScriptMgr::CheckPoint( const char *pName )
  225. {
  226. if ( !IsInitted() || IsTimerWaiting() )
  227. return;
  228. MEM_ALLOC_CREDIT();
  229. if ( testscript_debug.GetInt() )
  230. {
  231. if ( stricmp( pName, "frame_end" ) != 0 )
  232. {
  233. ConColorMsg( TESTSCRIPT_COLOR, "TESTSCRIPT: CheckPoint -> '%s'.\n", pName );
  234. }
  235. }
  236. m_CheckPointsHit.Insert( pName, 0 ); // Remember that we hit this checkpoint.
  237. if ( m_NextCheckPoint[0] )
  238. {
  239. if ( Q_stricmp( m_NextCheckPoint, pName ) != 0 )
  240. {
  241. // This isn't the checkpoint you're looking for.
  242. return;
  243. }
  244. }
  245. // Either the timer expired, or we hit the checkpoint we were waiting for. Run some more commands.
  246. m_NextCheckPoint[0] = 0;
  247. RunCommands();
  248. }
  249. void CTestScriptMgr::RunCommands()
  250. {
  251. Assert( IsInitted() );
  252. if ( Cbuf_IsProcessingCommands( Cbuf_GetCurrentPlayer() ) )
  253. {
  254. // Too bad, we got here from a concommand handler, any test script commands you think should be executed won't be executed correctly.
  255. // Cbuf_Execute() is not re-entrant, it won't crash, but it won't do the correct thing from the test script as
  256. // the commands get deferred causing the scripts to break/crash.
  257. // RunCommands() really can be only validly "ticked" from at least the CheckPoint( "frame_end" ) to be adherent
  258. // to the delays and still let frames pass.
  259. return;
  260. }
  261. while ( !IsTimerWaiting() && !IsCheckPointWaiting() )
  262. {
  263. // Parse out the next command.
  264. char curCommand[512];
  265. int iCurPos = 0;
  266. bool bInSlash = false;
  267. while ( 1 )
  268. {
  269. g_pFileSystem->Read( &curCommand[iCurPos], 1, m_hFile );
  270. if ( curCommand[iCurPos] == '/' )
  271. {
  272. if ( bInSlash )
  273. {
  274. // Ok, the rest of this line is a comment.
  275. char tempVal = !'\n';
  276. while ( tempVal != '\n' && !g_pFileSystem->EndOfFile( m_hFile ) )
  277. {
  278. g_pFileSystem->Read( &tempVal, 1, m_hFile );
  279. }
  280. --iCurPos;
  281. break;
  282. }
  283. else
  284. {
  285. bInSlash = true;
  286. }
  287. }
  288. else
  289. {
  290. bInSlash = false;
  291. if ( curCommand[iCurPos] == ';' || curCommand[iCurPos] == '\n' || g_pFileSystem->EndOfFile( m_hFile ) )
  292. {
  293. // End of this command.
  294. break;
  295. }
  296. }
  297. ++iCurPos;
  298. }
  299. curCommand[iCurPos] = 0;
  300. // Did we hit the end of the file?
  301. if ( curCommand[0] == 0 )
  302. {
  303. if ( g_pFileSystem->EndOfFile( m_hFile ) )
  304. {
  305. StopTestScript();
  306. break;
  307. }
  308. else
  309. {
  310. continue;
  311. }
  312. }
  313. if ( developer.GetBool() || testscript_debug.GetInt() )
  314. {
  315. ConColorMsg( TESTSCRIPT_COLOR, "TESTSCRIPT: Executing command from script: %s\n", curCommand );
  316. }
  317. Cbuf_AddText( Cbuf_GetCurrentPlayer(), curCommand );
  318. Cbuf_Execute();
  319. }
  320. }
  321. bool CTestScriptMgr::IsTimerWaiting() const
  322. {
  323. if ( Sys_FloatTime() < m_WaitUntil )
  324. {
  325. return true;
  326. }
  327. else
  328. {
  329. return false;
  330. }
  331. }
  332. bool CTestScriptMgr::IsCheckPointWaiting() const
  333. {
  334. return m_NextCheckPoint[0] != 0;
  335. }
  336. void CTestScriptMgr::SetWaitTime( float flSeconds )
  337. {
  338. m_WaitUntil = Sys_FloatTime() + flSeconds;
  339. }
  340. CLoopInfo* CTestScriptMgr::FindLoop( const char *pLoopName )
  341. {
  342. FOR_EACH_LL( m_Loops, i )
  343. {
  344. if ( Q_stricmp( pLoopName, m_Loops[i]->m_Name ) == 0 )
  345. return m_Loops[i];
  346. }
  347. return NULL;
  348. }
  349. void CTestScriptMgr::StartLoop( const char *pLoopName )
  350. {
  351. ErrorIfNotInitted();
  352. if ( FindLoop( pLoopName ) )
  353. {
  354. Error( "CTestScriptMgr::StartLoop( %s ): loop already exists.", pLoopName );
  355. }
  356. CLoopInfo *pLoop = new CLoopInfo;
  357. Q_strncpy( pLoop->m_Name, pLoopName, sizeof( pLoop->m_Name ) );
  358. pLoop->m_nCount = 0;
  359. pLoop->m_flStartTime = Sys_FloatTime();
  360. pLoop->m_iNextCommandPos = g_pFileSystem->Tell( m_hFile );
  361. pLoop->m_ListIndex = m_Loops.AddToTail( pLoop );
  362. }
  363. void CTestScriptMgr::LoopCount( const char *pLoopName, int nTimes )
  364. {
  365. ErrorIfNotInitted();
  366. CLoopInfo *pLoop = FindLoop( pLoopName );
  367. if ( !pLoop )
  368. {
  369. Warning( "CTestScriptMgr::LoopCount( %s ): no loop with this name exists.", pLoopName );
  370. return;
  371. }
  372. ++pLoop->m_nCount;
  373. if ( pLoop->m_nCount < nTimes || nTimes == -1 )
  374. {
  375. ConColorMsg( TESTSCRIPT_COLOR, "***************************************************\n" );
  376. ConColorMsg( TESTSCRIPT_COLOR, "TESTSCRIPT: Performing loop to %s (%d iterations)\n", pLoopName, pLoop->m_nCount );
  377. ConColorMsg( TESTSCRIPT_COLOR, "***************************************************\n" );
  378. g_pFileSystem->Seek( m_hFile, pLoop->m_iNextCommandPos, FILESYSTEM_SEEK_HEAD );
  379. }
  380. else
  381. {
  382. m_Loops.Remove( pLoop->m_ListIndex );
  383. delete pLoop;
  384. }
  385. }
  386. void CTestScriptMgr::LoopForNumSeconds( const char *pLoopName, double nSeconds )
  387. {
  388. ErrorIfNotInitted();
  389. CLoopInfo *pLoop = FindLoop( pLoopName );
  390. if ( !pLoop )
  391. {
  392. Error( "CTestScriptMgr::LoopForNumSeconds( %s ): no loop with this name exists.", pLoopName );
  393. }
  394. if ( Sys_FloatTime() - pLoop->m_flStartTime < nSeconds )
  395. {
  396. ConColorMsg( TESTSCRIPT_COLOR, "***************************************************\n" );
  397. ConColorMsg( TESTSCRIPT_COLOR, "TESTSCRIPT: Performing loop to %s\n", pLoopName );
  398. ConColorMsg( TESTSCRIPT_COLOR, "***************************************************\n" );
  399. g_pFileSystem->Seek( m_hFile, pLoop->m_iNextCommandPos, FILESYSTEM_SEEK_HEAD );
  400. }
  401. else
  402. {
  403. m_Loops.Remove( pLoop->m_ListIndex );
  404. delete pLoop;
  405. }
  406. }
  407. void CTestScriptMgr::ErrorIfNotInitted()
  408. {
  409. if ( !IsInitted() )
  410. {
  411. Error( "CTestScriptMgr: not initialized." );
  412. }
  413. }
  414. void CTestScriptMgr::SetWaitCheckPoint( const char *pCheckPointName, bool bOnce )
  415. {
  416. if ( testscript_debug.GetInt() )
  417. {
  418. if ( stricmp( pCheckPointName, "frame_end" ) != 0 )
  419. {
  420. ConColorMsg( TESTSCRIPT_COLOR, "TESTSCRIPT: waiting for checkpoint '%s'%s\n", pCheckPointName, bOnce ? " (once)." : "." );
  421. }
  422. }
  423. // Don't wait on this checkpoint if we alereayd
  424. if ( bOnce && m_CheckPointsHit.Find( pCheckPointName ) != m_CheckPointsHit.InvalidIndex() )
  425. return;
  426. Q_strncpy( m_NextCheckPoint, pCheckPointName, sizeof( m_NextCheckPoint ) );
  427. }