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.

1025 lines
26 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. //=======================================================================================//
  4. #include "cl_performancecontroller.h"
  5. #include "cl_replaycontext.h"
  6. #include "globalvars_base.h"
  7. #include "cl_replaycontext.h"
  8. #include "replay/replay.h"
  9. #include "replay/ireplaycamera.h"
  10. #include "replay/replayutils.h"
  11. #include "replay/ireplayperformanceplaybackhandler.h"
  12. #include "replay/ireplayperformanceeditor.h"
  13. #include "filesystem.h"
  14. #include "KeyValues.h"
  15. #include "replaysystem.h"
  16. #include "cl_replaymanager.h"
  17. #include "vprof.h"
  18. #include "cl_performance_common.h"
  19. #include "engine/ivdebugoverlay.h"
  20. #include "utlbuffer.h"
  21. #undef Yield
  22. #include "vstdlib/jobthread.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include "tier0/memdbgon.h"
  25. //----------------------------------------------------------------------------------------
  26. #ifdef _DEBUG
  27. ConVar replay_simulate_long_save( "replay_simulate_long_save", "0", FCVAR_DONTRECORD, "Simulate a long save. Seconds." );
  28. #endif
  29. //----------------------------------------------------------------------------------------
  30. class CSaveJob : public CJob
  31. {
  32. public:
  33. CSaveJob( KeyValues *pInData, const char *pFullFilename )
  34. : m_pInData( pInData )
  35. {
  36. SetFlags( GetFlags() | JF_IO );
  37. V_strncpy( m_szFullFilename, pFullFilename, sizeof( m_szFullFilename ) );
  38. }
  39. virtual JobStatus_t DoExecute()
  40. {
  41. if ( !m_pInData )
  42. return JOB_FAILED;
  43. if ( !V_strlen( m_szFullFilename ) )
  44. return JOB_FAILED;
  45. #ifdef _DEBUG
  46. const int nDelay = replay_simulate_long_save.GetInt();
  47. if ( nDelay )
  48. {
  49. ThreadSleep( nDelay * 1000 );
  50. }
  51. #endif
  52. return m_pInData->SaveToFile( g_pFullFileSystem, m_szFullFilename, "MOD" ) ? JOB_OK : JOB_FAILED;
  53. }
  54. private:
  55. KeyValues *m_pInData;
  56. char m_szFullFilename[MAX_OSPATH];
  57. };
  58. //----------------------------------------------------------------------------------------
  59. CPerformanceController::CPerformanceController()
  60. : m_pRoot( NULL ),
  61. m_pCurEvent( NULL ),
  62. m_pDbgRoot( NULL ),
  63. m_pPlaybackHandler( NULL ),
  64. m_pSetViewEvent( NULL ),
  65. m_pSaveJob( NULL ),
  66. m_bViewOverrideMode( false ),
  67. m_bDirty( false ),
  68. m_bLastSaveStatus( false ),
  69. m_bRewinding( false ),
  70. m_pSavedPerformance( NULL ),
  71. m_pScratchPerformance( NULL ),
  72. m_hReplay( REPLAY_HANDLE_INVALID ),
  73. m_nState( STATE_DORMANT ),
  74. m_pEditor( NULL ),
  75. m_flLastCamSetViewTime( 0.0f ),
  76. m_flTimeScale( 1.0f )
  77. {
  78. }
  79. CPerformanceController::~CPerformanceController()
  80. {
  81. Cleanup();
  82. }
  83. void CPerformanceController::Cleanup()
  84. {
  85. AssertMsg(
  86. !m_pScratchPerformance || m_pScratchPerformance != m_pSavedPerformance,
  87. "Sanity check failed. We should never be assigning saved to scratch or vice versa."
  88. );
  89. m_pSavedPerformance = NULL;
  90. if ( m_pScratchPerformance )
  91. {
  92. delete m_pScratchPerformance;
  93. m_pScratchPerformance = NULL;
  94. }
  95. CleanupStream();
  96. CleanupDbgStream();
  97. ClearDirtyFlag();
  98. m_pCurEvent = NULL;
  99. m_pEditor = NULL;
  100. m_pPlaybackHandler = NULL;
  101. m_hReplay = REPLAY_HANDLE_INVALID;
  102. m_nState = STATE_DORMANT;
  103. m_flLastCamSetViewTime = 0.0f;
  104. m_flTimeScale = 1.0f;
  105. // Remove all queued events
  106. FOR_EACH_LL( m_EventQueue, i )
  107. {
  108. m_EventQueue[ i ]->deleteThis();
  109. }
  110. m_EventQueue.RemoveAll();
  111. }
  112. void CPerformanceController::CleanupStream()
  113. {
  114. if ( m_pRoot )
  115. {
  116. m_pRoot->deleteThis();
  117. m_pRoot = NULL;
  118. }
  119. }
  120. void CPerformanceController::CleanupDbgStream()
  121. {
  122. if ( m_pDbgRoot )
  123. {
  124. m_pDbgRoot->deleteThis();
  125. m_pDbgRoot = NULL;
  126. }
  127. }
  128. float CPerformanceController::GetTime() const
  129. {
  130. Assert( m_pCurEvent );
  131. return atof( m_pCurEvent->GetName() );
  132. }
  133. void CPerformanceController::SetEditor( IReplayPerformanceEditor *pEditor )
  134. {
  135. AssertMsg( pEditor, "This is bad. You must supply a valid editor pointer." );
  136. // Cache editor
  137. m_pEditor = pEditor;
  138. }
  139. void CPerformanceController::StartRecording( CReplay *pReplay, bool bSnip )
  140. {
  141. Assert( !IsRecording() );
  142. AssertMsg(
  143. m_nState == STATE_PLAYING || !m_pRoot,
  144. "Unless we're playing, root should be NULL here"
  145. );
  146. if ( m_nState == STATE_DORMANT )
  147. {
  148. Assert( !m_pSavedPerformance );
  149. // Create the performance KeyValues
  150. m_pRoot = new KeyValues( "performance" );
  151. }
  152. else if ( m_nState == STATE_PLAYING )
  153. {
  154. // Nuke everything after the current event, or does nothing if we've past the end of the playback stream
  155. if ( bSnip )
  156. {
  157. Snip();
  158. }
  159. // When we go from playback to recording, we need to reset override view
  160. g_pClient->GetReplayCamera()->ClearOverrideView();
  161. }
  162. // Update the state
  163. m_nState = STATE_RECORDING;
  164. // Mark as dirty
  165. m_bDirty = true;
  166. }
  167. void CPerformanceController::Stop()
  168. {
  169. Assert( !m_bRewinding );
  170. ClearRewinding();
  171. Cleanup();
  172. }
  173. bool CPerformanceController::DumpStreamToFileAsync( const char *pFullFilename )
  174. {
  175. // m_pRoot can be NULL if the user only set an in and/or out point, and wants to save.
  176. if ( !m_pRoot )
  177. return true;
  178. // Save the file
  179. m_pSaveJob = new CSaveJob( m_pRoot, pFullFilename );
  180. if ( !m_pSaveJob )
  181. return false;
  182. IThreadPool *pThreadPool = CL_GetThreadPool();
  183. if ( !pThreadPool )
  184. return false;
  185. pThreadPool->AddJob( m_pSaveJob );
  186. return true;
  187. }
  188. bool CPerformanceController::FlushReplay()
  189. {
  190. // Get the replay
  191. CReplay *pReplay = GetReplay( m_hReplay );
  192. if ( !pReplay )
  193. return false;
  194. // Add the performance to the replay and save
  195. Assert( !m_pSavedPerformance || pReplay->HasPerformance( m_pSavedPerformance ) );
  196. CL_GetReplayManager()->FlagReplayForFlush( pReplay, true );
  197. return true;
  198. }
  199. bool CPerformanceController::SaveAsync()
  200. {
  201. if ( !m_pRoot )
  202. return false;
  203. if ( !m_pScratchPerformance )
  204. {
  205. AssertMsg( 0, "Scratch performance should always be valid at this point." );
  206. return false;
  207. }
  208. // NOTE: m_pSavedPerformance should always be valid here, as 'save' is disabled until it
  209. // has an actual performance to save to.
  210. // Copy the relevant data from scratch -> saved - we want to preserve the filename
  211. // the saved performance, and have no reason to copy over duplicate data (eg the replay
  212. // handle).
  213. m_pSavedPerformance->CopyTicks( m_pScratchPerformance );
  214. // Copy title
  215. V_wcsncpy( m_pSavedPerformance->m_wszTitle, m_pScratchPerformance->m_wszTitle, sizeof( m_pSavedPerformance->m_wszTitle ) );
  216. // Use the saved performance's filename
  217. DumpStreamToFileAsync( m_pSavedPerformance->GetFullPerformanceFilename() );
  218. // Save the replay file
  219. FlushReplay();
  220. // Clear dirty flag
  221. ClearDirtyFlag();
  222. return true;
  223. }
  224. bool CPerformanceController::SaveAsAsync( const wchar_t *pTitle )
  225. {
  226. //
  227. // NOTE: This function assumes the following:
  228. //
  229. // * We've already dealt with checking the given title versus existing performances
  230. // in the replay and that the user has selected to overwrite.
  231. //
  232. CReplay *pReplay = m_pEditor->GetReplay();
  233. if ( !pReplay )
  234. {
  235. AssertMsg( 0, "Replay must exist!" );
  236. return false;
  237. }
  238. // Find existing performance in replay, if it exists.
  239. CReplayPerformance *pExistingPerformance = pReplay->GetPerformanceWithTitle( pTitle );
  240. if ( !pExistingPerformance )
  241. {
  242. // Create and add a new performance to the replay with a unique filename - do not generate a title since we will
  243. // use the incoming title.
  244. CReplayPerformance *pCopy = pReplay->AddNewPerformance( false, true );
  245. // Copy the ticks, which is all we care about
  246. pCopy->CopyTicks( m_pScratchPerformance );
  247. // Set the title
  248. pCopy->SetTitle( pTitle );
  249. // Dump to the new file and save the replay
  250. if ( !DumpStreamToFileAsync( pCopy->GetFullPerformanceFilename() ) ||
  251. !FlushReplay() )
  252. {
  253. return false;
  254. }
  255. // If we didn't spawn a thread, we want this to be true here, since the replay flushed
  256. // and DumpStreamToFileAsync() succeeded.
  257. m_bLastSaveStatus = true;
  258. // Saved performance is now replaced with the newly created performance
  259. m_pSavedPerformance = pCopy;
  260. // Clear dirty flag
  261. ClearDirtyFlag();
  262. return true;
  263. }
  264. // Overwriting an existing performance?
  265. else
  266. {
  267. // Performance with the given name already exists - overwrite it (again, this function
  268. // assumes that any UI around asking the user if they're sure they want to replace has
  269. // already been navigated, and the user has selected to overwrite).
  270. m_pSavedPerformance = pExistingPerformance;
  271. }
  272. // Copy the title to the scratch
  273. V_wcsncpy( m_pScratchPerformance->m_wszTitle, pTitle, MAX_TAKE_TITLE_LENGTH * sizeof( wchar_t ) );
  274. // Attempt to save
  275. if ( !SaveAsync() )
  276. return false;
  277. // Clear dirty flag
  278. ClearDirtyFlag();
  279. return true;
  280. }
  281. bool CPerformanceController::IsSaving() const
  282. {
  283. return m_pSaveJob != NULL;
  284. }
  285. void CPerformanceController::SaveThink()
  286. {
  287. if ( !m_pSaveJob )
  288. return;
  289. if ( m_pSaveJob->IsFinished() )
  290. {
  291. // Cache save status
  292. m_bLastSaveStatus = m_pSaveJob->GetStatus() == JOB_OK;
  293. m_pSaveJob->Release();
  294. m_pSaveJob = NULL;
  295. }
  296. }
  297. bool CPerformanceController::GetLastSaveStatus() const
  298. {
  299. return m_bLastSaveStatus;
  300. }
  301. void CPerformanceController::ClearDirtyFlag()
  302. {
  303. m_bDirty = false;
  304. }
  305. bool CPerformanceController::IsRecording() const
  306. {
  307. return m_nState == STATE_RECORDING;
  308. }
  309. bool CPerformanceController::IsPlaying() const
  310. {
  311. return m_nState == STATE_PLAYING;
  312. }
  313. bool CPerformanceController::IsPlaybackDataLeft()
  314. {
  315. return m_pCurEvent && m_pCurEvent->GetNextTrueSubKey();
  316. }
  317. bool CPerformanceController::IsDirty() const
  318. {
  319. return m_bDirty;
  320. }
  321. void CPerformanceController::NotifyDirty()
  322. {
  323. AssertMsg( GetPerformance() != NULL, "Can't mark empty performance as dirty." );
  324. m_bDirty = true;
  325. }
  326. void CPerformanceController::OnSignonStateFull()
  327. {
  328. if ( !g_pEngineClient->IsDemoPlayingBack() )
  329. return;
  330. // User hit rewind button (which reloads the map)?
  331. if ( m_bRewinding )
  332. {
  333. // Setup controller for playback from existing data.
  334. SetupPlaybackExistingStream();
  335. // Clear rewinding
  336. ClearRewinding();
  337. // Let the editor know the rewind has completed.
  338. m_pEditor->OnRewindComplete();
  339. }
  340. else
  341. {
  342. AssertMsg( !m_pScratchPerformance, "Scratch replay should not be valid yet." );
  343. // If we've gotten this far and the replay is invalid, we're likely playing back a
  344. // regular demo and didn't early out somewhere up the chain.
  345. CReplay *pReplay = g_pReplayDemoPlayer->GetCurrentReplay();
  346. if ( !pReplay )
  347. return;
  348. // Cache replay
  349. m_hReplay = pReplay->GetHandle();
  350. // Play a performance from the beginning.
  351. CReplayPerformance *pPerformance = g_pReplayDemoPlayer->GetCurrentPerformance();
  352. if ( pPerformance )
  353. {
  354. SetupPlaybackFromPerformance( pPerformance );
  355. // Make a copy of the performance we're playing back so the user can make changes
  356. // w/o fucking up the original.
  357. m_pScratchPerformance = pPerformance->MakeCopy();
  358. }
  359. else
  360. {
  361. CreateNewScratchPerformance( pReplay );
  362. }
  363. }
  364. }
  365. float CPerformanceController::GetPlaybackTimeScale() const
  366. {
  367. return m_flTimeScale;
  368. }
  369. void CPerformanceController::CreateNewScratchPerformance( CReplay *pReplay )
  370. {
  371. // Create a new performance, but don't add it to the replay yet
  372. m_pScratchPerformance = CL_GetPerformanceManager()->CreatePerformance( pReplay );
  373. // Give it a default name
  374. m_pScratchPerformance->AutoNameIfHasNoTitle( pReplay->m_szMapName );
  375. // Generate a filename for the new performance
  376. m_pScratchPerformance->SetFilename( CL_GetPerformanceManager()->GeneratePerformanceFilename( pReplay ) );
  377. }
  378. //----------------------------------------------------------------------------------------
  379. void CPerformanceController::NotifyPauseState( bool bPaused )
  380. {
  381. if ( m_bPaused == bPaused )
  382. return;
  383. m_bPaused = bPaused;
  384. // Unpause?
  385. if ( !bPaused )
  386. {
  387. // Add queued events
  388. for( int i = m_EventQueue.Tail(); i != m_EventQueue.InvalidIndex(); i = m_EventQueue.Previous( i ) )
  389. {
  390. KeyValues *pCurEvent = m_EventQueue[ i ];
  391. AddEvent( pCurEvent );
  392. }
  393. m_EventQueue.RemoveAll();
  394. }
  395. }
  396. CReplayPerformance *CPerformanceController::GetPerformance()
  397. {
  398. return m_pScratchPerformance;
  399. }
  400. CReplayPerformance *CPerformanceController::GetSavedPerformance()
  401. {
  402. return m_pSavedPerformance;
  403. }
  404. bool CPerformanceController::HasSavedPerformance()
  405. {
  406. return m_pSavedPerformance != NULL;
  407. }
  408. void CPerformanceController::Snip()
  409. {
  410. if ( !m_pCurEvent )
  411. return;
  412. const float flTime = GetTime();
  413. // Go through all events and delete anything on or after flSnipTime
  414. for ( KeyValues *pCurEvent = m_pRoot->GetFirstTrueSubKey(); pCurEvent != NULL; )
  415. {
  416. // Get next first, in case we delete
  417. KeyValues *pNext = pCurEvent->GetNextTrueSubKey();
  418. const float flCurEventTime = atof( pCurEvent->GetName() );
  419. if ( flCurEventTime >= flTime )
  420. {
  421. // Delete the key
  422. m_pRoot->RemoveSubKey( pCurEvent );
  423. pCurEvent->deleteThis();
  424. }
  425. pCurEvent = pNext;
  426. }
  427. }
  428. bool CPerformanceController::IsCameraChangeEvent( int nType ) const
  429. {
  430. return nType >= EVENTTYPE_CAMERA_CHANGE_BEGIN && nType <= EVENTTYPE_CAMERA_CHANGE_END;
  431. }
  432. void CPerformanceController::NotifyRewinding()
  433. {
  434. m_bRewinding = true;
  435. m_flLastCamSetViewTime = 0.0f;
  436. }
  437. void CPerformanceController::ClearRewinding()
  438. {
  439. m_bRewinding = false;
  440. }
  441. //----------------------------------------------------------------------------------------
  442. #define CREATE_EVENT( time_, type_ ) \
  443. new KeyValues( Replay_va( "%f", time_ ), "type", type_ )
  444. #define RECORD_EVENT_( event_, time_, type_ ) \
  445. event_ = CREATE_EVENT( time_, type_ ); \
  446. AddEvent( event_ )
  447. #define RECORD_EVENT( event_, time_, type_ ) \
  448. KeyValues *event_ = RECORD_EVENT_( event_, time_, type_ )
  449. #define QUEUE_OR_RECORD_EVENT( event_, time_, type_ ) \
  450. if ( !m_pRoot ) \
  451. return; \
  452. \
  453. KeyValues *event_; \
  454. if ( m_bPaused ) \
  455. { \
  456. KeyValues *pQueuedEvent = CREATE_EVENT( time_, type_ ); \
  457. event_ = pQueuedEvent; \
  458. m_EventQueue.AddToHead( pQueuedEvent ); \
  459. RemoveDuplicateEventsFromQueue(); \
  460. } \
  461. else \
  462. { \
  463. RECORD_EVENT_( event_, time_, type_ ); \
  464. }
  465. void CPerformanceController::RemoveDuplicateEventsFromQueue()
  466. {
  467. // Add queued events - only add the most recent camera change event, and the most recent
  468. // player change event.
  469. bool bFoundCameraChange = false;
  470. bool bFoundPlayerChange = false;
  471. bool bFoundSetView = false;
  472. bool bFoundTimeScale = false;
  473. for( int i = m_EventQueue.Head(); i != m_EventQueue.InvalidIndex(); )
  474. {
  475. KeyValues *pCurEvent = m_EventQueue[ i ];
  476. const int nType = pCurEvent->GetInt( "type" );
  477. bool bDitchEvent = false;
  478. bool bSetupCut = false;
  479. // Determine whether we should record the event or not
  480. if ( nType == EVENTTYPE_CHANGEPLAYER )
  481. {
  482. bDitchEvent = bFoundPlayerChange;
  483. bFoundPlayerChange = true;
  484. }
  485. else if ( IsCameraChangeEvent( nType ) )
  486. {
  487. bDitchEvent = bFoundCameraChange;
  488. bFoundCameraChange = true;
  489. }
  490. else if ( nType == EVENTTYPE_CAMERA_SETVIEW )
  491. {
  492. bDitchEvent = bFoundSetView;
  493. bFoundSetView = true;
  494. bSetupCut = true; // If we end up keeping this event, it should be a cut.
  495. }
  496. else if ( nType == EVENTTYPE_TIMESCALE )
  497. {
  498. bDitchEvent = bFoundTimeScale;
  499. bFoundTimeScale = true;
  500. }
  501. // Setup as cut
  502. if ( bSetupCut )
  503. {
  504. pCurEvent->SetInt( "cut", 1 );
  505. }
  506. int itNext = m_EventQueue.Next( i );
  507. if ( bDitchEvent )
  508. {
  509. #if _DEBUG
  510. CUtlBuffer buf;
  511. pCurEvent->RecursiveSaveToFile( buf, 1 );
  512. IF_REPLAY_DBG( Warning( "Ditching event of type %s\n...", ( const char * )buf.Base() ) );
  513. #endif
  514. // Free the event
  515. pCurEvent->deleteThis();
  516. m_EventQueue.Remove( i );
  517. }
  518. i = itNext;
  519. }
  520. }
  521. void CPerformanceController::AddEvent( KeyValues *pEvent )
  522. {
  523. IF_REPLAY_DBG2(
  524. CUtlBuffer buf;
  525. pEvent->RecursiveSaveToFile( buf, 1 );
  526. Warning( "Recording event:\n%s\n", ( const char * )buf.Base() );
  527. );
  528. m_pRoot->AddSubKey( pEvent );
  529. }
  530. void CPerformanceController::AddEvent_Camera_Change_FirstPerson( float flTime, int nEntityIndex )
  531. {
  532. QUEUE_OR_RECORD_EVENT( pEvent, flTime, EVENTTYPE_CAMERA_CHANGE_FIRSTPERSON );
  533. pEvent->SetInt( "ent", nEntityIndex );
  534. }
  535. void CPerformanceController::AddEvent_Camera_Change_ThirdPerson( float flTime, int nEntityIndex )
  536. {
  537. QUEUE_OR_RECORD_EVENT( pEvent, flTime, EVENTTYPE_CAMERA_CHANGE_THIRDPERSON );
  538. pEvent->SetInt( "ent", nEntityIndex );
  539. }
  540. void CPerformanceController::AddEvent_Camera_Change_Free( float flTime )
  541. {
  542. QUEUE_OR_RECORD_EVENT( pEvent, flTime, EVENTTYPE_CAMERA_CHANGE_FREE );
  543. }
  544. void CPerformanceController::AddEvent_Camera_ChangePlayer( float flTime, int nEntIndex )
  545. {
  546. QUEUE_OR_RECORD_EVENT( pEvent, flTime, EVENTTYPE_CHANGEPLAYER );
  547. pEvent->SetInt( "ent", nEntIndex );
  548. }
  549. void CPerformanceController::AddEvent_Camera_SetView( const SetViewParams_t &params )
  550. {
  551. QUEUE_OR_RECORD_EVENT( pEvent, params.m_flTime, EVENTTYPE_CAMERA_SETVIEW );
  552. pEvent->SetString( "pos", Replay_va( "%f %f %f", params.m_pOrigin->x, params.m_pOrigin->y, params.m_pOrigin->z ) );
  553. pEvent->SetString( "ang", Replay_va( "%f %f %f", params.m_pAngles->x, params.m_pAngles->y, params.m_pAngles->z ) );
  554. pEvent->SetFloat( "fov", params.m_flFov );
  555. pEvent->SetFloat( "a", params.m_flAccel );
  556. pEvent->SetFloat( "s", params.m_flSpeed );
  557. pEvent->SetFloat( "rf", params.m_flRotationFilter );
  558. }
  559. void CPerformanceController::AddEvent_TimeScale( float flTime, float flScale )
  560. {
  561. QUEUE_OR_RECORD_EVENT( pEvent, flTime, EVENTTYPE_TIMESCALE );
  562. pEvent->SetFloat( "scale", flScale );
  563. m_flTimeScale = flScale;
  564. }
  565. //----------------------------------------------------------------------------------------
  566. bool CPerformanceController::SetupPlaybackHandler()
  567. {
  568. IReplayPerformancePlaybackHandler *pHandler = g_pClient->GetPerformancePlaybackHandler();
  569. if ( !pHandler )
  570. return false;
  571. // Cache
  572. m_pPlaybackHandler = pHandler;
  573. return true;
  574. }
  575. void CPerformanceController::FinishBeginPerformancePlayback()
  576. {
  577. // Root should be setup by now
  578. Assert( m_pRoot );
  579. // Make sure the camera isn't setup for camera override
  580. // TODO: Definitely need this here?
  581. g_pClient->GetReplayCamera()->ClearOverrideView();
  582. // Set to initial event
  583. m_pCurEvent = m_pRoot->GetFirstTrueSubKey();
  584. IF_REPLAY_DBG(
  585. m_pDbgRoot = m_pRoot->MakeCopy();
  586. );
  587. m_nState = STATE_PLAYING;
  588. m_bViewOverrideMode = false;
  589. }
  590. void CPerformanceController::SetupPlaybackExistingStream()
  591. {
  592. // m_pRoot can be NULL here if the user is watching the original replay and has rewound
  593. // without changing anything.
  594. if ( !m_pRoot )
  595. return;
  596. if ( !SetupPlaybackHandler() )
  597. return;
  598. FinishBeginPerformancePlayback();
  599. }
  600. void CPerformanceController::SetupPlaybackFromPerformance( CReplayPerformance *pPerformance )
  601. {
  602. AssertMsg( !m_pSavedPerformance, "This probably hit because either SaveNow() or Discard() were not called. One of those should always be called on disconnect after watching or editing a replay." );
  603. AssertMsg( !m_pScratchPerformance, "Scratch performance should be NULL here." );
  604. if ( !pPerformance )
  605. return;
  606. if ( !pPerformance->m_pReplay )
  607. {
  608. AssertMsg( 0, "Performance passed in with an invalid replay pointer! This bad!" );
  609. return;
  610. }
  611. if ( !SetupPlaybackHandler() )
  612. return;
  613. // Cache off the performance and replay for playback
  614. m_pSavedPerformance = pPerformance;
  615. m_pScratchPerformance = NULL;
  616. m_hReplay = pPerformance->m_pReplay->GetHandle();
  617. // Read the file
  618. Assert( !m_pRoot );
  619. const char *pFilename = pPerformance->GetFullPerformanceFilename();
  620. m_pRoot = new KeyValues( pFilename );
  621. if ( !m_pRoot->LoadFromFile( g_pFullFileSystem, pFilename ) )
  622. {
  623. Warning( "Failed to load replay file, \"%s\"!\n", pFilename );
  624. return;
  625. }
  626. FinishBeginPerformancePlayback();
  627. }
  628. void CPerformanceController::ReadSetViewEvent( KeyValues *pEventSubKey, Vector &origin, QAngle &angles, float &fov,
  629. float *pAccel, float *pSpeed, float *pRotFilter )
  630. {
  631. const char *pViewStr[2];
  632. pViewStr[0] = pEventSubKey->GetString( "pos" );
  633. pViewStr[1] = pEventSubKey->GetString( "ang" );
  634. sscanf( pViewStr[0], "%f %f %f", &origin.x, &origin.y, &origin.z );
  635. sscanf( pViewStr[1], "%f %f %f", &angles.x, &angles.y, &angles.z );
  636. fov = pEventSubKey->GetFloat( "fov", 90 );
  637. if ( pAccel && pSpeed && pRotFilter )
  638. {
  639. *pAccel = pEventSubKey->GetFloat( "a" );
  640. *pSpeed = pEventSubKey->GetFloat( "s" );
  641. *pRotFilter = pEventSubKey->GetFloat( "rf" );
  642. }
  643. }
  644. void CPerformanceController::PlaybackThink()
  645. {
  646. static Vector aOrigin[3];
  647. static QAngle aAngles[3];
  648. static float aFov[3];
  649. float flAccel = 0.0f, flSpeed = 0.0f, flRotFilter = 0.0f;
  650. KeyValues *pSearch = NULL;
  651. float t;
  652. if ( !IsPlaying() )
  653. return;
  654. if ( !m_pCurEvent )
  655. return;
  656. if ( !m_pPlaybackHandler )
  657. return;
  658. CReplay *pReplay = GetReplay( m_hReplay );
  659. if ( !pReplay )
  660. return;
  661. const CGlobalVarsBase *g_pClientGlobalVariables = g_pEngineClient->GetClientGlobalVars();
  662. const int nReplaySpawnTick = pReplay->m_nSpawnTick;
  663. Assert( nReplaySpawnTick >= 0 );
  664. const float flCurTime = g_pClientGlobalVariables->curtime - g_pEngine->TicksToTime( nReplaySpawnTick );
  665. float flEventTime = 0;
  666. bool bShouldCut = false;
  667. while ( 1 )
  668. {
  669. // Get event time
  670. flEventTime = GetTime();
  671. // Get out if this event shouldn't fire yet
  672. if ( flEventTime > flCurTime )
  673. break;
  674. IF_REPLAY_DBG2(
  675. CUtlBuffer buf;
  676. m_pCurEvent->RecursiveSaveToFile( buf, 1 );
  677. Warning( "%s\n", ( const char * )buf.Base() );
  678. );
  679. switch ( m_pCurEvent->GetInt( "type", EVENTTYPE_INVALID ) )
  680. {
  681. case EVENTTYPE_CAMERA_CHANGE_FIRSTPERSON:
  682. m_bViewOverrideMode = false;
  683. m_pPlaybackHandler->OnEvent_Camera_Change_FirstPerson( flEventTime, m_pCurEvent->GetInt( "ent" ) );
  684. break;
  685. case EVENTTYPE_CAMERA_CHANGE_THIRDPERSON:
  686. m_bViewOverrideMode = true;
  687. m_pPlaybackHandler->OnEvent_Camera_Change_ThirdPerson( flEventTime, m_pCurEvent->GetInt( "ent" ) );
  688. break;
  689. case EVENTTYPE_CAMERA_CHANGE_FREE:
  690. m_bViewOverrideMode = true;
  691. m_pPlaybackHandler->OnEvent_Camera_Change_Free( flEventTime );
  692. break;
  693. case EVENTTYPE_CHANGEPLAYER:
  694. m_pPlaybackHandler->OnEvent_Camera_ChangePlayer( flEventTime, m_pCurEvent->GetInt( "ent" ) );
  695. break;
  696. case EVENTTYPE_CAMERA_SETVIEW:
  697. AssertMsg( m_bViewOverrideMode, "Camera mode needs to be set before a setview can take effect." );
  698. if ( m_bViewOverrideMode )
  699. {
  700. // Get sample for current time
  701. ReadSetViewEvent( m_pCurEvent, aOrigin[0], aAngles[0], aFov[0], &flAccel, &flSpeed, &flRotFilter );
  702. // g_pEngineClient->Con_NPrintf( 0, "sample 0 time: %f", flEventTime );
  703. m_flLastCamSetViewTime = flEventTime;
  704. m_pSetViewEvent = m_pCurEvent; // Stomp any previous set view - we want the last one
  705. bShouldCut = bShouldCut || m_pCurEvent->GetBool( "cut" ); // We cut if any set-view event cuts, otherwise we will interpolate
  706. }
  707. break;
  708. case EVENTTYPE_TIMESCALE:
  709. m_flTimeScale = m_pCurEvent->GetFloat( "scale" );
  710. m_pPlaybackHandler->OnEvent_TimeScale( flEventTime, m_flTimeScale );
  711. break;
  712. default:
  713. AssertMsg( 0, "Unknown event in performance playback!\n" );
  714. Warning( "Unknown event in performance playback!\n" );
  715. }
  716. // Get next event (or NULL if there isn't one)
  717. m_pCurEvent = m_pCurEvent->GetNextTrueSubKey();
  718. // Get out if no more events
  719. if ( !m_pCurEvent )
  720. break;
  721. }
  722. // If in override mode, interpolate and setup camera
  723. if ( m_bViewOverrideMode && m_pSetViewEvent )
  724. {
  725. if ( bShouldCut )
  726. {
  727. DBG2( "CUT\n" );
  728. aOrigin[2] = aOrigin[0];
  729. aAngles[2] = aAngles[0];
  730. aFov[2] = aFov[0];
  731. }
  732. else
  733. {
  734. // Default second sample to first, in case we don't find a sample to interpolate with
  735. aOrigin[1] = aOrigin[0];
  736. aAngles[1] = aAngles[0];
  737. aFov[1] = aFov[0];
  738. // Parameter for interpolation
  739. t = 0.0f;
  740. // Seek forward to half a second from current event time and see if there
  741. // are any other set view events.
  742. pSearch = m_pSetViewEvent->GetNextTrueSubKey();
  743. while ( pSearch )
  744. {
  745. // Another sample not available
  746. float flSearchTime = atof( pSearch->GetName() );
  747. if ( flSearchTime > m_flLastCamSetViewTime + 0.5f )
  748. break;
  749. if ( pSearch->GetInt( "type", EVENTTYPE_INVALID ) == EVENTTYPE_CAMERA_SETVIEW )
  750. {
  751. // Found next sample within half a second - calc interpolation parameter & get data
  752. float flDiff = flSearchTime - m_flLastCamSetViewTime;
  753. Assert( flDiff > 0.0f );
  754. if ( flDiff > 0.0f )
  755. {
  756. t = clamp ( ( flCurTime - m_flLastCamSetViewTime ) / flDiff, 0.0f, 1.0f );
  757. // If the next set-view is a cut, we don't want to interpolate
  758. if ( pSearch->GetBool( "cut" ) )
  759. {
  760. const int iSrc = clamp( (int)( .5f + t ), 0, 1 ); // Round t to 0 or 1, so we set the camera to the current frame if t < 0.5f, and we set the camera to the 'cut'/next frame if t >= 0.5.
  761. aOrigin[2] = aOrigin[ iSrc ];
  762. aAngles[2] = aAngles[ iSrc ];
  763. aFov[2] = aFov[ iSrc ];
  764. }
  765. else
  766. {
  767. ReadSetViewEvent( pSearch, aOrigin[1], aAngles[1], aFov[1], &flAccel, &flSpeed, &flRotFilter );
  768. }
  769. }
  770. break;
  771. }
  772. pSearch = pSearch->GetNextTrueSubKey();
  773. }
  774. // Interpolate
  775. aOrigin[2] = Lerp( t, aOrigin[0], aOrigin[1] );
  776. aAngles[2] = Lerp( t, aAngles[0], aAngles[1] ); // NOTE: Calls QuaternionSlerp() internally
  777. aFov[2] = Lerp( t, aFov[0], aFov[1] );
  778. }
  779. // Setup current view
  780. SetViewParams_t params( flEventTime, &aOrigin[2], &aAngles[2], aFov[2], flAccel, flSpeed, flRotFilter );
  781. m_pPlaybackHandler->OnEvent_Camera_SetView( params );
  782. }
  783. IF_REPLAY_DBG( DebugRender() );
  784. }
  785. void CPerformanceController::DebugRender()
  786. {
  787. KeyValues *pIt = m_pDbgRoot->GetFirstTrueSubKey();
  788. Vector prevpos, pos;
  789. QAngle angles;
  790. float fov;
  791. bool bPrev = false;
  792. g_pDebugOverlay->ClearDeadOverlays();
  793. while ( pIt )
  794. {
  795. if ( pIt->GetInt( "type", EVENTTYPE_INVALID ) == EVENTTYPE_CAMERA_SETVIEW )
  796. {
  797. ReadSetViewEvent( pIt, pos, angles, fov, NULL, NULL, NULL );
  798. // Skip first view event since no previous
  799. if ( !bPrev )
  800. {
  801. bPrev = true;
  802. }
  803. else
  804. {
  805. const bool bCut = pIt->GetBool( "cut" );
  806. const int r = bCut ? 0 : 255;
  807. const int g = bCut ? 255 : 0;
  808. const int b = 0;
  809. Vector tickpos = pos + Vector(10,0,0);
  810. g_pDebugOverlay->AddLineOverlay( prevpos, pos, r, g, b, true, 0.0f );
  811. g_pDebugOverlay->AddLineOverlay( pos, tickpos, 0, 255, 255, true, 0.0f );
  812. }
  813. prevpos = pos;
  814. }
  815. pIt = pIt->GetNextTrueSubKey();
  816. }
  817. }
  818. //----------------------------------------------------------------------------------------
  819. void CPerformanceController::Think()
  820. {
  821. VPROF_BUDGET( "CReplayPerformancePlayer::Think", VPROF_BUDGETGROUP_REPLAY );
  822. CBaseThinker::Think();
  823. PlaybackThink();
  824. }
  825. float CPerformanceController::GetNextThinkTime() const
  826. {
  827. return 0.0f;
  828. }
  829. //----------------------------------------------------------------------------------------