Merge pull request #7837 from Robyt3/Demo-Recorder-Improvements

Refactor demo recorder usage
This commit is contained in:
Dennis Felsing 2024-01-21 22:39:46 +00:00 committed by GitHub
commit fca6e0abe3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 161 additions and 151 deletions

View file

@ -165,7 +165,7 @@ public:
#endif #endif
virtual void DemoRecorder_Start(const char *pFilename, bool WithTimestamp, int Recorder, bool Verbose = false) = 0; virtual void DemoRecorder_Start(const char *pFilename, bool WithTimestamp, int Recorder, bool Verbose = false) = 0;
virtual void DemoRecorder_HandleAutoStart() = 0; virtual void DemoRecorder_HandleAutoStart() = 0;
virtual void DemoRecorder_Stop(int Recorder, bool RemoveFile = false) = 0; virtual void DemoRecorder_UpdateReplayRecorder() = 0;
virtual class IDemoRecorder *DemoRecorder(int Recorder) = 0; virtual class IDemoRecorder *DemoRecorder(int Recorder) = 0;
virtual void AutoScreenshot_Start() = 0; virtual void AutoScreenshot_Start() = 0;
virtual void AutoStatScreenshot_Start() = 0; virtual void AutoStatScreenshot_Start() = 0;

View file

