|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//
#include "cl_replaycontext.h"
#include "replaysystem.h"
#include "replay/iclientreplay.h"
#include "replay/ireplaymovierenderer.h"
#include "replay/shared_defs.h"
#include "cl_replaymanager.h"
#include "replay_dbg.h"
#include "baserecordingsessionmanager.h"
#include "baserecordingsessionblockmanager.h"
#include "cl_replaymoviemanager.h"
#include "cl_screenshotmanager.h"
#include "cl_performancemanager.h"
#include "cl_sessionblockdownloader.h"
#include "cl_downloader.h"
#include "cl_recordingsession.h"
#include "cl_recordingsessionblock.h"
#include "cl_renderqueue.h"
#include "replay_reconstructor.h"
#include "globalvars_base.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//----------------------------------------------------------------------------------------
CClientReplayContext::CClientReplayContext() : m_pReplayManager( NULL ), m_pScreenshotManager( NULL ), m_pMovieRenderer( NULL ), m_pMovieManager( NULL ), m_pPerformanceManager( NULL ), m_pPerformanceController( NULL ), m_pTestDownloader( NULL ), m_pRenderQueue( NULL ), m_bClientSideReplayDisabled( false ) { }
CClientReplayContext::~CClientReplayContext() { delete m_pSessionBlockDownloader; delete m_pReplayManager; delete m_pScreenshotManager; delete m_pMovieManager; delete m_pPerformanceManager; delete m_pShared; delete m_pTestDownloader; }
bool CClientReplayContext::Init( CreateInterfaceFn fnFactory ) { m_pShared = new CSharedReplayContext( this ); m_pShared->m_strSubDir = SUBDIR_CLIENT; m_pShared->m_pRecordingSessionManager = new CClientRecordingSessionManager( this ); m_pShared->m_pRecordingSessionBlockManager = new CClientRecordingSessionBlockManager( this ); m_pShared->m_pErrorSystem = new CErrorSystem( this );
if ( !m_pShared->Init( fnFactory ) ) return false;
m_pPerformanceManager = new CReplayPerformanceManager(); m_pPerformanceManager->Init();
m_pReplayManager = new CReplayManager(); m_pReplayManager->Init( fnFactory );
m_pScreenshotManager = new CScreenshotManager(); m_pScreenshotManager->Init();
m_pMovieManager = new CReplayMovieManager(); m_pMovieManager->Init();
m_pRenderQueue = new CRenderQueue(); if ( !m_pRenderQueue ) return false;
m_pSessionBlockDownloader = new CSessionBlockDownloader(); if ( !m_pSessionBlockDownloader ) return false;
m_pPerformanceController = new CPerformanceController();
// Cleanup any unneeded block data from disk - cleanup is done on the fly, but this will clean up
// blocks from the olden days, when block data was not cleaned up properly.
CleanupUnneededBlocks();
return true; }
void CClientReplayContext::Shutdown() { // NOTE: Must come first, as any existing downloads are aborted and may cause status
// changes in replays, etc, which will need to be saved in CReplayManager::Shutdown(), etc.
m_pSessionBlockDownloader->Shutdown();
m_pShared->Shutdown(); m_pReplayManager->Shutdown(); m_pMovieManager->Shutdown(); }
void CClientReplayContext::DebugThink() { if ( !replay_debug.GetBool() ) return;
int iLine = 15;
// Recording session in progress
CClientRecordingSession *pRecordingSession = CL_GetRecordingSessionInProgress(); if ( pRecordingSession ) { g_pEngineClient->Con_NPrintf( iLine++, "SESSION IN PROGRESS:" ); g_pEngineClient->Con_NPrintf( iLine++, " BLOCKS: %i", pRecordingSession->GetNumBlocks() ); g_pEngineClient->Con_NPrintf( iLine++, " NAME: %s", pRecordingSession->m_strName.Get() ); g_pEngineClient->Con_NPrintf( iLine++, " URL: %s", pRecordingSession->m_strBaseDownloadURL.Get() ); g_pEngineClient->Con_NPrintf( iLine++, " LAST CONSECUTIVE BLOCK DOWNLOADED: %i", pRecordingSession->GetGreatestConsecutiveBlockDownloaded() ); g_pEngineClient->Con_NPrintf( iLine++, " LAST BLOCK TO DOWNLOAD: %i", pRecordingSession->GetLastBlockToDownload() ); } else { g_pEngineClient->Con_NPrintf( iLine++, "NO SESSION IN PROGRESS" ); }
iLine++;
// Server state
CClientRecordingSessionManager::ServerRecordingState_t *pServerState = &CL_GetRecordingSessionManager()->m_ServerRecordingState; g_pEngineClient->Con_NPrintf( iLine++, "SERVER STATE:" ); g_pEngineClient->Con_NPrintf( iLine++, " NAME: %s\n", pServerState->m_strSessionName.Get() ); g_pEngineClient->Con_NPrintf( iLine++, " DUMP INTERVAL: %i\n", pServerState->m_nDumpInterval ); g_pEngineClient->Con_NPrintf( iLine++, " CURRENT BLOCK: %i\n", pServerState->m_nCurrentBlock ); }
void CClientReplayContext::Think() { DebugThink();
if ( m_pTestDownloader ) { m_pTestDownloader->Think(); if ( m_pTestDownloader->IsDone() ) { delete m_pTestDownloader; m_pTestDownloader = NULL; } }
if ( !g_pReplay->IsReplayEnabled() ) return;
m_pShared->Think(); }
CReplay *CClientReplayContext::GetReplay( ReplayHandle_t hReplay ) { return m_pReplayManager->GetReplay( hReplay ); }
IReplayManager *CClientReplayContext::GetReplayManager() { return m_pReplayManager; }
IReplayScreenshotManager *CClientReplayContext::GetScreenshotManager() { return m_pScreenshotManager; }
IReplayPerformanceManager *CClientReplayContext::GetPerformanceManager() { return m_pPerformanceManager; }
IReplayPerformanceController *CClientReplayContext::GetPerformanceController() { return m_pPerformanceController; }
IReplayRenderQueue *CClientReplayContext::GetRenderQueue() { return m_pRenderQueue; }
void CClientReplayContext::SetMovieRenderer( IReplayMovieRenderer *pMovieRenderer ) { m_pMovieRenderer = pMovieRenderer; }
IReplayMovieRenderer *CClientReplayContext::GetMovieRenderer() { return m_pMovieRenderer; }
IReplayMovieManager *CClientReplayContext::GetMovieManager() { return m_pMovieManager; }
void CClientReplayContext::TestDownloader( const char *pURL ) { // Don't overwrite existing test
if ( m_pTestDownloader ) return;
// Download the file
m_pTestDownloader = new CHttpDownloader(); m_pTestDownloader->BeginDownload( pURL, NULL ); }
void CClientReplayContext::OnSignonStateFull() { // Notify the demo player that we've reached full signon state
if ( g_pEngineClient->IsPlayingReplayDemo() ) { g_pReplayDemoPlayer->OnSignonStateFull(); }
// Play a performance? This will play a performance from the beginning, if we're loading
// one (ie the 'watch' button in the details panel of the replay browser), or will continue
// playback if the user rewound while watching or editing a performance.
CL_GetPerformanceController()->OnSignonStateFull();
// If we're rendering, display the viewport
if ( CL_GetMovieManager()->IsRendering() ) { extern IClientReplay *g_pClient; g_pClient->OnRenderStart();
// Prepare audio system for recording.
g_pEngineClient->InitSoundRecord();
// Init renderer
IReplayMovie *pMovie = CL_GetMovieManager()->GetPendingMovie(); if ( !m_pMovieRenderer->SetupRenderer( CL_GetMovieManager()->GetRenderMovieSettings(), pMovie ) ) { Warning( "Render failed!\n" ); CL_GetMovieManager()->CancelRender(); } }
// If we're not rendering and are playing back a replay, initialize the performance editor -
// won't actually show until the user presses space, if they do at all.
else if ( g_pEngineClient->IsPlayingReplayDemo() ) { const CReplay *pReplay = g_pReplayDemoPlayer->GetCurrentReplay(); if ( pReplay ) { g_pClient->InitPerformanceEditor( pReplay->GetHandle() ); } else { AssertMsg( 0, "Replay should exist here!" ); Warning( "No current replay in demo player!\n" ); } } }
void CClientReplayContext::OnClientSideDisconnect() { if ( !g_pEngine->IsSupportedModAndPlatform() ) return;
// Reset replay_recording or we'll continue to think we're recording
extern ConVar replay_recording; replay_recording.SetValue( 0 );
if ( !g_pEngineClient->IsPlayingReplayDemo() ) { // Saves dangling replay, if there is one, clears out everything
// NOTE: We need to let the replay manager deal before we end the session, otherwise the
// state of the session will be cleared.
m_pReplayManager->OnClientSideDisconnect();
// Mark the session as no longer recording.
CClientRecordingSession *pSession = CL_GetRecordingSessionInProgress(); if ( pSession ) { pSession->OnStopRecording(); }
// Sets recording flag to false in session in progress, clears session in progress,
// and clears server state
CL_GetRecordingSessionManager()->OnSessionEnd(); } }
void CClientReplayContext::PlayReplay( ReplayHandle_t hReplay, int iPerformance, bool bPlaySound ) { CReplay *pReplay = m_pReplayManager->GetReplay( hReplay ); if ( !pReplay ) return;
if ( !ReconstructReplayIfNecessary( pReplay ) ) { Replay_MsgBox( iPerformance < 0 ? "#Replay_Err_User_FailedToPlayReplay" : "#Replay_Err_User_FailedToPlayTake" ); return; }
// Play a sound?
if ( bPlaySound ) { g_pClient->PlaySound( iPerformance >= 0 ? "replay\\playperformance.wav" : "replay\\playoriginalreplay.wav" ); }
// Play the replay!
g_pReplayDemoPlayer->PlayReplay( hReplay, iPerformance ); }
bool CClientReplayContext::ReconstructReplayIfNecessary( CReplay *pReplay ) { // If reconstruction hasn't happened yet, try to reconstruct
extern ConVar replay_forcereconstruct; if ( !pReplay->HasReconstructedReplay() || replay_forcereconstruct.GetBool() ) { if ( !Replay_Reconstruct( pReplay ) ) { CL_GetErrorSystem()->AddErrorFromTokenName( "#Replay_Err_Reconstruction_Fail" ); return false; } }
return true; }
void CClientReplayContext::OnPlayerSpawn() { DBG( "OnPlayerSpawn()\n" ); m_pReplayManager->AttemptToSetupNewReplay(); }
void CClientReplayContext::OnPlayerClassChanged() { DBG( "OnPlayerClassChanged()\n" ); m_pReplayManager->CompletePendingReplay(); }
void CClientReplayContext::GetPlaybackTimes( float &flOutTime, float &flOutLength, const CReplay *pReplay, const CReplayPerformance *pPerformance ) { flOutTime = 0.0f; flOutLength = 0.0f; // Get server start record tick
const int nServerRecordStartTick = CL_GetRecordingSessionManager()->GetServerStartTickForSession( pReplay->m_hSession );
// Don't let it be -1. Take performance in tick into account.
int nStartTick = MAX( 0, pReplay->m_nSpawnTick ); if ( pPerformance && pPerformance->m_nTickIn >= 0 ) { nStartTick = pPerformance->m_nTickIn; }
// Calculate length
const int nReplayEndTick = pReplay->m_nSpawnTick + g_pEngine->TimeToTicks( pReplay->m_flLength ); const int nEndTick = ( pPerformance && pPerformance->m_nTickOut > 0 ) ? pPerformance->m_nTickOut : nReplayEndTick; flOutLength = pPerformance ? g_pEngine->TicksToTime( nEndTick - nStartTick ) : pReplay->m_flLength;
// Calculate current time
const int nCurTick = MAX( g_pEngineClient->GetClientGlobalVars()->tickcount - nStartTick - nServerRecordStartTick, 0 ); flOutTime = MIN( g_pEngine->TicksToTime( nCurTick ), flOutLength ); }
uint64 CClientReplayContext::GetServerSessionId( ReplayHandle_t hReplay ) { CReplay *pReplay = GetReplay( hReplay ); if ( !pReplay ) return 0;
CClientRecordingSession *pSession = CL_CastSession( CL_GetRecordingSessionManager()->FindSession( pReplay->m_hSession ) ); if ( !pSession ) return 0;
return pSession->GetServerSessionID(); }
void CClientReplayContext::CleanupUnneededBlocks() { CL_GetRecordingSessionManager()->CleanupUnneededBlocks(); }
void CClientReplayContext::ReportErrorsToUser( wchar_t *pErrorText ) { // Display a message now
// Replay_MsgBox( pErrorText );
if ( !pErrorText || pErrorText[0] == L'\0' ) return;
const int nErrorLen = wcslen( pErrorText ); static char s_szError[1024]; wcstombs( s_szError, pErrorText, MIN( 1024, nErrorLen ) ); Warning( "Replay error system: %s\n", s_szError ); }
void CClientReplayContext::DisableReplayOnClient( bool bDisable ) { if ( m_bClientSideReplayDisabled == bDisable ) return;
m_bClientSideReplayDisabled = bDisable;
// Display a message to the user
Replay_HudMsg( bDisable ? "#Replay_ClientSideDisabled" : "#Replay_ClientSideEnabled", NULL, true ); }
//----------------------------------------------------------------------------------------
CClientRecordingSessionManager *CL_GetRecordingSessionManager() { return static_cast< CClientRecordingSessionManager * >( g_pClientReplayContextInternal->GetRecordingSessionManager() ); }
CClientRecordingSession *CL_GetRecordingSessionInProgress() { return CL_CastSession( CL_GetRecordingSessionManager()->GetRecordingSessionInProgress() ); }
//----------------------------------------------------------------------------------------
|