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.
266 lines
7.0 KiB
266 lines
7.0 KiB
//========= 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; |
|
} |
|
|
|
//----------------------------------------------------------------------------------------
|
|
|