@ -532,11 +532,13 @@ void CClient::DisconnectWithReason(const char *pReason)
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBuf, gs_ClientNetworkPrintColor); m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBuf, gs_ClientNetworkPrintColor);
// stop demo playback and recorder // stop demo playback and recorder
// make sure to remove replay tmp demo
m_DemoPlayer.Stop(); m_DemoPlayer.Stop();
for(int i = 0; i < RECORDER_MAX; i++) for(int Recorder = 0; Recorder < RECORDER_MAX; Recorder++)
DemoRecorder_Stop(i); {
DemoRecorder(Recorder)->Stop(Recorder == RECORDER_REPLAYS ? IDemoRecorder::EStopMode::REMOVE_FILE : IDemoRecorder::EStopMode::KEEP_FILE);
}
//
m_aRconAuthed[0] = 0; m_aRconAuthed[0] = 0;
mem_zero(m_aRconUsername, sizeof(m_aRconUsername)); mem_zero(m_aRconUsername, sizeof(m_aRconUsername));
mem_zero(m_aRconPassword, sizeof(m_aRconPassword)); mem_zero(m_aRconPassword, sizeof(m_aRconPassword));
@ -581,14 +583,9 @@ void CClient::DisconnectWithReason(const char *pReason)
void CClient::Disconnect() void CClient::Disconnect()
{ {
m_ButtonRender = false;
if(m_State != IClient::STATE_OFFLINE) if(m_State != IClient::STATE_OFFLINE)
DisconnectWithReason(0);
// make sure to remove replay tmp demo
if(g_Config.m_ClReplays)
{ {
DemoRecorder_Stop(RECORDER_REPLAYS, true); DisconnectWithReason(nullptr);
} }
} }
@ -946,8 +943,10 @@ const char *CClient::LoadMap(const char *pName, const char *pFilename, SHA256_DI
} }
// stop demo recording if we loaded a new map // stop demo recording if we loaded a new map
for(int i = 0; i < RECORDER_MAX; i++) for(int Recorder = 0; Recorder < RECORDER_MAX; Recorder++)
DemoRecorder_Stop(i, i == RECORDER_REPLAYS); {
DemoRecorder(Recorder)->Stop(Recorder == RECORDER_REPLAYS ? IDemoRecorder::EStopMode::REMOVE_FILE : IDemoRecorder::EStopMode::KEEP_FILE);
}
char aBuf[256]; char aBuf[256];
str_format(aBuf, sizeof(aBuf), "loaded map '%s'", pFilename); str_format(aBuf, sizeof(aBuf), "loaded map '%s'", pFilename);
@ -2352,22 +2351,20 @@ void CClient::Update()
{ {
if(State() == IClient::STATE_DEMOPLAYBACK) if(State() == IClient::STATE_DEMOPLAYBACK)
{ {
if(m_DemoPlayer.IsPlaying())
{
#if defined(CONF_VIDEORECORDER) #if defined(CONF_VIDEORECORDER)
if(m_DemoPlayer.IsPlaying() && IVideo::Current()) if(IVideo::Current())
{ {
IVideo::Current()->NextVideoFrame(); IVideo::Current()->NextVideoFrame();
IVideo::Current()->NextAudioFrameTimeline([this](short *pFinalOut, unsigned Frames) { IVideo::Current()->NextAudioFrameTimeline([this](short *pFinalOut, unsigned Frames) {
Sound()->Mix(pFinalOut, Frames); Sound()->Mix(pFinalOut, Frames);
}); });
} }
else if(m_ButtonRender)
Disconnect();
#endif #endif
m_DemoPlayer.Update(); m_DemoPlayer.Update();
if(m_DemoPlayer.IsPlaying())
{
// update timers // update timers
const CDemoPlayer::CPlaybackInfo *pInfo = m_DemoPlayer.Info(); const CDemoPlayer::CPlaybackInfo *pInfo = m_DemoPlayer.Info();
m_aCurGameTick[g_Config.m_ClDummy] = pInfo->m_Info.m_CurrentTick; m_aCurGameTick[g_Config.m_ClDummy] = pInfo->m_Info.m_CurrentTick;
@ -2377,7 +2374,8 @@ void CClient::Update()
} }
else else
{ {
// disconnect on error // Disconnect when demo playback stopped, either due to playback error
// or because the end of the demo was reached when rendering it.
DisconnectWithReason(m_DemoPlayer.ErrorMessage()); DisconnectWithReason(m_DemoPlayer.ErrorMessage());
if(m_DemoPlayer.ErrorMessage()[0] != '\0') if(m_DemoPlayer.ErrorMessage()[0] != '\0')
{ {
@ -3472,26 +3470,32 @@ void CClient::SaveReplay(const int Length, const char *pFilename)
} }
if(!DemoRecorder(RECORDER_REPLAYS)->IsRecording()) if(!DemoRecorder(RECORDER_REPLAYS)->IsRecording())
{
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "replay", "ERROR: demorecorder isn't recording. Try to rejoin to fix that."); m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "replay", "ERROR: demorecorder isn't recording. Try to rejoin to fix that.");
}
else if(DemoRecorder(RECORDER_REPLAYS)->Length() < 1) else if(DemoRecorder(RECORDER_REPLAYS)->Length() < 1)
{
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "replay", "ERROR: demorecorder isn't recording for at least 1 second."); m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "replay", "ERROR: demorecorder isn't recording for at least 1 second.");
}
else else
{ {
// First we stop the recorder to slice correctly the demo after // First we stop the recorder to slice correctly the demo after
DemoRecorder_Stop(RECORDER_REPLAYS); DemoRecorder(RECORDER_REPLAYS)->Stop(IDemoRecorder::EStopMode::KEEP_FILE);
char aDate[64];
str_timestamp(aDate, sizeof(aDate));
char aFilename[IO_MAX_PATH_LENGTH]; char aFilename[IO_MAX_PATH_LENGTH];
if(str_comp(pFilename, "") == 0) if(pFilename[0] == '\0')
str_format(aFilename, sizeof(aFilename), "demos/replays/%s_%s (replay).demo", m_aCurrentMap, aDate); {
char aTimestamp[20];
str_timestamp(aTimestamp, sizeof(aTimestamp));
str_format(aFilename, sizeof(aFilename), "demos/replays/%s_%s_(replay).demo", m_aCurrentMap, aTimestamp);
}
else else
{
str_format(aFilename, sizeof(aFilename), "demos/replays/%s.demo", pFilename); str_format(aFilename, sizeof(aFilename), "demos/replays/%s.demo", pFilename);
}
char *pSrc = m_aDemoRecorder[RECORDER_REPLAYS].GetCurrentFilename();
// Slice the demo to get only the last cl_replay_length seconds // Slice the demo to get only the last cl_replay_length seconds
const char *pSrc = m_aDemoRecorder[RECORDER_REPLAYS].CurrentFilename();
const int EndTick = GameTick(g_Config.m_ClDummy); const int EndTick = GameTick(g_Config.m_ClDummy);
const int StartTick = EndTick - Length * GameTickSpeed(); const int StartTick = EndTick - Length * GameTickSpeed();
@ -3503,7 +3507,7 @@ void CClient::SaveReplay(const int Length, const char *pFilename)
m_EditJobs.push_back(pDemoEditTask); m_EditJobs.push_back(pDemoEditTask);
// And we restart the recorder // And we restart the recorder
DemoRecorder_StartReplayRecorder(); DemoRecorder_UpdateReplayRecorder();
} }
} }
@ -3586,7 +3590,6 @@ const char *CClient::DemoPlayer_Render(const char *pFilename, int StorageType, c
const char *pError = DemoPlayer_Play(pFilename, StorageType); const char *pError = DemoPlayer_Play(pFilename, StorageType);
if(pError) if(pError)
return pError; return pError;
m_ButtonRender = true;
this->CClient::StartVideo(NULL, this, pVideoName); this->CClient::StartVideo(NULL, this, pVideoName);
m_DemoPlayer.Play(); m_DemoPlayer.Play();
@ -3632,19 +3635,23 @@ void CClient::DemoRecorder_Start(const char *pFilename, bool WithTimestamp, int
if(State() != IClient::STATE_ONLINE) if(State() != IClient::STATE_ONLINE)
{ {
if(Verbose) if(Verbose)
{
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
{ {
char aFilename[IO_MAX_PATH_LENGTH]; char aFilename[IO_MAX_PATH_LENGTH];
if(WithTimestamp) if(WithTimestamp)
{ {
char aDate[20]; char aTimestamp[20];
str_timestamp(aDate, sizeof(aDate)); str_timestamp(aTimestamp, sizeof(aTimestamp));
str_format(aFilename, sizeof(aFilename), "demos/%s_%s.demo", pFilename, aDate); str_format(aFilename, sizeof(aFilename), "demos/%s_%s.demo", pFilename, aTimestamp);
} }
else else
{
str_format(aFilename, sizeof(aFilename), "demos/%s.demo", pFilename); str_format(aFilename, sizeof(aFilename), "demos/%s.demo", pFilename);
}
m_aDemoRecorder[Recorder].Start(Storage(), m_pConsole, aFilename, GameClient()->NetVersion(), m_aCurrentMap, m_pMap->Sha256(), m_pMap->Crc(), "client", m_pMap->MapSize(), 0, m_pMap->File()); m_aDemoRecorder[Recorder].Start(Storage(), m_pConsole, aFilename, GameClient()->NetVersion(), m_aCurrentMap, m_pMap->Sha256(), m_pMap->Crc(), "client", m_pMap->MapSize(), 0, m_pMap->File());
} }
@ -3654,10 +3661,12 @@ void CClient::DemoRecorder_HandleAutoStart()
{ {
if(g_Config.m_ClAutoDemoRecord) if(g_Config.m_ClAutoDemoRecord)
{ {
DemoRecorder_Stop(RECORDER_AUTO); DemoRecorder(RECORDER_AUTO)->Stop(IDemoRecorder::EStopMode::KEEP_FILE);
char aBuf[512];
str_format(aBuf, sizeof(aBuf), "auto/%s", m_aCurrentMap); char aFilename[IO_MAX_PATH_LENGTH];
DemoRecorder_Start(aBuf, true, RECORDER_AUTO); str_format(aFilename, sizeof(aFilename), "auto/%s", m_aCurrentMap);
DemoRecorder_Start(aFilename, true, RECORDER_AUTO);
if(g_Config.m_ClAutoDemoMax) if(g_Config.m_ClAutoDemoMax)
{ {
// clean up auto recorded demos // clean up auto recorded demos
@ -3665,34 +3674,22 @@ void CClient::DemoRecorder_HandleAutoStart()
AutoDemos.Init(Storage(), "demos/auto", "" /* empty for wild card */, ".demo", g_Config.m_ClAutoDemoMax); AutoDemos.Init(Storage(), "demos/auto", "" /* empty for wild card */, ".demo", g_Config.m_ClAutoDemoMax);
} }
} }
if(!DemoRecorder(RECORDER_REPLAYS)->IsRecording())
{ DemoRecorder_UpdateReplayRecorder();
DemoRecorder_StartReplayRecorder();
}
} }
void CClient::DemoRecorder_StartReplayRecorder() void CClient::DemoRecorder_UpdateReplayRecorder()
{ {
if(g_Config.m_ClReplays) if(!g_Config.m_ClReplays && DemoRecorder(RECORDER_REPLAYS)->IsRecording())
{ {
DemoRecorder_Stop(RECORDER_REPLAYS); DemoRecorder(RECORDER_REPLAYS)->Stop(IDemoRecorder::EStopMode::REMOVE_FILE);
char aBuf[512];
str_format(aBuf, sizeof(aBuf), "replays/replay_tmp-%s", m_aCurrentMap);
DemoRecorder_Start(aBuf, true, RECORDER_REPLAYS);
}
} }
void CClient::DemoRecorder_Stop(int Recorder, bool RemoveFile) if(g_Config.m_ClReplays && !DemoRecorder(RECORDER_REPLAYS)->IsRecording())
{ {
m_aDemoRecorder[Recorder].Stop(); char aFilename[IO_MAX_PATH_LENGTH];
if(RemoveFile) str_format(aFilename, sizeof(aFilename), "replays/replay_tmp_%s", m_aCurrentMap);
{ DemoRecorder_Start(aFilename, true, RECORDER_REPLAYS);
const char *pFilename = m_aDemoRecorder[Recorder].GetCurrentFilename();
if(pFilename[0] != '\0')
{
Storage()->RemoveFile(pFilename, IStorage::TYPE_SAVE);
m_aDemoRecorder[Recorder].ClearCurrentFilename();
}
} }
} }
@ -3725,7 +3722,7 @@ void CClient::Con_Record(IConsole::IResult *pResult, void *pUserData)
void CClient::Con_StopRecord(IConsole::IResult *pResult, void *pUserData) void CClient::Con_StopRecord(IConsole::IResult *pResult, void *pUserData)
{ {
CClient *pSelf = (CClient *)pUserData; CClient *pSelf = (CClient *)pUserData;
pSelf->DemoRecorder_Stop(RECORDER_MANUAL); pSelf->DemoRecorder(RECORDER_MANUAL)->Stop(IDemoRecorder::EStopMode::KEEP_FILE);
} }
void CClient::Con_AddDemoMarker(IConsole::IResult *pResult, void *pUserData) void CClient::Con_AddDemoMarker(IConsole::IResult *pResult, void *pUserData)
@ -4019,17 +4016,7 @@ void CClient::ConchainReplays(IConsole::IResult *pResult, void *pUserData, ICons
pfnCallback(pResult, pCallbackUserData); pfnCallback(pResult, pCallbackUserData);
if(pResult->NumArguments()) if(pResult->NumArguments())
{ {
int Status = pResult->GetInteger(0); pSelf->DemoRecorder_UpdateReplayRecorder();
if(Status == 0)
{
// stop recording and remove the tmp demo file
pSelf->DemoRecorder_Stop(RECORDER_REPLAYS, true);
}
else
{
// start recording
pSelf->DemoRecorder_HandleAutoStart();
}
} }
} }
@ -4562,7 +4549,9 @@ void CClient::RaceRecord_Start(const char *pFilename)
void CClient::RaceRecord_Stop() void CClient::RaceRecord_Stop()
{ {
if(m_aDemoRecorder[RECORDER_RACE].IsRecording()) if(m_aDemoRecorder[RECORDER_RACE].IsRecording())
m_aDemoRecorder[RECORDER_RACE].Stop(); {
m_aDemoRecorder[RECORDER_RACE].Stop(IDemoRecorder::EStopMode::KEEP_FILE);
}
} }
bool CClient::RaceRecord_IsRecording() bool CClient::RaceRecord_IsRecording()

View file

@ -119,7 +119,6 @@ class CClient : public IClient, public CDemoPlayer::IListener
int m_UseTempRconCommands = 0; int m_UseTempRconCommands = 0;
char m_aPassword[sizeof(g_Config.m_Password)] = ""; char m_aPassword[sizeof(g_Config.m_Password)] = "";
bool m_SendPassword = false; bool m_SendPassword = false;
bool m_ButtonRender = false;
// version-checking // version-checking
char m_aVersionStr[10] = "0"; char m_aVersionStr[10] = "0";
@ -437,8 +436,7 @@ public:
const char *DemoPlayer_Play(const char *pFilename, int StorageType) override; const char *DemoPlayer_Play(const char *pFilename, int StorageType) override;
void DemoRecorder_Start(const char *pFilename, bool WithTimestamp, int Recorder, bool Verbose = false) override; void DemoRecorder_Start(const char *pFilename, bool WithTimestamp, int Recorder, bool Verbose = false) override;
void DemoRecorder_HandleAutoStart() override; void DemoRecorder_HandleAutoStart() override;
void DemoRecorder_StartReplayRecorder(); void DemoRecorder_UpdateReplayRecorder() override;
void DemoRecorder_Stop(int Recorder, bool RemoveFile = false) override;
void DemoRecorder_AddDemoMarker(int Recorder); void DemoRecorder_AddDemoMarker(int Recorder);
IDemoRecorder *DemoRecorder(int Recorder) override; IDemoRecorder *DemoRecorder(int Recorder) override;

View file

@ -101,11 +101,17 @@ class IDemoRecorder : public IInterface
{ {
MACRO_INTERFACE("demorecorder") MACRO_INTERFACE("demorecorder")
public: public:
enum class EStopMode
{
KEEP_FILE,
REMOVE_FILE,
};
virtual ~IDemoRecorder() {} virtual ~IDemoRecorder() {}
virtual bool IsRecording() const = 0; virtual bool IsRecording() const = 0;
virtual int Stop() = 0; virtual int Stop(IDemoRecorder::EStopMode Mode, const char *pTargetFilename = "") = 0;
virtual int Length() const = 0; virtual int Length() const = 0;
virtual char *GetCurrentFilename() = 0; virtual const char *CurrentFilename() const = 0;
}; };
class IDemoEditor : public IInterface class IDemoEditor : public IInterface

View file

@ -3412,12 +3412,14 @@ void CServer::DemoRecorder_HandleAutoStart()
{ {
if(Config()->m_SvAutoDemoRecord) if(Config()->m_SvAutoDemoRecord)
{ {
m_aDemoRecorder[RECORDER_AUTO].Stop(); m_aDemoRecorder[RECORDER_AUTO].Stop(IDemoRecorder::EStopMode::KEEP_FILE);
char aTimestamp[20];
str_timestamp(aTimestamp, sizeof(aTimestamp));
char aFilename[IO_MAX_PATH_LENGTH]; char aFilename[IO_MAX_PATH_LENGTH];
char aDate[20]; str_format(aFilename, sizeof(aFilename), "demos/auto/server/%s_%s.demo", m_aCurrentMap, aTimestamp);
str_timestamp(aDate, sizeof(aDate));
str_format(aFilename, sizeof(aFilename), "demos/auto/server/%s_%s.demo", m_aCurrentMap, aDate);
m_aDemoRecorder[RECORDER_AUTO].Start(Storage(), m_pConsole, aFilename, GameServer()->NetVersion(), m_aCurrentMap, m_aCurrentMapSha256[MAP_TYPE_SIX], m_aCurrentMapCrc[MAP_TYPE_SIX], "server", m_aCurrentMapSize[MAP_TYPE_SIX], m_apCurrentMapData[MAP_TYPE_SIX]); m_aDemoRecorder[RECORDER_AUTO].Start(Storage(), m_pConsole, aFilename, GameServer()->NetVersion(), m_aCurrentMap, m_aCurrentMapSha256[MAP_TYPE_SIX], m_aCurrentMapCrc[MAP_TYPE_SIX], "server", m_aCurrentMapSize[MAP_TYPE_SIX], m_apCurrentMapData[MAP_TYPE_SIX]);
if(Config()->m_SvAutoDemoMax) if(Config()->m_SvAutoDemoMax)
{ {
// clean up auto recorded demos // clean up auto recorded demos
@ -3431,14 +3433,9 @@ void CServer::SaveDemo(int ClientID, float Time)
{ {
if(IsRecording(ClientID)) if(IsRecording(ClientID))
{ {
m_aDemoRecorder[ClientID].Stop();
// rename the demo
char aOldFilename[IO_MAX_PATH_LENGTH];
char aNewFilename[IO_MAX_PATH_LENGTH]; char aNewFilename[IO_MAX_PATH_LENGTH];
str_format(aOldFilename, sizeof(aOldFilename), "demos/%s_%d_%d_tmp.demo", m_aCurrentMap, m_NetServer.Address().port, ClientID);
str_format(aNewFilename, sizeof(aNewFilename), "demos/%s_%s_%05.2f.demo", m_aCurrentMap, m_aClients[ClientID].m_aName, Time); str_format(aNewFilename, sizeof(aNewFilename), "demos/%s_%s_%05.2f.demo", m_aCurrentMap, m_aClients[ClientID].m_aName, Time);
Storage()->RenameFile(aOldFilename, aNewFilename, IStorage::TYPE_SAVE); m_aDemoRecorder[ClientID].Stop(IDemoRecorder::EStopMode::KEEP_FILE, aNewFilename);
} }
} }
@ -3456,11 +3453,7 @@ void CServer::StopRecord(int ClientID)
{ {
if(IsRecording(ClientID)) if(IsRecording(ClientID))
{ {
m_aDemoRecorder[ClientID].Stop(); m_aDemoRecorder[ClientID].Stop(IDemoRecorder::EStopMode::REMOVE_FILE);
char aFilename[IO_MAX_PATH_LENGTH];
str_format(aFilename, sizeof(aFilename), "demos/%s_%d_%d_tmp.demo", m_aCurrentMap, m_NetServer.Address().port, ClientID);
Storage()->RemoveFile(aFilename, IStorage::TYPE_SAVE);
} }
} }
@ -3476,22 +3469,13 @@ void CServer::StopDemos()
if(!m_aDemoRecorder[i].IsRecording()) if(!m_aDemoRecorder[i].IsRecording())
continue; continue;
m_aDemoRecorder[i].Stop(); m_aDemoRecorder[i].Stop(i < MAX_CLIENTS ? IDemoRecorder::EStopMode::REMOVE_FILE : IDemoRecorder::EStopMode::KEEP_FILE);
// remove tmp demos
if(i < MAX_CLIENTS)
{
char aPath[256];
str_format(aPath, sizeof(aPath), "demos/%s_%d_%d_tmp.demo", m_aCurrentMap, m_NetServer.Address().port, i);
Storage()->RemoveFile(aPath, IStorage::TYPE_SAVE);
}
} }
} }
void CServer::ConRecord(IConsole::IResult *pResult, void *pUser) void CServer::ConRecord(IConsole::IResult *pResult, void *pUser)
{ {
CServer *pServer = (CServer *)pUser; CServer *pServer = (CServer *)pUser;
char aFilename[IO_MAX_PATH_LENGTH];
if(pServer->IsRecording(RECORDER_MANUAL)) if(pServer->IsRecording(RECORDER_MANUAL))
{ {
@ -3499,20 +3483,23 @@ void CServer::ConRecord(IConsole::IResult *pResult, void *pUser)
return; return;
} }
char aFilename[IO_MAX_PATH_LENGTH];
if(pResult->NumArguments()) if(pResult->NumArguments())
{
str_format(aFilename, sizeof(aFilename), "demos/%s.demo", pResult->GetString(0)); str_format(aFilename, sizeof(aFilename), "demos/%s.demo", pResult->GetString(0));
}
else else
{ {
char aDate[20]; char aTimestamp[20];
str_timestamp(aDate, sizeof(aDate)); str_timestamp(aTimestamp, sizeof(aTimestamp));
str_format(aFilename, sizeof(aFilename), "demos/demo_%s.demo", aDate); str_format(aFilename, sizeof(aFilename), "demos/demo_%s.demo", aTimestamp);
} }
pServer->m_aDemoRecorder[RECORDER_MANUAL].Start(pServer->Storage(), pServer->Console(), aFilename, pServer->GameServer()->NetVersion(), pServer->m_aCurrentMap, pServer->m_aCurrentMapSha256[MAP_TYPE_SIX], pServer->m_aCurrentMapCrc[MAP_TYPE_SIX], "server", pServer->m_aCurrentMapSize[MAP_TYPE_SIX], pServer->m_apCurrentMapData[MAP_TYPE_SIX]); pServer->m_aDemoRecorder[RECORDER_MANUAL].Start(pServer->Storage(), pServer->Console(), aFilename, pServer->GameServer()->NetVersion(), pServer->m_aCurrentMap, pServer->m_aCurrentMapSha256[MAP_TYPE_SIX], pServer->m_aCurrentMapCrc[MAP_TYPE_SIX], "server", pServer->m_aCurrentMapSize[MAP_TYPE_SIX], pServer->m_apCurrentMapData[MAP_TYPE_SIX]);
} }
void CServer::ConStopRecord(IConsole::IResult *pResult, void *pUser) void CServer::ConStopRecord(IConsole::IResult *pResult, void *pUser)
{ {
((CServer *)pUser)->m_aDemoRecorder[RECORDER_MANUAL].Stop(); ((CServer *)pUser)->m_aDemoRecorder[RECORDER_MANUAL].Stop(IDemoRecorder::EStopMode::KEEP_FILE);
} }
void CServer::ConMapReload(IConsole::IResult *pResult, void *pUser) void CServer::ConMapReload(IConsole::IResult *pResult, void *pUser)

View file

@ -62,18 +62,15 @@ int CDemoRecorder::Start(class IStorage *pStorage, class IConsole *pConsole, con
{ {
dbg_assert(m_File == 0, "Demo recorder already recording"); dbg_assert(m_File == 0, "Demo recorder already recording");
m_pfnFilter = pfnFilter;
m_pUser = pUser;
m_pMapData = pMapData;
m_pConsole = pConsole; m_pConsole = pConsole;
m_pStorage = pStorage;
IOHANDLE DemoFile = pStorage->OpenFile(pFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE); IOHANDLE DemoFile = pStorage->OpenFile(pFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE);
if(!DemoFile) if(!DemoFile)
{ {
if(m_pConsole) if(m_pConsole)
{ {
char aBuf[256]; char aBuf[64 + IO_MAX_PATH_LENGTH];
str_format(aBuf, sizeof(aBuf), "Unable to open '%s' for recording", pFilename); str_format(aBuf, sizeof(aBuf), "Unable to open '%s' for recording", pFilename);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_recorder", aBuf, gs_DemoPrintColor); m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_recorder", aBuf, gs_DemoPrintColor);
} }
@ -186,6 +183,10 @@ int CDemoRecorder::Start(class IStorage *pStorage, class IConsole *pConsole, con
str_format(aBuf, sizeof(aBuf), "Recording to '%s'", pFilename); str_format(aBuf, sizeof(aBuf), "Recording to '%s'", pFilename);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_recorder", aBuf, gs_DemoPrintColor); m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_recorder", aBuf, gs_DemoPrintColor);
} }
m_pfnFilter = pfnFilter;
m_pUser = pUser;
m_File = DemoFile; m_File = DemoFile;
str_copy(m_aCurrentFilename, pFilename); str_copy(m_aCurrentFilename, pFilename);
@ -337,11 +338,13 @@ void CDemoRecorder::RecordMessage(const void *pData, int Size)
Write(CHUNKTYPE_MESSAGE, pData, Size); Write(CHUNKTYPE_MESSAGE, pData, Size);
} }
int CDemoRecorder::Stop() int CDemoRecorder::Stop(IDemoRecorder::EStopMode Mode, const char *pTargetFilename)
{ {
if(!m_File) if(!m_File)
return -1; return -1;
if(Mode == IDemoRecorder::EStopMode::KEEP_FILE)
{
// add the demo length to the header // add the demo length to the header
io_seek(m_File, gs_LengthOffset, IOSEEK_START); io_seek(m_File, gs_LengthOffset, IOSEEK_START);
unsigned char aLength[sizeof(int32_t)]; unsigned char aLength[sizeof(int32_t)];
@ -359,11 +362,44 @@ int CDemoRecorder::Stop()
uint_to_bytes_be(aMarker, m_aTimelineMarkers[i]); uint_to_bytes_be(aMarker, m_aTimelineMarkers[i]);
io_write(m_File, aMarker, sizeof(aMarker)); io_write(m_File, aMarker, sizeof(aMarker));
} }
}
io_close(m_File); io_close(m_File);
m_File = 0; m_File = 0;
if(Mode == IDemoRecorder::EStopMode::REMOVE_FILE)
{
if(!m_pStorage->RemoveFile(m_aCurrentFilename, IStorage::TYPE_SAVE))
{
if(m_pConsole) if(m_pConsole)
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_recorder", "Stopped recording", gs_DemoPrintColor); {
char aBuf[64 + IO_MAX_PATH_LENGTH];
str_format(aBuf, sizeof(aBuf), "Could not remove demo file '%s'.", m_aCurrentFilename);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_recorder", aBuf, gs_DemoPrintColor);
}
return -1;
}
}
else if(pTargetFilename[0] != '\0')
{
if(!m_pStorage->RenameFile(m_aCurrentFilename, pTargetFilename, IStorage::TYPE_SAVE))
{
if(m_pConsole)
{
char aBuf[64 + 2 * IO_MAX_PATH_LENGTH];
str_format(aBuf, sizeof(aBuf), "Could not move demo file '%s' to '%s'.", m_aCurrentFilename, pTargetFilename);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_recorder", aBuf, gs_DemoPrintColor);
}
return -1;
}
}
if(m_pConsole)
{
char aBuf[64 + IO_MAX_PATH_LENGTH];
str_format(aBuf, sizeof(aBuf), "Stopped recording to '%s'", m_aCurrentFilename);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_recorder", aBuf, gs_DemoPrintColor);
}
return 0; return 0;
} }
@ -1229,5 +1265,5 @@ void CDemoEditor::Slice(const char *pDemo, const char *pDst, int StartTick, int
} }
DemoPlayer.Stop(); DemoPlayer.Stop();
DemoRecorder.Stop(); DemoRecorder.Stop(IDemoRecorder::EStopMode::KEEP_FILE);
} }

