diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /replay/sv_sessionrecorder.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'replay/sv_sessionrecorder.cpp')
| -rw-r--r-- | replay/sv_sessionrecorder.cpp | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/replay/sv_sessionrecorder.cpp b/replay/sv_sessionrecorder.cpp new file mode 100644 index 0000000..b52665e --- /dev/null +++ b/replay/sv_sessionrecorder.cpp @@ -0,0 +1,223 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +//=======================================================================================// + +#include "sv_sessionrecorder.h" +#include "replay/replayutils.h" +#include "replay/shared_defs.h" +#include "baserecordingsessionblock.h" +#include "replaysystem.h" +#include "baserecordingsessionblockmanager.h" +#include "sv_recordingsessionmanager.h" +#include "sv_replaycontext.h" +#include "sv_sessionpublishmanager.h" +#include "sv_recordingsession.h" +#include "sv_recordingsessionblock.h" +#include "fmtstr.h" +#include "vprof.h" +#include "iserver.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#undef CreateEvent + +//---------------------------------------------------------------------------------------- + +#define SERVER_REPLAY_INDEX_FILENAME ".replayindex" +#define SERVER_REPLAY_ERROR_LOST "The server crashed before the replay could be finalized. Replay lost." + +//---------------------------------------------------------------------------------------- + +CSessionRecorder::CSessionRecorder() +: m_bRecordingAborted( false ), + m_nCurrentRecordingStartTick( -1 ) +{ +} + +CSessionRecorder::~CSessionRecorder() +{ +} + +bool CSessionRecorder::Init() +{ + g_pFullFileSystem->CreateDirHierarchy( Replay_va( "%s%s", SV_GetBasePath(), SUBDIR_SESSIONS ) ); + return true; +} + +void CSessionRecorder::AbortCurrentSessionRecording() +{ + StopRecording( true ); + + CSessionPublishManager *pCurrentPublishManager = GetCurrentPublishManager(); + if ( !pCurrentPublishManager ) + { + AssertMsg( 0, "Could not get current publish manager." ); + return; + } + + pCurrentPublishManager->AbortPublish(); + + m_bRecordingAborted = true; +} + +void CSessionRecorder::SetCurrentRecordingStartTick( int nStartTick ) +{ + m_nCurrentRecordingStartTick = nStartTick; +} + +void CSessionRecorder::PublishAllSynchronous() +{ + FOR_EACH_LL( m_lstPublishManagers, i ) + { + m_lstPublishManagers[ i ]->PublishAllSynchronous(); + } +} + +void CSessionRecorder::StartRecording() +{ + m_bRecordingAborted = false; + + IServer *pServer = ReplayServerAsIServer(); + if ( !pServer || !pServer->IsActive() ) + { + ConMsg( "ERROR: Replay not active.\n" ); + return; + } + + // We only care about local fileserver path in the case that we aren't offloading files to an external sfileserver + const char *pWritePath = g_pServerReplayContext->GetLocalFileServerPath(); + if ( ( !pWritePath || !pWritePath[0] ) ) + { + 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 ); + return; + } + + IReplayServer *pReplayServer = ReplayServer(); + if ( pReplayServer->IsRecording() ) + { + ConMsg( "ERROR: Replay already recording.\n" ); + return; + } + + // Tell the replay server to begin recording + pReplayServer->StartRecording(); + + // Notify session manager + CBaseRecordingSession *pSession = SV_GetRecordingSessionManager()->OnSessionStart( m_nCurrentRecordingStartTick, NULL ); + + // Create a new publish manager and add it. The dump interval and any additional setup is done there. + CreateAndAddNewPublishManager( static_cast< CServerRecordingSession * >( pSession ) ); +} + +void CSessionRecorder::CreateAndAddNewPublishManager( CServerRecordingSession *pSession ) +{ + CSessionPublishManager *pNewPublishManager = new CSessionPublishManager( pSession ); + + // Let the publish manager know that it is the 'current' publish manager. + pNewPublishManager->OnStartRecording(); + + // Add to the head of the list, since the desired convention is for the list to be + // sorted from newest to oldest. + m_lstPublishManagers.AddToHead( pNewPublishManager ); +} + +float CSessionRecorder::GetNextThinkTime() const +{ + return 0.0f; +} + +void CSessionRecorder::Think() +{ + CBaseThinker::Think(); + + VPROF_BUDGET( "CSessionRecorder::Think", VPROF_BUDGETGROUP_REPLAY ); + + // This gets called even if replay is disabled. This is intentional. + PublishThink(); +} + +CSessionPublishManager *CSessionRecorder::GetCurrentPublishManager() const +{ + if ( !m_lstPublishManagers.Count() ) + return NULL; + + return m_lstPublishManagers[ m_lstPublishManagers.Head() ]; +} + +void CSessionRecorder::PublishThink() +{ + UpdateSessionLocks(); +} + +void CSessionRecorder::UpdateSessionLocks() +{ + for ( int i = m_lstPublishManagers.Head(); i != m_lstPublishManagers.InvalidIndex(); ) + { + CSessionPublishManager *pCurManager = m_lstPublishManagers[ i ]; + + // Cache off 'next' in case we delete the current object + const int itNext = m_lstPublishManagers.Next( i ); + + if ( pCurManager->IsDone() ) + { +#ifdef _DEBUG + pCurManager->Validate(); +#endif + + // We can unlock the associated session now. + pCurManager->UnlockSession(); + + // Remove and delete it. + m_lstPublishManagers.Remove( i ); + delete pCurManager; + + IF_REPLAY_DBG( Warning( "\n---\n*\n* All publishing done for session. %i still publishing.\n*\n---\n", m_lstPublishManagers.Count() ) ); + } + else + { + pCurManager->Think(); + } + + i = itNext; + } +} + +void CSessionRecorder::StopRecording( bool bAborting ) +{ +#if !defined( DEDICATED ) + if ( g_pEngineClient->IsPlayingReplayDemo() ) + return; +#endif + if ( !ReplayServer() ) + return; + + DBG( "StopRecording()\n" ); + + CServerRecordingSession *pSession = SV_GetRecordingSessionInProgress(); + if ( pSession ) + { + // Mark the session as not recording + pSession->OnStopRecording(); + + // Get the current publish manager and notify it that recording has stopped. + CSessionPublishManager *pManager = GetCurrentPublishManager(); + if ( pManager ) + { + pManager->OnStopRecord( bAborting ); + } + + // Notify session manager - the session will be flagged for unload or deletion, but + // will not actually be free'd until it is "unlocked" by the publish manager. + SV_GetRecordingSessionManager()->OnSessionEnd(); + } + + // Stop recording + ReplayServer()->StopRecording(); + + // Clear replay_recording + extern ConVar replay_recording; + replay_recording.SetValue( 0 ); +} + +//---------------------------------------------------------------------------------------- |