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.

423 lines
9.3 KiB

  1. //========= Copyright 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 <stdlib.h>
  17. #if defined( _X360 )
  18. #include "xbox/xbox_win32stubs.h"
  19. #endif
  20. // memdbgon must be the last include file in a .cpp file!!!
  21. #include "tier0/memdbgon.h"
  22. CTestScriptMgr g_TestScriptMgr;
  23. ConVar testscript_debug( "testscript_debug", "0", 0, "Debug test scripts." );
  24. #ifdef _DEBUG
  25. // --------------------------------------------------------------------------------------------------- //
  26. // Global console commands the test script manager implements.
  27. // --------------------------------------------------------------------------------------------------- //
  28. CON_COMMAND_EXTERN( Test_Wait, Test_Wait, "" )
  29. {
  30. if ( args.ArgC() < 2 )
  31. {
  32. Error( "Test_Wait: requires seconds parameter." );
  33. return;
  34. }
  35. float flSeconds = atof( args[ 1 ] );
  36. GetTestScriptMgr()->SetWaitTime( flSeconds );
  37. }
  38. CON_COMMAND_EXTERN( Test_RunFrame, Test_RunFrame, "" )
  39. {
  40. GetTestScriptMgr()->SetWaitCheckPoint( "frame_end" );
  41. }
  42. CON_COMMAND_EXTERN( Test_WaitForCheckPoint, Test_WaitForCheckPoint, "" )
  43. {
  44. if ( args.ArgC() < 2 )
  45. {
  46. Error( "Test_WaitForCheckPoint <checkpoint name> [once]: requires checkpoint name." );
  47. return;
  48. }
  49. bool bOnce = ( args.ArgC() >= 3 && Q_stricmp( args[2], "once" ) == 0 );
  50. GetTestScriptMgr()->SetWaitCheckPoint( args[ 1 ], bOnce );
  51. }
  52. CON_COMMAND_EXTERN( Test_StartLoop, Test_StartLoop, "Test_StartLoop <loop name> - Denote the start of a loop. Really just defines a named point you can jump to." )
  53. {
  54. if ( args.ArgC() < 2 )
  55. {
  56. Error( "Test_StartLoop: requires a loop name." );
  57. return;
  58. }
  59. GetTestScriptMgr()->StartLoop( args[ 1 ] );
  60. }
  61. CON_COMMAND_EXTERN( Test_LoopCount, Test_LoopCount, "Test_LoopCount <loop name> <count> - loop back to the specified loop start point the specified # of times." )
  62. {
  63. if ( args.ArgC() < 3 )
  64. {
  65. Error( "Test_LoopCount: requires a loop name and number of times to loop." );
  66. return;
  67. }
  68. GetTestScriptMgr()->LoopCount( args[ 1 ], atoi( args[ 2 ] ) );
  69. }
  70. CON_COMMAND_EXTERN( Test_Loop, Test_Loop, "Test_Loop <loop name> - loop back to the specified loop start point unconditionally." )
  71. {
  72. if ( args.ArgC() < 2 )
  73. {
  74. Error( "Test_Loop: requires a loop name." );
  75. return;
  76. }
  77. GetTestScriptMgr()->LoopCount( args[ 1 ], -1 );
  78. }
  79. CON_COMMAND_EXTERN( Test_LoopForNumSeconds, Test_LoopForNumSeconds, "Test_LoopForNumSeconds <loop name> <time> - loop back to the specified start point for the specified # of seconds." )
  80. {
  81. if ( args.ArgC() < 3 )
  82. {
  83. Error( "Test_LoopLoopForNumSeconds: requires a loop name and number of seconds to loop." );
  84. return;
  85. }
  86. GetTestScriptMgr()->LoopForNumSeconds( args[ 1 ], atof( args[ 2 ] ) );
  87. }
  88. CON_COMMAND( Test_RandomChance, "Test_RandomChance <percent chance, 0-100> <token1> <token2...> - Roll the dice and maybe run the command following the percentage chance." )
  89. {
  90. if ( args.ArgC() < 3 )
  91. {
  92. Error( "Test_RandomChance: requires percentage chance parameter (0-100) followed by command to execute." );
  93. }
  94. float flPercent = atof( args[ 1 ] );
  95. if ( RandomFloat( 0, 100 ) < flPercent )
  96. {
  97. char newString[1024];
  98. newString[0] = 0;
  99. for ( int i=2; i < args.ArgC(); i++ )
  100. {
  101. Q_strncat( newString, args[ i ], sizeof( newString ), COPY_ALL_CHARACTERS );
  102. Q_strncat( newString, " ", sizeof( newString ), COPY_ALL_CHARACTERS );
  103. }
  104. Cbuf_InsertText( newString );
  105. }
  106. }
  107. CON_COMMAND( Test_SendKey, "" )
  108. {
  109. if ( args.ArgC() < 2 )
  110. {
  111. Error( "Test_SendKey: requires key to send." );
  112. }
  113. Sys_TestSendKey( args[1] );
  114. }
  115. CON_COMMAND( Test_StartScript, "Start a test script running.." )
  116. {
  117. if ( args.ArgC() < 2 )
  118. {
  119. Warning( "Test_StartScript: requires filename of script to start (file must be under testscripts directory).\n" );
  120. }
  121. if ( !GetTestScriptMgr()->StartTestScript( args[1] ) )
  122. Warning( "Error starting testscript '%s'\n", args[1] );
  123. }
  124. #endif
  125. // --------------------------------------------------------------------------------------------------- //
  126. // CTestScriptMgr implementation.
  127. // --------------------------------------------------------------------------------------------------- //
  128. CTestScriptMgr::CTestScriptMgr()
  129. {
  130. m_hFile = FILESYSTEM_INVALID_HANDLE;
  131. m_NextCheckPoint[0] = 0;
  132. m_WaitUntil = Sys_FloatTime();
  133. }
  134. CTestScriptMgr::~CTestScriptMgr()
  135. {
  136. Term();
  137. }
  138. bool CTestScriptMgr::StartTestScript( const char *pFilename )
  139. {
  140. Term();
  141. char fullName[512];
  142. Q_snprintf( fullName, sizeof( fullName ), "testscripts\\%s", pFilename );
  143. m_hFile = g_pFileSystem->Open( fullName, "rt" );
  144. if ( m_hFile == FILESYSTEM_INVALID_HANDLE )
  145. return false;
  146. RunCommands();
  147. return true;
  148. }
  149. void CTestScriptMgr::Term()
  150. {
  151. if ( m_hFile != FILESYSTEM_INVALID_HANDLE )
  152. {
  153. g_pFileSystem->Close( m_hFile );
  154. m_hFile = FILESYSTEM_INVALID_HANDLE;
  155. }
  156. }
  157. bool CTestScriptMgr::IsInitted() const
  158. {
  159. return m_hFile != FILESYSTEM_INVALID_HANDLE;
  160. }
  161. void CTestScriptMgr::CheckPoint( const char *pName )
  162. {
  163. if ( !IsInitted() || IsTimerWaiting() )
  164. return;
  165. if ( testscript_debug.GetInt() )
  166. {
  167. if ( stricmp( pName, "frame_end" ) != 0 )
  168. {
  169. Msg( "TESTSCRIPT: CheckPoint -> '%s'.\n", pName );
  170. }
  171. }
  172. m_CheckPointsHit.Insert( pName, 0 ); // Remember that we hit this checkpoint.
  173. if ( m_NextCheckPoint[0] )
  174. {
  175. if ( Q_stricmp( m_NextCheckPoint, pName ) != 0 )
  176. {
  177. // This isn't the checkpoint you're looking for.
  178. return;
  179. }
  180. }
  181. // Either the timer expired, or we hit the checkpoint we were waiting for. Run some more commands.
  182. m_NextCheckPoint[0] = 0;
  183. RunCommands();
  184. }
  185. void CTestScriptMgr::RunCommands()
  186. {
  187. Assert( IsInitted() );
  188. while ( !IsTimerWaiting() && !IsCheckPointWaiting() )
  189. {
  190. // Parse out the next command.
  191. char curCommand[512];
  192. int iCurPos = 0;
  193. bool bInSlash = false;
  194. while ( 1 )
  195. {
  196. g_pFileSystem->Read( &curCommand[iCurPos], 1, m_hFile );
  197. if ( curCommand[iCurPos] == '/' )
  198. {
  199. if ( bInSlash )
  200. {
  201. // Ok, the rest of this line is a comment.
  202. char tempVal = !'\n';
  203. while ( tempVal != '\n' && !g_pFileSystem->EndOfFile( m_hFile ) )
  204. {
  205. g_pFileSystem->Read( &tempVal, 1, m_hFile );
  206. }
  207. --iCurPos;
  208. break;
  209. }
  210. else
  211. {
  212. bInSlash = true;
  213. }
  214. }
  215. else
  216. {
  217. bInSlash = false;
  218. if ( curCommand[iCurPos] == ';' || curCommand[iCurPos] == '\n' || g_pFileSystem->EndOfFile( m_hFile ) )
  219. {
  220. // End of this command.
  221. break;
  222. }
  223. }
  224. ++iCurPos;
  225. }
  226. curCommand[iCurPos] = 0;
  227. // Did we hit the end of the file?
  228. if ( curCommand[0] == 0 )
  229. {
  230. if ( g_pFileSystem->EndOfFile( m_hFile ) )
  231. {
  232. Term();
  233. break;
  234. }
  235. else
  236. {
  237. continue;
  238. }
  239. }
  240. Cbuf_AddText( curCommand );
  241. Cbuf_Execute();
  242. }
  243. }
  244. bool CTestScriptMgr::IsTimerWaiting() const
  245. {
  246. if ( Sys_FloatTime() < m_WaitUntil )
  247. {
  248. return true;
  249. }
  250. else
  251. {
  252. return false;
  253. }
  254. }
  255. bool CTestScriptMgr::IsCheckPointWaiting() const
  256. {
  257. return m_NextCheckPoint[0] != 0;
  258. }
  259. void CTestScriptMgr::SetWaitTime( float flSeconds )
  260. {
  261. m_WaitUntil = Sys_FloatTime() + flSeconds;
  262. }
  263. CLoopInfo* CTestScriptMgr::FindLoop( const char *pLoopName )
  264. {
  265. FOR_EACH_LL( m_Loops, i )
  266. {
  267. if ( Q_stricmp( pLoopName, m_Loops[i]->m_Name ) == 0 )
  268. return m_Loops[i];
  269. }
  270. return NULL;
  271. }
  272. void CTestScriptMgr::StartLoop( const char *pLoopName )
  273. {
  274. ErrorIfNotInitted();
  275. if ( FindLoop( pLoopName ) )
  276. {
  277. Error( "CTestScriptMgr::StartLoop( %s ): loop already exists.", pLoopName );
  278. }
  279. CLoopInfo *pLoop = new CLoopInfo;
  280. Q_strncpy( pLoop->m_Name, pLoopName, sizeof( pLoop->m_Name ) );
  281. pLoop->m_nCount = 0;
  282. pLoop->m_flStartTime = Sys_FloatTime();
  283. pLoop->m_iNextCommandPos = g_pFileSystem->Tell( m_hFile );
  284. pLoop->m_ListIndex = m_Loops.AddToTail( pLoop );
  285. }
  286. void CTestScriptMgr::LoopCount( const char *pLoopName, int nTimes )
  287. {
  288. ErrorIfNotInitted();
  289. CLoopInfo *pLoop = FindLoop( pLoopName );
  290. if ( !pLoop )
  291. {
  292. Error( "CTestScriptMgr::LoopCount( %s ): no loop with this name exists.", pLoopName );
  293. }
  294. ++pLoop->m_nCount;
  295. if ( pLoop->m_nCount < nTimes || nTimes == -1 )
  296. {
  297. g_pFileSystem->Seek( m_hFile, pLoop->m_iNextCommandPos, FILESYSTEM_SEEK_HEAD );
  298. }
  299. else
  300. {
  301. m_Loops.Remove( pLoop->m_ListIndex );
  302. delete pLoop;
  303. }
  304. }
  305. void CTestScriptMgr::LoopForNumSeconds( const char *pLoopName, double nSeconds )
  306. {
  307. ErrorIfNotInitted();
  308. CLoopInfo *pLoop = FindLoop( pLoopName );
  309. if ( !pLoop )
  310. {
  311. Error( "CTestScriptMgr::LoopForNumSeconds( %s ): no loop with this name exists.", pLoopName );
  312. }
  313. if ( Sys_FloatTime() - pLoop->m_flStartTime < nSeconds )
  314. {
  315. g_pFileSystem->Seek( m_hFile, pLoop->m_iNextCommandPos, FILESYSTEM_SEEK_HEAD );
  316. }
  317. else
  318. {
  319. m_Loops.Remove( pLoop->m_ListIndex );
  320. delete pLoop;
  321. }
  322. }
  323. void CTestScriptMgr::ErrorIfNotInitted()
  324. {
  325. if ( !IsInitted() )
  326. {
  327. Error( "CTestScriptMgr: not initialized." );
  328. }
  329. }
  330. void CTestScriptMgr::SetWaitCheckPoint( const char *pCheckPointName, bool bOnce )
  331. {
  332. if ( testscript_debug.GetInt() )
  333. {
  334. if ( stricmp( pCheckPointName, "frame_end" ) != 0 )
  335. {
  336. Msg( "TESTSCRIPT: waiting for checkpoint '%s'%s\n", pCheckPointName, bOnce ? " (once)." : "." );
  337. }
  338. }
  339. // Don't wait on this checkpoint if we alereayd
  340. if ( bOnce && m_CheckPointsHit.Find( pCheckPointName ) != m_CheckPointsHit.InvalidIndex() )
  341. return;
  342. Q_strncpy( m_NextCheckPoint, pCheckPointName, sizeof( m_NextCheckPoint ) );
  343. }