View file

@ -18,17 +18,21 @@ typedef std::function<void()> TUpdateIntraTimesFunc;
class CDemoRecorder : public IDemoRecorder class CDemoRecorder : public IDemoRecorder
{ {
class IConsole *m_pConsole; class IConsole *m_pConsole;
class IStorage *m_pStorage;
IOHANDLE m_File; IOHANDLE m_File;
char m_aCurrentFilename[IO_MAX_PATH_LENGTH]; char m_aCurrentFilename[IO_MAX_PATH_LENGTH];
int m_LastTickMarker; int m_LastTickMarker;
int m_LastKeyFrame; int m_LastKeyFrame;
int m_FirstTick; int m_FirstTick;
unsigned char m_aLastSnapshotData[CSnapshot::MAX_SIZE]; unsigned char m_aLastSnapshotData[CSnapshot::MAX_SIZE];
class CSnapshotDelta *m_pSnapshotDelta; class CSnapshotDelta *m_pSnapshotDelta;
int m_NumTimelineMarkers; int m_NumTimelineMarkers;
int m_aTimelineMarkers[MAX_TIMELINE_MARKERS]; int m_aTimelineMarkers[MAX_TIMELINE_MARKERS];
bool m_NoMapData; bool m_NoMapData;
unsigned char *m_pMapData;
DEMOFUNC_FILTER m_pfnFilter; DEMOFUNC_FILTER m_pfnFilter;
void *m_pUser; void *m_pUser;
@ -42,7 +46,7 @@ public:
~CDemoRecorder() override; ~CDemoRecorder() override;
int Start(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, const char *pNetversion, const char *pMap, const SHA256_DIGEST &Sha256, unsigned MapCrc, const char *pType, unsigned MapSize, unsigned char *pMapData, IOHANDLE MapFile = nullptr, DEMOFUNC_FILTER pfnFilter = nullptr, void *pUser = nullptr); int Start(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, const char *pNetversion, const char *pMap, const SHA256_DIGEST &Sha256, unsigned MapCrc, const char *pType, unsigned MapSize, unsigned char *pMapData, IOHANDLE MapFile = nullptr, DEMOFUNC_FILTER pfnFilter = nullptr, void *pUser = nullptr);
int Stop() override; int Stop(IDemoRecorder::EStopMode Mode, const char *pTargetFilename = "") override;
void AddDemoMarker(); void AddDemoMarker();
void AddDemoMarker(int Tick); void AddDemoMarker(int Tick);
@ -51,8 +55,7 @@ public:
void RecordMessage(const void *pData, int Size); void RecordMessage(const void *pData, int Size);
bool IsRecording() const override { return m_File != nullptr; } bool IsRecording() const override { return m_File != nullptr; }
char *GetCurrentFilename() override { return m_aCurrentFilename; } const char *CurrentFilename() const override { return m_aCurrentFilename; }
void ClearCurrentFilename() { m_aCurrentFilename[0] = '\0'; }
int Length() const override { return (m_LastTickMarker - m_FirstTick) / SERVER_TICK_SPEED; } int Length() const override { return (m_LastTickMarker - m_FirstTick) / SERVER_TICK_SPEED; }
}; };

