summaryrefslogtreecommitdiff
path: root/replay/baserecordingsessionmanager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'replay/baserecordingsessionmanager.cpp')
-rw-r--r--replay/baserecordingsessionmanager.cpp266
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;
+}
+
+//----------------------------------------------------------------------------------------