summaryrefslogtreecommitdiff
path: root/replay/sv_sessionrecorder.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /replay/sv_sessionrecorder.cpp
downloadarchived-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.cpp223
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 );
+}
+
+//----------------------------------------------------------------------------------------