View file

@ -105,13 +105,13 @@ void CMenus::RenderGame(CUIRect MainView)
ButtonBar.VSplitRight(140.0f, &ButtonBar, &Button); ButtonBar.VSplitRight(140.0f, &ButtonBar, &Button);
static CButtonContainer s_DemoButton; static CButtonContainer s_DemoButton;
bool Recording = DemoRecorder(RECORDER_MANUAL)->IsRecording(); const bool Recording = DemoRecorder(RECORDER_MANUAL)->IsRecording();
if(DoButton_Menu(&s_DemoButton, Recording ? Localize("Stop record") : Localize("Record demo"), 0, &Button)) if(DoButton_Menu(&s_DemoButton, Recording ? Localize("Stop record") : Localize("Record demo"), 0, &Button))
{ {
if(!Recording) if(!Recording)
Client()->DemoRecorder_Start(Client()->GetCurrentMap(), true, RECORDER_MANUAL); Client()->DemoRecorder_Start(Client()->GetCurrentMap(), true, RECORDER_MANUAL);
else else
Client()->DemoRecorder_Stop(RECORDER_MANUAL); Client()->DemoRecorder(RECORDER_MANUAL)->Stop(IDemoRecorder::EStopMode::KEEP_FILE);
} }
static CButtonContainer s_SpectateButton; static CButtonContainer s_SpectateButton;

View file

@ -3194,16 +3194,7 @@ void CMenus::RenderSettingsDDNet(CUIRect MainView)
if(DoButton_CheckBox(&g_Config.m_ClReplays, Localize("Enable replays"), g_Config.m_ClReplays, &Button)) if(DoButton_CheckBox(&g_Config.m_ClReplays, Localize("Enable replays"), g_Config.m_ClReplays, &Button))
{ {
g_Config.m_ClReplays ^= 1; g_Config.m_ClReplays ^= 1;
if(!g_Config.m_ClReplays) Client()->DemoRecorder_UpdateReplayRecorder();
{
// stop recording and remove the tmp demo file
Client()->DemoRecorder_Stop(RECORDER_REPLAYS, true);
}
else
{
// start recording
Client()->DemoRecorder_HandleAutoStart();
}
} }
Left.HSplitTop(20.0f, &Button, &Left); Left.HSplitTop(20.0f, &Button, &Left);