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.

421 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. //=======================================================================================//
  4. #include "cl_replaycontext.h"
  5. #include "replaysystem.h"
  6. #include "replay/iclientreplay.h"
  7. #include "replay/ireplaymovierenderer.h"
  8. #include "replay/shared_defs.h"
  9. #include "cl_replaymanager.h"
  10. #include "replay_dbg.h"
  11. #include "baserecordingsessionmanager.h"
  12. #include "baserecordingsessionblockmanager.h"
  13. #include "cl_replaymoviemanager.h"
  14. #include "cl_screenshotmanager.h"
  15. #include "cl_performancemanager.h"
  16. #include "cl_sessionblockdownloader.h"
  17. #include "cl_downloader.h"
  18. #include "cl_recordingsession.h"
  19. #include "cl_recordingsessionblock.h"
  20. #include "cl_renderqueue.h"
  21. #include "replay_reconstructor.h"
  22. #include "globalvars_base.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include "tier0/memdbgon.h"
  25. //----------------------------------------------------------------------------------------
  26. CClientReplayContext::CClientReplayContext()
  27. : m_pReplayManager( NULL ),
  28. m_pScreenshotManager( NULL ),
  29. m_pMovieRenderer( NULL ),
  30. m_pMovieManager( NULL ),
  31. m_pPerformanceManager( NULL ),
  32. m_pPerformanceController( NULL ),
  33. m_pTestDownloader( NULL ),
  34. m_pRenderQueue( NULL ),
  35. m_bClientSideReplayDisabled( false )
  36. {
  37. }
  38. CClientReplayContext::~CClientReplayContext()
  39. {
  40. delete m_pSessionBlockDownloader;
  41. delete m_pReplayManager;
  42. delete m_pScreenshotManager;
  43. delete m_pMovieManager;
  44. delete m_pPerformanceManager;
  45. delete m_pShared;
  46. delete m_pTestDownloader;
  47. }
  48. bool CClientReplayContext::Init( CreateInterfaceFn fnFactory )
  49. {
  50. m_pShared = new CSharedReplayContext( this );
  51. m_pShared->m_strSubDir = SUBDIR_CLIENT;
  52. m_pShared->m_pRecordingSessionManager = new CClientRecordingSessionManager( this );
  53. m_pShared->m_pRecordingSessionBlockManager = new CClientRecordingSessionBlockManager( this );
  54. m_pShared->m_pErrorSystem = new CErrorSystem( this );
  55. if ( !m_pShared->Init( fnFactory ) )
  56. return false;
  57. m_pPerformanceManager = new CReplayPerformanceManager();
  58. m_pPerformanceManager->Init();
  59. m_pReplayManager = new CReplayManager();
  60. m_pReplayManager->Init( fnFactory );
  61. m_pScreenshotManager = new CScreenshotManager();
  62. m_pScreenshotManager->Init();
  63. m_pMovieManager = new CReplayMovieManager();
  64. m_pMovieManager->Init();
  65. m_pRenderQueue = new CRenderQueue();
  66. if ( !m_pRenderQueue )
  67. return false;
  68. m_pSessionBlockDownloader = new CSessionBlockDownloader();
  69. if ( !m_pSessionBlockDownloader )
  70. return false;
  71. m_pPerformanceController = new CPerformanceController();
  72. // Cleanup any unneeded block data from disk - cleanup is done on the fly, but this will clean up
  73. // blocks from the olden days, when block data was not cleaned up properly.
  74. CleanupUnneededBlocks();
  75. return true;
  76. }
  77. void CClientReplayContext::Shutdown()
  78. {
  79. // NOTE: Must come first, as any existing downloads are aborted and may cause status
  80. // changes in replays, etc, which will need to be saved in CReplayManager::Shutdown(), etc.
  81. m_pSessionBlockDownloader->Shutdown();
  82. m_pShared->Shutdown();
  83. m_pReplayManager->Shutdown();
  84. m_pMovieManager->Shutdown();
  85. }
  86. void CClientReplayContext::DebugThink()
  87. {
  88. if ( !replay_debug.GetBool() )
  89. return;
  90. int iLine = 15;
  91. // Recording session in progress
  92. CClientRecordingSession *pRecordingSession = CL_GetRecordingSessionInProgress();
  93. if ( pRecordingSession )
  94. {
  95. g_pEngineClient->Con_NPrintf( iLine++, "SESSION IN PROGRESS:" );
  96. g_pEngineClient->Con_NPrintf( iLine++, " BLOCKS: %i", pRecordingSession->GetNumBlocks() );
  97. g_pEngineClient->Con_NPrintf( iLine++, " NAME: %s", pRecordingSession->m_strName.Get() );
  98. g_pEngineClient->Con_NPrintf( iLine++, " URL: %s", pRecordingSession->m_strBaseDownloadURL.Get() );
  99. g_pEngineClient->Con_NPrintf( iLine++, " LAST CONSECUTIVE BLOCK DOWNLOADED: %i", pRecordingSession->GetGreatestConsecutiveBlockDownloaded() );
  100. g_pEngineClient->Con_NPrintf( iLine++, " LAST BLOCK TO DOWNLOAD: %i", pRecordingSession->GetLastBlockToDownload() );
  101. }
  102. else
  103. {
  104. g_pEngineClient->Con_NPrintf( iLine++, "NO SESSION IN PROGRESS" );
  105. }
  106. iLine++;
  107. // Server state
  108. CClientRecordingSessionManager::ServerRecordingState_t *pServerState = &CL_GetRecordingSessionManager()->m_ServerRecordingState;
  109. g_pEngineClient->Con_NPrintf( iLine++, "SERVER STATE:" );
  110. g_pEngineClient->Con_NPrintf( iLine++, " NAME: %s\n", pServerState->m_strSessionName.Get() );
  111. g_pEngineClient->Con_NPrintf( iLine++, " DUMP INTERVAL: %i\n", pServerState->m_nDumpInterval );
  112. g_pEngineClient->Con_NPrintf( iLine++, " CURRENT BLOCK: %i\n", pServerState->m_nCurrentBlock );
  113. }
  114. void CClientReplayContext::Think()
  115. {
  116. DebugThink();
  117. if ( m_pTestDownloader )
  118. {
  119. m_pTestDownloader->Think();
  120. if ( m_pTestDownloader->IsDone() )
  121. {
  122. delete m_pTestDownloader;
  123. m_pTestDownloader = NULL;
  124. }
  125. }
  126. if ( !g_pReplay->IsReplayEnabled() )
  127. return;
  128. m_pShared->Think();
  129. }
  130. CReplay *CClientReplayContext::GetReplay( ReplayHandle_t hReplay )
  131. {
  132. return m_pReplayManager->GetReplay( hReplay );
  133. }
  134. IReplayManager *CClientReplayContext::GetReplayManager()
  135. {
  136. return m_pReplayManager;
  137. }
  138. IReplayScreenshotManager *CClientReplayContext::GetScreenshotManager()
  139. {
  140. return m_pScreenshotManager;
  141. }
  142. IReplayPerformanceManager *CClientReplayContext::GetPerformanceManager()
  143. {
  144. return m_pPerformanceManager;
  145. }
  146. IReplayPerformanceController *CClientReplayContext::GetPerformanceController()
  147. {
  148. return m_pPerformanceController;
  149. }
  150. IReplayRenderQueue *CClientReplayContext::GetRenderQueue()
  151. {
  152. return m_pRenderQueue;
  153. }
  154. void CClientReplayContext::SetMovieRenderer( IReplayMovieRenderer *pMovieRenderer )
  155. {
  156. m_pMovieRenderer = pMovieRenderer;
  157. }
  158. IReplayMovieRenderer *CClientReplayContext::GetMovieRenderer()
  159. {
  160. return m_pMovieRenderer;
  161. }
  162. IReplayMovieManager *CClientReplayContext::GetMovieManager()
  163. {
  164. return m_pMovieManager;
  165. }
  166. void CClientReplayContext::TestDownloader( const char *pURL )
  167. {
  168. // Don't overwrite existing test
  169. if ( m_pTestDownloader )
  170. return;
  171. // Download the file
  172. m_pTestDownloader = new CHttpDownloader();
  173. m_pTestDownloader->BeginDownload( pURL, NULL );
  174. }
  175. void CClientReplayContext::OnSignonStateFull()
  176. {
  177. // Notify the demo player that we've reached full signon state
  178. if ( g_pEngineClient->IsPlayingReplayDemo() )
  179. {
  180. g_pReplayDemoPlayer->OnSignonStateFull();
  181. }
  182. // Play a performance? This will play a performance from the beginning, if we're loading
  183. // one (ie the 'watch' button in the details panel of the replay browser), or will continue
  184. // playback if the user rewound while watching or editing a performance.
  185. CL_GetPerformanceController()->OnSignonStateFull();
  186. // If we're rendering, display the viewport
  187. if ( CL_GetMovieManager()->IsRendering() )
  188. {
  189. extern IClientReplay *g_pClient;
  190. g_pClient->OnRenderStart();
  191. // Prepare audio system for recording.
  192. g_pEngineClient->InitSoundRecord();
  193. // Init renderer
  194. IReplayMovie *pMovie = CL_GetMovieManager()->GetPendingMovie();
  195. if ( !m_pMovieRenderer->SetupRenderer( CL_GetMovieManager()->GetRenderMovieSettings(), pMovie ) )
  196. {
  197. Warning( "Render failed!\n" );
  198. CL_GetMovieManager()->CancelRender();
  199. }
  200. }
  201. // If we're not rendering and are playing back a replay, initialize the performance editor -
  202. // won't actually show until the user presses space, if they do at all.
  203. else if ( g_pEngineClient->IsPlayingReplayDemo() )
  204. {
  205. const CReplay *pReplay = g_pReplayDemoPlayer->GetCurrentReplay();
  206. if ( pReplay )
  207. {
  208. g_pClient->InitPerformanceEditor( pReplay->GetHandle() );
  209. }
  210. else
  211. {
  212. AssertMsg( 0, "Replay should exist here!" );
  213. Warning( "No current replay in demo player!\n" );
  214. }
  215. }
  216. }
  217. void CClientReplayContext::OnClientSideDisconnect()
  218. {
  219. if ( !g_pEngine->IsSupportedModAndPlatform() )
  220. return;
  221. // Reset replay_recording or we'll continue to think we're recording
  222. extern ConVar replay_recording;
  223. replay_recording.SetValue( 0 );
  224. if ( !g_pEngineClient->IsPlayingReplayDemo() )
  225. {
  226. // Saves dangling replay, if there is one, clears out everything
  227. // NOTE: We need to let the replay manager deal before we end the session, otherwise the
  228. // state of the session will be cleared.
  229. m_pReplayManager->OnClientSideDisconnect();
  230. // Mark the session as no longer recording.
  231. CClientRecordingSession *pSession = CL_GetRecordingSessionInProgress();
  232. if ( pSession )
  233. {
  234. pSession->OnStopRecording();
  235. }
  236. // Sets recording flag to false in session in progress, clears session in progress,
  237. // and clears server state
  238. CL_GetRecordingSessionManager()->OnSessionEnd();
  239. }
  240. }
  241. void CClientReplayContext::PlayReplay( ReplayHandle_t hReplay, int iPerformance, bool bPlaySound )
  242. {
  243. CReplay *pReplay = m_pReplayManager->GetReplay( hReplay );
  244. if ( !pReplay )
  245. return;
  246. if ( !ReconstructReplayIfNecessary( pReplay ) )
  247. {
  248. Replay_MsgBox( iPerformance < 0 ? "#Replay_Err_User_FailedToPlayReplay" : "#Replay_Err_User_FailedToPlayTake" );
  249. return;
  250. }
  251. // Play a sound?
  252. if ( bPlaySound )
  253. {
  254. g_pClient->PlaySound( iPerformance >= 0 ? "replay\\playperformance.wav" : "replay\\playoriginalreplay.wav" );
  255. }
  256. // Play the replay!
  257. g_pReplayDemoPlayer->PlayReplay( hReplay, iPerformance );
  258. }
  259. bool CClientReplayContext::ReconstructReplayIfNecessary( CReplay *pReplay )
  260. {
  261. // If reconstruction hasn't happened yet, try to reconstruct
  262. extern ConVar replay_forcereconstruct;
  263. if ( !pReplay->HasReconstructedReplay() || replay_forcereconstruct.GetBool() )
  264. {
  265. if ( !Replay_Reconstruct( pReplay ) )
  266. {
  267. CL_GetErrorSystem()->AddErrorFromTokenName( "#Replay_Err_Reconstruction_Fail" );
  268. return false;
  269. }
  270. }
  271. return true;
  272. }
  273. void CClientReplayContext::OnPlayerSpawn()
  274. {
  275. DBG( "OnPlayerSpawn()\n" );
  276. m_pReplayManager->AttemptToSetupNewReplay();
  277. }
  278. void CClientReplayContext::OnPlayerClassChanged()
  279. {
  280. DBG( "OnPlayerClassChanged()\n" );
  281. m_pReplayManager->CompletePendingReplay();
  282. }
  283. void CClientReplayContext::GetPlaybackTimes( float &flOutTime, float &flOutLength, const CReplay *pReplay, const CReplayPerformance *pPerformance )
  284. {
  285. flOutTime = 0.0f;
  286. flOutLength = 0.0f;
  287. // Get server start record tick
  288. const int nServerRecordStartTick = CL_GetRecordingSessionManager()->GetServerStartTickForSession( pReplay->m_hSession );
  289. // Don't let it be -1. Take performance in tick into account.
  290. int nStartTick = MAX( 0, pReplay->m_nSpawnTick );
  291. if ( pPerformance && pPerformance->m_nTickIn >= 0 )
  292. {
  293. nStartTick = pPerformance->m_nTickIn;
  294. }
  295. // Calculate length
  296. const int nReplayEndTick = pReplay->m_nSpawnTick + g_pEngine->TimeToTicks( pReplay->m_flLength );
  297. const int nEndTick = ( pPerformance && pPerformance->m_nTickOut > 0 ) ? pPerformance->m_nTickOut : nReplayEndTick;
  298. flOutLength = pPerformance ? g_pEngine->TicksToTime( nEndTick - nStartTick ) : pReplay->m_flLength;
  299. // Calculate current time
  300. const int nCurTick = MAX( g_pEngineClient->GetClientGlobalVars()->tickcount - nStartTick - nServerRecordStartTick, 0 );
  301. flOutTime = MIN( g_pEngine->TicksToTime( nCurTick ), flOutLength );
  302. }
  303. uint64 CClientReplayContext::GetServerSessionId( ReplayHandle_t hReplay )
  304. {
  305. CReplay *pReplay = GetReplay( hReplay );
  306. if ( !pReplay )
  307. return 0;
  308. CClientRecordingSession *pSession = CL_CastSession( CL_GetRecordingSessionManager()->FindSession( pReplay->m_hSession ) );
  309. if ( !pSession )
  310. return 0;
  311. return pSession->GetServerSessionID();
  312. }
  313. void CClientReplayContext::CleanupUnneededBlocks()
  314. {
  315. CL_GetRecordingSessionManager()->CleanupUnneededBlocks();
  316. }
  317. void CClientReplayContext::ReportErrorsToUser( wchar_t *pErrorText )
  318. {
  319. // Display a message now
  320. // Replay_MsgBox( pErrorText );
  321. if ( !pErrorText || pErrorText[0] == L'\0' )
  322. return;
  323. const int nErrorLen = wcslen( pErrorText );
  324. static char s_szError[1024];
  325. wcstombs( s_szError, pErrorText, MIN( 1024, nErrorLen ) );
  326. Warning( "Replay error system: %s\n", s_szError );
  327. }
  328. void CClientReplayContext::DisableReplayOnClient( bool bDisable )
  329. {
  330. if ( m_bClientSideReplayDisabled == bDisable )
  331. return;
  332. m_bClientSideReplayDisabled = bDisable;
  333. // Display a message to the user
  334. Replay_HudMsg( bDisable ? "#Replay_ClientSideDisabled" : "#Replay_ClientSideEnabled", NULL, true );
  335. }
  336. //----------------------------------------------------------------------------------------
  337. CClientRecordingSessionManager *CL_GetRecordingSessionManager()
  338. {
  339. return static_cast< CClientRecordingSessionManager * >( g_pClientReplayContextInternal->GetRecordingSessionManager() );
  340. }
  341. CClientRecordingSession *CL_GetRecordingSessionInProgress()
  342. {
  343. return CL_CastSession( CL_GetRecordingSessionManager()->GetRecordingSessionInProgress() );
  344. }
  345. //----------------------------------------------------------------------------------------