mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-19 14:38:18 +00:00
Improved ghost and race recorder file handling
This commit is contained in:
parent
66cc527af4
commit
e9a0271c29
|
@ -177,14 +177,13 @@ public:
|
||||||
|
|
||||||
virtual const char *GetCurrentMap() = 0;
|
virtual const char *GetCurrentMap() = 0;
|
||||||
virtual const char *GetCurrentMapPath() = 0;
|
virtual const char *GetCurrentMapPath() = 0;
|
||||||
|
virtual unsigned GetMapCrc() = 0;
|
||||||
|
|
||||||
virtual void RaceRecord_GetName(char *pBuf, int Size, int Time = -1) = 0;
|
virtual void RaceRecord_Start(const char *pFilename) = 0;
|
||||||
virtual void RaceRecord_Start() = 0;
|
|
||||||
virtual void RaceRecord_Stop() = 0;
|
virtual void RaceRecord_Stop() = 0;
|
||||||
virtual bool RaceRecord_IsRecording() = 0;
|
virtual bool RaceRecord_IsRecording() = 0;
|
||||||
|
|
||||||
virtual void Ghost_GetPath(char *pBuf, int Size, const char *pPlayerName, int Time = -1) = 0;
|
virtual void GhostRecorder_Start(const char *pFilename, const char *pPlayerName) = 0;
|
||||||
virtual void GhostRecorder_Start(const char *pPlayerName, int Time = -1) = 0;
|
|
||||||
virtual bool GhostLoader_Load(const char *pFilename) = 0;
|
virtual bool GhostLoader_Load(const char *pFilename) = 0;
|
||||||
virtual bool GhostLoader_GetGhostInfo(const char *pFilename, struct CGhostHeader *pGhostHeader) = 0;
|
virtual bool GhostLoader_GetGhostInfo(const char *pFilename, struct CGhostHeader *pGhostHeader) = 0;
|
||||||
|
|
||||||
|
|
|
@ -3596,35 +3596,17 @@ const char *CClient::GetCurrentMapPath()
|
||||||
return m_aCurrentMapPath;
|
return m_aCurrentMapPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClient::RaceRecord_GetName(char *pBuf, int Size, int Time)
|
unsigned CClient::GetMapCrc()
|
||||||
{
|
{
|
||||||
// check the player name
|
return m_pMap->Crc();
|
||||||
char aPlayerName[MAX_NAME_LENGTH];
|
|
||||||
str_copy(aPlayerName, g_Config.m_PlayerName, sizeof(aPlayerName));
|
|
||||||
str_sanitize_filename(aPlayerName);
|
|
||||||
|
|
||||||
if(Time < 0)
|
|
||||||
str_format(pBuf, Size, "%s_tmp_%d", m_aCurrentMap, pid());
|
|
||||||
else if(g_Config.m_ClDemoName)
|
|
||||||
str_format(pBuf, Size, "%s_%d.%03d_%s", m_aCurrentMap, Time / 1000, Time % 1000, aPlayerName);
|
|
||||||
else
|
|
||||||
str_format(pBuf, Size, "%s_%d.%03d", m_aCurrentMap, Time / 1000, Time % 1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClient::RaceRecord_Start()
|
void CClient::RaceRecord_Start(const char *pFilename)
|
||||||
{
|
{
|
||||||
if(State() != IClient::STATE_ONLINE)
|
if(State() != IClient::STATE_ONLINE)
|
||||||
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demorec/record", "client is not online");
|
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demorec/record", "client is not online");
|
||||||
else
|
else
|
||||||
{
|
m_DemoRecorder[RECORDER_RACE].Start(Storage(), m_pConsole, pFilename, GameClient()->NetVersion(), m_aCurrentMap, m_pMap->Crc(), "client", m_pMap->MapSize(), 0, m_pMap->File());
|
||||||
char aDemoName[128];
|
|
||||||
char aFilename[128];
|
|
||||||
|
|
||||||
RaceRecord_GetName(aDemoName, sizeof(aDemoName));
|
|
||||||
str_format(aFilename, sizeof(aFilename), "demos/%s.demo", aDemoName);
|
|
||||||
|
|
||||||
m_DemoRecorder[RECORDER_RACE].Start(Storage(), m_pConsole, aFilename, GameClient()->NetVersion(), m_aCurrentMap, m_pMap->Crc(), "client", m_pMap->MapSize(), 0, m_pMap->File());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClient::RaceRecord_Stop()
|
void CClient::RaceRecord_Stop()
|
||||||
|
@ -3638,24 +3620,9 @@ bool CClient::RaceRecord_IsRecording()
|
||||||
return m_DemoRecorder[RECORDER_RACE].IsRecording();
|
return m_DemoRecorder[RECORDER_RACE].IsRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClient::Ghost_GetPath(char *pBuf, int Size, const char *pPlayerName, int Time)
|
void CClient::GhostRecorder_Start(const char *pFilename, const char *pPlayerName)
|
||||||
{
|
{
|
||||||
// check the player name
|
m_GhostRecorder.Start(Storage(), m_pConsole, pFilename, m_aCurrentMap, m_pMap->Crc(), pPlayerName);
|
||||||
char aPlayerName[MAX_NAME_LENGTH];
|
|
||||||
str_copy(aPlayerName, pPlayerName, sizeof(aPlayerName));
|
|
||||||
str_sanitize_filename(aPlayerName);
|
|
||||||
|
|
||||||
if(Time < 0)
|
|
||||||
str_format(pBuf, Size, "ghosts/%s_%s_%08x_tmp_%d.gho", m_aCurrentMap, aPlayerName, m_pMap->Crc(), pid());
|
|
||||||
else
|
|
||||||
str_format(pBuf, Size, "ghosts/%s_%s_%d.%03d_%08x.gho", m_aCurrentMap, aPlayerName, Time / 1000, Time % 1000, m_pMap->Crc());
|
|
||||||
}
|
|
||||||
|
|
||||||
void CClient::GhostRecorder_Start(const char *pPlayerName, int Time)
|
|
||||||
{
|
|
||||||
char aFilename[128];
|
|
||||||
Ghost_GetPath(aFilename, sizeof(aFilename), pPlayerName, Time);
|
|
||||||
m_GhostRecorder.Start(Storage(), m_pConsole, aFilename, m_aCurrentMap, m_pMap->Crc(), pPlayerName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CClient::GhostLoader_Load(const char *pFilename)
|
bool CClient::GhostLoader_Load(const char *pFilename)
|
||||||
|
|
|
@ -385,14 +385,13 @@ public:
|
||||||
|
|
||||||
const char *GetCurrentMap();
|
const char *GetCurrentMap();
|
||||||
const char *GetCurrentMapPath();
|
const char *GetCurrentMapPath();
|
||||||
|
unsigned GetMapCrc();
|
||||||
|
|
||||||
void RaceRecord_GetName(char *pBuf, int Size, int Time = -1);
|
void RaceRecord_Start(const char *pFilename);
|
||||||
void RaceRecord_Start();
|
|
||||||
void RaceRecord_Stop();
|
void RaceRecord_Stop();
|
||||||
bool RaceRecord_IsRecording();
|
bool RaceRecord_IsRecording();
|
||||||
|
|
||||||
void Ghost_GetPath(char *pBuf, int Size, const char *pPlayerName, int Time = -1);
|
void GhostRecorder_Start(const char *pFilename, const char *pPlayerName);
|
||||||
void GhostRecorder_Start(const char *pPlayerName, int Time = -1);
|
|
||||||
bool GhostLoader_Load(const char *pFilename);
|
bool GhostLoader_Load(const char *pFilename);
|
||||||
bool GhostLoader_GetGhostInfo(const char *pFilename, struct CGhostHeader *pGhostHeader);
|
bool GhostLoader_GetGhostInfo(const char *pFilename, struct CGhostHeader *pGhostHeader);
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#include "menus.h"
|
#include "menus.h"
|
||||||
#include "ghost.h"
|
#include "ghost.h"
|
||||||
|
|
||||||
|
const char *CGhost::ms_pGhostDir = "ghosts";
|
||||||
|
|
||||||
CGhost::CGhost() : m_StartRenderTick(-1), m_LastDeathTick(-1), m_Recording(false), m_Rendering(false) {}
|
CGhost::CGhost() : m_StartRenderTick(-1), m_LastDeathTick(-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)
|
||||||
|
@ -113,6 +115,21 @@ CGhostCharacter *CGhost::CGhostPath::Get(int Index)
|
||||||
return &m_lChunks[Chunk][Pos];
|
return &m_lChunks[Chunk][Pos];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CGhost::GetPath(char *pBuf, int Size, const char *pPlayerName, int Time) const
|
||||||
|
{
|
||||||
|
const char *pMap = Client()->GetCurrentMap();
|
||||||
|
unsigned Crc = Client()->GetMapCrc();
|
||||||
|
|
||||||
|
char aPlayerName[MAX_NAME_LENGTH];
|
||||||
|
str_copy(aPlayerName, pPlayerName, sizeof(aPlayerName));
|
||||||
|
str_sanitize_filename(aPlayerName);
|
||||||
|
|
||||||
|
if(Time < 0)
|
||||||
|
str_format(pBuf, Size, "%s/%s_%s_%08x_tmp_%d.gho", ms_pGhostDir, pMap, aPlayerName, Crc, pid());
|
||||||
|
else
|
||||||
|
str_format(pBuf, Size, "%s/%s_%s_%d.%03d_%08x.gho", ms_pGhostDir, pMap, aPlayerName, Time / 1000, Time % 1000, Crc);
|
||||||
|
}
|
||||||
|
|
||||||
void CGhost::AddInfos(const CNetObj_Character *pChar)
|
void CGhost::AddInfos(const CNetObj_Character *pChar)
|
||||||
{
|
{
|
||||||
int NumTicks = m_CurGhost.m_Path.Size();
|
int NumTicks = m_CurGhost.m_Path.Size();
|
||||||
|
@ -120,7 +137,8 @@ void CGhost::AddInfos(const CNetObj_Character *pChar)
|
||||||
// do not start writing to file as long as we still touch the start line
|
// do not start writing to file as long as we still touch the start line
|
||||||
if(g_Config.m_ClRaceSaveGhost && !GhostRecorder()->IsRecording() && NumTicks > 0)
|
if(g_Config.m_ClRaceSaveGhost && !GhostRecorder()->IsRecording() && NumTicks > 0)
|
||||||
{
|
{
|
||||||
Client()->GhostRecorder_Start(m_CurGhost.m_aPlayer);
|
GetPath(m_aTmpFilename, sizeof(m_aTmpFilename), m_CurGhost.m_aPlayer);
|
||||||
|
Client()->GhostRecorder_Start(m_aTmpFilename, m_CurGhost.m_aPlayer);
|
||||||
|
|
||||||
GhostRecorder()->WriteData(GHOSTDATA_TYPE_START_TICK, (const char*)&m_CurGhost.m_StartTick, sizeof(int));
|
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_SKIN, (const char*)&m_CurGhost.m_Skin, sizeof(CGhostSkin));
|
||||||
|
@ -347,9 +365,6 @@ void CGhost::StopRecord(int Time)
|
||||||
if(RecordingToFile)
|
if(RecordingToFile)
|
||||||
GhostRecorder()->Stop(m_CurGhost.m_Path.Size(), Time);
|
GhostRecorder()->Stop(m_CurGhost.m_Path.Size(), Time);
|
||||||
|
|
||||||
char aTmpFilename[128];
|
|
||||||
Client()->Ghost_GetPath(aTmpFilename, sizeof(aTmpFilename), m_CurGhost.m_aPlayer);
|
|
||||||
|
|
||||||
CMenus::CGhostItem *pOwnGhost = m_pClient->m_pMenus->GetOwnGhost();
|
CMenus::CGhostItem *pOwnGhost = m_pClient->m_pMenus->GetOwnGhost();
|
||||||
if(Time > 0 && (!pOwnGhost || Time < pOwnGhost->m_Time))
|
if(Time > 0 && (!pOwnGhost || Time < pOwnGhost->m_Time))
|
||||||
{
|
{
|
||||||
|
@ -364,20 +379,22 @@ void CGhost::StopRecord(int Time)
|
||||||
// create ghost item
|
// create ghost item
|
||||||
CMenus::CGhostItem Item;
|
CMenus::CGhostItem Item;
|
||||||
if(RecordingToFile)
|
if(RecordingToFile)
|
||||||
Client()->Ghost_GetPath(Item.m_aFilename, sizeof(Item.m_aFilename), m_CurGhost.m_aPlayer, Time);
|
GetPath(Item.m_aFilename, sizeof(Item.m_aFilename), m_CurGhost.m_aPlayer, Time);
|
||||||
str_copy(Item.m_aPlayer, m_CurGhost.m_aPlayer, sizeof(Item.m_aPlayer));
|
str_copy(Item.m_aPlayer, m_CurGhost.m_aPlayer, sizeof(Item.m_aPlayer));
|
||||||
Item.m_Time = Time;
|
Item.m_Time = Time;
|
||||||
Item.m_Slot = Slot;
|
Item.m_Slot = Slot;
|
||||||
|
|
||||||
// save new ghost file
|
// save new ghost file
|
||||||
if(Item.HasFile())
|
if(Item.HasFile())
|
||||||
Storage()->RenameFile(aTmpFilename, Item.m_aFilename, IStorage::TYPE_SAVE);
|
Storage()->RenameFile(m_aTmpFilename, Item.m_aFilename, IStorage::TYPE_SAVE);
|
||||||
|
|
||||||
// add item to menu list
|
// add item to menu list
|
||||||
m_pClient->m_pMenus->UpdateOwnGhost(Item);
|
m_pClient->m_pMenus->UpdateOwnGhost(Item);
|
||||||
}
|
}
|
||||||
else if(RecordingToFile) // no new record
|
else if(RecordingToFile) // no new record
|
||||||
Storage()->RemoveFile(aTmpFilename, IStorage::TYPE_SAVE);
|
Storage()->RemoveFile(m_aTmpFilename, IStorage::TYPE_SAVE);
|
||||||
|
|
||||||
|
m_aTmpFilename[0] = 0;
|
||||||
|
|
||||||
m_CurGhost.Reset();
|
m_CurGhost.Reset();
|
||||||
}
|
}
|
||||||
|
@ -507,7 +524,8 @@ void CGhost::SaveGhost(CMenus::CGhostItem *pItem)
|
||||||
CGhostItem *pGhost = &m_aActiveGhosts[Slot];
|
CGhostItem *pGhost = &m_aActiveGhosts[Slot];
|
||||||
|
|
||||||
int NumTicks = pGhost->m_Path.Size();
|
int NumTicks = pGhost->m_Path.Size();
|
||||||
Client()->GhostRecorder_Start(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()->WriteData(GHOSTDATA_TYPE_START_TICK, (const char*)&pGhost->m_StartTick, sizeof(int));
|
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_SKIN, (const char*)&pGhost->m_Skin, sizeof(CGhostSkin));
|
||||||
|
@ -515,7 +533,6 @@ void CGhost::SaveGhost(CMenus::CGhostItem *pItem)
|
||||||
GhostRecorder()->WriteData(GHOSTDATA_TYPE_CHARACTER, (const char*)pGhost->m_Path.Get(i), sizeof(CGhostCharacter));
|
GhostRecorder()->WriteData(GHOSTDATA_TYPE_CHARACTER, (const char*)pGhost->m_Path.Get(i), sizeof(CGhostCharacter));
|
||||||
|
|
||||||
GhostRecorder()->Stop(NumTicks, pItem->m_Time);
|
GhostRecorder()->Stop(NumTicks, pItem->m_Time);
|
||||||
Client()->Ghost_GetPath(pItem->m_aFilename, sizeof(pItem->m_aFilename), pItem->m_aPlayer, pItem->m_Time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGhost::ConGPlay(IConsole::IResult *pResult, void *pUserData)
|
void CGhost::ConGPlay(IConsole::IResult *pResult, void *pUserData)
|
||||||
|
|
|
@ -99,12 +99,16 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char *ms_pGhostDir;
|
||||||
|
|
||||||
class IGhostLoader *m_pGhostLoader;
|
class IGhostLoader *m_pGhostLoader;
|
||||||
class IGhostRecorder *m_pGhostRecorder;
|
class IGhostRecorder *m_pGhostRecorder;
|
||||||
|
|
||||||
CGhostItem m_aActiveGhosts[MAX_ACTIVE_GHOSTS];
|
CGhostItem m_aActiveGhosts[MAX_ACTIVE_GHOSTS];
|
||||||
CGhostItem m_CurGhost;
|
CGhostItem m_CurGhost;
|
||||||
|
|
||||||
|
char m_aTmpFilename[128];
|
||||||
|
|
||||||
int m_StartRenderTick;
|
int m_StartRenderTick;
|
||||||
int m_LastDeathTick;
|
int m_LastDeathTick;
|
||||||
bool m_Recording;
|
bool m_Recording;
|
||||||
|
@ -114,6 +118,8 @@ private:
|
||||||
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);
|
||||||
|
|
||||||
|
void GetPath(char *pBuf, int Size, const char *pPlayerName, int Time = -1) const;
|
||||||
|
|
||||||
void AddInfos(const CNetObj_Character *pChar);
|
void AddInfos(const CNetObj_Character *pChar);
|
||||||
int GetSlot() const;
|
int GetSlot() const;
|
||||||
|
|
||||||
|
|
|
@ -836,10 +836,8 @@ int CMenus::GhostlistFetchCallback(const char *pName, int IsDir, int StorageType
|
||||||
{
|
{
|
||||||
CMenus *pSelf = (CMenus *)pUser;
|
CMenus *pSelf = (CMenus *)pUser;
|
||||||
int Length = str_length(pName);
|
int Length = str_length(pName);
|
||||||
const char *pMap = pSelf->m_pClient->Client()->GetCurrentMap();
|
const char *pMap = pSelf->Client()->GetCurrentMap();
|
||||||
if((pName[0] == '.' && (pName[1] == 0 ||
|
if(IsDir || Length < 4 || str_comp(pName+Length-4, ".gho") != 0 || str_comp_num(pName, pMap, str_length(pMap)) != 0)
|
||||||
(pName[1] == '.' && pName[2] == 0))) ||
|
|
||||||
(!IsDir && (Length < 4 || str_comp(pName+Length-4, ".gho") || str_comp_num(pName, pMap, str_length(pMap)))))
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
char aFilename[256];
|
char aFilename[256];
|
||||||
|
|
|
@ -1,16 +1,47 @@
|
||||||
/* (c) Redix and Sushi */
|
/* (c) Redix and Sushi */
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#include <base/system.h>
|
#include <base/system.h>
|
||||||
#include <engine/shared/config.h>
|
#include <engine/shared/config.h>
|
||||||
#include <engine/serverbrowser.h>
|
#include <engine/serverbrowser.h>
|
||||||
#include <engine/storage.h>
|
#include <engine/storage.h>
|
||||||
|
|
||||||
#include "menus.h"
|
|
||||||
#include "race.h"
|
#include "race.h"
|
||||||
#include "race_demo.h"
|
#include "race_demo.h"
|
||||||
|
|
||||||
|
const char *CRaceDemo::ms_pRaceDemoDir = "demos";
|
||||||
|
|
||||||
|
struct CDemoItem
|
||||||
|
{
|
||||||
|
char m_aName[128];
|
||||||
|
int m_Time;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CDemoListParam
|
||||||
|
{
|
||||||
|
std::vector<CDemoItem> *m_plDemos;
|
||||||
|
const char *pMap;
|
||||||
|
};
|
||||||
|
|
||||||
CRaceDemo::CRaceDemo() : m_RaceState(RACE_NONE), m_RaceStartTick(-1), m_RecordStopTick(-1), m_Time(0) {}
|
CRaceDemo::CRaceDemo() : m_RaceState(RACE_NONE), m_RaceStartTick(-1), m_RecordStopTick(-1), m_Time(0) {}
|
||||||
|
|
||||||
|
void CRaceDemo::GetPath(char *pBuf, int Size, int Time) const
|
||||||
|
{
|
||||||
|
const char *pMap = Client()->GetCurrentMap();
|
||||||
|
|
||||||
|
char aPlayerName[MAX_NAME_LENGTH];
|
||||||
|
str_copy(aPlayerName, g_Config.m_PlayerName, sizeof(aPlayerName));
|
||||||
|
str_sanitize_filename(aPlayerName);
|
||||||
|
|
||||||
|
if(Time < 0)
|
||||||
|
str_format(pBuf, Size, "%s/%s_tmp_%d.demo", ms_pRaceDemoDir, pMap, pid());
|
||||||
|
else if(g_Config.m_ClDemoName)
|
||||||
|
str_format(pBuf, Size, "%s/%s_%d.%03d_%s.demo", ms_pRaceDemoDir, pMap, Time / 1000, Time % 1000, aPlayerName);
|
||||||
|
else
|
||||||
|
str_format(pBuf, Size, "%s/%s_%d.%03d.demo", ms_pRaceDemoDir, pMap, Time / 1000, Time % 1000);
|
||||||
|
}
|
||||||
|
|
||||||
void CRaceDemo::OnStateChange(int NewState, int OldState)
|
void CRaceDemo::OnStateChange(int NewState, int OldState)
|
||||||
{
|
{
|
||||||
if(OldState == IClient::STATE_ONLINE)
|
if(OldState == IClient::STATE_ONLINE)
|
||||||
|
@ -47,7 +78,10 @@ void CRaceDemo::OnRender()
|
||||||
if(m_RaceState == RACE_STARTED)
|
if(m_RaceState == RACE_STARTED)
|
||||||
Client()->RaceRecord_Stop();
|
Client()->RaceRecord_Stop();
|
||||||
if(m_RaceState != RACE_PREPARE) // start recording again
|
if(m_RaceState != RACE_PREPARE) // start recording again
|
||||||
Client()->RaceRecord_Start();
|
{
|
||||||
|
GetPath(m_aTmpFilename, sizeof(m_aTmpFilename));
|
||||||
|
Client()->RaceRecord_Start(m_aTmpFilename);
|
||||||
|
}
|
||||||
m_RaceStartTick = Client()->GameTick();
|
m_RaceStartTick = Client()->GameTick();
|
||||||
m_RaceState = RACE_STARTED;
|
m_RaceState = RACE_STARTED;
|
||||||
}
|
}
|
||||||
|
@ -56,7 +90,8 @@ void CRaceDemo::OnRender()
|
||||||
// start recording before the player passes the start line, so we can see some preparation steps
|
// start recording before the player passes the start line, so we can see some preparation steps
|
||||||
if(m_RaceState == RACE_NONE)
|
if(m_RaceState == RACE_NONE)
|
||||||
{
|
{
|
||||||
Client()->RaceRecord_Start();
|
GetPath(m_aTmpFilename, sizeof(m_aTmpFilename));
|
||||||
|
Client()->RaceRecord_Start(m_aTmpFilename);
|
||||||
m_RaceStartTick = Client()->GameTick();
|
m_RaceStartTick = Client()->GameTick();
|
||||||
m_RaceState = RACE_PREPARE;
|
m_RaceState = RACE_PREPARE;
|
||||||
}
|
}
|
||||||
|
@ -64,7 +99,7 @@ void CRaceDemo::OnRender()
|
||||||
// stop recording if the player did not pass the start line after 20 seconds
|
// stop recording if the player did not pass the start line after 20 seconds
|
||||||
if(m_RaceState == RACE_PREPARE && Client()->GameTick() - m_RaceStartTick >= Client()->GameTickSpeed() * 20)
|
if(m_RaceState == RACE_PREPARE && Client()->GameTick() - m_RaceStartTick >= Client()->GameTickSpeed() * 20)
|
||||||
{
|
{
|
||||||
OnReset();
|
StopRecord();
|
||||||
m_RaceState = RACE_IDLE;
|
m_RaceState = RACE_IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,22 +160,18 @@ void CRaceDemo::StopRecord(int Time)
|
||||||
if(Client()->RaceRecord_IsRecording())
|
if(Client()->RaceRecord_IsRecording())
|
||||||
Client()->RaceRecord_Stop();
|
Client()->RaceRecord_Stop();
|
||||||
|
|
||||||
char aDemoName[128];
|
|
||||||
char aTmpFilename[512];
|
|
||||||
Client()->RaceRecord_GetName(aDemoName, sizeof(aDemoName));
|
|
||||||
str_format(aTmpFilename, sizeof(aTmpFilename), "demos/%s.demo", aDemoName);
|
|
||||||
|
|
||||||
if(Time > 0 && CheckDemo(Time))
|
if(Time > 0 && CheckDemo(Time))
|
||||||
{
|
{
|
||||||
// save file
|
// save file
|
||||||
char aNewFilename[512];
|
char aNewFilename[512];
|
||||||
Client()->RaceRecord_GetName(aDemoName, sizeof(aDemoName), m_Time);
|
GetPath(aNewFilename, sizeof(aNewFilename), m_Time);
|
||||||
str_format(aNewFilename, sizeof(aNewFilename), "demos/%s.demo", aDemoName);
|
|
||||||
|
|
||||||
Storage()->RenameFile(aTmpFilename, aNewFilename, IStorage::TYPE_SAVE);
|
Storage()->RenameFile(m_aTmpFilename, aNewFilename, IStorage::TYPE_SAVE);
|
||||||
}
|
}
|
||||||
else // no new record
|
else // no new record
|
||||||
Storage()->RemoveFile(aTmpFilename, IStorage::TYPE_SAVE);
|
Storage()->RemoveFile(m_aTmpFilename, IStorage::TYPE_SAVE);
|
||||||
|
|
||||||
|
m_aTmpFilename[0] = 0;
|
||||||
|
|
||||||
m_Time = 0;
|
m_Time = 0;
|
||||||
m_RaceState = RACE_NONE;
|
m_RaceState = RACE_NONE;
|
||||||
|
@ -148,51 +179,57 @@ void CRaceDemo::StopRecord(int Time)
|
||||||
m_RecordStopTick = -1;
|
m_RecordStopTick = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CRaceDemo::RaceDemolistFetchCallback(const char *pName, time_t Date, int IsDir, int StorageType, void *pUser)
|
||||||
|
{
|
||||||
|
CDemoListParam *pParam = (CDemoListParam*) pUser;
|
||||||
|
int Length = str_length(pName);
|
||||||
|
int MapLen = str_length(pParam->pMap);
|
||||||
|
if(IsDir || Length < 5 || str_comp(pName + Length - 5, ".demo") != 0 || str_comp_num(pName, pParam->pMap, MapLen) != 0 || pName[MapLen] != '_')
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
CDemoItem Item;
|
||||||
|
str_copy(Item.m_aName, pName, min(static_cast<int>(sizeof(Item.m_aName)), Length - 4));
|
||||||
|
|
||||||
|
const char *pTime = Item.m_aName + MapLen + 1;
|
||||||
|
const char *pTEnd = pTime;
|
||||||
|
while(isdigit(*pTEnd) || *pTEnd == ' ' || *pTEnd == '.' || *pTEnd == ',')
|
||||||
|
pTEnd++;
|
||||||
|
|
||||||
|
if(g_Config.m_ClDemoName)
|
||||||
|
{
|
||||||
|
char aPlayerName[MAX_NAME_LENGTH];
|
||||||
|
str_copy(aPlayerName, g_Config.m_PlayerName, sizeof(aPlayerName));
|
||||||
|
str_sanitize_filename(aPlayerName);
|
||||||
|
|
||||||
|
if(pTEnd[0] != '_' || str_comp(pTEnd + 1, aPlayerName) != 0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if(pTEnd[0])
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Item.m_Time = CRaceHelper::TimeFromSecondsStr(pTime);
|
||||||
|
if(Item.m_Time > 0)
|
||||||
|
pParam->m_plDemos->push_back(Item);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool CRaceDemo::CheckDemo(int Time) const
|
bool CRaceDemo::CheckDemo(int Time) const
|
||||||
{
|
{
|
||||||
if(str_comp(m_pClient->m_pMenus->GetCurrentDemoFolder(), "demos") != 0)
|
std::vector<CDemoItem> lDemos;
|
||||||
return true;
|
CDemoListParam Param = { &lDemos, Client()->GetCurrentMap() };
|
||||||
|
Storage()->ListDirectoryInfo(IStorage::TYPE_SAVE, ms_pRaceDemoDir, RaceDemolistFetchCallback, &Param);
|
||||||
char aTmpDemoName[128];
|
|
||||||
Client()->RaceRecord_GetName(aTmpDemoName, sizeof(aTmpDemoName));
|
|
||||||
|
|
||||||
// loop through demo files
|
// loop through demo files
|
||||||
m_pClient->m_pMenus->DemolistPopulate();
|
for(int i = 0; i < lDemos.size(); i++)
|
||||||
for(int i = 0; i < m_pClient->m_pMenus->m_lDemos.size(); i++)
|
|
||||||
{
|
{
|
||||||
// skip temp file
|
if(Time >= lDemos[i].m_Time) // found a better demo
|
||||||
const char *pDemoName = m_pClient->m_pMenus->m_lDemos[i].m_aName;
|
return false;
|
||||||
if(str_comp(pDemoName, aTmpDemoName) == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int MapLen = str_length(Client()->GetCurrentMap());
|
// delete old demo
|
||||||
if(str_comp_num(pDemoName, Client()->GetCurrentMap(), MapLen) != 0 || pDemoName[MapLen] != '_')
|
char aFilename[512];
|
||||||
continue;
|
str_format(aFilename, sizeof(aFilename), "%s/%s.demo", ms_pRaceDemoDir, lDemos[i].m_aName);
|
||||||
|
Storage()->RemoveFile(aFilename, IStorage::TYPE_SAVE);
|
||||||
// set cursor
|
|
||||||
pDemoName += MapLen + 1;
|
|
||||||
|
|
||||||
if(g_Config.m_ClDemoName)
|
|
||||||
{
|
|
||||||
char aPlayerName[MAX_NAME_LENGTH];
|
|
||||||
str_copy(aPlayerName, g_Config.m_PlayerName, sizeof(aPlayerName));
|
|
||||||
str_sanitize_filename(aPlayerName);
|
|
||||||
|
|
||||||
if(!str_find(pDemoName, aPlayerName))
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DemoTime = CRaceHelper::TimeFromSecondsStr(pDemoName);
|
|
||||||
if(DemoTime > 0)
|
|
||||||
{
|
|
||||||
if(Time >= DemoTime) // found a better demo
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// delete old demo
|
|
||||||
char aFilename[512];
|
|
||||||
str_format(aFilename, sizeof(aFilename), "demos/%s", m_pClient->m_pMenus->m_lDemos[i].m_aFilename);
|
|
||||||
Storage()->RemoveFile(aFilename, IStorage::TYPE_SAVE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -16,11 +16,19 @@ class CRaceDemo : public CComponent
|
||||||
RACE_FINISHED,
|
RACE_FINISHED,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char *ms_pRaceDemoDir;
|
||||||
|
|
||||||
|
char m_aTmpFilename[128];
|
||||||
|
|
||||||
int m_RaceState;
|
int m_RaceState;
|
||||||
int m_RaceStartTick;
|
int m_RaceStartTick;
|
||||||
int m_RecordStopTick;
|
int m_RecordStopTick;
|
||||||
int m_Time;
|
int m_Time;
|
||||||
|
|
||||||
|
static int RaceDemolistFetchCallback(const char *pName, time_t Date, int IsDir, int StorageType, void *pUser);
|
||||||
|
|
||||||
|
void GetPath(char *pBuf, int Size, int Time = -1) const;
|
||||||
|
|
||||||
void StopRecord(int Time = -1);
|
void StopRecord(int Time = -1);
|
||||||
bool CheckDemo(int Time) const;
|
bool CheckDemo(int Time) const;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue