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.

374 lines
9.7 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. //=======================================================================================//
  4. #include "replay/replay.h"
  5. #include "replay/iclientreplaycontext.h"
  6. #include "replay/ireplaymanager.h"
  7. #include "replay/replayutils.h"
  8. #include "replay/screenshot.h"
  9. #include "replay/shared_defs.h"
  10. #include "replay/ireplayscreenshotmanager.h"
  11. #include "replay/ireplayperformancemanager.h"
  12. #include "replay/performance.h"
  13. #include "KeyValues.h"
  14. #include "filesystem.h"
  15. #include "vgui/ILocalize.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. //----------------------------------------------------------------------------------------
  19. extern IClientReplayContext *g_pClientReplayContext;
  20. extern vgui::ILocalize *g_pVGuiLocalize;
  21. //----------------------------------------------------------------------------------------
  22. CReplay::CReplay()
  23. : m_pDownloadEventHandler( NULL ),
  24. m_pUserData( NULL ),
  25. m_bComplete( false ),
  26. m_bRequestedByUser( false ),
  27. m_bSaved( false ),
  28. m_bRendered( false ),
  29. m_bDirty( false ),
  30. m_bSavedDuringThisSession( true ),
  31. m_flLength( 0 ),
  32. m_nPlayerSlot( -1 ),
  33. m_nSpawnTick( -1 ),
  34. m_nDeathTick( -1 ),
  35. m_iMaxSessionBlockRequired( 0 ),
  36. m_nStatus( REPLAYSTATUS_INVALID ),
  37. m_hSession( REPLAY_HANDLE_INVALID ),
  38. m_pFileURL( NULL ),
  39. m_nPostDeathRecordTime( 0 ),
  40. m_flStartTime( 0.0f ),
  41. m_flNextUpdateTime( 0.0f )
  42. {
  43. m_wszTitle[0] = L'\0';
  44. m_szMapName[0] = 0;
  45. }
  46. bool CReplay::IsDownloaded() const
  47. {
  48. return m_nStatus == REPLAYSTATUS_READYTOCONVERT;
  49. }
  50. const CReplayPerformance *CReplay::GetPerformance( int i ) const
  51. {
  52. return const_cast< CReplay * >( this )->GetPerformance( i );
  53. }
  54. CReplayPerformance *CReplay::GetPerformance( int i )
  55. {
  56. if ( i < 0 || i >= m_vecPerformances.Count() )
  57. return NULL;
  58. return m_vecPerformances[ i ];
  59. }
  60. bool CReplay::FindPerformance( CReplayPerformance *pPerformance, int &iResult )
  61. {
  62. const int it = m_vecPerformances.Find( pPerformance );
  63. if ( it == m_vecPerformances.InvalidIndex() )
  64. {
  65. iResult = -1;
  66. return false;
  67. }
  68. iResult = it;
  69. return true;
  70. }
  71. CReplayPerformance *CReplay::GetPerformanceWithTitle( const wchar_t *pTitle )
  72. {
  73. FOR_EACH_VEC( m_vecPerformances, i )
  74. {
  75. CReplayPerformance *pCurPerformance = m_vecPerformances[ i ];
  76. if ( !V_wcscmp( pTitle, pCurPerformance->m_wszTitle ) )
  77. {
  78. return pCurPerformance;
  79. }
  80. }
  81. return NULL;
  82. }
  83. CReplayPerformance *CReplay::AddNewPerformance( bool bGenTitle/*=true*/, bool bGenFilename/*=true*/ )
  84. {
  85. // Create a performance
  86. IReplayPerformanceManager *pPerformanceManager = g_pClientReplayContext->GetPerformanceManager();
  87. CReplayPerformance *pPerformance = pPerformanceManager->CreatePerformance( this );
  88. if ( bGenTitle )
  89. {
  90. // Give the performance a name
  91. pPerformance->AutoNameIfHasNoTitle( m_szMapName );
  92. }
  93. if ( bGenFilename )
  94. {
  95. // Generate a filename for the new performance
  96. pPerformance->SetFilename( pPerformanceManager->GeneratePerformanceFilename( this ) );
  97. }
  98. // Cache
  99. m_vecPerformances.AddToTail( pPerformance );
  100. return pPerformance;
  101. }
  102. void CReplay::AddPerformance( KeyValues *pIn )
  103. {
  104. // Create a performance
  105. IReplayPerformanceManager *pPerformanceManager = g_pClientReplayContext->GetPerformanceManager();
  106. CReplayPerformance *pPerformance = pPerformanceManager->CreatePerformance( this );
  107. // Read
  108. pPerformance->Read( pIn );
  109. // Cache
  110. m_vecPerformances.AddToTail( pPerformance );
  111. }
  112. void CReplay::AddPerformance( CReplayPerformance *pPerformance )
  113. {
  114. Assert( pPerformance );
  115. m_vecPerformances.AddToTail( pPerformance );
  116. }
  117. const CReplayTime &CReplay::GetItemDate() const
  118. {
  119. return m_RecordTime;
  120. }
  121. bool CReplay::IsItemRendered() const
  122. {
  123. return m_bRendered;
  124. }
  125. CReplay *CReplay::GetItemReplay()
  126. {
  127. return this;
  128. }
  129. ReplayHandle_t CReplay::GetItemReplayHandle() const
  130. {
  131. return GetHandle();
  132. }
  133. QueryableReplayItemHandle_t CReplay::GetItemHandle() const
  134. {
  135. return GetHandle();
  136. }
  137. const wchar_t *CReplay::GetItemTitle() const
  138. {
  139. return m_wszTitle;
  140. }
  141. void CReplay::SetItemTitle( const wchar_t *pTitle )
  142. {
  143. V_wcsncpy( m_wszTitle, pTitle, sizeof( m_wszTitle ) );
  144. }
  145. float CReplay::GetItemLength() const
  146. {
  147. return m_flLength;
  148. }
  149. void *CReplay::GetUserData()
  150. {
  151. return m_pUserData;
  152. }
  153. void CReplay::SetUserData( void* pUserData )
  154. {
  155. m_pUserData = pUserData;
  156. }
  157. bool CReplay::IsItemAMovie() const
  158. {
  159. return false;
  160. }
  161. void CReplay::AddScreenshot( int nWidth, int nHeight, const char *pBaseFilename )
  162. {
  163. m_vecScreenshots.AddToTail( new CReplayScreenshot( nWidth, nHeight, pBaseFilename ) );
  164. }
  165. void CReplay::AutoNameTitleIfEmpty()
  166. {
  167. // Autoname it
  168. if ( !m_wszTitle[0] )
  169. {
  170. Replay_GetAutoName( m_wszTitle, sizeof( m_wszTitle ), m_szMapName );
  171. }
  172. }
  173. const char *CReplay::GetSubKeyTitle() const
  174. {
  175. return Replay_va( "replay_%i", GetHandle() );
  176. }
  177. const char *CReplay::GetPath() const
  178. {
  179. return Replay_va( "%s%s%c", g_pClientReplayContext->GetBaseDir(), SUBDIR_REPLAYS, CORRECT_PATH_SEPARATOR );
  180. }
  181. void CReplay::OnDelete()
  182. {
  183. BaseClass::OnDelete();
  184. // Delete reconstructed replay if one exists
  185. if ( HasReconstructedReplay() )
  186. {
  187. g_pFullFileSystem->RemoveFile( m_strReconstructedFilename.Get() );
  188. }
  189. // Delete screenshots
  190. g_pClientReplayContext->GetScreenshotManager()->DeleteScreenshotsForReplay( this );
  191. // TODO: Delete performance(s)
  192. }
  193. bool CReplay::Read( KeyValues *pIn )
  194. {
  195. if ( !BaseClass::Read( pIn ) )
  196. return false;
  197. m_hSession = (ReplayHandle_t)pIn->GetInt( "session", REPLAY_HANDLE_INVALID );
  198. V_strcpy_safe( m_szMapName, pIn->GetString( "map", "" ) );
  199. m_nSpawnTick = pIn->GetInt( "spawn_tick", -1 );
  200. m_nDeathTick = pIn->GetInt( "death_tick", -1 );
  201. m_nStatus = static_cast< CReplay::ReplayStatus_t >( pIn->GetInt( "status", (int)CReplay::REPLAYSTATUS_INVALID ) );
  202. m_bComplete = pIn->GetInt( "complete" ) != 0;
  203. m_flLength = pIn->GetFloat( "length" );
  204. m_nPostDeathRecordTime = pIn->GetInt( "postdeathrecordtime" );
  205. m_bRendered = pIn->GetInt( "rendered" ) != 0;
  206. m_nPlayerSlot = pIn->GetInt( "player_slot", -1 );
  207. m_iMaxSessionBlockRequired = pIn->GetInt( "max_block", 0 ); Assert( m_iMaxSessionBlockRequired >= 0 );
  208. m_flStartTime = pIn->GetFloat( "start_time", -1.0f ); Assert( m_flStartTime >= 0.0f );
  209. V_wcsncpy( m_wszTitle, pIn->GetWString( "title" ), sizeof( m_wszTitle ) );
  210. // Read reconstructed filename and infer path
  211. const char *pReplaysDir = g_pClientReplayContext->GetReplayManager()->GetReplaysDir();
  212. const char *pReconFilename = pIn->GetString( "recon_filename" );
  213. if ( pReconFilename[0] != 0 )
  214. {
  215. m_strReconstructedFilename = Replay_va( "%s%s", pReplaysDir, pReconFilename );
  216. }
  217. // Read screenshots
  218. KeyValues *pScreenshots = pIn->FindKey( "screenshots" );
  219. if ( pScreenshots )
  220. {
  221. FOR_EACH_TRUE_SUBKEY( pScreenshots, pScreenshot )
  222. {
  223. int nWidth = pScreenshot->GetInt( "width" );
  224. int nHeight = pScreenshot->GetInt( "height" );
  225. const char *pBaseFilename = pScreenshot->GetString( "base_filename" );
  226. AddScreenshot( nWidth, nHeight, pBaseFilename );
  227. }
  228. }
  229. // Read performances
  230. KeyValues *pPerformances = pIn->FindKey( "edits" );
  231. if ( pPerformances )
  232. {
  233. FOR_EACH_TRUE_SUBKEY( pPerformances, pPerformance )
  234. {
  235. AddPerformance( pPerformance );
  236. }
  237. }
  238. // Record time
  239. KeyValues *pRecordTimeSubKey = pIn->FindKey( "record_time" );
  240. if ( pRecordTimeSubKey )
  241. {
  242. m_RecordTime.Read( pRecordTimeSubKey );
  243. }
  244. // Mark replay as saved, since it was just loaded from disk
  245. m_bSaved = true;
  246. return true;
  247. }
  248. void CReplay::Write( KeyValues *pOut )
  249. {
  250. BaseClass::Write( pOut );
  251. pOut->SetString( "map", m_szMapName );
  252. pOut->SetInt( "session", m_hSession );
  253. pOut->SetInt( "spawn_tick", m_nSpawnTick );
  254. pOut->SetInt( "death_tick", m_nDeathTick );
  255. pOut->SetInt( "status", static_cast< int >( m_nStatus ) );
  256. pOut->SetInt( "complete", static_cast< int >( m_bComplete ) );
  257. pOut->SetFloat( "length", m_flLength );
  258. pOut->SetInt( "postdeathrecordtime", m_nPostDeathRecordTime );
  259. pOut->SetInt( "rendered", m_bRendered );
  260. pOut->SetInt( "player_slot", m_nPlayerSlot );
  261. pOut->SetInt( "max_block", m_iMaxSessionBlockRequired );
  262. pOut->SetFloat( "start_time", m_flStartTime );
  263. pOut->SetWString( "title", m_wszTitle );
  264. // Store only filename for reconstructed .dem
  265. if ( !m_strReconstructedFilename.IsEmpty() )
  266. {
  267. const char *pReconFilename = V_UnqualifiedFileName( m_strReconstructedFilename.Get() );
  268. if ( pReconFilename[0] )
  269. {
  270. pOut->SetString( "recon_filename", pReconFilename );
  271. }
  272. }
  273. // Write screenshots
  274. KeyValues *pScreenshots = new KeyValues( "screenshots" );
  275. pOut->AddSubKey( pScreenshots );
  276. for ( int i = 0; i < m_vecScreenshots.Count(); ++i )
  277. {
  278. KeyValues *pScreenshotOut = new KeyValues( "screenshot" );
  279. CReplayScreenshot *pScreenshot = m_vecScreenshots[ i ];
  280. pScreenshotOut->SetInt( "width", pScreenshot->m_nWidth );
  281. pScreenshotOut->SetInt( "height", pScreenshot->m_nHeight );
  282. pScreenshotOut->SetString( "base_filename", pScreenshot->m_szBaseFilename );
  283. pScreenshots->AddSubKey( pScreenshotOut );
  284. }
  285. // Write performances
  286. KeyValues *pPerformances = new KeyValues( "edits" );
  287. pOut->AddSubKey( pPerformances );
  288. for ( int i = 0; i < m_vecPerformances.Count(); ++i )
  289. {
  290. KeyValues *pPerfOut = new KeyValues( "edit" );
  291. CReplayPerformance *pPerformance = m_vecPerformances[ i ];
  292. pPerformance->Write( pPerfOut );
  293. pPerformances->AddSubKey( pPerfOut );
  294. }
  295. KeyValues *pRecordTime = new KeyValues( "record_time" );
  296. pOut->AddSubKey( pRecordTime );
  297. m_RecordTime.Write( pRecordTime );
  298. // Mark as saved
  299. m_bSaved = true;
  300. }
  301. bool CReplay::HasReconstructedReplay() const
  302. {
  303. return !m_strReconstructedFilename.IsEmpty() &&
  304. g_pFullFileSystem->FileExists( m_strReconstructedFilename.Get() );
  305. }
  306. bool CReplay::IsSignificantBlock( int iBlockReconstruction ) const
  307. {
  308. return iBlockReconstruction <= m_iMaxSessionBlockRequired;
  309. }
  310. void CReplay::OnComplete()
  311. {
  312. AutoNameTitleIfEmpty();
  313. }
  314. //----------------------------------------------------------------------------------------