Files
FC1/CryGame/XDemoMgr.cpp
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

280 lines
7.4 KiB
C++

//////////////////////////////////////////////////////////////////////
//
// Crytek Source code
// Copyright (c) Crytek 2001-2004
//
// XDemoMgr.cpp: implementation of the CXDemoMgr class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "XDemoMgr.h"
#include "TimeDemoRecorder.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
CXDemoMgr::CXDemoMgr()
{
m_pFile=NULL;
Stop();
}
//////////////////////////////////////////////////////////////////////////
CXDemoMgr::~CXDemoMgr()
{
Stop();
}
//////////////////////////////////////////////////////////////////////////
void CXDemoMgr::Stop()
{
m_nFileSize = 0;
m_fCurrTime = 0;
m_fGameTime = 0;
if(m_pFile) fclose(m_pFile);
m_pFile = NULL;
bStreamStarted = false;
};
#define DEMOMAGIC1 "CRYDEMO"
#define DEMOMAGIC2 "CRYSTREAM"
#define DEMOVERSION 1
//////////////////////////////////////////////////////////////////////////
// Prepares recording of a new demo.
bool CXDemoMgr::StartRecord(const char *sFileName, CStream &stm)
{
Stop();
m_pFile=fopen(sFileName,"wb+");
if(m_pFile==NULL)
return false;
// header: string + version + savegamesize
if(fwrite(DEMOMAGIC1, strlen(DEMOMAGIC1)+1, 1, m_pFile)!=1)
return false;
int hd[2] = { DEMOVERSION, (int)stm.GetSize() };
if(fwrite(&hd, sizeof(hd), 1, m_pFile)!=1)
return false;
// savegame, has its own header and version
if(fwrite(stm.GetPtr(), BITS2BYTES(stm.GetSize()), 1, m_pFile)!=1)
return false;
// header for the network stream part, just as sanity check
if(fwrite(DEMOMAGIC2, strlen(DEMOMAGIC2)+1, 1,m_pFile)!=1)
return false;
return true;
}
//////////////////////////////////////////////////////////////////////////
// Stores a chunk of demo-data on disk.
bool CXDemoMgr::AddChunk(float fTimestamp, CStream &stm, IEntity *player)
{
unsigned int nStreamSizeInBytes=BITS2BYTES(stm.GetSize());
unsigned int nStreamSizeInBits=stm.GetSize();
if(nStreamSizeInBits==0)
return false;
if(m_pFile==NULL)
return false;
if(fwrite(&fTimestamp,sizeof(fTimestamp),1,m_pFile)!=1)
return false;
if(fwrite(&nStreamSizeInBits,sizeof(nStreamSizeInBits),1,m_pFile)!=1)
return false;
if(fwrite(stm.GetPtr(),nStreamSizeInBytes,1,m_pFile)!=1)
return false;
if(fwrite(&player->GetAngles(),sizeof(Vec3d),1,m_pFile)!=1)
return false;
return true;
}
//////////////////////////////////////////////////////////////////////////
// Opens a recorded demo for playback.
bool CXDemoMgr::StartDemoPlay(const char *sFileName, CXGame *pGame)
{
Stop();
m_pGame = pGame;
m_pFile=fopen(sFileName,"rb");
if(m_pFile==NULL)
return false;
//measure the file size
fseek(m_pFile,0,SEEK_END);
m_nFileSize=ftell(m_pFile);
fseek(m_pFile,0,SEEK_SET);
char magic[32];
if(fread(magic, strlen(DEMOMAGIC1)+1, 1, m_pFile)!=1 || strcmp(magic, DEMOMAGIC1))
return false;
int hd[2];
if(fread(hd, sizeof(hd), 1, m_pFile)!=1 || hd[0]!=DEMOVERSION)
return false;
int nNumBits = hd[1];
int nNumBytes = BITS2BYTES(nNumBits);
CDefaultStreamAllocator sa;
CStream stm(300, &sa);
stm.SetSize(nNumBits);
if(fread(stm.GetPtr(), nNumBytes, 1, m_pFile)!=1)
return false;
if(!pGame->LoadFromStream(stm, true))
return false;
if(fread(magic, strlen(DEMOMAGIC2)+1, 1, m_pFile)!=1 || strcmp(magic, DEMOMAGIC2))
return false;
//read the first chunk
if(!ReadChunk(m_ChunkToPlay.stm, m_ChunkToPlay.fTimestamp, m_ChunkToPlay.angles))
return false;
//set the chunk as non played
m_ChunkToPlay.bPlayed=false;
//set the first chunk timestamp as "base time"
m_fCurrTime=m_ChunkToPlay.fTimestamp;
pGame->GetSystem()->GetIConsole()->ShowConsole(false);
bStreamStarted = true;
return true;
}
//////////////////////////////////////////////////////////////////////////
// Play a recorded demo.
bool CXDemoMgr::PlayChunk(float fCurrentTime, CXClient *pClient)
{
if(!m_pFile || !bStreamStarted)
return false;
//the first time this function is called the base timer is initialized
if(m_fGameTime==0)
m_fGameTime = fCurrentTime;
m_fCurrTime += fCurrentTime-m_fGameTime;
m_fGameTime = fCurrentTime;
if(m_fCurrTime==0 || ((m_fCurrTime>m_ChunkToPlay.fTimestamp) && (!m_ChunkToPlay.bPlayed)))
{
m_ChunkToPlay.bPlayed=false;
m_ChunkToPlay.stm.Seek(0);
pClient->OnXData(m_ChunkToPlay.stm);
IEntity *e = m_pGame->GetXSystem()->GetEntity(pClient->GetPlayerId());
e->SetAngles(m_ChunkToPlay.angles);
if(!EOD())
{
ReadChunk(m_ChunkToPlay.stm, m_ChunkToPlay.fTimestamp, m_ChunkToPlay.angles);
m_ChunkToPlay.bPlayed = false;
}
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
// Reads a demo-chunk from disk.
bool CXDemoMgr::ReadChunk(CStream &stm, float &fTimestamp, Vec3d &angles)
{
unsigned int nStreamSizeInBytes;
unsigned int nStreamSizeInBits;
if(m_pFile==NULL)
return false;
stm.Reset();
if(fread(&fTimestamp, sizeof(fTimestamp),1,m_pFile)!=1)
return false;
if(fread(&nStreamSizeInBits,sizeof(nStreamSizeInBits),1,m_pFile)!=1)
return false;
nStreamSizeInBytes = BITS2BYTES(nStreamSizeInBits);
#ifdef _DEBUG
if(nStreamSizeInBits>stm.GetAllocatedSize())
CryError( "<CryGame> (CXDemoMgr::ReadChunk) Invalid stream size" );
#endif
stm.SetSize(nStreamSizeInBits);
if(fread(stm.GetPtr(), nStreamSizeInBytes, 1, m_pFile)!=1)
return false;
if(fread(&angles, sizeof(Vec3d), 1, m_pFile)!=1)
return false;
return true;
}
//////////////////////////////////////////////////////////////////////////
// Check if the end of the demo is reached.
bool CXDemoMgr::EOD()
{
if(m_pFile==NULL)
return true;
if(ftell(m_pFile)<int(m_nFileSize))
return false;
return true;
}
//////////////////////////////////////////////////////////////////////////
//! Start recording a demo.
bool CXGame::StartRecording(const char *sFileName)
{
g_timedemo_file->Set( sFileName );
m_pTimeDemoRecorder->Record(true);
return false;
}
//////////////////////////////////////////////////////////////////////////
//! Stop recording a demo.
void CXGame::StopRecording()
{
m_pTimeDemoRecorder->Record(false);
String filename = m_currentLevelFolder + "/" + g_timedemo_file->GetString() + ".tmd";
m_pTimeDemoRecorder->Save( filename.c_str() );
}
//////////////////////////////////////////////////////////////////////////
//! Start playing a demo.
void CXGame::StartDemoPlay(const char *sFileName)
{
g_timedemo_file->Set( sFileName );
String filename = m_currentLevelFolder + "/" + g_timedemo_file->GetString() + ".tmd";
m_pTimeDemoRecorder->Load( filename.c_str() );
m_pTimeDemoRecorder->Play(true);
};
//////////////////////////////////////////////////////////////////////////
//! Stop playing a demo.
void CXGame::StopDemoPlay()
{
if (m_pTimeDemoRecorder->IsRecording())
{
StopRecording();
}
m_pTimeDemoRecorder->Play(false);
};
//////////////////////////////////////////////////////////////////////////
void CXGame::PlaybackChunk()
{
m_XDemoMgr.PlayChunk(GetSystem()->GetITimer()->GetCurrTime(), m_pClient);
};
//////////////////////////////////////////////////////////////////////////
//! Adding a chunk to the demo.
bool CXGame::AddDemoChunk(CStream &stm)
{
IEntity *e = GetXSystem()->GetEntity(m_pClient->GetPlayerId());
assert(e);
return m_XDemoMgr.AddChunk(GetSystem()->GetITimer()->GetCurrTime(), stm, e);
}