diff options
Diffstat (limited to 'replay/baserecordingsessionmanager.cpp')
| -rw-r--r-- | replay/baserecordingsessionmanager.cpp | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/replay/baserecordingsessionmanager.cpp b/replay/baserecordingsessionmanager.cpp new file mode 100644 index 0000000..06a1d26 --- /dev/null +++ b/replay/baserecordingsessionmanager.cpp @@ -0,0 +1,266 @@ +//========= 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; +} + +//---------------------------------------------------------------------------------------- |