Team Fortress 2 Source Code as on 22/4/2020
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

//========= 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;
}
//----------------------------------------------------------------------------------------