Improved ghost and race recorder file handling

This commit is contained in:
Redix 2017-09-28 19:13:20 +02:00
parent 66cc527af4
commit e9a0271c29
8 changed files with 143 additions and 112 deletions

View file

@ -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;

View file

@ -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)

View file

@ -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);

View file

@ -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)

View file

@ -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;

View file

@ -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];

View file

@ -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,29 +179,21 @@ void CRaceDemo::StopRecord(int Time)
m_RecordStopTick = -1; m_RecordStopTick = -1;
} }
bool CRaceDemo::CheckDemo(int Time) const int CRaceDemo::RaceDemolistFetchCallback(const char *pName, time_t Date, int IsDir, int StorageType, void *pUser)
{ {
if(str_comp(m_pClient->m_pMenus->GetCurrentDemoFolder(), "demos") != 0) CDemoListParam *pParam = (CDemoListParam*) pUser;
return true; 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;
char aTmpDemoName[128]; CDemoItem Item;
Client()->RaceRecord_GetName(aTmpDemoName, sizeof(aTmpDemoName)); str_copy(Item.m_aName, pName, min(static_cast<int>(sizeof(Item.m_aName)), Length - 4));
// loop through demo files const char *pTime = Item.m_aName + MapLen + 1;
m_pClient->m_pMenus->DemolistPopulate(); const char *pTEnd = pTime;
for(int i = 0; i < m_pClient->m_pMenus->m_lDemos.size(); i++) while(isdigit(*pTEnd) || *pTEnd == ' ' || *pTEnd == '.' || *pTEnd == ',')
{ pTEnd++;
// skip temp file
const char *pDemoName = m_pClient->m_pMenus->m_lDemos[i].m_aName;
if(str_comp(pDemoName, aTmpDemoName) == 0)
continue;
int MapLen = str_length(Client()->GetCurrentMap());
if(str_comp_num(pDemoName, Client()->GetCurrentMap(), MapLen) != 0 || pDemoName[MapLen] != '_')
continue;
// set cursor
pDemoName += MapLen + 1;
if(g_Config.m_ClDemoName) if(g_Config.m_ClDemoName)
{ {
@ -178,22 +201,36 @@ bool CRaceDemo::CheckDemo(int Time) const
str_copy(aPlayerName, g_Config.m_PlayerName, sizeof(aPlayerName)); str_copy(aPlayerName, g_Config.m_PlayerName, sizeof(aPlayerName));
str_sanitize_filename(aPlayerName); str_sanitize_filename(aPlayerName);
if(!str_find(pDemoName, aPlayerName)) if(pTEnd[0] != '_' || str_comp(pTEnd + 1, aPlayerName) != 0)
continue; 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;
} }
int DemoTime = CRaceHelper::TimeFromSecondsStr(pDemoName); bool CRaceDemo::CheckDemo(int Time) const
if(DemoTime > 0)
{ {
if(Time >= DemoTime) // found a better demo std::vector<CDemoItem> lDemos;
CDemoListParam Param = { &lDemos, Client()->GetCurrentMap() };
Storage()->ListDirectoryInfo(IStorage::TYPE_SAVE, ms_pRaceDemoDir, RaceDemolistFetchCallback, &Param);
// loop through demo files
for(int i = 0; i < lDemos.size(); i++)
{
if(Time >= lDemos[i].m_Time) // found a better demo
return false; return false;
// delete old demo // delete old demo
char aFilename[512]; char aFilename[512];
str_format(aFilename, sizeof(aFilename), "demos/%s", m_pClient->m_pMenus->m_lDemos[i].m_aFilename); str_format(aFilename, sizeof(aFilename), "%s/%s.demo", ms_pRaceDemoDir, lDemos[i].m_aName);
Storage()->RemoveFile(aFilename, IStorage::TYPE_SAVE); Storage()->RemoveFile(aFilename, IStorage::TYPE_SAVE);
} }
}
return true; return true;
} }

View file

@ -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;