mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 01:58:19 +00:00
Fixed several issues with the ghost (thanks to Learath2)
This commit is contained in:
parent
9289deb5c7
commit
de1c0cf24d
|
@ -183,10 +183,6 @@ public:
|
||||||
virtual void RaceRecord_Stop() = 0;
|
virtual void RaceRecord_Stop() = 0;
|
||||||
virtual bool RaceRecord_IsRecording() = 0;
|
virtual bool RaceRecord_IsRecording() = 0;
|
||||||
|
|
||||||
virtual void GhostRecorder_Start(const char *pFilename, const char *pPlayerName) = 0;
|
|
||||||
virtual bool GhostLoader_Load(const char *pFilename) = 0;
|
|
||||||
virtual bool GhostLoader_GetGhostInfo(const char *pFilename, struct CGhostHeader *pGhostHeader) = 0;
|
|
||||||
|
|
||||||
virtual void DemoSliceBegin() = 0;
|
virtual void DemoSliceBegin() = 0;
|
||||||
virtual void DemoSliceEnd() = 0;
|
virtual void DemoSliceEnd() = 0;
|
||||||
virtual void DemoSlice(const char *pDstPath, CLIENTFUNC_FILTER pfnFilter, void *pUser) = 0;
|
virtual void DemoSlice(const char *pDstPath, CLIENTFUNC_FILTER pfnFilter, void *pUser) = 0;
|
||||||
|
|
|
@ -2570,6 +2570,9 @@ void CClient::InitInterfaces()
|
||||||
|
|
||||||
m_Friends.Init();
|
m_Friends.Init();
|
||||||
m_Foes.Init(true);
|
m_Foes.Init(true);
|
||||||
|
|
||||||
|
m_GhostRecorder.Init();
|
||||||
|
m_GhostLoader.Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClient::Run()
|
void CClient::Run()
|
||||||
|
@ -3620,21 +3623,6 @@ bool CClient::RaceRecord_IsRecording()
|
||||||
return m_DemoRecorder[RECORDER_RACE].IsRecording();
|
return m_DemoRecorder[RECORDER_RACE].IsRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClient::GhostRecorder_Start(const char *pFilename, const char *pPlayerName)
|
|
||||||
{
|
|
||||||
m_GhostRecorder.Start(Storage(), m_pConsole, pFilename, m_aCurrentMap, m_pMap->Crc(), pPlayerName);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CClient::GhostLoader_Load(const char *pFilename)
|
|
||||||
{
|
|
||||||
return m_GhostLoader.Load(Storage(), m_pConsole, pFilename, m_aCurrentMap, m_pMap->Crc()) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CClient::GhostLoader_GetGhostInfo(const char *pFilename, struct CGhostHeader *pGhostHeader)
|
|
||||||
{
|
|
||||||
return m_GhostLoader.GetGhostInfo(Storage(), m_pConsole, pFilename, pGhostHeader, m_aCurrentMap, m_pMap->Crc());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void CClient::RequestDDNetInfo()
|
void CClient::RequestDDNetInfo()
|
||||||
{
|
{
|
||||||
|
|
|
@ -391,10 +391,6 @@ public:
|
||||||
void RaceRecord_Stop();
|
void RaceRecord_Stop();
|
||||||
bool RaceRecord_IsRecording();
|
bool RaceRecord_IsRecording();
|
||||||
|
|
||||||
void GhostRecorder_Start(const char *pFilename, const char *pPlayerName);
|
|
||||||
bool GhostLoader_Load(const char *pFilename);
|
|
||||||
bool GhostLoader_GetGhostInfo(const char *pFilename, struct CGhostHeader *pGhostHeader);
|
|
||||||
|
|
||||||
virtual void DemoSliceBegin();
|
virtual void DemoSliceBegin();
|
||||||
virtual void DemoSliceEnd();
|
virtual void DemoSliceEnd();
|
||||||
virtual void DemoSlice(const char *pDstPath, CLIENTFUNC_FILTER pfnFilter, void *pUser);
|
virtual void DemoSlice(const char *pDstPath, CLIENTFUNC_FILTER pfnFilter, void *pUser);
|
||||||
|
|
|
@ -5,8 +5,9 @@
|
||||||
|
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
|
|
||||||
struct CGhostHeader
|
class CGhostHeader
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
unsigned char m_aMarker[8];
|
unsigned char m_aMarker[8];
|
||||||
unsigned char m_Version;
|
unsigned char m_Version;
|
||||||
char m_aOwner[MAX_NAME_LENGTH];
|
char m_aOwner[MAX_NAME_LENGTH];
|
||||||
|
@ -14,6 +15,16 @@ struct CGhostHeader
|
||||||
unsigned char m_aCrc[4];
|
unsigned char m_aCrc[4];
|
||||||
unsigned char m_aNumTicks[4];
|
unsigned char m_aNumTicks[4];
|
||||||
unsigned char m_aTime[4];
|
unsigned char m_aTime[4];
|
||||||
|
|
||||||
|
int GetTime() const
|
||||||
|
{
|
||||||
|
return (m_aTime[0] << 24) | (m_aTime[1] << 16) | (m_aTime[2] << 8) | (m_aTime[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetTicks() const
|
||||||
|
{
|
||||||
|
return (m_aNumTicks[0] << 24) | (m_aNumTicks[1] << 16) | (m_aNumTicks[2] << 8) | (m_aNumTicks[3]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class IGhostRecorder : public IInterface
|
class IGhostRecorder : public IInterface
|
||||||
|
@ -21,9 +32,11 @@ class IGhostRecorder : public IInterface
|
||||||
MACRO_INTERFACE("ghostrecorder", 0)
|
MACRO_INTERFACE("ghostrecorder", 0)
|
||||||
public:
|
public:
|
||||||
virtual ~IGhostRecorder() {}
|
virtual ~IGhostRecorder() {}
|
||||||
|
|
||||||
|
virtual int Start(const char *pFilename, const char *pMap, unsigned MapCrc, const char *pName) = 0;
|
||||||
virtual int Stop(int Ticks, int Time) = 0;
|
virtual int Stop(int Ticks, int Time) = 0;
|
||||||
|
|
||||||
virtual void WriteData(int Type, const char *pData, int Size) = 0;
|
virtual void WriteData(int Type, const void *pData, int Size) = 0;
|
||||||
virtual bool IsRecording() const = 0;
|
virtual bool IsRecording() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -32,15 +45,16 @@ class IGhostLoader : public IInterface
|
||||||
MACRO_INTERFACE("ghostloader", 0)
|
MACRO_INTERFACE("ghostloader", 0)
|
||||||
public:
|
public:
|
||||||
virtual ~IGhostLoader() {}
|
virtual ~IGhostLoader() {}
|
||||||
|
|
||||||
|
virtual int Load(const char *pFilename, const char *pMap, unsigned Crc) = 0;
|
||||||
virtual void Close() = 0;
|
virtual void Close() = 0;
|
||||||
|
|
||||||
virtual const CGhostHeader *GetHeader() const = 0;
|
virtual const CGhostHeader *GetHeader() const = 0;
|
||||||
|
|
||||||
virtual bool ReadNextType(int *pType) = 0;
|
virtual bool ReadNextType(int *pType) = 0;
|
||||||
virtual bool ReadData(int Type, char *pData, int Size) = 0;
|
virtual bool ReadData(int Type, void *pData, int Size) = 0;
|
||||||
|
|
||||||
virtual int GetTime(const CGhostHeader *pHeader) const = 0;
|
virtual bool GetGhostInfo(const char *pFilename, CGhostHeader *pGhostHeader, const char *pMap, unsigned Crc) = 0;
|
||||||
virtual int GetTicks(const CGhostHeader *pHeader) const = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -17,12 +17,16 @@ CGhostRecorder::CGhostRecorder()
|
||||||
ResetBuffer();
|
ResetBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record
|
void CGhostRecorder::Init()
|
||||||
int CGhostRecorder::Start(IStorage *pStorage, IConsole *pConsole, const char *pFilename, const char *pMap, unsigned Crc, const char* pName)
|
|
||||||
{
|
{
|
||||||
m_pConsole = pConsole;
|
m_pConsole = Kernel()->RequestInterface<IConsole>();
|
||||||
|
m_pStorage = Kernel()->RequestInterface<IStorage>();
|
||||||
|
}
|
||||||
|
|
||||||
m_File = pStorage->OpenFile(pFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE);
|
// Record
|
||||||
|
int CGhostRecorder::Start(const char *pFilename, const char *pMap, unsigned Crc, const char* pName)
|
||||||
|
{
|
||||||
|
m_File = m_pStorage->OpenFile(pFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE);
|
||||||
if(!m_File)
|
if(!m_File)
|
||||||
{
|
{
|
||||||
char aBuf[256];
|
char aBuf[256];
|
||||||
|
@ -71,7 +75,7 @@ static void DiffItem(int *pPast, int *pCurrent, int *pOut, int Size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGhostRecorder::WriteData(int Type, const char *pData, int Size)
|
void CGhostRecorder::WriteData(int Type, const void *pData, int Size)
|
||||||
{
|
{
|
||||||
if(!m_File || (unsigned)Size > MAX_ITEM_SIZE || Size <= 0 || Type == -1)
|
if(!m_File || (unsigned)Size > MAX_ITEM_SIZE || Size <= 0 || Type == -1)
|
||||||
return;
|
return;
|
||||||
|
@ -167,6 +171,12 @@ CGhostLoader::CGhostLoader()
|
||||||
ResetBuffer();
|
ResetBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CGhostLoader::Init()
|
||||||
|
{
|
||||||
|
m_pConsole = Kernel()->RequestInterface<IConsole>();
|
||||||
|
m_pStorage = Kernel()->RequestInterface<IStorage>();
|
||||||
|
}
|
||||||
|
|
||||||
void CGhostLoader::ResetBuffer()
|
void CGhostLoader::ResetBuffer()
|
||||||
{
|
{
|
||||||
m_pBufferPos = m_aBuffer;
|
m_pBufferPos = m_aBuffer;
|
||||||
|
@ -175,10 +185,9 @@ void CGhostLoader::ResetBuffer()
|
||||||
m_BufferPrevItem = -1;
|
m_BufferPrevItem = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CGhostLoader::Load(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, const char *pMap, unsigned Crc)
|
int CGhostLoader::Load(const char *pFilename, const char *pMap, unsigned Crc)
|
||||||
{
|
{
|
||||||
m_pConsole = pConsole;
|
m_File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, IStorage::TYPE_SAVE);
|
||||||
m_File = pStorage->OpenFile(pFilename, IOFLAG_READ, IStorage::TYPE_SAVE);
|
|
||||||
if(!m_File)
|
if(!m_File)
|
||||||
{
|
{
|
||||||
char aBuf[256];
|
char aBuf[256];
|
||||||
|
@ -299,7 +308,7 @@ static void UndiffItem(int *pPast, int *pDiff, int *pOut, int Size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CGhostLoader::ReadData(int Type, char *pData, int Size)
|
bool CGhostLoader::ReadData(int Type, void *pData, int Size)
|
||||||
{
|
{
|
||||||
if(!m_File || Size > MAX_ITEM_SIZE || Size <= 0 || Type == -1)
|
if(!m_File || Size > MAX_ITEM_SIZE || Size <= 0 || Type == -1)
|
||||||
return false;
|
return false;
|
||||||
|
@ -327,14 +336,14 @@ void CGhostLoader::Close()
|
||||||
m_File = 0;
|
m_File = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CGhostLoader::GetGhostInfo(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, CGhostHeader *pGhostHeader, const char *pMap, unsigned Crc) const
|
bool CGhostLoader::GetGhostInfo(const char *pFilename, CGhostHeader *pGhostHeader, const char *pMap, unsigned Crc)
|
||||||
{
|
{
|
||||||
if(!pGhostHeader)
|
if(!pGhostHeader)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mem_zero(pGhostHeader, sizeof(CGhostHeader));
|
mem_zero(pGhostHeader, sizeof(CGhostHeader));
|
||||||
|
|
||||||
IOHANDLE File = pStorage->OpenFile(pFilename, IOFLAG_READ, IStorage::TYPE_SAVE);
|
IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, IStorage::TYPE_SAVE);
|
||||||
if(!File)
|
if(!File)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -344,43 +353,29 @@ bool CGhostLoader::GetGhostInfo(class IStorage *pStorage, class IConsole *pConso
|
||||||
{
|
{
|
||||||
io_close(File);
|
io_close(File);
|
||||||
// old version... try to update
|
// old version... try to update
|
||||||
if(CGhostUpdater::Update(pStorage, pConsole, pFilename))
|
IGhostRecorder *pRecorder = Kernel()->RequestInterface<IGhostRecorder>();
|
||||||
|
if(CGhostUpdater::Update(pRecorder, m_pStorage, m_pConsole, pFilename))
|
||||||
{
|
{
|
||||||
// try again
|
// try again
|
||||||
File = pStorage->OpenFile(pFilename, IOFLAG_READ, IStorage::TYPE_SAVE);
|
File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, IStorage::TYPE_SAVE);
|
||||||
io_read(File, pGhostHeader, sizeof(CGhostHeader));
|
io_read(File, pGhostHeader, sizeof(CGhostHeader));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
io_close(File);
|
||||||
|
|
||||||
if(mem_comp(pGhostHeader->m_aMarker, gs_aHeaderMarker, sizeof(gs_aHeaderMarker)) || (pGhostHeader->m_Version != gs_ActVersion && pGhostHeader->m_Version != 4))
|
if(mem_comp(pGhostHeader->m_aMarker, gs_aHeaderMarker, sizeof(gs_aHeaderMarker)) || (pGhostHeader->m_Version != gs_ActVersion && pGhostHeader->m_Version != 4))
|
||||||
{
|
|
||||||
io_close(File);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
unsigned GhostMapCrc = (pGhostHeader->m_aCrc[0] << 24) | (pGhostHeader->m_aCrc[1] << 16) | (pGhostHeader->m_aCrc[2] << 8) | (pGhostHeader->m_aCrc[3]);
|
unsigned GhostMapCrc = (pGhostHeader->m_aCrc[0] << 24) | (pGhostHeader->m_aCrc[1] << 16) | (pGhostHeader->m_aCrc[2] << 8) | (pGhostHeader->m_aCrc[3]);
|
||||||
if(str_comp(pGhostHeader->m_aMap, pMap) != 0 || GhostMapCrc != Crc)
|
if(str_comp(pGhostHeader->m_aMap, pMap) != 0 || GhostMapCrc != Crc)
|
||||||
{
|
|
||||||
io_close(File);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
io_close(File);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CGhostLoader::GetTime(const CGhostHeader *pHeader) const
|
|
||||||
{
|
|
||||||
return (pHeader->m_aTime[0] << 24) | (pHeader->m_aTime[1] << 16) | (pHeader->m_aTime[2] << 8) | (pHeader->m_aTime[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
int CGhostLoader::GetTicks(const CGhostHeader *pHeader) const
|
|
||||||
{
|
|
||||||
return (pHeader->m_aNumTicks[0] << 24) | (pHeader->m_aNumTicks[1] << 16) | (pHeader->m_aNumTicks[2] << 8) | (pHeader->m_aNumTicks[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void StrToInts(int *pInts, int Num, const char *pStr)
|
inline void StrToInts(int *pInts, int Num, const char *pStr)
|
||||||
{
|
{
|
||||||
int Index = 0;
|
int Index = 0;
|
||||||
|
@ -398,9 +393,7 @@ inline void StrToInts(int *pInts, int Num, const char *pStr)
|
||||||
pInts[-1] &= 0xffffff00;
|
pInts[-1] &= 0xffffff00;
|
||||||
}
|
}
|
||||||
|
|
||||||
CGhostRecorder CGhostUpdater::ms_Recorder;
|
bool CGhostUpdater::Update(class IGhostRecorder *pRecorder, class IStorage *pStorage, class IConsole *pConsole, const char *pFilename)
|
||||||
|
|
||||||
bool CGhostUpdater::Update(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename)
|
|
||||||
{
|
{
|
||||||
pStorage->CreateFolder("ghosts/backup", IStorage::TYPE_SAVE);
|
pStorage->CreateFolder("ghosts/backup", IStorage::TYPE_SAVE);
|
||||||
|
|
||||||
|
@ -443,11 +436,11 @@ bool CGhostUpdater::Update(class IStorage *pStorage, class IConsole *pConsole, c
|
||||||
Time = ExtHeader.m_Time * 1000;
|
Time = ExtHeader.m_Time * 1000;
|
||||||
|
|
||||||
unsigned Crc = (ExtHeader.m_aCrc[0] << 24) | (ExtHeader.m_aCrc[1] << 16) | (ExtHeader.m_aCrc[2] << 8) | (ExtHeader.m_aCrc[3]);
|
unsigned Crc = (ExtHeader.m_aCrc[0] << 24) | (ExtHeader.m_aCrc[1] << 16) | (ExtHeader.m_aCrc[2] << 8) | (ExtHeader.m_aCrc[3]);
|
||||||
ms_Recorder.Start(pStorage, pConsole, pFilename, ExtHeader.m_aMap, Crc, ExtHeader.m_aOwner);
|
pRecorder->Start(pFilename, ExtHeader.m_aMap, Crc, ExtHeader.m_aOwner);
|
||||||
|
|
||||||
CGhostSkin Skin;
|
CGhostSkin Skin;
|
||||||
mem_copy(&Skin, aSkinData + ms_SkinOffsetV2, sizeof(Skin));
|
mem_copy(&Skin, aSkinData + ms_SkinOffsetV2, sizeof(Skin));
|
||||||
ms_Recorder.WriteData(0 /* GHOSTDATA_TYPE_SKIN */, (const char*)&Skin, sizeof(Skin));
|
pRecorder->WriteData(0 /* GHOSTDATA_TYPE_SKIN */, &Skin, sizeof(Skin));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -459,14 +452,14 @@ bool CGhostUpdater::Update(class IStorage *pStorage, class IConsole *pConsole, c
|
||||||
Time = ExtHeader.m_Time * 1000;
|
Time = ExtHeader.m_Time * 1000;
|
||||||
|
|
||||||
unsigned Crc = (ExtHeader.m_aCrc[0] << 24) | (ExtHeader.m_aCrc[1] << 16) | (ExtHeader.m_aCrc[2] << 8) | (ExtHeader.m_aCrc[3]);
|
unsigned Crc = (ExtHeader.m_aCrc[0] << 24) | (ExtHeader.m_aCrc[1] << 16) | (ExtHeader.m_aCrc[2] << 8) | (ExtHeader.m_aCrc[3]);
|
||||||
ms_Recorder.Start(pStorage, pConsole, pFilename, ExtHeader.m_aMap, Crc, ExtHeader.m_aOwner);
|
pRecorder->Start(pFilename, ExtHeader.m_aMap, Crc, ExtHeader.m_aOwner);
|
||||||
|
|
||||||
CGhostSkin Skin;
|
CGhostSkin Skin;
|
||||||
StrToInts(&Skin.m_Skin0, 6, ExtHeader.m_aSkinName);
|
StrToInts(&Skin.m_Skin0, 6, ExtHeader.m_aSkinName);
|
||||||
Skin.m_UseCustomColor = ExtHeader.m_UseCustomColor;
|
Skin.m_UseCustomColor = ExtHeader.m_UseCustomColor;
|
||||||
Skin.m_ColorBody = ExtHeader.m_ColorBody;
|
Skin.m_ColorBody = ExtHeader.m_ColorBody;
|
||||||
Skin.m_ColorFeet = ExtHeader.m_ColorFeet;
|
Skin.m_ColorFeet = ExtHeader.m_ColorFeet;
|
||||||
ms_Recorder.WriteData(0 /* GHOSTDATA_TYPE_SKIN */, (const char*)&Skin, sizeof(Skin));
|
pRecorder->WriteData(0 /* GHOSTDATA_TYPE_SKIN */, &Skin, sizeof(Skin));
|
||||||
}
|
}
|
||||||
|
|
||||||
// read data
|
// read data
|
||||||
|
@ -505,7 +498,7 @@ bool CGhostUpdater::Update(class IStorage *pStorage, class IConsole *pConsole, c
|
||||||
char *pTmp = s_aData;
|
char *pTmp = s_aData;
|
||||||
for(int i = 0; i < DataSize / ms_GhostCharacterSize; i++)
|
for(int i = 0; i < DataSize / ms_GhostCharacterSize; i++)
|
||||||
{
|
{
|
||||||
ms_Recorder.WriteData(1 /* GHOSTDATA_TYPE_CHARACTER_NO_TICK */, pTmp, ms_GhostCharacterSize);
|
pRecorder->WriteData(1 /* GHOSTDATA_TYPE_CHARACTER_NO_TICK */, pTmp, ms_GhostCharacterSize);
|
||||||
pTmp += ms_GhostCharacterSize;
|
pTmp += ms_GhostCharacterSize;
|
||||||
Index++;
|
Index++;
|
||||||
}
|
}
|
||||||
|
@ -514,6 +507,6 @@ bool CGhostUpdater::Update(class IStorage *pStorage, class IConsole *pConsole, c
|
||||||
io_close(File);
|
io_close(File);
|
||||||
|
|
||||||
bool Error = Ticks != Index;
|
bool Error = Ticks != Index;
|
||||||
ms_Recorder.Stop(Index, Error ? 0 : Time);
|
pRecorder->Stop(Index, Error ? 0 : Time);
|
||||||
return !Error;
|
return !Error;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ enum
|
||||||
class CGhostItem
|
class CGhostItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
char m_aData[MAX_ITEM_SIZE];
|
unsigned char m_aData[MAX_ITEM_SIZE];
|
||||||
int m_Type;
|
int m_Type;
|
||||||
|
|
||||||
CGhostItem() : m_Type(-1) {}
|
CGhostItem() : m_Type(-1) {}
|
||||||
|
@ -24,6 +24,7 @@ class CGhostRecorder : public IGhostRecorder
|
||||||
{
|
{
|
||||||
IOHANDLE m_File;
|
IOHANDLE m_File;
|
||||||
class IConsole *m_pConsole;
|
class IConsole *m_pConsole;
|
||||||
|
class IStorage *m_pStorage;
|
||||||
|
|
||||||
CGhostItem m_LastItem;
|
CGhostItem m_LastItem;
|
||||||
|
|
||||||
|
@ -37,10 +38,12 @@ class CGhostRecorder : public IGhostRecorder
|
||||||
public:
|
public:
|
||||||
CGhostRecorder();
|
CGhostRecorder();
|
||||||
|
|
||||||
int Start(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, const char *pMap, unsigned MapCrc, const char *pName);
|
void Init();
|
||||||
|
|
||||||
|
int Start(const char *pFilename, const char *pMap, unsigned MapCrc, const char *pName);
|
||||||
int Stop(int Ticks, int Time);
|
int Stop(int Ticks, int Time);
|
||||||
|
|
||||||
void WriteData(int Type, const char *pData, int Size);
|
void WriteData(int Type, const void *pData, int Size);
|
||||||
bool IsRecording() const { return m_File != 0; }
|
bool IsRecording() const { return m_File != 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,6 +51,7 @@ class CGhostLoader : public IGhostLoader
|
||||||
{
|
{
|
||||||
IOHANDLE m_File;
|
IOHANDLE m_File;
|
||||||
class IConsole *m_pConsole;
|
class IConsole *m_pConsole;
|
||||||
|
class IStorage *m_pStorage;
|
||||||
|
|
||||||
CGhostHeader m_Header;
|
CGhostHeader m_Header;
|
||||||
|
|
||||||
|
@ -65,16 +69,16 @@ class CGhostLoader : public IGhostLoader
|
||||||
public:
|
public:
|
||||||
CGhostLoader();
|
CGhostLoader();
|
||||||
|
|
||||||
int Load(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, const char *pMap, unsigned Crc);
|
void Init();
|
||||||
|
|
||||||
|
int Load(const char *pFilename, const char *pMap, unsigned Crc);
|
||||||
void Close();
|
void Close();
|
||||||
const CGhostHeader *GetHeader() const { return &m_Header; }
|
const CGhostHeader *GetHeader() const { return &m_Header; }
|
||||||
|
|
||||||
bool ReadNextType(int *pType);
|
bool ReadNextType(int *pType);
|
||||||
bool ReadData(int Type, char *pData, int Size);
|
bool ReadData(int Type, void *pData, int Size);
|
||||||
|
|
||||||
bool GetGhostInfo(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, CGhostHeader *pGhostHeader, const char *pMap, unsigned Crc) const;
|
bool GetGhostInfo(const char *pFilename, CGhostHeader *pGhostHeader, const char *pMap, unsigned Crc);
|
||||||
int GetTime(const CGhostHeader *pHeader) const;
|
|
||||||
int GetTicks(const CGhostHeader *pHeader) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CGhostUpdater
|
class CGhostUpdater
|
||||||
|
@ -133,10 +137,8 @@ class CGhostUpdater
|
||||||
|
|
||||||
static const int ms_GhostCharacterSize = 11 * sizeof(int);
|
static const int ms_GhostCharacterSize = 11 * sizeof(int);
|
||||||
|
|
||||||
static CGhostRecorder ms_Recorder;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static bool Update(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename);
|
static bool Update(class IGhostRecorder *pRecorder, class IStorage *pStorage, class IConsole *pConsole, const char *pFilename);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
const char *CGhost::ms_pGhostDir = "ghosts";
|
const char *CGhost::ms_pGhostDir = "ghosts";
|
||||||
|
|
||||||
CGhost::CGhost() : m_NewRenderTick(-1), m_StartRenderTick(-1), m_LastDeathTick(-1), m_Recording(false), m_Rendering(false) {}
|
CGhost::CGhost() : m_NewRenderTick(-1), m_StartRenderTick(-1), m_LastDeathTick(-1), m_LastRaceTick(-1), m_Recording(false), m_Rendering(false) {}
|
||||||
|
|
||||||
void CGhost::GetGhostSkin(CGhostSkin *pSkin, const char *pSkinName, int UseCustomColor, int ColorBody, int ColorFeet)
|
void CGhost::GetGhostSkin(CGhostSkin *pSkin, const char *pSkinName, int UseCustomColor, int ColorBody, int ColorFeet)
|
||||||
{
|
{
|
||||||
|
@ -139,19 +139,19 @@ void CGhost::AddInfos(const CNetObj_Character *pChar)
|
||||||
if(g_Config.m_ClRaceSaveGhost && !GhostRecorder()->IsRecording() && NumTicks > 0)
|
if(g_Config.m_ClRaceSaveGhost && !GhostRecorder()->IsRecording() && NumTicks > 0)
|
||||||
{
|
{
|
||||||
GetPath(m_aTmpFilename, sizeof(m_aTmpFilename), m_CurGhost.m_aPlayer);
|
GetPath(m_aTmpFilename, sizeof(m_aTmpFilename), m_CurGhost.m_aPlayer);
|
||||||
Client()->GhostRecorder_Start(m_aTmpFilename, m_CurGhost.m_aPlayer);
|
GhostRecorder()->Start(m_aTmpFilename, Client()->GetCurrentMap(), Client()->GetMapCrc(), m_CurGhost.m_aPlayer);
|
||||||
|
|
||||||
GhostRecorder()->WriteData(GHOSTDATA_TYPE_START_TICK, (const char*)&m_CurGhost.m_StartTick, sizeof(int));
|
GhostRecorder()->WriteData(GHOSTDATA_TYPE_START_TICK, &m_CurGhost.m_StartTick, sizeof(int));
|
||||||
GhostRecorder()->WriteData(GHOSTDATA_TYPE_SKIN, (const char*)&m_CurGhost.m_Skin, sizeof(CGhostSkin));
|
GhostRecorder()->WriteData(GHOSTDATA_TYPE_SKIN, &m_CurGhost.m_Skin, sizeof(CGhostSkin));
|
||||||
for(int i = 0; i < NumTicks; i++)
|
for(int i = 0; i < NumTicks; i++)
|
||||||
GhostRecorder()->WriteData(GHOSTDATA_TYPE_CHARACTER, (const char*)m_CurGhost.m_Path.Get(i), sizeof(CGhostCharacter));
|
GhostRecorder()->WriteData(GHOSTDATA_TYPE_CHARACTER, m_CurGhost.m_Path.Get(i), sizeof(CGhostCharacter));
|
||||||
}
|
}
|
||||||
|
|
||||||
CGhostCharacter GhostChar;
|
CGhostCharacter GhostChar;
|
||||||
GetGhostCharacter(&GhostChar, pChar);
|
GetGhostCharacter(&GhostChar, pChar);
|
||||||
m_CurGhost.m_Path.Add(GhostChar);
|
m_CurGhost.m_Path.Add(GhostChar);
|
||||||
if(GhostRecorder()->IsRecording())
|
if(GhostRecorder()->IsRecording())
|
||||||
GhostRecorder()->WriteData(GHOSTDATA_TYPE_CHARACTER, (const char*)&GhostChar, sizeof(CGhostCharacter));
|
GhostRecorder()->WriteData(GHOSTDATA_TYPE_CHARACTER, &GhostChar, sizeof(CGhostCharacter));
|
||||||
}
|
}
|
||||||
|
|
||||||
int CGhost::GetSlot() const
|
int CGhost::GetSlot() const
|
||||||
|
@ -162,7 +162,7 @@ int CGhost::GetSlot() const
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CGhost::FreeSlot() const
|
int CGhost::FreeSlots() const
|
||||||
{
|
{
|
||||||
int Num = 0;
|
int Num = 0;
|
||||||
for(int i = 0; i < MAX_ACTIVE_GHOSTS; i++)
|
for(int i = 0; i < MAX_ACTIVE_GHOSTS; i++)
|
||||||
|
@ -171,104 +171,133 @@ int CGhost::FreeSlot() const
|
||||||
return Num;
|
return Num;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGhost::OnNewSnapshot(bool Predicted)
|
void CGhost::CheckStart()
|
||||||
|
{
|
||||||
|
int RaceTick = -m_pClient->m_Snap.m_pGameInfoObj->m_WarmupTimer;
|
||||||
|
int RenderTick = m_NewRenderTick;
|
||||||
|
|
||||||
|
if(m_LastRaceTick != RaceTick && Client()->GameTick() - RaceTick < Client()->GameTickSpeed())
|
||||||
|
{
|
||||||
|
if(m_Rendering && m_RenderingStartedByServer) // race restarted: stop rendering
|
||||||
|
StopRender();
|
||||||
|
if(m_Recording && m_LastRaceTick != -1) // race restarted: activate restarting for local start detection so we have a smooth transition
|
||||||
|
m_AllowRestart = true;
|
||||||
|
if(m_LastRaceTick == -1) // no restart: reset rendering preparations
|
||||||
|
m_NewRenderTick = -1;
|
||||||
|
if(GhostRecorder()->IsRecording()) // race restarted: stop recording
|
||||||
|
GhostRecorder()->Stop(0, -1);
|
||||||
|
int StartTick = RaceTick;
|
||||||
|
|
||||||
|
CServerInfo ServerInfo;
|
||||||
|
Client()->GetServerInfo(&ServerInfo);
|
||||||
|
if(IsDDRace(&ServerInfo)) // the client recognizes the start one tick earlier than ddrace servers
|
||||||
|
StartTick--;
|
||||||
|
StartRecord(StartTick);
|
||||||
|
RenderTick = StartTick;
|
||||||
|
}
|
||||||
|
|
||||||
|
TryRenderStart(RenderTick, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGhost::CheckStartLocal(bool Predicted)
|
||||||
|
{
|
||||||
|
if(Predicted) // rendering
|
||||||
|
{
|
||||||
|
int RenderTick = m_NewRenderTick;
|
||||||
|
|
||||||
|
vec2 PrevPos = m_pClient->m_PredictedPrevChar.m_Pos;
|
||||||
|
vec2 Pos = m_pClient->m_PredictedChar.m_Pos;
|
||||||
|
if(((!m_Rendering && RenderTick == -1) || m_AllowRestart) && CRaceHelper::IsStart(m_pClient, PrevPos, Pos))
|
||||||
|
{
|
||||||
|
if(m_Rendering && !m_RenderingStartedByServer) // race restarted: stop rendering
|
||||||
|
StopRender();
|
||||||
|
RenderTick = Client()->PredGameTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
TryRenderStart(RenderTick, false);
|
||||||
|
}
|
||||||
|
else // recording
|
||||||
|
{
|
||||||
|
int PrevTick = m_pClient->m_Snap.m_pLocalPrevCharacter->m_Tick;
|
||||||
|
int CurTick = m_pClient->m_Snap.m_pLocalCharacter->m_Tick;
|
||||||
|
vec2 PrevPos = vec2(m_pClient->m_Snap.m_pLocalPrevCharacter->m_X, m_pClient->m_Snap.m_pLocalPrevCharacter->m_Y);
|
||||||
|
vec2 Pos = vec2(m_pClient->m_Snap.m_pLocalCharacter->m_X, m_pClient->m_Snap.m_pLocalCharacter->m_Y);
|
||||||
|
|
||||||
|
// detecting death, needed because race allows immediate respawning
|
||||||
|
if((!m_Recording || m_AllowRestart) && m_LastDeathTick < PrevTick)
|
||||||
|
{
|
||||||
|
// estimate the exact start tick
|
||||||
|
int RecordTick = -1;
|
||||||
|
int TickDiff = CurTick - PrevTick;
|
||||||
|
for(int i = 0; i < TickDiff; i++)
|
||||||
|
{
|
||||||
|
if(CRaceHelper::IsStart(m_pClient, mix(PrevPos, Pos, (float)i / TickDiff), mix(PrevPos, Pos, (float)(i + 1) / TickDiff)))
|
||||||
|
{
|
||||||
|
RecordTick = PrevTick + i + 1;
|
||||||
|
if(!m_AllowRestart)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(RecordTick != -1)
|
||||||
|
{
|
||||||
|
if(GhostRecorder()->IsRecording()) // race restarted: stop recording
|
||||||
|
GhostRecorder()->Stop(0, -1);
|
||||||
|
StartRecord(RecordTick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGhost::TryRenderStart(int Tick, bool ServerControl)
|
||||||
|
{
|
||||||
|
// only restart rendering if it did not change since last tick to prevent stuttering
|
||||||
|
if(m_NewRenderTick != -1 && m_NewRenderTick == Tick)
|
||||||
|
{
|
||||||
|
StartRender(Tick);
|
||||||
|
Tick = -1;
|
||||||
|
m_RenderingStartedByServer = ServerControl;
|
||||||
|
}
|
||||||
|
m_NewRenderTick = Tick;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGhost::OnNewSnapshot()
|
||||||
{
|
{
|
||||||
CServerInfo ServerInfo;
|
CServerInfo ServerInfo;
|
||||||
Client()->GetServerInfo(&ServerInfo);
|
Client()->GetServerInfo(&ServerInfo);
|
||||||
if(!IsRace(&ServerInfo) || !g_Config.m_ClRaceGhost || Client()->State() != IClient::STATE_ONLINE)
|
if(!IsRace(&ServerInfo) || !g_Config.m_ClRaceGhost || Client()->State() != IClient::STATE_ONLINE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(!m_pClient->m_Snap.m_pGameInfoObj || m_pClient->m_Snap.m_SpecInfo.m_Active || !m_pClient->m_Snap.m_pLocalCharacter || !m_pClient->m_Snap.m_pLocalPrevCharacter)
|
if(!m_pClient->m_Snap.m_pGameInfoObj || m_pClient->m_Snap.m_SpecInfo.m_Active || !m_pClient->m_Snap.m_pLocalCharacter || !m_pClient->m_Snap.m_pLocalPrevCharacter)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool RaceFlag = m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_RACETIME;
|
bool RaceFlag = m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_RACETIME;
|
||||||
bool ServerControl = RaceFlag && g_Config.m_ClRaceGhostServerControl;
|
bool ServerControl = RaceFlag && g_Config.m_ClRaceGhostServerControl;
|
||||||
|
|
||||||
|
if(!ServerControl)
|
||||||
|
CheckStartLocal(false);
|
||||||
|
else
|
||||||
|
CheckStart();
|
||||||
|
|
||||||
|
if(m_Recording)
|
||||||
|
AddInfos(m_pClient->m_Snap.m_pLocalCharacter);
|
||||||
|
|
||||||
int RaceTick = -m_pClient->m_Snap.m_pGameInfoObj->m_WarmupTimer;
|
int RaceTick = -m_pClient->m_Snap.m_pGameInfoObj->m_WarmupTimer;
|
||||||
|
m_LastRaceTick = RaceFlag ? RaceTick : -1;
|
||||||
|
}
|
||||||
|
|
||||||
int RenderTick = m_NewRenderTick;
|
void CGhost::OnNewPredictedSnapshot()
|
||||||
|
{
|
||||||
|
CServerInfo ServerInfo;
|
||||||
|
Client()->GetServerInfo(&ServerInfo);
|
||||||
|
if(!IsRace(&ServerInfo) || !g_Config.m_ClRaceGhost || Client()->State() != IClient::STATE_ONLINE)
|
||||||
|
return;
|
||||||
|
if(!m_pClient->m_Snap.m_pGameInfoObj || m_pClient->m_Snap.m_SpecInfo.m_Active || !m_pClient->m_Snap.m_pLocalCharacter || !m_pClient->m_Snap.m_pLocalPrevCharacter)
|
||||||
|
return;
|
||||||
|
|
||||||
static bool s_RenderingStartedByServer = false;
|
bool RaceFlag = m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_RACETIME;
|
||||||
|
bool ServerControl = RaceFlag && g_Config.m_ClRaceGhostServerControl;
|
||||||
|
|
||||||
if(!ServerControl && Predicted)
|
if(!ServerControl)
|
||||||
{
|
CheckStartLocal(true);
|
||||||
vec2 PrevPos = m_pClient->m_PredictedPrevChar.m_Pos;
|
|
||||||
vec2 Pos = m_pClient->m_PredictedChar.m_Pos;
|
|
||||||
if(((!m_Rendering && RenderTick == -1) || m_AllowRestart) && CRaceHelper::IsStart(m_pClient, PrevPos, Pos))
|
|
||||||
{
|
|
||||||
if(m_Rendering && !s_RenderingStartedByServer) // race restarted: stop rendering
|
|
||||||
StopRender();
|
|
||||||
RenderTick = Client()->PredGameTick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!Predicted)
|
|
||||||
{
|
|
||||||
static int s_LastRaceTick = -1;
|
|
||||||
|
|
||||||
if(ServerControl && s_LastRaceTick != RaceTick && Client()->GameTick() - RaceTick < Client()->GameTickSpeed())
|
|
||||||
{
|
|
||||||
if(m_Rendering && s_RenderingStartedByServer) // race restarted: stop rendering
|
|
||||||
StopRender();
|
|
||||||
if(m_Recording && s_LastRaceTick != -1) // race restarted: activate restarting for local start detection so we have a smooth transition
|
|
||||||
m_AllowRestart = true;
|
|
||||||
if(s_LastRaceTick == -1) // no restart: reset rendering preparations
|
|
||||||
m_NewRenderTick = -1;
|
|
||||||
if(GhostRecorder()->IsRecording()) // race restarted: stop recording
|
|
||||||
GhostRecorder()->Stop(0, -1);
|
|
||||||
int StartTick = RaceTick;
|
|
||||||
if(IsDDRace(&ServerInfo)) // the client recognizes the start one tick earlier than ddrace servers
|
|
||||||
StartTick--;
|
|
||||||
StartRecord(StartTick);
|
|
||||||
RenderTick = StartTick;
|
|
||||||
}
|
|
||||||
else if(!ServerControl)
|
|
||||||
{
|
|
||||||
int PrevTick = m_pClient->m_Snap.m_pLocalPrevCharacter->m_Tick;
|
|
||||||
int CurTick = m_pClient->m_Snap.m_pLocalCharacter->m_Tick;
|
|
||||||
vec2 PrevPos = vec2(m_pClient->m_Snap.m_pLocalPrevCharacter->m_X, m_pClient->m_Snap.m_pLocalPrevCharacter->m_Y);
|
|
||||||
vec2 Pos = vec2(m_pClient->m_Snap.m_pLocalCharacter->m_X, m_pClient->m_Snap.m_pLocalCharacter->m_Y);
|
|
||||||
|
|
||||||
// detecting death, needed because race allows immediate respawning
|
|
||||||
if((!m_Recording || m_AllowRestart) && m_LastDeathTick < PrevTick)
|
|
||||||
{
|
|
||||||
// estimate the exact start tick
|
|
||||||
int RecordTick = -1;
|
|
||||||
int TickDiff = CurTick - PrevTick;
|
|
||||||
for(int i = 0; i < TickDiff; i++)
|
|
||||||
{
|
|
||||||
if(CRaceHelper::IsStart(m_pClient, mix(PrevPos, Pos, (float)i / TickDiff), mix(PrevPos, Pos, (float)(i + 1) / TickDiff)))
|
|
||||||
{
|
|
||||||
RecordTick = PrevTick + i + 1;
|
|
||||||
if(!m_AllowRestart)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(RecordTick != -1)
|
|
||||||
{
|
|
||||||
if(GhostRecorder()->IsRecording()) // race restarted: stop recording
|
|
||||||
GhostRecorder()->Stop(0, -1);
|
|
||||||
StartRecord(RecordTick);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(m_Recording)
|
|
||||||
AddInfos(m_pClient->m_Snap.m_pLocalCharacter);
|
|
||||||
|
|
||||||
s_LastRaceTick = RaceFlag ? RaceTick : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ServerControl != Predicted)
|
|
||||||
{
|
|
||||||
// only restart rendering if it did not change since last tick to prevent stuttering
|
|
||||||
if(m_NewRenderTick != -1 && m_NewRenderTick == RenderTick)
|
|
||||||
{
|
|
||||||
StartRender(RenderTick);
|
|
||||||
RenderTick = -1;
|
|
||||||
s_RenderingStartedByServer = ServerControl;
|
|
||||||
}
|
|
||||||
m_NewRenderTick = RenderTick;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGhost::OnRender()
|
void CGhost::OnRender()
|
||||||
|
@ -282,7 +311,7 @@ void CGhost::OnRender()
|
||||||
for(int i = 0; i < MAX_ACTIVE_GHOSTS; i++)
|
for(int i = 0; i < MAX_ACTIVE_GHOSTS; i++)
|
||||||
{
|
{
|
||||||
CGhostItem *pGhost = &m_aActiveGhosts[i];
|
CGhostItem *pGhost = &m_aActiveGhosts[i];
|
||||||
if(pGhost->Empty() || pGhost->m_PlaybackPos < 0)
|
if(pGhost->Empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int GhostTick = pGhost->m_StartTick + PlaybackTick;
|
int GhostTick = pGhost->m_StartTick + PlaybackTick;
|
||||||
|
@ -424,13 +453,13 @@ int CGhost::Load(const char *pFilename)
|
||||||
if(Slot == -1)
|
if(Slot == -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if(!Client()->GhostLoader_Load(pFilename))
|
if(GhostLoader()->Load(pFilename, Client()->GetCurrentMap(), Client()->GetMapCrc()) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
const CGhostHeader *pHeader = GhostLoader()->GetHeader();
|
const CGhostHeader *pHeader = GhostLoader()->GetHeader();
|
||||||
|
|
||||||
int NumTicks = GhostLoader()->GetTicks(pHeader);
|
int NumTicks = pHeader->GetTicks();
|
||||||
int Time = GhostLoader()->GetTime(pHeader);
|
int Time = pHeader->GetTime();
|
||||||
if(NumTicks <= 0 || Time <= 0)
|
if(NumTicks <= 0 || Time <= 0)
|
||||||
{
|
{
|
||||||
GhostLoader()->Close();
|
GhostLoader()->Close();
|
||||||
|
@ -461,23 +490,23 @@ int CGhost::Load(const char *pFilename)
|
||||||
if(Type == GHOSTDATA_TYPE_SKIN && !FoundSkin)
|
if(Type == GHOSTDATA_TYPE_SKIN && !FoundSkin)
|
||||||
{
|
{
|
||||||
FoundSkin = true;
|
FoundSkin = true;
|
||||||
if(!GhostLoader()->ReadData(Type, (char*)&pGhost->m_Skin, sizeof(CGhostSkin)))
|
if(!GhostLoader()->ReadData(Type, &pGhost->m_Skin, sizeof(CGhostSkin)))
|
||||||
Error = true;
|
Error = true;
|
||||||
}
|
}
|
||||||
else if(Type == GHOSTDATA_TYPE_CHARACTER_NO_TICK)
|
else if(Type == GHOSTDATA_TYPE_CHARACTER_NO_TICK)
|
||||||
{
|
{
|
||||||
NoTick = true;
|
NoTick = true;
|
||||||
if(!GhostLoader()->ReadData(Type, (char*)pGhost->m_Path.Get(Index++), sizeof(CGhostCharacter_NoTick)))
|
if(!GhostLoader()->ReadData(Type, pGhost->m_Path.Get(Index++), sizeof(CGhostCharacter_NoTick)))
|
||||||
Error = true;
|
Error = true;
|
||||||
}
|
}
|
||||||
else if(Type == GHOSTDATA_TYPE_CHARACTER)
|
else if(Type == GHOSTDATA_TYPE_CHARACTER)
|
||||||
{
|
{
|
||||||
if(!GhostLoader()->ReadData(Type, (char*)pGhost->m_Path.Get(Index++), sizeof(CGhostCharacter)))
|
if(!GhostLoader()->ReadData(Type, pGhost->m_Path.Get(Index++), sizeof(CGhostCharacter)))
|
||||||
Error = true;
|
Error = true;
|
||||||
}
|
}
|
||||||
else if(Type == GHOSTDATA_TYPE_START_TICK)
|
else if(Type == GHOSTDATA_TYPE_START_TICK)
|
||||||
{
|
{
|
||||||
if(!GhostLoader()->ReadData(Type, (char*)&pGhost->m_StartTick, sizeof(int)))
|
if(!GhostLoader()->ReadData(Type, &pGhost->m_StartTick, sizeof(int)))
|
||||||
Error = true;
|
Error = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -531,12 +560,12 @@ void CGhost::SaveGhost(CMenus::CGhostItem *pItem)
|
||||||
|
|
||||||
int NumTicks = pGhost->m_Path.Size();
|
int NumTicks = pGhost->m_Path.Size();
|
||||||
GetPath(pItem->m_aFilename, sizeof(pItem->m_aFilename), pItem->m_aPlayer, pItem->m_Time);
|
GetPath(pItem->m_aFilename, sizeof(pItem->m_aFilename), pItem->m_aPlayer, pItem->m_Time);
|
||||||
Client()->GhostRecorder_Start(pItem->m_aFilename, pItem->m_aPlayer);
|
GhostRecorder()->Start(pItem->m_aFilename, Client()->GetCurrentMap(), Client()->GetMapCrc(), pItem->m_aPlayer);
|
||||||
|
|
||||||
GhostRecorder()->WriteData(GHOSTDATA_TYPE_START_TICK, (const char*)&pGhost->m_StartTick, sizeof(int));
|
GhostRecorder()->WriteData(GHOSTDATA_TYPE_START_TICK, &pGhost->m_StartTick, sizeof(int));
|
||||||
GhostRecorder()->WriteData(GHOSTDATA_TYPE_SKIN, (const char*)&pGhost->m_Skin, sizeof(CGhostSkin));
|
GhostRecorder()->WriteData(GHOSTDATA_TYPE_SKIN, &pGhost->m_Skin, sizeof(CGhostSkin));
|
||||||
for(int i = 0; i < NumTicks; i++)
|
for(int i = 0; i < NumTicks; i++)
|
||||||
GhostRecorder()->WriteData(GHOSTDATA_TYPE_CHARACTER, (const char*)pGhost->m_Path.Get(i), sizeof(CGhostCharacter));
|
GhostRecorder()->WriteData(GHOSTDATA_TYPE_CHARACTER, pGhost->m_Path.Get(i), sizeof(CGhostCharacter));
|
||||||
|
|
||||||
GhostRecorder()->Stop(NumTicks, pItem->m_Time);
|
GhostRecorder()->Stop(NumTicks, pItem->m_Time);
|
||||||
}
|
}
|
||||||
|
@ -590,6 +619,7 @@ void CGhost::OnReset()
|
||||||
StopRecord();
|
StopRecord();
|
||||||
StopRender();
|
StopRender();
|
||||||
m_LastDeathTick = -1;
|
m_LastDeathTick = -1;
|
||||||
|
m_LastRaceTick = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGhost::OnMapLoad()
|
void CGhost::OnMapLoad()
|
||||||
|
|
|
@ -112,9 +112,12 @@ private:
|
||||||
int m_NewRenderTick;
|
int m_NewRenderTick;
|
||||||
int m_StartRenderTick;
|
int m_StartRenderTick;
|
||||||
int m_LastDeathTick;
|
int m_LastDeathTick;
|
||||||
|
int m_LastRaceTick;
|
||||||
bool m_Recording;
|
bool m_Recording;
|
||||||
bool m_Rendering;
|
bool m_Rendering;
|
||||||
|
|
||||||
|
bool m_RenderingStartedByServer;
|
||||||
|
|
||||||
static void GetGhostSkin(CGhostSkin *pSkin, const char *pSkinName, int UseCustomColor, int ColorBody, int ColorFeet);
|
static void GetGhostSkin(CGhostSkin *pSkin, const char *pSkinName, int UseCustomColor, int ColorBody, int ColorFeet);
|
||||||
static void GetGhostCharacter(CGhostCharacter *pGhostChar, const CNetObj_Character *pChar);
|
static void GetGhostCharacter(CGhostCharacter *pGhostChar, const CNetObj_Character *pChar);
|
||||||
static void GetNetObjCharacter(CNetObj_Character *pChar, const CGhostCharacter *pGhostChar);
|
static void GetNetObjCharacter(CNetObj_Character *pChar, const CGhostCharacter *pGhostChar);
|
||||||
|
@ -124,6 +127,10 @@ private:
|
||||||
void AddInfos(const CNetObj_Character *pChar);
|
void AddInfos(const CNetObj_Character *pChar);
|
||||||
int GetSlot() const;
|
int GetSlot() const;
|
||||||
|
|
||||||
|
void CheckStart();
|
||||||
|
void CheckStartLocal(bool Predicted);
|
||||||
|
void TryRenderStart(int Tick, bool ServerControl);
|
||||||
|
|
||||||
void StartRecord(int Tick);
|
void StartRecord(int Tick);
|
||||||
void StopRecord(int Time = -1);
|
void StopRecord(int Time = -1);
|
||||||
void StartRender(int Tick);
|
void StartRender(int Tick);
|
||||||
|
@ -144,9 +151,10 @@ public:
|
||||||
virtual void OnMessage(int MsgType, void *pRawMsg);
|
virtual void OnMessage(int MsgType, void *pRawMsg);
|
||||||
virtual void OnMapLoad();
|
virtual void OnMapLoad();
|
||||||
|
|
||||||
void OnNewSnapshot(bool Predicted = false);
|
void OnNewSnapshot();
|
||||||
|
void OnNewPredictedSnapshot();
|
||||||
|
|
||||||
int FreeSlot() const;
|
int FreeSlots() const;
|
||||||
int Load(const char *pFilename);
|
int Load(const char *pFilename);
|
||||||
void Unload(int Slot);
|
void Unload(int Slot);
|
||||||
void UnloadAll();
|
void UnloadAll();
|
||||||
|
|
|
@ -844,13 +844,13 @@ int CMenus::GhostlistFetchCallback(const char *pName, int IsDir, int StorageType
|
||||||
str_format(aFilename, sizeof(aFilename), "%s/%s", pSelf->m_pClient->m_pGhost->GetGhostDir(), pName);
|
str_format(aFilename, sizeof(aFilename), "%s/%s", pSelf->m_pClient->m_pGhost->GetGhostDir(), pName);
|
||||||
|
|
||||||
CGhostHeader Header;
|
CGhostHeader Header;
|
||||||
if(!pSelf->Client()->GhostLoader_GetGhostInfo(aFilename, &Header))
|
if(!pSelf->m_pClient->m_pGhost->GhostLoader()->GetGhostInfo(aFilename, &Header, pMap, pSelf->Client()->GetMapCrc()))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
CGhostItem Item;
|
CGhostItem Item;
|
||||||
str_copy(Item.m_aFilename, aFilename, sizeof(Item.m_aFilename));
|
str_copy(Item.m_aFilename, aFilename, sizeof(Item.m_aFilename));
|
||||||
str_copy(Item.m_aPlayer, Header.m_aOwner, sizeof(Item.m_aPlayer));
|
str_copy(Item.m_aPlayer, Header.m_aOwner, sizeof(Item.m_aPlayer));
|
||||||
Item.m_Time = pSelf->m_pClient->m_pGhost->GhostLoader()->GetTime(&Header);
|
Item.m_Time = Header.GetTime();
|
||||||
if(Item.m_Time > 0)
|
if(Item.m_Time > 0)
|
||||||
pSelf->m_lGhosts.add(Item);
|
pSelf->m_lGhosts.add(Item);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1146,7 +1146,7 @@ void CMenus::RenderGhost(CUIRect MainView)
|
||||||
|
|
||||||
CGhostItem *pOwnGhost = GetOwnGhost();
|
CGhostItem *pOwnGhost = GetOwnGhost();
|
||||||
int ReservedSlots = !pGhost->m_Own && !(pOwnGhost && pOwnGhost->Active());
|
int ReservedSlots = !pGhost->m_Own && !(pOwnGhost && pOwnGhost->Active());
|
||||||
if(pGhost->HasFile() && (pGhost->Active() || m_pClient->m_pGhost->FreeSlot() > ReservedSlots))
|
if(pGhost->HasFile() && (pGhost->Active() || m_pClient->m_pGhost->FreeSlots() > ReservedSlots))
|
||||||
{
|
{
|
||||||
Status.VSplitRight(120.0f, &Status, &Button);
|
Status.VSplitRight(120.0f, &Status, &Button);
|
||||||
|
|
||||||
|
|
|
@ -1801,7 +1801,7 @@ void CGameClient::OnPredict()
|
||||||
m_PredictedTick = Client()->PredGameTick();
|
m_PredictedTick = Client()->PredGameTick();
|
||||||
|
|
||||||
if(m_NewPredictedTick)
|
if(m_NewPredictedTick)
|
||||||
m_pGhost->OnNewSnapshot(true);
|
m_pGhost->OnNewPredictedSnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameClient::OnActivateEditor()
|
void CGameClient::OnActivateEditor()
|
||||||
|
|
Loading…
Reference in a new issue