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.

346 lines
11 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. //=======================================================================================//
  4. #include "cl_screenshotmanager.h"
  5. #include "convar.h"
  6. #include "replaysystem.h"
  7. #include "netmessages.h"
  8. #include "cl_replaymanager.h"
  9. #include "cl_sessionblockdownloader.h"
  10. #include "cl_recordingsession.h"
  11. #include "cl_renderqueue.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. //----------------------------------------------------------------------------------------
  15. extern ConVar replay_postdeathrecordtime;
  16. //----------------------------------------------------------------------------------------
  17. CON_COMMAND( save_replay, "Save a replay of the current life if possible." )
  18. {
  19. // Is the user running a listen server?
  20. if ( g_pEngineClient->IsListenServer() )
  21. {
  22. Replay_HudMsg( "#Replay_NoListenServer", "replay\\record_fail.wav", true );
  23. return;
  24. }
  25. // Replay enabled on the server?
  26. if ( !g_pReplay->IsReplayEnabled() )
  27. {
  28. Replay_HudMsg( "#Replay_NotEnabled", "replay\\record_fail.wav", true );
  29. return;
  30. }
  31. // Are we recording?
  32. if ( !g_pReplay->IsRecording() )
  33. {
  34. Replay_HudMsg( "#Replay_NotRecording", "replay\\record_fail.wav", true );
  35. return;
  36. }
  37. // Is replay disabled on the client?
  38. if ( g_pClientReplayContextInternal->IsClientSideReplayDisabled() )
  39. {
  40. Replay_HudMsg( "#Replay_ClientSideDisabled", NULL, true );
  41. return;
  42. }
  43. // Get the replay for the current life
  44. CReplay *pReplayForCurrentLife = CL_GetReplayManager()->m_pReplayThisLife;
  45. // Already saved this replay?
  46. if ( !pReplayForCurrentLife || pReplayForCurrentLife->m_bRequestedByUser || pReplayForCurrentLife->m_bSaved )
  47. {
  48. Replay_HudMsg( "#Replay_AlreadySaved", "replay\\record_fail.wav" );
  49. return;
  50. }
  51. // Take a screenshot and write it to disk if one hasn't been taken already
  52. if ( !pReplayForCurrentLife->GetScreenshotCount() )
  53. {
  54. CaptureScreenshotParams_t params;
  55. V_memset( &params, 0, sizeof( params ) );
  56. params.m_flDelay = 0.0f;
  57. params.m_bPrimary = true;
  58. CL_GetScreenshotManager()->CaptureScreenshot( params );
  59. }
  60. // Send a message to the server, regardless of whether the player is alive or dead, requesting
  61. // that a demo be written. Format a file name with the client's steam id and a timestamp
  62. // (gpGlobals->tickcount).
  63. CLC_SaveReplay msgSaveReplay;
  64. g_pEngineClient->GetNetChannel()->SendNetMsg( msgSaveReplay, true );
  65. // Get the session
  66. CClientRecordingSession *pSession = CL_CastSession( CL_GetRecordingSessionManager()->Find( pReplayForCurrentLife->m_hSession ) );
  67. if ( !pSession )
  68. {
  69. AssertMsg( 0, "Replay points to a non-existent session - should never happen!" );
  70. CL_GetErrorSystem()->AddErrorFromTokenName( "#Replay_ReplayBadSession" );
  71. return;
  72. }
  73. // Replay for current life is complete (ie, player is dead and replay is ready to be committed)
  74. if ( pReplayForCurrentLife->m_bComplete )
  75. {
  76. CL_GetReplayManager()->CommitPendingReplayAndBeginDownload();
  77. }
  78. else
  79. {
  80. // Mark the replay as requested by the user, so we can commit automatically as soon as
  81. // the replay is complete (ie when the player dies, etc.).
  82. pReplayForCurrentLife->m_bRequestedByUser = true;
  83. }
  84. // Cache replay pointer in owning session
  85. pSession->CacheReplay( pReplayForCurrentLife );
  86. // Make sure downloading is enabled
  87. pSession->EnsureDownloadingEnabled();
  88. // Add the new entry to the replay browser
  89. g_pClient->OnSaveReplay( pReplayForCurrentLife->GetHandle(), true );
  90. }
  91. //----------------------------------------------------------------------------------------
  92. CON_COMMAND( replay_add_fake_replays, "Adds a set of fake replays" )
  93. {
  94. if ( args.ArgC() < 2 )
  95. {
  96. DevMsg( "Use \'replay_add_fake_replays\' <num fake replays to add> <today only>\n" );
  97. return;
  98. }
  99. // bool bTodayOnly = args.ArgC() >= 3 && args[2][0] == '1';
  100. for ( int i = 0; i < atoi(args[1]); ++i )
  101. {
  102. // TODO:
  103. }
  104. }
  105. //----------------------------------------------------------------------------------------
  106. CON_COMMAND_F( replay_confirmquit, "Make sure all replays are rendered before quitting", FCVAR_HIDDEN | FCVAR_DONTRECORD )
  107. {
  108. // TODO: Check to see if any replays are downloading - warn user. If user wants to
  109. // quit anyway, make sure to set any blocks to not downloaded, save, and delete any
  110. // files that were only partially downloaded.
  111. // Unrendered replays? Display the quit confirmation dialog with the option to render all and quit
  112. if ( CL_GetReplayManager()->GetUnrenderedReplayCount() > 0 && g_pClient->OnConfirmQuit() )
  113. {
  114. // Play a sound.
  115. g_pClient->PlaySound( "replay\\confirmquit.wav" );
  116. }
  117. else
  118. {
  119. g_pEngine->Cbuf_AddText( "quit" );
  120. g_pEngine->Cbuf_AddText( "\n" );
  121. }
  122. }
  123. //----------------------------------------------------------------------------------------
  124. CON_COMMAND_F( replay_deleteclientreplays, "Deletes all replays from client replay history, as well as all files associated with each replay.", FCVAR_DONTRECORD )
  125. {
  126. CUtlVector< ReplayHandle_t > vecReplayHandles;
  127. FOR_EACH_REPLAY( i )
  128. {
  129. vecReplayHandles.AddToTail( GET_REPLAY_AT( i )->GetHandle() );
  130. }
  131. FOR_EACH_VEC( vecReplayHandles, i )
  132. {
  133. CL_GetReplayManager()->DeleteReplay( vecReplayHandles[ i ], true );
  134. }
  135. }
  136. //----------------------------------------------------------------------------------------
  137. CON_COMMAND_F( replay_removeclientreplay, "Remove the replay at the given index.", FCVAR_DONTRECORD )
  138. {
  139. if ( args.ArgC() != 2 )
  140. {
  141. Msg( "Not enough parameters.\n" );
  142. return;
  143. }
  144. CL_GetReplayManager()->DeleteReplay( atoi(args[ 1 ]), true );
  145. }
  146. //----------------------------------------------------------------------------------------
  147. CON_COMMAND_F( replay_printclientreplays, "Prints out all client replay info", FCVAR_DONTRECORD )
  148. {
  149. FOR_EACH_REPLAY( i )
  150. {
  151. const CReplay *pReplay = GET_REPLAY_AT( i );
  152. if ( !pReplay )
  153. continue;
  154. int nMonth, nDay, nYear;
  155. pReplay->m_RecordTime.GetDate( nDay, nMonth, nYear );
  156. int nHour, nMin, nSec;
  157. pReplay->m_RecordTime.GetTime( nHour, nMin, nSec );
  158. int nSpawnTick = pReplay->m_nSpawnTick;
  159. int nDeathTick = pReplay->m_nDeathTick;
  160. // TODO: All of this should go into a virtual function in CReplay, rather than some here and some in DumpGameSpecificData()
  161. char szTitle[MAX_REPLAY_TITLE_LENGTH];
  162. g_pVGuiLocalize->ConvertUnicodeToANSI( pReplay->m_wszTitle, szTitle, sizeof( szTitle ) );
  163. Msg( "replay %i: \"%s\"\n", i, szTitle );
  164. Msg( " handle: %i\n", pReplay->GetHandle() );
  165. Msg( " spawn/death tick: %i / %i\n", nSpawnTick, nDeathTick );
  166. Msg( " date: %i/%i/%i\n", nMonth, nDay, nYear );
  167. Msg( " time: %i:%i:%i\n", nHour, nMin, nSec );
  168. Msg( " map: %s\n", pReplay->m_szMapName );
  169. CClientRecordingSession *pSession = CL_CastSession( CL_GetRecordingSessionManager()->FindSession( pReplay->m_hSession ) );
  170. const char *pSessionName = pSession ? pSession->m_strName.Get() : NULL;
  171. Msg( " session name: %s\n", pSessionName ? pSessionName : "" );
  172. if ( pSession )
  173. {
  174. Msg( " last block downloaded: %i\n", pSession->GetGreatestConsecutiveBlockDownloaded() );
  175. Msg( " last block to download: %i\n", pSession->GetLastBlockToDownload() );
  176. }
  177. int nScreenshotCount = pReplay->GetScreenshotCount();
  178. Msg( "\n" );
  179. Msg( " # screenshots: %i\n", nScreenshotCount );
  180. Msg( " session handle: %i\n", (int)pReplay->m_hSession );
  181. for ( int i = 0; i < nScreenshotCount; ++i )
  182. {
  183. const CReplayScreenshot *pScreenshot = pReplay->GetScreenshot( i );
  184. Msg( " screenshot %i:\n", i );
  185. Msg( " dimensions: w=%i, h=%i\n", pScreenshot->m_nWidth, pScreenshot->m_nHeight );
  186. Msg( " base filename: %s\n", pScreenshot->m_szBaseFilename );
  187. }
  188. int nPerfCount = pReplay->GetPerformanceCount();
  189. Msg( "\n" );
  190. Msg( "# performances: %i\n", nPerfCount );
  191. for ( int i = 0; i < nPerfCount; ++i )
  192. {
  193. const CReplayPerformance *pCurPerformance = pReplay->GetPerformance( i );
  194. g_pVGuiLocalize->ConvertUnicodeToANSI( pCurPerformance->m_wszTitle, szTitle, sizeof( szTitle ) );
  195. Msg( " performance %i:\n", i );
  196. Msg( " title: %s\n", szTitle );
  197. Msg( " ticks: in=%i out=%i\n", pCurPerformance->m_nTickIn, pCurPerformance->m_nTickOut );
  198. Msg( " filename: %s\n", pCurPerformance->m_szBaseFilename );
  199. }
  200. Msg( "\n" );
  201. pReplay->DumpGameSpecificData();
  202. // Print replay status
  203. const char *pStatus;
  204. switch ( pReplay->m_nStatus )
  205. {
  206. case CReplay::REPLAYSTATUS_INVALID: pStatus = "invalid"; break;
  207. case CReplay::REPLAYSTATUS_DOWNLOADPHASE: pStatus = "download phase"; break;
  208. case CReplay::REPLAYSTATUS_READYTOCONVERT: pStatus = "ready to convert"; break;
  209. case CReplay::REPLAYSTATUS_RENDERING: pStatus = "rendering"; break;
  210. case CReplay::REPLAYSTATUS_RENDERED: pStatus = "rendered"; break;
  211. case CReplay::REPLAYSTATUS_ERROR: pStatus = "error"; break;
  212. default: pStatus = "";
  213. }
  214. Msg( " status: %s\n\n\n", pStatus );
  215. }
  216. }
  217. //----------------------------------------------------------------------------------------
  218. CON_COMMAND_F( replay_renderpause, "Pause Replay rendering.", FCVAR_DONTRECORD )
  219. {
  220. if ( !CL_GetMovieManager()->IsRendering() )
  221. return;
  222. if ( g_pReplayDemoPlayer->IsReplayPaused() )
  223. {
  224. Msg( "Replay rendering already paused.\n" );
  225. return;
  226. }
  227. // Pause playback
  228. g_pReplayDemoPlayer->PauseReplay();
  229. }
  230. //----------------------------------------------------------------------------------------
  231. CON_COMMAND_F( replay_renderunpause, "Unpause Replay rendering.", FCVAR_DONTRECORD )
  232. {
  233. if ( !CL_GetMovieManager()->IsRendering() )
  234. return;
  235. if ( !g_pReplayDemoPlayer->IsReplayPaused() )
  236. {
  237. Msg( "Replay rendering not paused.\n" );
  238. return;
  239. }
  240. // Unpause
  241. g_pReplayDemoPlayer->ResumeReplay();
  242. }
  243. //----------------------------------------------------------------------------------------
  244. CON_COMMAND_F( replay_printqueuedtakes, "Print a list of takes queued for rendering.", FCVAR_DONTRECORD )
  245. {
  246. const int nCount = CL_GetRenderQueue()->GetCount();
  247. if ( !nCount )
  248. {
  249. ConMsg( "No takes queued for render.\n" );
  250. return;
  251. }
  252. ConMsg( "Takes queued for render:\n" );
  253. ConMsg( " %65s%65s\n", "Replay Name", "Take Name" );
  254. for ( int i = 0; i < nCount; ++i )
  255. {
  256. ReplayHandle_t hReplay;
  257. int iPerf;
  258. CL_GetRenderQueue()->GetEntryData( i, &hReplay, &iPerf );
  259. const CReplay *pReplay = CL_GetReplayManager()->GetReplay( hReplay );
  260. if ( !pReplay )
  261. continue;
  262. char szTakeName[MAX_REPLAY_TITLE_LENGTH];
  263. if ( iPerf == -1 )
  264. {
  265. V_strcpy( szTakeName, "original" );
  266. }
  267. else
  268. {
  269. const CReplayPerformance *pPerformance = pReplay->GetPerformance( iPerf );
  270. if ( !pPerformance )
  271. continue;
  272. V_wcstostr( pPerformance->m_wszTitle, -1, szTakeName, sizeof( szTakeName ) );
  273. }
  274. char szReplayTitle[MAX_REPLAY_TITLE_LENGTH];
  275. V_wcstostr( pReplay->m_wszTitle, -1, szReplayTitle, sizeof( szReplayTitle ) );
  276. ConMsg( " %02i:%65s%65s\n", i, szReplayTitle, szTakeName );
  277. }
  278. }
  279. //----------------------------------------------------------------------------------------
  280. CON_COMMAND_F( replay_clearqueuedtakes, "Clear takes from render queue.", FCVAR_DONTRECORD )
  281. {
  282. CL_GetRenderQueue()->Clear();
  283. ConMsg( "Cleared.\n" );
  284. }
  285. //----------------------------------------------------------------------------------------