Refactor demo recorder usage

To simplify the usage of the demo recorder, parameters are added to the `IDemoRecorder::Stop` function to control whether the demo file is removed or renamed when stopping the recording.

Ensure demo files of the replay recorder are always removed (except on crashes). The temporary replay demos were previously not removed when being disconnected from a server.

Fix auto demos being stopped and restarted when enabling replays in the settings menu. Now only the replay recorder is stopped/started when necessary.

Fix replay recorder being restarted when setting `cl_replay` variable with console without changing the value.

Remove unnecessary `CClient::m_ButtonRender` variable. Demo rendering is already stopped correctly when reaching the end of the demo without this additional variable.

Remove unused `CDemoRecorder::m_pMapData` variable.
This commit is contained in:
Robert Müller 2024-01-01 18:17:05 +01:00
parent 94931fadc0
commit 9150f48562
9 changed files with 161 additions and 151 deletions

View file

@ -165,7 +165,7 @@ public:
#endif
virtual void DemoRecorder_Start(const char *pFilename, bool WithTimestamp, int Recorder, bool Verbose = false) = 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 void AutoScreenshot_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);
// stop demo playback and recorder
// make sure to remove replay tmp demo
m_DemoPlayer.Stop();
for(int i = 0; i < RECORDER_MAX; i++)
DemoRecorder_Stop(i);
for(int Recorder = 0; Recorder < RECORDER_MAX; Recorder++)
{
DemoRecorder(Recorder)->Stop(Recorder == RECORDER_REPLAYS ? IDemoRecorder::EStopMode::REMOVE_FILE : IDemoRecorder::EStopMode::KEEP_FILE);
}
//
m_aRconAuthed[0] = 0;
mem_zero(m_aRconUsername, sizeof(m_aRconUsername));
mem_zero(m_aRconPassword, sizeof(m_aRconPassword));
@ -581,14 +583,9 @@ void CClient::DisconnectWithReason(const char *pReason)
void CClient::Disconnect()
{
m_ButtonRender = false;
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
for(int i = 0; i < RECORDER_MAX; i++)
DemoRecorder_Stop(i, i == RECORDER_REPLAYS);
for(int Recorder = 0; Recorder < RECORDER_MAX; Recorder++)
{
DemoRecorder(Recorder)->Stop(Recorder == RECORDER_REPLAYS ? IDemoRecorder::EStopMode::REMOVE_FILE : IDemoRecorder::EStopMode::KEEP_FILE);
}
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "loaded map '%s'", pFilename);
@ -2352,22 +2351,20 @@ void CClient::Update()
{
if(State() == IClient::STATE_DEMOPLAYBACK)
{
#if defined(CONF_VIDEORECORDER)
if(m_DemoPlayer.IsPlaying() && IVideo::Current())
{
IVideo::Current()->NextVideoFrame();
IVideo::Current()->NextAudioFrameTimeline([this](short *pFinalOut, unsigned Frames) {
Sound()->Mix(pFinalOut, Frames);
});
}
else if(m_ButtonRender)
Disconnect();
#endif
m_DemoPlayer.Update();
if(m_DemoPlayer.IsPlaying())
{
#if defined(CONF_VIDEORECORDER)
if(IVideo::Current())
{
IVideo::Current()->NextVideoFrame();
IVideo::Current()->NextAudioFrameTimeline([this](short *pFinalOut, unsigned Frames) {
Sound()->Mix(pFinalOut, Frames);
});
}
#endif
m_DemoPlayer.Update();
// update timers
const CDemoPlayer::CPlaybackInfo *pInfo = m_DemoPlayer.Info();
m_aCurGameTick[g_Config.m_ClDummy] = pInfo->m_Info.m_CurrentTick;
@ -2377,7 +2374,8 @@ void CClient::Update()
}
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());
if(m_DemoPlayer.ErrorMessage()[0] != '\0')
{
@ -3472,26 +3470,32 @@ void CClient::SaveReplay(const int Length, const char *pFilename)
}
if(!DemoRecorder(RECORDER_REPLAYS)->IsRecording())
{
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)
{
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "replay", "ERROR: demorecorder isn't recording for at least 1 second.");
}
else
{
// First we stop the recorder to slice correctly the demo after
DemoRecorder_Stop(RECORDER_REPLAYS);
char aDate[64];
str_timestamp(aDate, sizeof(aDate));
DemoRecorder(RECORDER_REPLAYS)->Stop(IDemoRecorder::EStopMode::KEEP_FILE);
char aFilename[IO_MAX_PATH_LENGTH];
if(str_comp(pFilename, "") == 0)
str_format(aFilename, sizeof(aFilename), "demos/replays/%s_%s (replay).demo", m_aCurrentMap, aDate);
if(pFilename[0] == '\0')
{
char aTimestamp[20];
str_timestamp(aTimestamp, sizeof(aTimestamp));
str_format(aFilename, sizeof(aFilename), "demos/replays/%s_%s_(replay).demo", m_aCurrentMap, aTimestamp);
}
else
{
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
const char *pSrc = m_aDemoRecorder[RECORDER_REPLAYS].CurrentFilename();
const int EndTick = GameTick(g_Config.m_ClDummy);
const int StartTick = EndTick - Length * GameTickSpeed();
@ -3503,7 +3507,7 @@ void CClient::SaveReplay(const int Length, const char *pFilename)
m_EditJobs.push_back(pDemoEditTask);
// 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);
if(pError)
return pError;
m_ButtonRender = true;
this->CClient::StartVideo(NULL, this, pVideoName);
m_DemoPlayer.Play();
@ -3632,19 +3635,23 @@ void CClient::DemoRecorder_Start(const char *pFilename, bool WithTimestamp, int
if(State() != IClient::STATE_ONLINE)
{
if(Verbose)
{
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demorec/record", "client is not online");
}
}
else
{
char aFilename[IO_MAX_PATH_LENGTH];
if(WithTimestamp)
{
char aDate[20];
str_timestamp(aDate, sizeof(aDate));
str_format(aFilename, sizeof(aFilename), "demos/%s_%s.demo", pFilename, aDate);
char aTimestamp[20];
str_timestamp(aTimestamp, sizeof(aTimestamp));
str_format(aFilename, sizeof(aFilename), "demos/%s_%s.demo", pFilename, aTimestamp);
}
else
{
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());
}
@ -3654,10 +3661,12 @@ void CClient::DemoRecorder_HandleAutoStart()
{
if(g_Config.m_ClAutoDemoRecord)
{
DemoRecorder_Stop(RECORDER_AUTO);
char aBuf[512];
str_format(aBuf, sizeof(aBuf), "auto/%s", m_aCurrentMap);
DemoRecorder_Start(aBuf, true, RECORDER_AUTO);
DemoRecorder(RECORDER_AUTO)->Stop(IDemoRecorder::EStopMode::KEEP_FILE);
char aFilename[IO_MAX_PATH_LENGTH];
str_format(aFilename, sizeof(aFilename), "auto/%s", m_aCurrentMap);
DemoRecorder_Start(aFilename, true, RECORDER_AUTO);
if(g_Config.m_ClAutoDemoMax)
{
// 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);
}
}
if(!DemoRecorder(RECORDER_REPLAYS)->IsRecording())
{
DemoRecorder_StartReplayRecorder();
}
DemoRecorder_UpdateReplayRecorder();
}
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);
char aBuf[512];
str_format(aBuf, sizeof(aBuf), "replays/replay_tmp-%s", m_aCurrentMap);
DemoRecorder_Start(aBuf, true, RECORDER_REPLAYS);
DemoRecorder(RECORDER_REPLAYS)->Stop(IDemoRecorder::EStopMode::REMOVE_FILE);
}
}
void CClient::DemoRecorder_Stop(int Recorder, bool RemoveFile)
{
m_aDemoRecorder[Recorder].Stop();
if(RemoveFile)
if(g_Config.m_ClReplays && !DemoRecorder(RECORDER_REPLAYS)->IsRecording())
{
const char *pFilename = m_aDemoRecorder[Recorder].GetCurrentFilename();
if(pFilename[0] != '\0')
{
Storage()->RemoveFile(pFilename, IStorage::TYPE_SAVE);
m_aDemoRecorder[Recorder].ClearCurrentFilename();
}
char aFilename[IO_MAX_PATH_LENGTH];
str_format(aFilename, sizeof(aFilename), "replays/replay_tmp_%s", m_aCurrentMap);
DemoRecorder_Start(aFilename, true, RECORDER_REPLAYS);
}
}
@ -3725,7 +3722,7 @@ void CClient::Con_Record(IConsole::IResult *pResult, void *pUserData)
void CClient::Con_StopRecord(IConsole::IResult *pResult, void *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)
@ -4019,17 +4016,7 @@ void CClient::ConchainReplays(IConsole::IResult *pResult, void *pUserData, ICons
pfnCallback(pResult, pCallbackUserData);
if(pResult->NumArguments())
{
int Status = pResult->GetInteger(0);
if(Status == 0)
{
// stop recording and remove the tmp demo file
pSelf->DemoRecorder_Stop(RECORDER_REPLAYS, true);
}
else
{
// start recording
pSelf->DemoRecorder_HandleAutoStart();
}
pSelf->DemoRecorder_UpdateReplayRecorder();
}
}
@ -4562,7 +4549,9 @@ void CClient::RaceRecord_Start(const char *pFilename)
void CClient::RaceRecord_Stop()
{
if(m_aDemoRecorder[RECORDER_RACE].IsRecording())
m_aDemoRecorder[RECORDER_RACE].Stop();
{
m_aDemoRecorder[RECORDER_RACE].Stop(IDemoRecorder::EStopMode::KEEP_FILE);
}
}
bool CClient::RaceRecord_IsRecording()

