summaryrefslogtreecommitdiff
path: root/replay/cl_recordingsessionmanager.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /replay/cl_recordingsessionmanager.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'replay/cl_recordingsessionmanager.cpp')
-rw-r--r--replay/cl_recordingsessionmanager.cpp309
1 files changed, 309 insertions, 0 deletions
diff --git a/replay/cl_recordingsessionmanager.cpp b/replay/cl_recordingsessionmanager.cpp
new file mode 100644
index 0000000..cda5ece
--- /dev/null
+++ b/replay/cl_recordingsessionmanager.cpp
@@ -0,0 +1,309 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#include "cl_recordingsessionmanager.h"
+#include "replaysystem.h"
+#include "cl_replaymanager.h"
+#include "cl_recordingsession.h"
+#include "cl_sessionblockdownloader.h"
+#include "vprof.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//----------------------------------------------------------------------------------------
+
+#define CLIENTRECORDINGSESSIONMANAGER_VERSION 0
+
+//----------------------------------------------------------------------------------------
+
+CClientRecordingSessionManager::CClientRecordingSessionManager( IReplayContext *pContext )
+: CBaseRecordingSessionManager( pContext ),
+ m_nNumSessionBlockDownloaders( 0 ),
+ m_flNextBlockUpdateTime( 0.0f ),
+ m_flNextPossibleDownloadTime( 0.0f )
+{
+}
+
+CClientRecordingSessionManager::~CClientRecordingSessionManager()
+{
+}
+
+bool CClientRecordingSessionManager::Init()
+{
+ AddEventsForListen();
+
+ return BaseClass::Init();
+}
+
+void CClientRecordingSessionManager::CleanupUnneededBlocks()
+{
+ Msg( "Cleaning up unneeded replay block data...\n" );
+ FOR_EACH_OBJ( this, i )
+ {
+ CClientRecordingSession *pCurSession = CL_CastSession( m_vecObjs[ i ] );
+ pCurSession->LoadBlocksForSession();
+ pCurSession->DeleteBlocks();
+ }
+ Msg( "Replay cleanup done.\n" );
+}
+
+void CClientRecordingSessionManager::AddEventsForListen()
+{
+ g_pGameEventManager->AddListener( this, "replay_endrecord", false );
+ g_pGameEventManager->AddListener( this, "replay_sessioninfo", false );
+ g_pGameEventManager->AddListener( this, "player_death", false );
+}
+
+const char *CClientRecordingSessionManager::GetNewSessionName() const
+{
+ return m_ServerRecordingState.m_strSessionName;
+}
+
+CBaseRecordingSession *CClientRecordingSessionManager::OnSessionStart( int nCurrentRecordingStartTick, const char *pSessionName )
+{
+ return BaseClass::OnSessionStart( nCurrentRecordingStartTick, pSessionName );
+}
+
+void CClientRecordingSessionManager::OnSessionEnd()
+{
+ if ( m_pRecordingSession )
+ {
+ // Update whether all blocks have been downloaded
+ AssertMsg( !m_pRecordingSession->m_bRecording, "This flag should have been cleared already! See CBaseRecordingSession::OnStopRecording()" );
+ CL_CastSession( m_pRecordingSession )->UpdateAllBlocksDownloaded();
+ }
+
+ BaseClass::OnSessionEnd();
+
+ m_ServerRecordingState.Clear();
+}
+
+void CClientRecordingSessionManager::FireGameEvent( IGameEvent *pEvent )
+{
+ DBG( "CReplayHistoryManager::FireGameEvent()\n" );
+
+ if ( g_pEngineClient->IsDemoPlayingBack() )
+ return;
+
+ const char *pEventName = pEvent->GetName();
+
+ if ( !V_stricmp( "replay_sessioninfo", pEventName ) )
+ {
+ DBG( " replay_sessioninfo\n" );
+
+ bool bDisableReplayOnClient = false;
+
+ const CUtlString strSessionName = pEvent->GetString( "sn" );
+ if ( strSessionName.IsEmpty() )
+ {
+ CL_GetErrorSystem()->AddErrorFromTokenName( "#Replay_Err_SessionInfo_BadSessionName" );
+ bDisableReplayOnClient = true;
+ }
+
+ const int nDumpInterval = pEvent->GetInt( "di", 0 );
+ if ( nDumpInterval < MIN_SERVER_DUMP_INTERVAL ||
+ nDumpInterval > MAX_SERVER_DUMP_INTERVAL )
+ {
+ CL_GetErrorSystem()->AddErrorFromTokenName( "#Replay_Err_SessionInfo_BadDumpInterval" );
+ bDisableReplayOnClient = true;
+ }
+
+ const int nCurrentBlock = pEvent->GetInt( "cb", -1 );
+ if ( nCurrentBlock < 0 )
+ {
+ CL_GetErrorSystem()->AddErrorFromTokenName( "#Replay_Err_SessionInfo_BadCurrentBlock" );
+ bDisableReplayOnClient = true;
+ }
+
+ const int nStartTick = pEvent->GetInt( "st", -1 );
+ if ( nStartTick < 0 )
+ {
+ CL_GetErrorSystem()->AddErrorFromTokenName( "Replay_Err_SessionInfo_BadStartTick" );
+ bDisableReplayOnClient = true;
+ }
+
+ // Cache session state
+ m_ServerRecordingState.m_strSessionName = strSessionName;
+ m_ServerRecordingState.m_nDumpInterval = nDumpInterval;
+ m_ServerRecordingState.m_nCurrentBlock = nCurrentBlock + 1; // This will account for any different between when the server actually dumps a block and the client predicts a dump
+ m_ServerRecordingState.m_nStartTick = nStartTick;
+
+ // If the server's in a weird state, disable replay on the client so they don't
+ // create any replays that don't play, etc.
+ g_pClientReplayContextInternal->DisableReplayOnClient( bDisableReplayOnClient );
+ if ( bDisableReplayOnClient )
+ return;
+
+ OnSessionStart( nStartTick, strSessionName.Get() );
+
+ CL_GetReplayManager()->OnSessionStart();
+
+ // Update the current block based on the dump interval passed down from
+ // the server so the client stays in sync w/ the current block index.
+ m_flNextBlockUpdateTime = g_pEngine->GetHostTime() + nDumpInterval;
+ }
+
+ else if ( !V_stricmp( "replay_endrecord", pEventName ) )
+ {
+ DBG( " replay_stoprecord\n" );
+
+ // Clear pending replay URL cache, complete any pending replay
+ CL_GetReplayManager()->OnSessionEnd();
+
+ // Notify the session - it will mark itself as no longer recording.
+ if ( m_pRecordingSession )
+ {
+ m_pRecordingSession->OnStopRecording();
+ }
+
+ // Resets current session pointer
+ OnSessionEnd();
+ }
+
+ // When the player dies, we fill out the rest of the data here
+ else if ( !V_stricmp( "player_death", pEventName ) &&
+ pEvent->GetInt( "victim_entindex" ) == g_pEngineClient->GetPlayerSlot() + 1 &&
+ g_pClient->ShouldCompletePendingReplay( pEvent ) )
+ {
+ CL_GetReplayManager()->CompletePendingReplay();
+ }
+}
+
+int CClientRecordingSessionManager::GetVersion() const
+{
+ return CLIENTRECORDINGSESSIONMANAGER_VERSION;
+}
+
+void CClientRecordingSessionManager::Think()
+{
+ VPROF_BUDGET( "CClientRecordingSessionManager::Think", VPROF_BUDGETGROUP_REPLAY );
+
+ BaseClass::Think();
+
+ // Manage all session block downloads
+ DownloadThink();
+
+ if ( !g_pReplay->IsRecording() )
+ return;
+
+ if ( g_pEngineClient->IsDemoPlayingBack() )
+ return;
+
+ const float flHostTime = g_pEngine->GetHostTime();
+
+ if ( replay_debug.GetBool() )
+ {
+ extern ConVar replay_postdeathrecordtime;
+ g_pEngineClient->Con_NPrintf( 100, "Time until block dump: ~%f", m_flNextBlockUpdateTime - flHostTime );
+ g_pEngineClient->Con_NPrintf( 101, "Post-death record time: %f", replay_postdeathrecordtime.GetFloat() );
+ }
+
+ if ( m_flNextBlockUpdateTime <= flHostTime )
+ {
+ // Increment current block
+ ++m_ServerRecordingState.m_nCurrentBlock;
+
+ // NOTE: Now the number of blocks in the recording session in progress should be
+ // different from the number of blocks in its list - so it should spawn a download
+ // thread to grab the session info and create the new block.
+
+ IF_REPLAY_DBG( Warning( "# session blocks updating: %i\n", m_ServerRecordingState.m_nCurrentBlock ) );
+
+ // Setup next think
+ m_flNextBlockUpdateTime = flHostTime + m_ServerRecordingState.m_nDumpInterval;
+ }
+}
+
+void CClientRecordingSessionManager::DownloadThink()
+{
+ bool bKickedOffDownload = false;
+ const float flHostTime = g_pEngine->GetHostTime();
+
+ // For session in progress - check predicted # of blocks on the server based on current number
+ // of blocks in our list - if different, download the session info and create any outstanding
+ // blocks on the client.
+ bool bEnoughTimeHasPassed = flHostTime >= m_flNextPossibleDownloadTime;
+ if ( !bEnoughTimeHasPassed )
+ return;
+
+ // Go through all sessions to see if we need to create session block downloaders
+ FOR_EACH_OBJ( this, i )
+ {
+ CClientRecordingSession *pCurSession = CL_CastSession( m_vecObjs[ i ] );
+
+ // Already have a session block downloader? NOTE: The think manager calls its Think().
+ if ( pCurSession->HasSessionInfoDownloader() )
+ continue;
+
+ // If the # of blocks on the client is out of sync with the number of blocks we need
+ // to eventually download, sync with the server, i.e. download the session info and
+ // create blocks/sync block data as needed.
+ if ( pCurSession->ShouldSyncBlocksWithServer() )
+ {
+ pCurSession->SyncSessionBlocks();
+ bKickedOffDownload = true;
+ }
+ }
+
+ // Set next possible download time if we just kicked off a download
+ if ( bKickedOffDownload )
+ {
+ m_flNextPossibleDownloadTime = flHostTime + MAX( MIN_SERVER_DUMP_INTERVAL, CL_GetRecordingSessionManager()->m_ServerRecordingState.m_nDumpInterval );
+ }
+}
+
+CBaseRecordingSession *CClientRecordingSessionManager::Create()
+{
+ return new CClientRecordingSession( m_pContext );
+}
+
+IReplayContext *CClientRecordingSessionManager::GetReplayContext() const
+{
+ return g_pClientReplayContextInternal;
+}
+
+void CClientRecordingSessionManager::OnObjLoaded( CBaseRecordingSession *pSession )
+{
+ // Make sure the session doesn't try to start downloading if it's done
+ CL_CastSession( pSession )->UpdateAllBlocksDownloaded();
+}
+
+void CClientRecordingSessionManager::OnReplayDeleted( CReplay *pReplay )
+{
+ // Notify the session that a replay has been deleted, in case it needs to do any cleanup.
+ CClientRecordingSession *pSession = CL_CastSession( FindSession( pReplay->m_hSession ) );
+ if ( pSession )
+ {
+ pSession->OnReplayDeleted( pReplay );
+ }
+
+ // Get the # of replays that depend on the given session
+ int nNumDependentReplays = CL_GetReplayManager()->GetNumReplaysDependentOnSession( pReplay->m_hSession );
+ if ( nNumDependentReplays == 1 )
+ {
+ // Delete the session - remove the item from the manager itself, delete the
+ // .dem file, and any .dmx.
+ DeleteSession( pReplay->m_hSession, false );
+ }
+}
+
+void CClientRecordingSessionManager::OnReplaysLoaded()
+{
+ // Cache replay pointers in sessions for quick access
+ FOR_EACH_REPLAY( i )
+ {
+ CReplay *pCurReplay = GET_REPLAY_AT( i );
+ CClientRecordingSession *pOwnerSession = CL_CastSession( CL_GetRecordingSessionManager()->FindSession( pCurReplay->m_hSession ) ); Assert( pOwnerSession );
+ if ( !pOwnerSession )
+ {
+ CL_GetErrorSystem()->AddErrorFromTokenName( "#Replay_Err_Load_BadOwnerSession" );
+ continue;
+ }
+
+ pOwnerSession->CacheReplay( pCurReplay );
+ }
+}
+
+//----------------------------------------------------------------------------------------