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.

511 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. //=======================================================================================//
  4. #if defined( REPLAY_ENABLED )
  5. #include "replaydemoplayer.h"
  6. #include "replay/ireplaymoviemanager.h"
  7. #include "replay/ireplayperformancemanager.h"
  8. #include "replay/ireplaymovierenderer.h"
  9. #include "replay/ireplayperformancecontroller.h"
  10. #include "replay/ireplaymanager.h"
  11. #include "replay/replay.h"
  12. #include "replay/replayutils.h"
  13. #include "replay/shared_defs.h"
  14. #include "replay/iclientreplay.h"
  15. #include "replay/performance.h"
  16. #include "replay_internal.h"
  17. #include "cmd.h"
  18. #include "KeyValues.h"
  19. #include "cdll_engine_int.h"
  20. #include "host.h"
  21. #include "fmtstr.h"
  22. #include "vgui_baseui_interface.h"
  23. #ifndef DEDICATED
  24. #include "screen.h"
  25. #endif
  26. // memdbgon must be the last include file in a .cpp file!!!
  27. #include "tier0/memdbgon.h"
  28. //----------------------------------------------------------------------------------------
  29. CReplayDemoPlayer s_ReplayDemoPlayer;
  30. IDemoPlayer *g_pReplayDemoPlayer = &s_ReplayDemoPlayer;
  31. //----------------------------------------------------------------------------------------
  32. ConVar replay_ignorereplayticks( "replay_ignorereplayticks", "0" );
  33. //----------------------------------------------------------------------------------------
  34. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CReplayDemoPlayer, IReplayDemoPlayer, INTERFACEVERSION_REPLAYDEMOPLAYER, s_ReplayDemoPlayer );
  35. //----------------------------------------------------------------------------------------
  36. CReplayDemoPlayer::CReplayDemoPlayer()
  37. : m_pMovie( NULL ),
  38. m_nCurReplayIndex( 0 ),
  39. m_flStartRenderTime( 0.0f ),
  40. m_bInStartPlayback( false ),
  41. m_bStopCommandEncountered( false ),
  42. m_bFullSignonStateReached( false )
  43. {
  44. }
  45. void CReplayDemoPlayer::ClearReplayList()
  46. {
  47. m_vecReplaysToPlay.PurgeAndDeleteElements();
  48. }
  49. void CReplayDemoPlayer::AddReplayToList( ReplayHandle_t hReplay, int iPerformance )
  50. {
  51. // Make sure the replay handle's OK
  52. CReplay *pReplay = g_pReplayManager->GetReplay( hReplay );
  53. if ( !pReplay )
  54. return;
  55. // Create new info
  56. PlaybackInfo_t *pNewPlaybackInfo = new PlaybackInfo_t();
  57. // Cache handle & performance
  58. pNewPlaybackInfo->m_hReplay = hReplay;
  59. pNewPlaybackInfo->m_iPerformance = iPerformance;
  60. // Figure out replay spawn/spawn+length ticks
  61. pNewPlaybackInfo->m_nStartTick = pReplay->m_nSpawnTick;
  62. pNewPlaybackInfo->m_nEndTick = -1;
  63. const int nLengthInTicks = TIME_TO_TICKS( pReplay->m_flLength );
  64. if ( nLengthInTicks > 0 )
  65. {
  66. pNewPlaybackInfo->m_nEndTick = pReplay->m_nSpawnTick + nLengthInTicks;
  67. }
  68. // If a performance was specified, override ticks as appropriate
  69. if ( iPerformance >= 0 )
  70. {
  71. // Get the performance from the replay
  72. const CReplayPerformance *pPerformance = pReplay->GetPerformance( iPerformance );
  73. if ( pPerformance->m_nTickIn >= 0 )
  74. {
  75. pNewPlaybackInfo->m_nStartTick = pPerformance->m_nTickIn;
  76. }
  77. if ( pPerformance->m_nTickOut >= 0 )
  78. {
  79. pNewPlaybackInfo->m_nEndTick = pPerformance->m_nTickOut;
  80. }
  81. }
  82. // Cache
  83. m_vecReplaysToPlay.AddToTail( pNewPlaybackInfo );
  84. }
  85. CReplay *CReplayDemoPlayer::GetCurrentReplay()
  86. {
  87. PlaybackInfo_t *pCurrentPlaybackInfo = GetCurrentPlaybackInfo();
  88. if ( !pCurrentPlaybackInfo )
  89. return NULL;
  90. return g_pReplayManager->GetReplay( pCurrentPlaybackInfo->m_hReplay );
  91. }
  92. const CReplay *CReplayDemoPlayer::GetCurrentReplay() const
  93. {
  94. return const_cast< CReplayDemoPlayer * >( this )->GetCurrentReplay();
  95. }
  96. CReplayPerformance *CReplayDemoPlayer::GetCurrentPerformance()
  97. {
  98. const PlaybackInfo_t *pCurrentPlaybackInfo = GetCurrentPlaybackInfo();
  99. if ( !pCurrentPlaybackInfo )
  100. return NULL;
  101. CReplay *pReplay = GetCurrentReplay();
  102. if ( !pReplay )
  103. return NULL;
  104. if ( pCurrentPlaybackInfo->m_iPerformance < 0 )
  105. return NULL;
  106. return pReplay->GetPerformance( pCurrentPlaybackInfo->m_iPerformance );
  107. }
  108. CReplayDemoPlayer::PlaybackInfo_t *CReplayDemoPlayer::GetCurrentPlaybackInfo()
  109. {
  110. if ( m_vecReplaysToPlay.Count() == 0 )
  111. return NULL;
  112. return m_vecReplaysToPlay[ m_nCurReplayIndex ];
  113. }
  114. const CReplayDemoPlayer::PlaybackInfo_t *CReplayDemoPlayer::GetCurrentPlaybackInfo() const
  115. {
  116. return const_cast< CReplayDemoPlayer * >( this )->GetCurrentPlaybackInfo();
  117. }
  118. void CReplayDemoPlayer::PauseReplay()
  119. {
  120. PausePlayback( -1.0f );
  121. }
  122. bool CReplayDemoPlayer::IsReplayPaused()
  123. {
  124. return IsPlaybackPaused();
  125. }
  126. void CReplayDemoPlayer::ResumeReplay()
  127. {
  128. ResumePlayback();
  129. }
  130. void CReplayDemoPlayer::OnSignonStateFull()
  131. {
  132. m_bFullSignonStateReached = true;
  133. }
  134. netpacket_t *CReplayDemoPlayer::ReadPacket( void )
  135. {
  136. if ( !m_bStopCommandEncountered )
  137. {
  138. return BaseClass::ReadPacket();
  139. }
  140. return NULL;
  141. }
  142. float CReplayDemoPlayer::GetPlaybackTimeScale()
  143. {
  144. if ( g_pReplayPerformanceController )
  145. {
  146. return g_pReplayPerformanceController->GetPlaybackTimeScale();
  147. }
  148. return 1.0f;
  149. }
  150. void CReplayDemoPlayer::OnStopCommand()
  151. {
  152. if ( m_bStopCommandEncountered )
  153. return;
  154. m_bStopCommandEncountered = true;
  155. if ( !g_pClientReplay->OnEndOfReplayReached() )
  156. {
  157. BaseClass::OnStopCommand();
  158. }
  159. }
  160. bool CReplayDemoPlayer::StartPlayback( const char *pFilename, bool bAsTimeDemo )
  161. {
  162. if ( !Replay_IsSupportedModAndPlatform() )
  163. return false;
  164. CInStartPlaybackGuard InStartPlaybackGuard( m_bInStartPlayback );
  165. const PlaybackInfo_t *pPlaybackInfo = GetCurrentPlaybackInfo();
  166. if ( !pPlaybackInfo )
  167. return false;
  168. // always display progress bar to ensure load screen background is redraw
  169. EngineVGui()->EnabledProgressBarForNextLoad();
  170. if ( !BaseClass::StartPlayback( pFilename, bAsTimeDemo ) )
  171. {
  172. DisplayFailedToPlayMsg( pPlaybackInfo->m_iPerformance );
  173. return false;
  174. }
  175. CReplay *pReplay = GetCurrentReplay();
  176. if ( !pReplay )
  177. return false;
  178. // Set this flag so we can detect whether the replay made it all the way to full signon state,
  179. // otherwise we'll display a message StopPlayback() is called.
  180. m_bFullSignonStateReached = false;
  181. // Reset so when we see a dem_stop we will handle it
  182. m_bStopCommandEncountered = false;
  183. // Reset skip to tick now
  184. m_nSkipToTick = -1;
  185. // Reset the rendering cancelled flag now
  186. g_pReplayMovieManager->ClearRenderCancelledFlag();
  187. // Setup playback timeframe
  188. if ( pPlaybackInfo->m_nStartTick >= 0 && !replay_ignorereplayticks.GetBool() )
  189. {
  190. SkipToTick( pPlaybackInfo->m_nStartTick, false, false );
  191. }
  192. if ( pPlaybackInfo->m_nEndTick >= 0 && !replay_ignorereplayticks.GetBool() )
  193. {
  194. SetEndTick( pPlaybackInfo->m_nEndTick );
  195. }
  196. if ( g_pReplayMovieManager->IsRendering() )
  197. {
  198. #ifdef USE_WEBM_FOR_REPLAY
  199. const char *pExtension = ".webm";
  200. #else
  201. const char *pExtension = ".mov";
  202. #endif
  203. // Start recording the movie
  204. char szIdealFilename[ MAX_OSPATH ];
  205. V_FileBase( pFilename, szIdealFilename, sizeof( szIdealFilename ) );
  206. V_strcat( szIdealFilename, va( "_%i", pReplay->m_nSpawnTick ), sizeof( szIdealFilename ) );
  207. V_SetExtension( szIdealFilename, pExtension, sizeof( szIdealFilename ) );
  208. char szRenderPath[ MAX_OSPATH ];
  209. V_snprintf( szRenderPath, sizeof( szRenderPath ), "%s%c%s%c%s%c%s",
  210. com_gamedir, CORRECT_PATH_SEPARATOR, SUBDIR_REPLAY, CORRECT_PATH_SEPARATOR,
  211. SUBDIR_CLIENT, CORRECT_PATH_SEPARATOR, SUBDIR_RENDERED
  212. );
  213. char szActualFilename[ MAX_OSPATH ];
  214. Replay_GetFirstAvailableFilename( szActualFilename, sizeof( szActualFilename ), szIdealFilename,
  215. pExtension, szRenderPath, 0 );
  216. // Create an entry in the movie manager & save to disk
  217. m_pMovie = g_pReplayMovieManager->CreateAndAddMovie( pReplay->GetHandle() );
  218. m_pMovie->SetMovieFilename( szActualFilename );
  219. wchar_t wszMovieTitle[MAX_REPLAY_TITLE_LENGTH] = L"";
  220. if ( pPlaybackInfo->m_iPerformance < 0 )
  221. {
  222. g_pReplayMovieManager->GetCachedMovieTitleAndClear( wszMovieTitle, MAX_REPLAY_TITLE_LENGTH );
  223. }
  224. else
  225. {
  226. const CReplayPerformance *pPerformance = pReplay->GetPerformance( pPlaybackInfo->m_iPerformance ); AssertMsg( pPerformance, "Performance should always be valid!" );
  227. if ( pPerformance )
  228. {
  229. V_wcsncpy( wszMovieTitle, pPerformance->m_wszTitle, sizeof( wszMovieTitle ) );
  230. }
  231. }
  232. m_pMovie->SetMovieTitle( wszMovieTitle );
  233. g_pReplayMovieManager->SetPendingMovie( m_pMovie );
  234. g_pReplayMovieManager->FlagMovieForFlush( m_pMovie, true );
  235. // Setup the start render time
  236. m_flStartRenderTime = realtime;
  237. }
  238. else
  239. {
  240. m_pMovie = NULL;
  241. }
  242. return true;
  243. }
  244. void CReplayDemoPlayer::PlayNextReplay()
  245. {
  246. const PlaybackInfo_t *pPlaybackInfo = GetCurrentPlaybackInfo();
  247. if ( !pPlaybackInfo )
  248. return;
  249. CReplay *pReplay = g_pReplayManager->GetReplay( pPlaybackInfo->m_hReplay );
  250. if ( !pReplay )
  251. return;
  252. Assert ( pReplay->m_nStatus != CReplay::REPLAYSTATUS_DOWNLOADPHASE );
  253. // Reconstruct now if necessary
  254. g_pClientReplayContext->ReconstructReplayIfNecessary( pReplay );
  255. // Open the demo file so we can read the start tick
  256. const char *pFilename = pReplay->m_strReconstructedFilename.Get();
  257. if ( !g_pFullFileSystem->FileExists( pFilename ) )
  258. {
  259. Warning( "\n** File %s does not exist!\n\n", pFilename );
  260. DisplayFailedToPlayMsg( pPlaybackInfo->m_iPerformance );
  261. return;
  262. }
  263. // Construct a con-command to play the demo, starting at the spawn tick.
  264. // Play the replay using the 'playreplay' command - pass in the performance as well, -1
  265. // meaning play without a performance.
  266. const char *pCmd = "replay_hidebrowser\ngameui_hide\nprogress_enable\n";
  267. // Execute the command
  268. Cbuf_AddText( pCmd );
  269. Cbuf_Execute();
  270. // Use the replay demo player
  271. extern IDemoPlayer *g_pReplayDemoPlayer;
  272. demoplayer = g_pReplayDemoPlayer;
  273. // Open the demo file
  274. if ( demoplayer->StartPlayback( pFilename, false ) )
  275. {
  276. // Remove extension
  277. char szBasename[ MAX_OSPATH ];
  278. V_StripExtension( pFilename, szBasename, sizeof( szBasename ) );
  279. extern IBaseClientDLL *g_ClientDLL;
  280. g_ClientDLL->OnDemoPlaybackStart( szBasename );
  281. }
  282. else
  283. {
  284. SCR_EndLoadingPlaque();
  285. }
  286. }
  287. void CReplayDemoPlayer::PlayReplay( ReplayHandle_t hReplay, int iPerformance )
  288. {
  289. // Cache the replay (this function will only ever cache one)
  290. s_ReplayDemoPlayer.ClearReplayList();
  291. s_ReplayDemoPlayer.AddReplayToList( hReplay, iPerformance );
  292. s_ReplayDemoPlayer.PlayNextReplay();
  293. }
  294. void CReplayDemoPlayer::OnLastDemoInLoopPlayed()
  295. {
  296. g_pReplayMovieManager->CompleteRender( true, true );
  297. }
  298. float CReplayDemoPlayer::CalcMovieLength() const
  299. {
  300. const PlaybackInfo_t *pPlaybackInfo = GetCurrentPlaybackInfo();
  301. if ( !pPlaybackInfo )
  302. return 0.0f;
  303. const CReplay *pReplay = GetCurrentReplay();
  304. if ( !pReplay )
  305. return 0.0f;
  306. const int nStartTick = pPlaybackInfo->m_nStartTick >= 0 ? pPlaybackInfo->m_nStartTick : pReplay->m_nSpawnTick;
  307. const int nEndTick = pPlaybackInfo->m_nEndTick >= 0 ? pPlaybackInfo->m_nEndTick : ( pReplay->m_nSpawnTick + TIME_TO_TICKS( pReplay->m_flLength ) );
  308. const bool bInvalidStartTick = nStartTick < 0;
  309. const bool bInvalidEndTick = nEndTick < 0;
  310. if ( bInvalidEndTick )
  311. {
  312. if ( !bInvalidStartTick )
  313. {
  314. // Valid start tick, invalid end tick
  315. return TICKS_TO_TIME( nStartTick ) + pReplay->m_flLength;
  316. }
  317. }
  318. else // Valid end tick.
  319. {
  320. if ( !bInvalidStartTick )
  321. {
  322. // Valid start tick, valid end tick
  323. return TICKS_TO_TIME( nEndTick - nStartTick );
  324. }
  325. }
  326. // Failed to calculate length
  327. return 0.0f;
  328. }
  329. void CReplayDemoPlayer::StopPlayback()
  330. {
  331. if ( !IsPlayingBack() )
  332. return;
  333. BaseClass::StopPlayback();
  334. if ( m_bInStartPlayback )
  335. return;
  336. bool bDoneWithBatch = m_nCurReplayIndex >= m_vecReplaysToPlay.Count() - 1;
  337. bool bRenderCancelled = g_pReplayMovieManager->RenderingCancelled();
  338. if ( g_pReplayMovieManager->IsRendering() )
  339. {
  340. // Update the replay's state
  341. CReplay *pReplay = GetCurrentReplay();
  342. if ( !pReplay )
  343. return;
  344. pReplay->m_bRendered = true; // We have rendered this replay at least once
  345. // Save replay
  346. g_pReplayManager->FlagReplayForFlush( pReplay, false );
  347. // Update the movie's state - the render succeeded
  348. m_pMovie->SetIsRendered( true );
  349. // Compute the time it took to render
  350. m_pMovie->SetRenderTime( MAX( 0, realtime - m_flStartRenderTime ) );
  351. // Sets the recorded date & time of the movie
  352. m_pMovie->CaptureRecordTime();
  353. // Get movie length
  354. m_pMovie->SetLength( CalcMovieLength() );
  355. // Save movie
  356. g_pReplayMovieManager->FlagMovieForFlush( m_pMovie, true );
  357. // Kill the renderer, show the browser if we're done rendering all replays
  358. g_pReplayMovieManager->CompleteRender( true, bDoneWithBatch );
  359. }
  360. else if ( !bRenderCancelled ) // Without this check, batch rendering will continue to try and render after cancel
  361. {
  362. CReplay *pReplay = GetCurrentReplay();
  363. if ( !pReplay )
  364. return;
  365. // Get the 'saved' performance from the performance controller, since the performance we initiated playback
  366. // with may not be the one we want to select in the replay browser. The user may have save as a new performance,
  367. // in which case we'll want to highlight that one.
  368. CReplayPerformance *pSavedPerformance = g_pReplayPerformanceController->GetSavedPerformance();
  369. // Get the index - FindPerformance() will set the output index to -1 if it can't find the performance
  370. int iHighlightPerformance;
  371. pReplay->FindPerformance( pSavedPerformance, iHighlightPerformance );
  372. // Notify UI that playback is complete
  373. g_pClientReplay->OnPlaybackComplete( pReplay->GetHandle(), iHighlightPerformance );
  374. // Hide the replay performance editor
  375. g_pClientReplay->HidePerformanceEditor();
  376. // End playback/recording as needed
  377. g_pReplayPerformanceController->Stop();
  378. }
  379. // Get the playback info before we incremeent the current replay
  380. const PlaybackInfo_t *pPlaybackInfo = GetCurrentPlaybackInfo();
  381. // Play the next replay, if one was queued
  382. ++m_nCurReplayIndex;
  383. if ( !bDoneWithBatch && !bRenderCancelled )
  384. {
  385. g_pReplayMovieManager->RenderNextMovie();
  386. }
  387. else
  388. {
  389. m_nCurReplayIndex = 0;
  390. m_vecReplaysToPlay.PurgeAndDeleteElements();
  391. if ( !m_bFullSignonStateReached )
  392. {
  393. DisplayFailedToPlayMsg( pPlaybackInfo ? pPlaybackInfo->m_iPerformance : -1 );
  394. }
  395. }
  396. }
  397. bool CReplayDemoPlayer::ShouldLoopDemos()
  398. {
  399. return false;
  400. }
  401. void CReplayDemoPlayer::DisplayFailedToPlayMsg( int iPerformance )
  402. {
  403. g_pClientReplay->DisplayReplayMessage(
  404. iPerformance < 0 ? "#Replay_Err_User_FailedToPlayReplay" : "#Replay_Err_User_FailedToPlayTake",
  405. false, true, NULL
  406. );
  407. }
  408. //----------------------------------------------------------------------------------------
  409. #endif // #if defined( REPLAY_ENABLED )