View file

@ -119,7 +119,6 @@ class CClient : public IClient, public CDemoPlayer::IListener
int m_UseTempRconCommands = 0;
char m_aPassword[sizeof(g_Config.m_Password)] = "";
bool m_SendPassword = false;
bool m_ButtonRender = false;
// version-checking
char m_aVersionStr[10] = "0";
@ -437,8 +436,7 @@ public:
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_HandleAutoStart() override;
void DemoRecorder_StartReplayRecorder();
void DemoRecorder_Stop(int Recorder, bool RemoveFile = false) override;
void DemoRecorder_UpdateReplayRecorder() override;
void DemoRecorder_AddDemoMarker(int Recorder);
IDemoRecorder *DemoRecorder(int Recorder) override;

View file

@ -101,11 +101,17 @@ class IDemoRecorder : public IInterface
{
MACRO_INTERFACE("demorecorder")
public:
enum class EStopMode
{
KEEP_FILE,
REMOVE_FILE,
};
virtual ~IDemoRecorder() {}
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 char *GetCurrentFilename() = 0;
virtual const char *CurrentFilename() const = 0;
};
class IDemoEditor : public IInterface

View file

@ -3412,12 +3412,14 @@ void CServer::DemoRecorder_HandleAutoStart()
{
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 aDate[20];
str_timestamp(aDate, sizeof(aDate));
str_format(aFilename, sizeof(aFilename), "demos/auto/server/%s_%s.demo", m_aCurrentMap, aDate);
str_format(aFilename, sizeof(aFilename), "demos/auto/server/%s_%s.demo", m_aCurrentMap, aTimestamp);
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)
{
// clean up auto recorded demos
@ -3431,14 +3433,9 @@ void CServer::SaveDemo(int ClientID, float Time)
{
if(IsRecording(ClientID))
{
m_aDemoRecorder[ClientID].Stop();
// rename the demo
char aOldFilename[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);
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))
{
m_aDemoRecorder[ClientID].Stop();
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);
m_aDemoRecorder[ClientID].Stop(IDemoRecorder::EStopMode::REMOVE_FILE);
}
}
@ -3476,22 +3469,13 @@ void CServer::StopDemos()
if(!m_aDemoRecorder[i].IsRecording())
continue;
m_aDemoRecorder[i].Stop();
// 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);
}
m_aDemoRecorder[i].Stop(i < MAX_CLIENTS ? IDemoRecorder::EStopMode::REMOVE_FILE : IDemoRecorder::EStopMode::KEEP_FILE);
}
}
void CServer::ConRecord(IConsole::IResult *pResult, void *pUser)
{
CServer *pServer = (CServer *)pUser;
char aFilename[IO_MAX_PATH_LENGTH];
if(pServer->IsRecording(RECORDER_MANUAL))
{
@ -3499,20 +3483,23 @@ void CServer::ConRecord(IConsole::IResult *pResult, void *pUser)
return;
}
char aFilename[IO_MAX_PATH_LENGTH];
if(pResult->NumArguments())
{
str_format(aFilename, sizeof(aFilename), "demos/%s.demo", pResult->GetString(0));
}
else
{
char aDate[20];
str_timestamp(aDate, sizeof(aDate));
str_format(aFilename, sizeof(aFilename), "demos/demo_%s.demo", aDate);
char aTimestamp[20];
str_timestamp(aTimestamp, sizeof(aTimestamp));
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]);
}
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)

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");
m_pfnFilter = pfnFilter;
m_pUser = pUser;
m_pMapData = pMapData;
m_pConsole = pConsole;
m_pStorage = pStorage;
IOHANDLE DemoFile = pStorage->OpenFile(pFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE);
if(!DemoFile)
{
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);
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);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_recorder", aBuf, gs_DemoPrintColor);
}
m_pfnFilter = pfnFilter;
m_pUser = pUser;
m_File = DemoFile;
str_copy(m_aCurrentFilename, pFilename);
@ -337,33 +338,68 @@ void CDemoRecorder::RecordMessage(const void *pData, int Size)
Write(CHUNKTYPE_MESSAGE, pData, Size);
}
int CDemoRecorder::Stop()
int CDemoRecorder::Stop(IDemoRecorder::EStopMode Mode, const char *pTargetFilename)
{
if(!m_File)
return -1;
// add the demo length to the header
io_seek(m_File, gs_LengthOffset, IOSEEK_START);
unsigned char aLength[sizeof(int32_t)];
uint_to_bytes_be(aLength, Length());
io_write(m_File, aLength, sizeof(aLength));
// add the timeline markers to the header
io_seek(m_File, gs_NumMarkersOffset, IOSEEK_START);
unsigned char aNumMarkers[sizeof(int32_t)];
uint_to_bytes_be(aNumMarkers, m_NumTimelineMarkers);
io_write(m_File, aNumMarkers, sizeof(aNumMarkers));
for(int i = 0; i < m_NumTimelineMarkers; i++)
if(Mode == IDemoRecorder::EStopMode::KEEP_FILE)
{
unsigned char aMarker[sizeof(int32_t)];
uint_to_bytes_be(aMarker, m_aTimelineMarkers[i]);
io_write(m_File, aMarker, sizeof(aMarker));
// add the demo length to the header
io_seek(m_File, gs_LengthOffset, IOSEEK_START);
unsigned char aLength[sizeof(int32_t)];
uint_to_bytes_be(aLength, Length());
io_write(m_File, aLength, sizeof(aLength));
// add the timeline markers to the header
io_seek(m_File, gs_NumMarkersOffset, IOSEEK_START);
unsigned char aNumMarkers[sizeof(int32_t)];
uint_to_bytes_be(aNumMarkers, m_NumTimelineMarkers);
io_write(m_File, aNumMarkers, sizeof(aNumMarkers));
for(int i = 0; i < m_NumTimelineMarkers; i++)
{
unsigned char aMarker[sizeof(int32_t)];
uint_to_bytes_be(aMarker, m_aTimelineMarkers[i]);
io_write(m_File, aMarker, sizeof(aMarker));
}
}
io_close(m_File);
m_File = 0;
if(Mode == IDemoRecorder::EStopMode::REMOVE_FILE)
{
if(!m_pStorage->RemoveFile(m_aCurrentFilename, IStorage::TYPE_SAVE))
{
if(m_pConsole)
{
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)
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), "Stopped recording to '%s'", m_aCurrentFilename);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_recorder", aBuf, gs_DemoPrintColor);
}
return 0;
}
@ -1229,5 +1265,5 @@ void CDemoEditor::Slice(const char *pDemo, const char *pDst, int StartTick, int
}
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 IConsole *m_pConsole;
class IStorage *m_pStorage;
IOHANDLE m_File;
char m_aCurrentFilename[IO_MAX_PATH_LENGTH];
int m_LastTickMarker;
int m_LastKeyFrame;
int m_FirstTick;
unsigned char m_aLastSnapshotData[CSnapshot::MAX_SIZE];
class CSnapshotDelta *m_pSnapshotDelta;
int m_NumTimelineMarkers;
int m_aTimelineMarkers[MAX_TIMELINE_MARKERS];
bool m_NoMapData;
unsigned char *m_pMapData;
DEMOFUNC_FILTER m_pfnFilter;
void *m_pUser;
@ -42,7 +46,7 @@ public:
~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 Stop() override;
int Stop(IDemoRecorder::EStopMode Mode, const char *pTargetFilename = "") override;
void AddDemoMarker();
void AddDemoMarker(int Tick);
@ -51,8 +55,7 @@ public:
void RecordMessage(const void *pData, int Size);
bool IsRecording() const override { return m_File != nullptr; }
char *GetCurrentFilename() override { return m_aCurrentFilename; }
void ClearCurrentFilename() { m_aCurrentFilename[0] = '\0'; }
const char *CurrentFilename() const override { return m_aCurrentFilename; }
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);
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(!Recording)
Client()->DemoRecorder_Start(Client()->GetCurrentMap(), true, RECORDER_MANUAL);
else
Client()->DemoRecorder_Stop(RECORDER_MANUAL);
Client()->DemoRecorder(RECORDER_MANUAL)->Stop(IDemoRecorder::EStopMode::KEEP_FILE);
}
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))
{
g_Config.m_ClReplays ^= 1;
if(!g_Config.m_ClReplays)
{
// stop recording and remove the tmp demo file
Client()->DemoRecorder_Stop(RECORDER_REPLAYS, true);
}
else
{
// start recording
Client()->DemoRecorder_HandleAutoStart();
}
Client()->DemoRecorder_UpdateReplayRecorder();
}
Left.HSplitTop(20.0f, &Button, &Left);