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.

266 lines
7.0 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. //=======================================================================================//
  4. #include "baserecordingsessionmanager.h"
  5. #include "baserecordingsession.h"
  6. #include "baserecordingsessionblock.h"
  7. #include "replay/replayutils.h"
  8. #include "replay/shared_defs.h"
  9. #include "replaysystem.h"
  10. #include "KeyValues.h"
  11. #include "shared_replaycontext.h"
  12. #include "filesystem.h"
  13. #include "iserver.h"
  14. #include "vprof.h"
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include "tier0/memdbgon.h"
  17. //----------------------------------------------------------------------------------------
  18. inline const char *GetSessionsFullFilename()
  19. {
  20. return Replay_va( "%s" SUBDIR_SESSIONS "%c", Replay_GetBaseDir(), CORRECT_PATH_SEPARATOR );
  21. }
  22. //----------------------------------------------------------------------------------------
  23. CBaseRecordingSessionManager::CBaseRecordingSessionManager( IReplayContext *pContext )
  24. : m_pContext( pContext ),
  25. m_pRecordingSession( NULL ),
  26. m_bLastSessionDitched( false )
  27. {
  28. }
  29. CBaseRecordingSessionManager::~CBaseRecordingSessionManager()
  30. {
  31. }
  32. bool CBaseRecordingSessionManager::Init()
  33. {
  34. if ( !BaseClass::Init() )
  35. return false;
  36. // Go through each block handle and attempt find the block in the block manager
  37. typedef CGenericPersistentManager< CBaseRecordingSessionBlock > BaseBlockManager_t;
  38. BaseBlockManager_t *pBlockManager = dynamic_cast< BaseBlockManager_t * >( m_pContext->GetRecordingSessionBlockManager() );
  39. FOR_EACH_OBJ( pBlockManager, it )
  40. {
  41. CBaseRecordingSessionBlock *pCurBlock = pBlockManager->m_vecObjs[ it ];
  42. // Find the session for the current block
  43. CBaseRecordingSession *pSession = m_pContext->GetRecordingSessionManager()->FindSession( pCurBlock->m_hSession );
  44. if ( !pSession )
  45. {
  46. m_pContext->GetErrorSystem()->AddErrorFromTokenName( "#Replay_Err_Load_CouldNotFindSession" );
  47. continue;
  48. }
  49. // Add the block
  50. pSession->AddBlock( pCurBlock, false );
  51. }
  52. return true;
  53. }
  54. CBaseRecordingSession *CBaseRecordingSessionManager::OnSessionStart( int nCurrentRecordingStartTick, const char *pSessionName )
  55. {
  56. // Add a new session if one w/ the given name doesn't already exist.
  57. // This is necessary on the client, where a session may already exist if, for example,
  58. // the client reconnects to a server where they were already playing/saved replays.
  59. // On the server, NULL will always be passed in for pSessionName.
  60. CBaseRecordingSession *pNewSession = pSessionName ? FindSessionByName( pSessionName ) : NULL;
  61. if ( !pNewSession )
  62. {
  63. pNewSession = CreateAndGenerateHandle();
  64. Add( pNewSession );
  65. }
  66. // Initialize
  67. pNewSession->PopulateWithRecordingData( nCurrentRecordingStartTick );
  68. Save();
  69. // Update recording session
  70. m_pRecordingSession = pNewSession;
  71. return m_pRecordingSession;
  72. }
  73. void CBaseRecordingSessionManager::OnSessionEnd()
  74. {
  75. if ( m_pRecordingSession )
  76. {
  77. // If we don't care about the given session, ditch it
  78. // NOTE: ShouldDitchSession() checks auto-delete flag!
  79. if ( m_pRecordingSession->ShouldDitchSession() )
  80. {
  81. m_bLastSessionDitched = true;
  82. DBG( "Marking session for ditch!\n" );
  83. MarkSessionForDelete( m_pRecordingSession->GetHandle() );
  84. }
  85. else
  86. {
  87. m_bLastSessionDitched = false;
  88. // Save
  89. FlagForFlush( m_pRecordingSession, false );
  90. // Unload from memory?
  91. if ( ShouldUnloadSessions() )
  92. {
  93. FlagForUnload( m_pRecordingSession );
  94. }
  95. }
  96. }
  97. m_pRecordingSession = NULL;
  98. }
  99. void CBaseRecordingSessionManager::DeleteSession( ReplayHandle_t hSession, bool bForce )
  100. {
  101. CBaseRecordingSession *pSession = Find( hSession );
  102. if ( !pSession )
  103. {
  104. AssertMsg( 0, "Trying to delete a non-existent session - should never happen!" );
  105. return;
  106. }
  107. AssertMsg( !pSession->IsLocked(), "Shouldn't be free'ing a locked session!" );
  108. // If the given session is recording, flag for delete but don't actually remove now
  109. if ( pSession == m_pRecordingSession && !bForce )
  110. {
  111. pSession->m_bAutoDelete = true;
  112. return;
  113. }
  114. // Remove the session and save
  115. Remove( pSession );
  116. Save();
  117. }
  118. void CBaseRecordingSessionManager::MarkSessionForDelete( ReplayHandle_t hSession )
  119. {
  120. m_lstSessionsToDelete.AddToTail( hSession );
  121. }
  122. const char *CBaseRecordingSessionManager::GetCurrentSessionName() const
  123. {
  124. if ( !m_pRecordingSession )
  125. {
  126. AssertMsg( 0, "GetCurrentSessionName() called w/o a session context" );
  127. return NULL;
  128. }
  129. return m_pRecordingSession->m_strName.Get();
  130. }
  131. int CBaseRecordingSessionManager::GetCurrentSessionBlockIndex() const
  132. {
  133. if ( !m_pRecordingSession )
  134. {
  135. AssertMsg( 0, "GetCurrentPartialIndex() called w/o a session context" );
  136. return -1;
  137. }
  138. // Need this MAX() here since GetNumBlocks() will return 0 until the first block is actually written.
  139. return MAX( 0, m_pRecordingSession->GetNumBlocks() - 1 );
  140. }
  141. void CBaseRecordingSessionManager::FlagSessionForFlush( CBaseRecordingSession *pSession, bool bForceImmediate )
  142. {
  143. FlagForFlush( pSession, bForceImmediate );
  144. }
  145. int CBaseRecordingSessionManager::GetServerStartTickForSession( ReplayHandle_t hSession )
  146. {
  147. CBaseRecordingSession *pSession = FindSession( hSession );
  148. if ( !pSession )
  149. return -1;
  150. return pSession->m_nServerStartRecordTick;
  151. }
  152. CBaseRecordingSession *CBaseRecordingSessionManager::FindSession( ReplayHandle_t hSession )
  153. {
  154. return Find( hSession );
  155. }
  156. const CBaseRecordingSession *CBaseRecordingSessionManager::FindSession( ReplayHandle_t hSession ) const
  157. {
  158. return const_cast< CBaseRecordingSessionManager * >( this )->Find( hSession );
  159. }
  160. CBaseRecordingSession *CBaseRecordingSessionManager::FindSessionByName( const char *pSessionName )
  161. {
  162. if ( !pSessionName || !pSessionName[0] )
  163. return NULL;
  164. FOR_EACH_OBJ( this, i )
  165. {
  166. CBaseRecordingSession *pCurSession = m_vecObjs[ i ];
  167. if ( !V_stricmp( pSessionName, pCurSession->m_strName.Get() ) )
  168. return pCurSession;
  169. }
  170. return NULL;
  171. }
  172. const char *CBaseRecordingSessionManager::GetRelativeIndexPath() const
  173. {
  174. return Replay_va( "%s%c", SUBDIR_SESSIONS, CORRECT_PATH_SEPARATOR );
  175. }
  176. void CBaseRecordingSessionManager::Think()
  177. {
  178. VPROF_BUDGET( "CBaseRecordingSessionManager::Think", VPROF_BUDGETGROUP_REPLAY );
  179. DeleteSessionThink();
  180. BaseClass::Think();
  181. }
  182. void CBaseRecordingSessionManager::DeleteSessionThink()
  183. {
  184. DoSessionCleanup();
  185. }
  186. void CBaseRecordingSessionManager::DoSessionCleanup()
  187. {
  188. bool bDeletedASession = false;
  189. for ( int i = m_lstSessionsToDelete.Head(); i != m_lstSessionsToDelete.InvalidIndex(); )
  190. {
  191. ReplayHandle_t hSession = m_lstSessionsToDelete[ i ];
  192. const int itNext = m_lstSessionsToDelete.Next( i );
  193. if ( CanDeleteSession( hSession ) )
  194. {
  195. DBG( "Unloading session.\n" );
  196. DeleteSession( hSession, true );
  197. m_lstSessionsToDelete.Remove( i );
  198. bDeletedASession = true;
  199. }
  200. i = itNext;
  201. }
  202. // If we just deleted the last session, let the derived class do any post-work
  203. if ( !m_lstSessionsToDelete.Count() && bDeletedASession )
  204. {
  205. OnAllSessionsDeleted();
  206. }
  207. }
  208. float CBaseRecordingSessionManager::GetNextThinkTime() const
  209. {
  210. return g_pEngine->GetHostTime() + 0.1f;
  211. }
  212. //----------------------------------------------------------------------------------------