267 lines
7.0 KiB
C++
267 lines
7.0 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
//=======================================================================================//
|
|
|
|
#include "baserecordingsessionmanager.h"
|
|
#include "baserecordingsession.h"
|
|
#include "baserecordingsessionblock.h"
|
|
#include "replay/replayutils.h"
|
|
#include "replay/shared_defs.h"
|
|
#include "replaysystem.h"
|
|
#include "KeyValues.h"
|
|
#include "shared_replaycontext.h"
|
|
#include "filesystem.h"
|
|
#include "iserver.h"
|
|
#include "vprof.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
|
|
inline const char *GetSessionsFullFilename()
|
|
{
|
|
return Replay_va( "%s" SUBDIR_SESSIONS "%c", Replay_GetBaseDir(), CORRECT_PATH_SEPARATOR );
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
|
|
CBaseRecordingSessionManager::CBaseRecordingSessionManager( IReplayContext *pContext )
|
|
: m_pContext( pContext ),
|
|
m_pRecordingSession( NULL ),
|
|
m_bLastSessionDitched( false )
|
|
{
|
|
}
|
|
|
|
CBaseRecordingSessionManager::~CBaseRecordingSessionManager()
|
|
{
|
|
}
|
|
|
|
bool CBaseRecordingSessionManager::Init()
|
|
{
|
|
if ( !BaseClass::Init() )
|
|
return false;
|
|
|
|
// Go through each block handle and attempt find the block in the block manager
|
|
typedef CGenericPersistentManager< CBaseRecordingSessionBlock > BaseBlockManager_t;
|
|
BaseBlockManager_t *pBlockManager = dynamic_cast< BaseBlockManager_t * >( m_pContext->GetRecordingSessionBlockManager() );
|
|
FOR_EACH_OBJ( pBlockManager, it )
|
|
{
|
|
CBaseRecordingSessionBlock *pCurBlock = pBlockManager->m_vecObjs[ it ];
|
|
|
|
// Find the session for the current block
|
|
CBaseRecordingSession *pSession = m_pContext->GetRecordingSessionManager()->FindSession( pCurBlock->m_hSession );
|
|
if ( !pSession )
|
|
{
|
|
m_pContext->GetErrorSystem()->AddErrorFromTokenName( "#Replay_Err_Load_CouldNotFindSession" );
|
|
continue;
|
|
}
|
|
|
|
// Add the block
|
|
pSession->AddBlock( pCurBlock, false );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
CBaseRecordingSession *CBaseRecordingSessionManager::OnSessionStart( int nCurrentRecordingStartTick, const char *pSessionName )
|
|
{
|
|
// Add a new session if one w/ the given name doesn't already exist.
|
|
// This is necessary on the client, where a session may already exist if, for example,
|
|
// the client reconnects to a server where they were already playing/saved replays.
|
|
// On the server, NULL will always be passed in for pSessionName.
|
|
CBaseRecordingSession *pNewSession = pSessionName ? FindSessionByName( pSessionName ) : NULL;
|
|
if ( !pNewSession )
|
|
{
|
|
pNewSession = CreateAndGenerateHandle();
|
|
Add( pNewSession );
|
|
}
|
|
|
|
// Initialize
|
|
pNewSession->PopulateWithRecordingData( nCurrentRecordingStartTick );
|
|
|
|
Save();
|
|
|
|
// Update recording session
|
|
m_pRecordingSession = pNewSession;
|
|
|
|
return m_pRecordingSession;
|
|
}
|
|
|
|
void CBaseRecordingSessionManager::OnSessionEnd()
|
|
{
|
|
if ( m_pRecordingSession )
|
|
{
|
|
// If we don't care about the given session, ditch it
|
|
// NOTE: ShouldDitchSession() checks auto-delete flag!
|
|
if ( m_pRecordingSession->ShouldDitchSession() )
|
|
{
|
|
m_bLastSessionDitched = true;
|
|
|
|
DBG( "Marking session for ditch!\n" );
|
|
|
|
MarkSessionForDelete( m_pRecordingSession->GetHandle() );
|
|
}
|
|
else
|
|
{
|
|
m_bLastSessionDitched = false;
|
|
|
|
// Save
|
|
FlagForFlush( m_pRecordingSession, false );
|
|
|
|
// Unload from memory?
|
|
if ( ShouldUnloadSessions() )
|
|
{
|
|
FlagForUnload( m_pRecordingSession );
|
|
}
|
|
}
|
|
}
|
|
m_pRecordingSession = NULL;
|
|
}
|
|
|
|
void CBaseRecordingSessionManager::DeleteSession( ReplayHandle_t hSession, bool bForce )
|
|
{
|
|
CBaseRecordingSession *pSession = Find( hSession );
|
|
if ( !pSession )
|
|
{
|
|
AssertMsg( 0, "Trying to delete a non-existent session - should never happen!" );
|
|
return;
|
|
}
|
|
|
|
AssertMsg( !pSession->IsLocked(), "Shouldn't be free'ing a locked session!" );
|
|
|
|
// If the given session is recording, flag for delete but don't actually remove now
|
|
if ( pSession == m_pRecordingSession && !bForce )
|
|
{
|
|
pSession->m_bAutoDelete = true;
|
|
return;
|
|
}
|
|
|
|
// Remove the session and save
|
|
Remove( pSession );
|
|
Save();
|
|
}
|
|
|
|
void CBaseRecordingSessionManager::MarkSessionForDelete( ReplayHandle_t hSession )
|
|
{
|
|
m_lstSessionsToDelete.AddToTail( hSession );
|
|
}
|
|
|
|
const char *CBaseRecordingSessionManager::GetCurrentSessionName() const
|
|
{
|
|
if ( !m_pRecordingSession )
|
|
{
|
|
AssertMsg( 0, "GetCurrentSessionName() called w/o a session context" );
|
|
return NULL;
|
|
}
|
|
|
|
return m_pRecordingSession->m_strName.Get();
|
|
}
|
|
|
|
int CBaseRecordingSessionManager::GetCurrentSessionBlockIndex() const
|
|
{
|
|
if ( !m_pRecordingSession )
|
|
{
|
|
AssertMsg( 0, "GetCurrentPartialIndex() called w/o a session context" );
|
|
return -1;
|
|
}
|
|
|
|
// Need this MAX() here since GetNumBlocks() will return 0 until the first block is actually written.
|
|
return MAX( 0, m_pRecordingSession->GetNumBlocks() - 1 );
|
|
}
|
|
|
|
void CBaseRecordingSessionManager::FlagSessionForFlush( CBaseRecordingSession *pSession, bool bForceImmediate )
|
|
{
|
|
FlagForFlush( pSession, bForceImmediate );
|
|
}
|
|
|
|
int CBaseRecordingSessionManager::GetServerStartTickForSession( ReplayHandle_t hSession )
|
|
{
|
|
CBaseRecordingSession *pSession = FindSession( hSession );
|
|
if ( !pSession )
|
|
return -1;
|
|
|
|
return pSession->m_nServerStartRecordTick;
|
|
}
|
|
|
|
CBaseRecordingSession *CBaseRecordingSessionManager::FindSession( ReplayHandle_t hSession )
|
|
{
|
|
return Find( hSession );
|
|
}
|
|
|
|
const CBaseRecordingSession *CBaseRecordingSessionManager::FindSession( ReplayHandle_t hSession ) const
|
|
{
|
|
return const_cast< CBaseRecordingSessionManager * >( this )->Find( hSession );
|
|
}
|
|
|
|
CBaseRecordingSession *CBaseRecordingSessionManager::FindSessionByName( const char *pSessionName )
|
|
{
|
|
if ( !pSessionName || !pSessionName[0] )
|
|
return NULL;
|
|
|
|
FOR_EACH_OBJ( this, i )
|
|
{
|
|
CBaseRecordingSession *pCurSession = m_vecObjs[ i ];
|
|
if ( !V_stricmp( pSessionName, pCurSession->m_strName.Get() ) )
|
|
return pCurSession;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const char *CBaseRecordingSessionManager::GetRelativeIndexPath() const
|
|
{
|
|
return Replay_va( "%s%c", SUBDIR_SESSIONS, CORRECT_PATH_SEPARATOR );
|
|
}
|
|
|
|
void CBaseRecordingSessionManager::Think()
|
|
{
|
|
VPROF_BUDGET( "CBaseRecordingSessionManager::Think", VPROF_BUDGETGROUP_REPLAY );
|
|
|
|
DeleteSessionThink();
|
|
|
|
BaseClass::Think();
|
|
}
|
|
|
|
void CBaseRecordingSessionManager::DeleteSessionThink()
|
|
{
|
|
DoSessionCleanup();
|
|
}
|
|
|
|
void CBaseRecordingSessionManager::DoSessionCleanup()
|
|
{
|
|
bool bDeletedASession = false;
|
|
|
|
for ( int i = m_lstSessionsToDelete.Head(); i != m_lstSessionsToDelete.InvalidIndex(); )
|
|
{
|
|
ReplayHandle_t hSession = m_lstSessionsToDelete[ i ];
|
|
|
|
const int itNext = m_lstSessionsToDelete.Next( i );
|
|
|
|
if ( CanDeleteSession( hSession ) )
|
|
{
|
|
DBG( "Unloading session.\n" );
|
|
|
|
DeleteSession( hSession, true );
|
|
m_lstSessionsToDelete.Remove( i );
|
|
|
|
bDeletedASession = true;
|
|
}
|
|
|
|
i = itNext;
|
|
}
|
|
|
|
// If we just deleted the last session, let the derived class do any post-work
|
|
if ( !m_lstSessionsToDelete.Count() && bDeletedASession )
|
|
{
|
|
OnAllSessionsDeleted();
|
|
}
|
|
}
|
|
|
|
float CBaseRecordingSessionManager::GetNextThinkTime() const
|
|
{
|
|
return g_pEngine->GetHostTime() + 0.1f;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------
|