mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-09 18:29:35 +00:00
1
This commit is contained in:
@@ -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 );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
Reference in New Issue
Block a user