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 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 DemoSliceEnd() = 0;
|
||||
virtual void DemoSlice(const char *pDstPath, CLIENTFUNC_FILTER pfnFilter, void *pUser) = 0;
|
||||
|
|
|
@ -2570,6 +2570,9 @@ void CClient::InitInterfaces()
|
|||
|
||||
m_Friends.Init();
|
||||
m_Foes.Init(true);
|
||||
|
||||
m_GhostRecorder.Init();
|
||||
m_GhostLoader.Init();
|
||||
}
|
||||
|
||||
void CClient::Run()
|
||||
|
@ -3620,21 +3623,6 @@ bool CClient::RaceRecord_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()
|
||||
{
|
||||
|
|
|
@ -391,10 +391,6 @@ public:
|
|||
void RaceRecord_Stop();
|
||||
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 DemoSliceEnd();
|
||||
virtual void DemoSlice(const char *pDstPath, CLIENTFUNC_FILTER pfnFilter, void *pUser);
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
|
||||
#include "kernel.h"
|
||||
|
||||
struct CGhostHeader
|
||||
class CGhostHeader
|
||||
{
|
||||
public:
|
||||
unsigned char m_aMarker[8];
|
||||
unsigned char m_Version;
|
||||
char m_aOwner[MAX_NAME_LENGTH];
|
||||
|
@ -14,6 +15,16 @@ struct CGhostHeader
|
|||
unsigned char m_aCrc[4];
|
||||
unsigned char m_aNumTicks[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
|
||||
|
@ -21,9 +32,11 @@ class IGhostRecorder : public IInterface
|
|||
MACRO_INTERFACE("ghostrecorder", 0)
|
||||
public:
|
||||
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 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;
|
||||
};
|
||||
|
||||
|
@ -32,15 +45,16 @@ class IGhostLoader : public IInterface
|
|||
MACRO_INTERFACE("ghostloader", 0)
|
||||
public:
|
||||
virtual ~IGhostLoader() {}
|
||||
|
||||
virtual int Load(const char *pFilename, const char *pMap, unsigned Crc) = 0;
|
||||
virtual void Close() = 0;
|
||||
|
||||
virtual const CGhostHeader *GetHeader() const = 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 int GetTicks(const CGhostHeader *pHeader) const = 0;
|
||||
virtual bool GetGhostInfo(const char *pFilename, CGhostHeader *pGhostHeader, const char *pMap, unsigned Crc) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,12 +17,16 @@ CGhostRecorder::CGhostRecorder()
|
|||
ResetBuffer();
|
||||
}
|
||||
|
||||
// Record
|
||||
int CGhostRecorder::Start(IStorage *pStorage, IConsole *pConsole, const char *pFilename, const char *pMap, unsigned Crc, const char* pName)
|
||||
void CGhostRecorder::Init()
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
return;
|
||||
|
@ -167,6 +171,12 @@ CGhostLoader::CGhostLoader()
|
|||
ResetBuffer();
|
||||
}
|
||||
|
||||
void CGhostLoader::Init()
|
||||
{
|
||||
m_pConsole = Kernel()->RequestInterface<IConsole>();
|
||||
m_pStorage = Kernel()->RequestInterface<IStorage>();
|
||||
}
|
||||
|
||||
void CGhostLoader::ResetBuffer()
|
||||
{
|
||||
m_pBufferPos = m_aBuffer;
|
||||
|
@ -175,10 +185,9 @@ void CGhostLoader::ResetBuffer()
|
|||
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 = pStorage->OpenFile(pFilename, IOFLAG_READ, IStorage::TYPE_SAVE);
|
||||
m_File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, IStorage::TYPE_SAVE);
|
||||
if(!m_File)
|
||||
{
|
||||
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)
|
||||
return false;
|
||||
|
@ -327,14 +336,14 @@ void CGhostLoader::Close()
|
|||
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)
|
||||
return false;
|
||||
|
||||
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)
|
||||
return false;
|
||||
|
||||
|
@ -344,43 +353,29 @@ bool CGhostLoader::GetGhostInfo(class IStorage *pStorage, class IConsole *pConso
|
|||
{
|
||||
io_close(File);
|
||||
// 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
|
||||
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));
|
||||
}
|
||||
else
|
||||
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))
|
||||
{
|
||||
io_close(File);
|
||||
return false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
io_close(File);
|
||||
return false;
|
||||
}
|
||||
|
||||
io_close(File);
|
||||
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)
|
||||
{
|
||||
int Index = 0;
|
||||
|
@ -398,9 +393,7 @@ inline void StrToInts(int *pInts, int Num, const char *pStr)
|
|||
pInts[-1] &= 0xffffff00;
|
||||
}
|
||||
|
||||
CGhostRecorder CGhostUpdater::ms_Recorder;
|
||||
|
||||
bool CGhostUpdater::Update(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename)
|
||||
bool CGhostUpdater::Update(class IGhostRecorder *pRecorder, class IStorage *pStorage, class IConsole *pConsole, const char *pFilename)
|
||||
{
|
||||
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;
|
||||
|
||||
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;
|
||||
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
|
||||
{
|
||||
|
@ -459,14 +452,14 @@ bool CGhostUpdater::Update(class IStorage *pStorage, class IConsole *pConsole, c
|
|||
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]);
|
||||
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;
|
||||
StrToInts(&Skin.m_Skin0, 6, ExtHeader.m_aSkinName);
|
||||
Skin.m_UseCustomColor = ExtHeader.m_UseCustomColor;
|
||||
Skin.m_ColorBody = ExtHeader.m_ColorBody;
|
||||
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
|
||||
|
@ -505,7 +498,7 @@ bool CGhostUpdater::Update(class IStorage *pStorage, class IConsole *pConsole, c
|
|||
char *pTmp = s_aData;
|
||||
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;
|
||||
Index++;
|
||||
}
|
||||
|
@ -514,6 +507,6 @@ bool CGhostUpdater::Update(class IStorage *pStorage, class IConsole *pConsole, c
|
|||
io_close(File);
|
||||
|
||||
bool Error = Ticks != Index;
|
||||
ms_Recorder.Stop(Index, Error ? 0 : Time);
|
||||
pRecorder->Stop(Index, Error ? 0 : Time);
|
||||
return !Error;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ enum
|
|||
class CGhostItem
|
||||
{
|
||||
public:
|
||||
char m_aData[MAX_ITEM_SIZE];
|
||||
unsigned char m_aData[MAX_ITEM_SIZE];
|
||||
int m_Type;
|
||||
|
||||
CGhostItem() : m_Type(-1) {}
|
||||
|
@ -24,6 +24,7 @@ class CGhostRecorder : public IGhostRecorder
|
|||
{
|
||||
IOHANDLE m_File;
|
||||
class IConsole *m_pConsole;
|
||||
class IStorage *m_pStorage;
|
||||
|
||||
CGhostItem m_LastItem;
|
||||
|
||||
|
@ -37,10 +38,12 @@ class CGhostRecorder : public IGhostRecorder
|
|||
public:
|
||||
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);
|
||||
|
||||
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; }
|
||||
};
|
||||
|
||||
|
@ -48,6 +51,7 @@ class CGhostLoader : public IGhostLoader
|
|||
{
|
||||
IOHANDLE m_File;
|
||||
class IConsole *m_pConsole;
|
||||
class IStorage *m_pStorage;
|
||||
|
||||
CGhostHeader m_Header;
|
||||
|
||||
|
@ -65,16 +69,16 @@ class CGhostLoader : public IGhostLoader
|
|||
public:
|
||||
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();
|
||||
const CGhostHeader *GetHeader() const { return &m_Header; }
|
||||
|
||||
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;
|
||||
int GetTime(const CGhostHeader *pHeader) const;
|
||||
int GetTicks(const CGhostHeader *pHeader) const;
|
||||
bool GetGhostInfo(const char *pFilename, CGhostHeader *pGhostHeader, const char *pMap, unsigned Crc);
|
||||
};
|
||||
|
||||
class CGhostUpdater
|
||||
|
@ -133,10 +137,8 @@ class CGhostUpdater
|
|||
|
||||
static const int ms_GhostCharacterSize = 11 * sizeof(int);
|
||||
|
||||
static CGhostRecorder ms_Recorder;
|
||||
|
||||
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
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
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)
|
||||
{
|
||||
|
@ -139,19 +139,19 @@ void CGhost::AddInfos(const CNetObj_Character *pChar)
|
|||
if(g_Config.m_ClRaceSaveGhost && !GhostRecorder()->IsRecording() && NumTicks > 0)
|
||||
{
|
||||
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_SKIN, (const char*)&m_CurGhost.m_Skin, sizeof(CGhostSkin));
|
||||
GhostRecorder()->WriteData(GHOSTDATA_TYPE_START_TICK, &m_CurGhost.m_StartTick, sizeof(int));
|
||||
GhostRecorder()->WriteData(GHOSTDATA_TYPE_SKIN, &m_CurGhost.m_Skin, sizeof(CGhostSkin));
|
||||
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;
|
||||
GetGhostCharacter(&GhostChar, pChar);
|
||||
m_CurGhost.m_Path.Add(GhostChar);
|
||||
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
|
||||
|
@ -162,7 +162,7 @@ int CGhost::GetSlot() const
|
|||
return -1;
|
||||
}
|
||||
|
||||
int CGhost::FreeSlot() const
|
||||
int CGhost::FreeSlots() const
|
||||
{
|
||||
int Num = 0;
|
||||
for(int i = 0; i < MAX_ACTIVE_GHOSTS; i++)
|
||||
|
@ -171,104 +171,133 @@ int CGhost::FreeSlot() const
|
|||
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;
|
||||
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;
|
||||
|
||||
bool RaceFlag = m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_RACETIME;
|
||||
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;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
if(!ServerControl)
|
||||
CheckStartLocal(true);
|
||||
}
|
||||
|
||||
void CGhost::OnRender()
|
||||
|
@ -282,7 +311,7 @@ void CGhost::OnRender()
|
|||
for(int i = 0; i < MAX_ACTIVE_GHOSTS; i++)
|
||||
{
|
||||
CGhostItem *pGhost = &m_aActiveGhosts[i];
|
||||
if(pGhost->Empty() || pGhost->m_PlaybackPos < 0)
|
||||
if(pGhost->Empty())
|
||||
continue;
|
||||
|
||||
int GhostTick = pGhost->m_StartTick + PlaybackTick;
|
||||
|
@ -424,13 +453,13 @@ int CGhost::Load(const char *pFilename)
|
|||
if(Slot == -1)
|
||||
return -1;
|
||||
|
||||
if(!Client()->GhostLoader_Load(pFilename))
|
||||
if(GhostLoader()->Load(pFilename, Client()->GetCurrentMap(), Client()->GetMapCrc()) != 0)
|
||||
return -1;
|
||||
|
||||
const CGhostHeader *pHeader = GhostLoader()->GetHeader();
|
||||
|
||||
int NumTicks = GhostLoader()->GetTicks(pHeader);
|
||||
int Time = GhostLoader()->GetTime(pHeader);
|
||||
int NumTicks = pHeader->GetTicks();
|
||||
int Time = pHeader->GetTime();
|
||||
if(NumTicks <= 0 || Time <= 0)
|
||||
{
|
||||
GhostLoader()->Close();
|
||||
|
@ -461,23 +490,23 @@ int CGhost::Load(const char *pFilename)
|
|||
if(Type == GHOSTDATA_TYPE_SKIN && !FoundSkin)
|
||||
{
|
||||
FoundSkin = true;
|
||||
if(!GhostLoader()->ReadData(Type, (char*)&pGhost->m_Skin, sizeof(CGhostSkin)))
|
||||
if(!GhostLoader()->ReadData(Type, &pGhost->m_Skin, sizeof(CGhostSkin)))
|
||||
Error = true;
|
||||
}
|
||||
else if(Type == GHOSTDATA_TYPE_CHARACTER_NO_TICK)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -531,12 +560,12 @@ void CGhost::SaveGhost(CMenus::CGhostItem *pItem)
|
|||
|
||||
int NumTicks = pGhost->m_Path.Size();
|
||||
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_SKIN, (const char*)&pGhost->m_Skin, sizeof(CGhostSkin));
|
||||
GhostRecorder()->WriteData(GHOSTDATA_TYPE_START_TICK, &pGhost->m_StartTick, sizeof(int));
|
||||
GhostRecorder()->WriteData(GHOSTDATA_TYPE_SKIN, &pGhost->m_Skin, sizeof(CGhostSkin));
|
||||
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);
|
||||
}
|
||||
|
@ -590,6 +619,7 @@ void CGhost::OnReset()
|
|||
StopRecord();
|
||||
StopRender();
|
||||
m_LastDeathTick = -1;
|
||||
m_LastRaceTick = -1;
|
||||
}
|
||||
|
||||
void CGhost::OnMapLoad()
|
||||
|
|
|
@ -112,9 +112,12 @@ private:
|
|||
int m_NewRenderTick;
|
||||
int m_StartRenderTick;
|
||||
int m_LastDeathTick;
|
||||
int m_LastRaceTick;
|
||||
bool m_Recording;
|
||||
bool m_Rendering;
|
||||
|
||||
bool m_RenderingStartedByServer;
|
||||
|
||||
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 GetNetObjCharacter(CNetObj_Character *pChar, const CGhostCharacter *pGhostChar);
|
||||
|
@ -124,6 +127,10 @@ private:
|
|||
void AddInfos(const CNetObj_Character *pChar);
|
||||
int GetSlot() const;
|
||||
|
||||
void CheckStart();
|
||||
void CheckStartLocal(bool Predicted);
|
||||
void TryRenderStart(int Tick, bool ServerControl);
|
||||
|
||||
void StartRecord(int Tick);
|
||||
void StopRecord(int Time = -1);
|
||||
void StartRender(int Tick);
|
||||
|
@ -144,9 +151,10 @@ public:
|
|||
virtual void OnMessage(int MsgType, void *pRawMsg);
|
||||
virtual void OnMapLoad();
|
||||
|
||||
void OnNewSnapshot(bool Predicted = false);
|
||||
void OnNewSnapshot();
|
||||
void OnNewPredictedSnapshot();
|
||||
|
||||
int FreeSlot() const;
|
||||
int FreeSlots() const;
|
||||
int Load(const char *pFilename);
|
||||
void Unload(int Slot);
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
CGhostItem Item;
|
||||
str_copy(Item.m_aFilename, aFilename, sizeof(Item.m_aFilename));
|
||||
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)
|
||||
pSelf->m_lGhosts.add(Item);
|
||||
return 0;
|
||||
|
@ -1146,7 +1146,7 @@ void CMenus::RenderGhost(CUIRect MainView)
|
|||
|
||||
CGhostItem *pOwnGhost = GetOwnGhost();
|
||||
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);
|
||||
|
||||
|
|
|
@ -1801,7 +1801,7 @@ void CGameClient::OnPredict()
|
|||
m_PredictedTick = Client()->PredGameTick();
|
||||
|
||||
if(m_NewPredictedTick)
|
||||
m_pGhost->OnNewSnapshot(true);
|
||||
m_pGhost->OnNewPredictedSnapshot();
|
||||
}
|
||||
|
||||
void CGameClient::OnActivateEditor()
|
||||
|
|
Loading…
Reference in a new issue