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.
375 lines
9.7 KiB
375 lines
9.7 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
//=======================================================================================//
|
||
|
|
||
|
#include "replay/replay.h"
|
||
|
#include "replay/iclientreplaycontext.h"
|
||
|
#include "replay/ireplaymanager.h"
|
||
|
#include "replay/replayutils.h"
|
||
|
#include "replay/screenshot.h"
|
||
|
#include "replay/shared_defs.h"
|
||
|
#include "replay/ireplayscreenshotmanager.h"
|
||
|
#include "replay/ireplayperformancemanager.h"
|
||
|
#include "replay/performance.h"
|
||
|
#include "KeyValues.h"
|
||
|
#include "filesystem.h"
|
||
|
#include "vgui/ILocalize.h"
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
//----------------------------------------------------------------------------------------
|
||
|
|
||
|
extern IClientReplayContext *g_pClientReplayContext;
|
||
|
extern vgui::ILocalize *g_pVGuiLocalize;
|
||
|
|
||
|
//----------------------------------------------------------------------------------------
|
||
|
|
||
|
CReplay::CReplay()
|
||
|
: m_pDownloadEventHandler( NULL ),
|
||
|
m_pUserData( NULL ),
|
||
|
m_bComplete( false ),
|
||
|
m_bRequestedByUser( false ),
|
||
|
m_bSaved( false ),
|
||
|
m_bRendered( false ),
|
||
|
m_bDirty( false ),
|
||
|
m_bSavedDuringThisSession( true ),
|
||
|
m_flLength( 0 ),
|
||
|
m_nPlayerSlot( -1 ),
|
||
|
m_nSpawnTick( -1 ),
|
||
|
m_nDeathTick( -1 ),
|
||
|
m_iMaxSessionBlockRequired( 0 ),
|
||
|
m_nStatus( REPLAYSTATUS_INVALID ),
|
||
|
m_hSession( REPLAY_HANDLE_INVALID ),
|
||
|
m_pFileURL( NULL ),
|
||
|
m_nPostDeathRecordTime( 0 ),
|
||
|
m_flStartTime( 0.0f ),
|
||
|
m_flNextUpdateTime( 0.0f )
|
||
|
{
|
||
|
m_wszTitle[0] = L'\0';
|
||
|
m_szMapName[0] = 0;
|
||
|
}
|
||
|
|
||
|
bool CReplay::IsDownloaded() const
|
||
|
{
|
||
|
return m_nStatus == REPLAYSTATUS_READYTOCONVERT;
|
||
|
}
|
||
|
|
||
|
const CReplayPerformance *CReplay::GetPerformance( int i ) const
|
||
|
{
|
||
|
return const_cast< CReplay * >( this )->GetPerformance( i );
|
||
|
}
|
||
|
|
||
|
CReplayPerformance *CReplay::GetPerformance( int i )
|
||
|
{
|
||
|
if ( i < 0 || i >= m_vecPerformances.Count() )
|
||
|
return NULL;
|
||
|
|
||
|
return m_vecPerformances[ i ];
|
||
|
}
|
||
|
|
||
|
bool CReplay::FindPerformance( CReplayPerformance *pPerformance, int &iResult )
|
||
|
{
|
||
|
const int it = m_vecPerformances.Find( pPerformance );
|
||
|
if ( it == m_vecPerformances.InvalidIndex() )
|
||
|
{
|
||
|
iResult = -1;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
iResult = it;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
CReplayPerformance *CReplay::GetPerformanceWithTitle( const wchar_t *pTitle )
|
||
|
{
|
||
|
FOR_EACH_VEC( m_vecPerformances, i )
|
||
|
{
|
||
|
CReplayPerformance *pCurPerformance = m_vecPerformances[ i ];
|
||
|
if ( !V_wcscmp( pTitle, pCurPerformance->m_wszTitle ) )
|
||
|
{
|
||
|
return pCurPerformance;
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
CReplayPerformance *CReplay::AddNewPerformance( bool bGenTitle/*=true*/, bool bGenFilename/*=true*/ )
|
||
|
{
|
||
|
// Create a performance
|
||
|
IReplayPerformanceManager *pPerformanceManager = g_pClientReplayContext->GetPerformanceManager();
|
||
|
CReplayPerformance *pPerformance = pPerformanceManager->CreatePerformance( this );
|
||
|
|
||
|
if ( bGenTitle )
|
||
|
{
|
||
|
// Give the performance a name
|
||
|
pPerformance->AutoNameIfHasNoTitle( m_szMapName );
|
||
|
}
|
||
|
|
||
|
if ( bGenFilename )
|
||
|
{
|
||
|
// Generate a filename for the new performance
|
||
|
pPerformance->SetFilename( pPerformanceManager->GeneratePerformanceFilename( this ) );
|
||
|
}
|
||
|
|
||
|
// Cache
|
||
|
m_vecPerformances.AddToTail( pPerformance );
|
||
|
|
||
|
return pPerformance;
|
||
|
}
|
||
|
|
||
|
void CReplay::AddPerformance( KeyValues *pIn )
|
||
|
{
|
||
|
// Create a performance
|
||
|
IReplayPerformanceManager *pPerformanceManager = g_pClientReplayContext->GetPerformanceManager();
|
||
|
CReplayPerformance *pPerformance = pPerformanceManager->CreatePerformance( this );
|
||
|
|
||
|
// Read
|
||
|
pPerformance->Read( pIn );
|
||
|
|
||
|
// Cache
|
||
|
m_vecPerformances.AddToTail( pPerformance );
|
||
|
}
|
||
|
|
||
|
void CReplay::AddPerformance( CReplayPerformance *pPerformance )
|
||
|
{
|
||
|
Assert( pPerformance );
|
||
|
m_vecPerformances.AddToTail( pPerformance );
|
||
|
}
|
||
|
|
||
|
const CReplayTime &CReplay::GetItemDate() const
|
||
|
{
|
||
|
return m_RecordTime;
|
||
|
}
|
||
|
|
||
|
bool CReplay::IsItemRendered() const
|
||
|
{
|
||
|
return m_bRendered;
|
||
|
}
|
||
|
|
||
|
CReplay *CReplay::GetItemReplay()
|
||
|
{
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
ReplayHandle_t CReplay::GetItemReplayHandle() const
|
||
|
{
|
||
|
return GetHandle();
|
||
|
}
|
||
|
|
||
|
QueryableReplayItemHandle_t CReplay::GetItemHandle() const
|
||
|
{
|
||
|
return GetHandle();
|
||
|
}
|
||
|
|
||
|
const wchar_t *CReplay::GetItemTitle() const
|
||
|
{
|
||
|
return m_wszTitle;
|
||
|
}
|
||
|
|
||
|
void CReplay::SetItemTitle( const wchar_t *pTitle )
|
||
|
{
|
||
|
V_wcsncpy( m_wszTitle, pTitle, sizeof( m_wszTitle ) );
|
||
|
}
|
||
|
|
||
|
float CReplay::GetItemLength() const
|
||
|
{
|
||
|
return m_flLength;
|
||
|
}
|
||
|
|
||
|
void *CReplay::GetUserData()
|
||
|
{
|
||
|
return m_pUserData;
|
||
|
}
|
||
|
|
||
|
void CReplay::SetUserData( void* pUserData )
|
||
|
{
|
||
|
m_pUserData = pUserData;
|
||
|
}
|
||
|
|
||
|
bool CReplay::IsItemAMovie() const
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void CReplay::AddScreenshot( int nWidth, int nHeight, const char *pBaseFilename )
|
||
|
{
|
||
|
m_vecScreenshots.AddToTail( new CReplayScreenshot( nWidth, nHeight, pBaseFilename ) );
|
||
|
}
|
||
|
|
||
|
void CReplay::AutoNameTitleIfEmpty()
|
||
|
{
|
||
|
// Autoname it
|
||
|
if ( !m_wszTitle[0] )
|
||
|
{
|
||
|
Replay_GetAutoName( m_wszTitle, sizeof( m_wszTitle ), m_szMapName );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const char *CReplay::GetSubKeyTitle() const
|
||
|
{
|
||
|
return Replay_va( "replay_%i", GetHandle() );
|
||
|
}
|
||
|
|
||
|
const char *CReplay::GetPath() const
|
||
|
{
|
||
|
return Replay_va( "%s%s%c", g_pClientReplayContext->GetBaseDir(), SUBDIR_REPLAYS, CORRECT_PATH_SEPARATOR );
|
||
|
}
|
||
|
|
||
|
void CReplay::OnDelete()
|
||
|
{
|
||
|
BaseClass::OnDelete();
|
||
|
|
||
|
// Delete reconstructed replay if one exists
|
||
|
if ( HasReconstructedReplay() )
|
||
|
{
|
||
|
g_pFullFileSystem->RemoveFile( m_strReconstructedFilename.Get() );
|
||
|
}
|
||
|
|
||
|
// Delete screenshots
|
||
|
g_pClientReplayContext->GetScreenshotManager()->DeleteScreenshotsForReplay( this );
|
||
|
|
||
|
// TODO: Delete performance(s)
|
||
|
}
|
||
|
|
||
|
bool CReplay::Read( KeyValues *pIn )
|
||
|
{
|
||
|
if ( !BaseClass::Read( pIn ) )
|
||
|
return false;
|
||
|
|
||
|
m_hSession = (ReplayHandle_t)pIn->GetInt( "session", REPLAY_HANDLE_INVALID );
|
||
|
V_strcpy_safe( m_szMapName, pIn->GetString( "map", "" ) );
|
||
|
m_nSpawnTick = pIn->GetInt( "spawn_tick", -1 );
|
||
|
m_nDeathTick = pIn->GetInt( "death_tick", -1 );
|
||
|
m_nStatus = static_cast< CReplay::ReplayStatus_t >( pIn->GetInt( "status", (int)CReplay::REPLAYSTATUS_INVALID ) );
|
||
|
m_bComplete = pIn->GetInt( "complete" ) != 0;
|
||
|
m_flLength = pIn->GetFloat( "length" );
|
||
|
m_nPostDeathRecordTime = pIn->GetInt( "postdeathrecordtime" );
|
||
|
m_bRendered = pIn->GetInt( "rendered" ) != 0;
|
||
|
m_nPlayerSlot = pIn->GetInt( "player_slot", -1 );
|
||
|
m_iMaxSessionBlockRequired = pIn->GetInt( "max_block", 0 ); Assert( m_iMaxSessionBlockRequired >= 0 );
|
||
|
m_flStartTime = pIn->GetFloat( "start_time", -1.0f ); Assert( m_flStartTime >= 0.0f );
|
||
|
V_wcsncpy( m_wszTitle, pIn->GetWString( "title" ), sizeof( m_wszTitle ) );
|
||
|
|
||
|
// Read reconstructed filename and infer path
|
||
|
const char *pReplaysDir = g_pClientReplayContext->GetReplayManager()->GetReplaysDir();
|
||
|
const char *pReconFilename = pIn->GetString( "recon_filename" );
|
||
|
if ( pReconFilename[0] != 0 )
|
||
|
{
|
||
|
m_strReconstructedFilename = Replay_va( "%s%s", pReplaysDir, pReconFilename );
|
||
|
}
|
||
|
|
||
|
// Read screenshots
|
||
|
KeyValues *pScreenshots = pIn->FindKey( "screenshots" );
|
||
|
if ( pScreenshots )
|
||
|
{
|
||
|
FOR_EACH_TRUE_SUBKEY( pScreenshots, pScreenshot )
|
||
|
{
|
||
|
int nWidth = pScreenshot->GetInt( "width" );
|
||
|
int nHeight = pScreenshot->GetInt( "height" );
|
||
|
const char *pBaseFilename = pScreenshot->GetString( "base_filename" );
|
||
|
AddScreenshot( nWidth, nHeight, pBaseFilename );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Read performances
|
||
|
KeyValues *pPerformances = pIn->FindKey( "edits" );
|
||
|
if ( pPerformances )
|
||
|
{
|
||
|
FOR_EACH_TRUE_SUBKEY( pPerformances, pPerformance )
|
||
|
{
|
||
|
AddPerformance( pPerformance );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Record time
|
||
|
KeyValues *pRecordTimeSubKey = pIn->FindKey( "record_time" );
|
||
|
if ( pRecordTimeSubKey )
|
||
|
{
|
||
|
m_RecordTime.Read( pRecordTimeSubKey );
|
||
|
}
|
||
|
|
||
|
// Mark replay as saved, since it was just loaded from disk
|
||
|
m_bSaved = true;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void CReplay::Write( KeyValues *pOut )
|
||
|
{
|
||
|
BaseClass::Write( pOut );
|
||
|
|
||
|
pOut->SetString( "map", m_szMapName );
|
||
|
pOut->SetInt( "session", m_hSession );
|
||
|
pOut->SetInt( "spawn_tick", m_nSpawnTick );
|
||
|
pOut->SetInt( "death_tick", m_nDeathTick );
|
||
|
pOut->SetInt( "status", static_cast< int >( m_nStatus ) );
|
||
|
pOut->SetInt( "complete", static_cast< int >( m_bComplete ) );
|
||
|
pOut->SetFloat( "length", m_flLength );
|
||
|
pOut->SetInt( "postdeathrecordtime", m_nPostDeathRecordTime );
|
||
|
pOut->SetInt( "rendered", m_bRendered );
|
||
|
pOut->SetInt( "player_slot", m_nPlayerSlot );
|
||
|
pOut->SetInt( "max_block", m_iMaxSessionBlockRequired );
|
||
|
pOut->SetFloat( "start_time", m_flStartTime );
|
||
|
pOut->SetWString( "title", m_wszTitle );
|
||
|
|
||
|
// Store only filename for reconstructed .dem
|
||
|
if ( !m_strReconstructedFilename.IsEmpty() )
|
||
|
{
|
||
|
const char *pReconFilename = V_UnqualifiedFileName( m_strReconstructedFilename.Get() );
|
||
|
if ( pReconFilename[0] )
|
||
|
{
|
||
|
pOut->SetString( "recon_filename", pReconFilename );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Write screenshots
|
||
|
KeyValues *pScreenshots = new KeyValues( "screenshots" );
|
||
|
pOut->AddSubKey( pScreenshots );
|
||
|
for ( int i = 0; i < m_vecScreenshots.Count(); ++i )
|
||
|
{
|
||
|
KeyValues *pScreenshotOut = new KeyValues( "screenshot" );
|
||
|
CReplayScreenshot *pScreenshot = m_vecScreenshots[ i ];
|
||
|
pScreenshotOut->SetInt( "width", pScreenshot->m_nWidth );
|
||
|
pScreenshotOut->SetInt( "height", pScreenshot->m_nHeight );
|
||
|
pScreenshotOut->SetString( "base_filename", pScreenshot->m_szBaseFilename );
|
||
|
pScreenshots->AddSubKey( pScreenshotOut );
|
||
|
}
|
||
|
|
||
|
// Write performances
|
||
|
KeyValues *pPerformances = new KeyValues( "edits" );
|
||
|
pOut->AddSubKey( pPerformances );
|
||
|
for ( int i = 0; i < m_vecPerformances.Count(); ++i )
|
||
|
{
|
||
|
KeyValues *pPerfOut = new KeyValues( "edit" );
|
||
|
CReplayPerformance *pPerformance = m_vecPerformances[ i ];
|
||
|
pPerformance->Write( pPerfOut );
|
||
|
pPerformances->AddSubKey( pPerfOut );
|
||
|
}
|
||
|
|
||
|
KeyValues *pRecordTime = new KeyValues( "record_time" );
|
||
|
pOut->AddSubKey( pRecordTime );
|
||
|
m_RecordTime.Write( pRecordTime );
|
||
|
|
||
|
// Mark as saved
|
||
|
m_bSaved = true;
|
||
|
}
|
||
|
|
||
|
bool CReplay::HasReconstructedReplay() const
|
||
|
{
|
||
|
return !m_strReconstructedFilename.IsEmpty() &&
|
||
|
g_pFullFileSystem->FileExists( m_strReconstructedFilename.Get() );
|
||
|
}
|
||
|
|
||
|
bool CReplay::IsSignificantBlock( int iBlockReconstruction ) const
|
||
|
{
|
||
|
return iBlockReconstruction <= m_iMaxSessionBlockRequired;
|
||
|
}
|
||
|
|
||
|
void CReplay::OnComplete()
|
||
|
{
|
||
|
AutoNameTitleIfEmpty();
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------------------
|