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.

223 lines
5.9 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. //=======================================================================================//
  4. #include "sv_sessionrecorder.h"
  5. #include "replay/replayutils.h"
  6. #include "replay/shared_defs.h"
  7. #include "baserecordingsessionblock.h"
  8. #include "replaysystem.h"
  9. #include "baserecordingsessionblockmanager.h"
  10. #include "sv_recordingsessionmanager.h"
  11. #include "sv_replaycontext.h"
  12. #include "sv_sessionpublishmanager.h"
  13. #include "sv_recordingsession.h"
  14. #include "sv_recordingsessionblock.h"
  15. #include "fmtstr.h"
  16. #include "vprof.h"
  17. #include "iserver.h"
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include "tier0/memdbgon.h"
  20. #undef CreateEvent
  21. //----------------------------------------------------------------------------------------
  22. #define SERVER_REPLAY_INDEX_FILENAME ".replayindex"
  23. #define SERVER_REPLAY_ERROR_LOST "The server crashed before the replay could be finalized. Replay lost."
  24. //----------------------------------------------------------------------------------------
  25. CSessionRecorder::CSessionRecorder()
  26. : m_bRecordingAborted( false ),
  27. m_nCurrentRecordingStartTick( -1 )
  28. {
  29. }
  30. CSessionRecorder::~CSessionRecorder()
  31. {
  32. }
  33. bool CSessionRecorder::Init()
  34. {
  35. g_pFullFileSystem->CreateDirHierarchy( Replay_va( "%s%s", SV_GetBasePath(), SUBDIR_SESSIONS ) );
  36. return true;
  37. }
  38. void CSessionRecorder::AbortCurrentSessionRecording()
  39. {
  40. StopRecording( true );
  41. CSessionPublishManager *pCurrentPublishManager = GetCurrentPublishManager();
  42. if ( !pCurrentPublishManager )
  43. {
  44. AssertMsg( 0, "Could not get current publish manager." );
  45. return;
  46. }
  47. pCurrentPublishManager->AbortPublish();
  48. m_bRecordingAborted = true;
  49. }
  50. void CSessionRecorder::SetCurrentRecordingStartTick( int nStartTick )
  51. {
  52. m_nCurrentRecordingStartTick = nStartTick;
  53. }
  54. void CSessionRecorder::PublishAllSynchronous()
  55. {
  56. FOR_EACH_LL( m_lstPublishManagers, i )
  57. {
  58. m_lstPublishManagers[ i ]->PublishAllSynchronous();
  59. }
  60. }
  61. void CSessionRecorder::StartRecording()
  62. {
  63. m_bRecordingAborted = false;
  64. IServer *pServer = ReplayServerAsIServer();
  65. if ( !pServer || !pServer->IsActive() )
  66. {
  67. ConMsg( "ERROR: Replay not active.\n" );
  68. return;
  69. }
  70. // We only care about local fileserver path in the case that we aren't offloading files to an external sfileserver
  71. const char *pWritePath = g_pServerReplayContext->GetLocalFileServerPath();
  72. if ( ( !pWritePath || !pWritePath[0] ) )
  73. {
  74. ConMsg( "\n*\n* ERROR: Failed to begin record: make sure \"replay_local_fileserver_path\" refers to a valid path!\n** replay_local_fileserver_path is currently set to: \"%s\"\n*\n\n", pWritePath );
  75. return;
  76. }
  77. IReplayServer *pReplayServer = ReplayServer();
  78. if ( pReplayServer->IsRecording() )
  79. {
  80. ConMsg( "ERROR: Replay already recording.\n" );
  81. return;
  82. }
  83. // Tell the replay server to begin recording
  84. pReplayServer->StartRecording();
  85. // Notify session manager
  86. CBaseRecordingSession *pSession = SV_GetRecordingSessionManager()->OnSessionStart( m_nCurrentRecordingStartTick, NULL );
  87. // Create a new publish manager and add it. The dump interval and any additional setup is done there.
  88. CreateAndAddNewPublishManager( static_cast< CServerRecordingSession * >( pSession ) );
  89. }
  90. void CSessionRecorder::CreateAndAddNewPublishManager( CServerRecordingSession *pSession )
  91. {
  92. CSessionPublishManager *pNewPublishManager = new CSessionPublishManager( pSession );
  93. // Let the publish manager know that it is the 'current' publish manager.
  94. pNewPublishManager->OnStartRecording();
  95. // Add to the head of the list, since the desired convention is for the list to be
  96. // sorted from newest to oldest.
  97. m_lstPublishManagers.AddToHead( pNewPublishManager );
  98. }
  99. float CSessionRecorder::GetNextThinkTime() const
  100. {
  101. return 0.0f;
  102. }
  103. void CSessionRecorder::Think()
  104. {
  105. CBaseThinker::Think();
  106. VPROF_BUDGET( "CSessionRecorder::Think", VPROF_BUDGETGROUP_REPLAY );
  107. // This gets called even if replay is disabled. This is intentional.
  108. PublishThink();
  109. }
  110. CSessionPublishManager *CSessionRecorder::GetCurrentPublishManager() const
  111. {
  112. if ( !m_lstPublishManagers.Count() )
  113. return NULL;
  114. return m_lstPublishManagers[ m_lstPublishManagers.Head() ];
  115. }
  116. void CSessionRecorder::PublishThink()
  117. {
  118. UpdateSessionLocks();
  119. }
  120. void CSessionRecorder::UpdateSessionLocks()
  121. {
  122. for ( int i = m_lstPublishManagers.Head(); i != m_lstPublishManagers.InvalidIndex(); )
  123. {
  124. CSessionPublishManager *pCurManager = m_lstPublishManagers[ i ];
  125. // Cache off 'next' in case we delete the current object
  126. const int itNext = m_lstPublishManagers.Next( i );
  127. if ( pCurManager->IsDone() )
  128. {
  129. #ifdef _DEBUG
  130. pCurManager->Validate();
  131. #endif
  132. // We can unlock the associated session now.
  133. pCurManager->UnlockSession();
  134. // Remove and delete it.
  135. m_lstPublishManagers.Remove( i );
  136. delete pCurManager;
  137. IF_REPLAY_DBG( Warning( "\n---\n*\n* All publishing done for session. %i still publishing.\n*\n---\n", m_lstPublishManagers.Count() ) );
  138. }
  139. else
  140. {
  141. pCurManager->Think();
  142. }
  143. i = itNext;
  144. }
  145. }
  146. void CSessionRecorder::StopRecording( bool bAborting )
  147. {
  148. #if !defined( DEDICATED )
  149. if ( g_pEngineClient->IsPlayingReplayDemo() )
  150. return;
  151. #endif
  152. if ( !ReplayServer() )
  153. return;
  154. DBG( "StopRecording()\n" );
  155. CServerRecordingSession *pSession = SV_GetRecordingSessionInProgress();
  156. if ( pSession )
  157. {
  158. // Mark the session as not recording
  159. pSession->OnStopRecording();
  160. // Get the current publish manager and notify it that recording has stopped.
  161. CSessionPublishManager *pManager = GetCurrentPublishManager();
  162. if ( pManager )
  163. {
  164. pManager->OnStopRecord( bAborting );
  165. }
  166. // Notify session manager - the session will be flagged for unload or deletion, but
  167. // will not actually be free'd until it is "unlocked" by the publish manager.
  168. SV_GetRecordingSessionManager()->OnSessionEnd();
  169. }
  170. // Stop recording
  171. ReplayServer()->StopRecording();
  172. // Clear replay_recording
  173. extern ConVar replay_recording;
  174. replay_recording.SetValue( 0 );
  175. }
  176. //----------------------------------------------------------------------------------------