diff --git a/src/openrct2/ReplayManager.cpp b/src/openrct2/ReplayManager.cpp index af2f7a8d39..a3179af263 100644 --- a/src/openrct2/ReplayManager.cpp +++ b/src/openrct2/ReplayManager.cpp @@ -20,6 +20,7 @@ #include "core/Path.hpp" #include "object/ObjectManager.h" #include "object/ObjectRepository.h" +#include "openrct2/management/NewsItem.h" #include "rct2/S6Exporter.h" #include "world/Park.h" @@ -85,6 +86,7 @@ namespace OpenRCT2 MemoryStream spriteSpatialData; MemoryStream parkParams; std::string name; // Name of play + std::string filePath; // File path of replay. uint64_t timeRecorded; // Posix Time. uint32_t tickStart; // First tick of replay. uint32_t tickEnd; // Last tick of replay. @@ -246,6 +248,10 @@ namespace OpenRCT2 else replayData->tickEnd = k_MaxReplayTicks; + std::string replayName = String::StdFormat("%s.sv6r", name.c_str()); + std::string outPath = GetContext()->GetPlatformEnvironment()->GetDirectoryPath(DIRBASE::USER, DIRID::REPLAY); + replayData->filePath = Path::Combine(outPath, replayName); + auto context = GetContext(); auto& objManager = context->GetObjectManager(); auto objects = objManager.GetPackableObjects(); @@ -281,14 +287,10 @@ namespace OpenRCT2 DataSerialiser serialiser(true); Serialise(serialiser, *_currentRecording); - char replayName[512] = {}; - snprintf(replayName, sizeof(replayName), "%s.sv6r", _currentRecording->name.c_str()); - - std::string outPath = GetContext()->GetPlatformEnvironment()->GetDirectoryPath(DIRBASE::USER, DIRID::REPLAY); - std::string outFile = Path::Combine(outPath, replayName); - bool result = true; + const std::string& outFile = _currentRecording->filePath; + FILE* fp = fopen(outFile.c_str(), "wb"); if (fp) { @@ -311,9 +313,37 @@ namespace OpenRCT2 _currentRecording.reset(); + NewsItem* news = news_item_add_to_queue_raw(NEWS_ITEM_BLANK, "Replay recording stopped", 0); + news->Flags |= NEWS_FLAG_HAS_BUTTON; // Has no subject. + return result; } + virtual bool GetCurrentReplayInfo(ReplayRecordInfo& info) const override + { + ReplayRecordData* data = nullptr; + + if (_mode == ReplayMode::PLAYING) + data = _currentReplay.get(); + else if (_mode == ReplayMode::RECORDING) + data = _currentRecording.get(); + else if (_mode == ReplayMode::NORMALISATION) + data = _currentRecording.get(); + + if (data == nullptr) + return false; + + info.FilePath = data->filePath; + info.Name = data->name; + info.Version = data->version; + info.TimeRecorded = data->timeRecorded; + info.Ticks = gCurrentTicks - data->tickStart; + info.NumCommands = (uint32_t)data->commands.size(); + info.NumChecksums = (uint32_t)data->checksums.size(); + + return true; + } + virtual bool StartPlayback(const std::string& file) override { if (_mode != ReplayMode::NONE && _mode != ReplayMode::NORMALISATION) @@ -345,6 +375,9 @@ namespace OpenRCT2 _currentReplay->checksumIndex = 0; _faultyChecksumIndex = -1; + // Make sure game is not paused. + gGamePaused = 0; + if (_mode != ReplayMode::NORMALISATION) _mode = ReplayMode::PLAYING; @@ -365,6 +398,13 @@ namespace OpenRCT2 if (_mode != ReplayMode::PLAYING && _mode != ReplayMode::NORMALISATION) return false; + // During normal playback we pause the game if stopped. + if (_mode == ReplayMode::PLAYING) + { + NewsItem* news = news_item_add_to_queue_raw(NEWS_ITEM_BLANK, "Replay playback complete", 0); + news->Flags |= NEWS_FLAG_HAS_BUTTON; // Has no subject. + } + // When normalizing the output we don't touch the mode. if (_mode != ReplayMode::NORMALISATION) { @@ -631,15 +671,16 @@ namespace OpenRCT2 serialiser << data.magic; if (data.magic != ReplayMagic) { - log_error("Magic does not match %08X", data.magic); + log_error("Magic does not match %08X, expected: %08X", data.magic, ReplayMagic); return false; } serialiser << data.version; if (data.version != ReplayVersion) { - log_error("Invalid version detected %04X", data.version); + log_error("Invalid version detected %04X, expected: %04X", data.version, ReplayVersion); return false; } + serialiser << data.networkId; serialiser << data.name; serialiser << data.parkData; diff --git a/src/openrct2/ReplayManager.h b/src/openrct2/ReplayManager.h index 760ae6fa91..976ae4dcb9 100644 --- a/src/openrct2/ReplayManager.h +++ b/src/openrct2/ReplayManager.h @@ -21,6 +21,17 @@ namespace OpenRCT2 { static constexpr uint32_t k_MaxReplayTicks = 0xFFFFFFFF; + struct ReplayRecordInfo + { + uint16_t Version; + uint32_t Ticks; + uint64_t TimeRecorded; + uint32_t NumCommands; + uint32_t NumChecksums; + std::string Name; + std::string FilePath; + }; + interface IReplayManager { public: @@ -41,6 +52,7 @@ namespace OpenRCT2 virtual bool StartRecording(const std::string& name, uint32_t maxTicks = k_MaxReplayTicks) = 0; virtual bool StopRecording() = 0; + virtual bool GetCurrentReplayInfo(ReplayRecordInfo & info) const = 0; virtual bool StartPlayback(const std::string& file) = 0; virtual bool IsPlaybackStateMismatching() const = 0; diff --git a/src/openrct2/interface/InteractiveConsole.cpp b/src/openrct2/interface/InteractiveConsole.cpp index e5e0ef73ad..204386bd4c 100644 --- a/src/openrct2/interface/InteractiveConsole.cpp +++ b/src/openrct2/interface/InteractiveConsole.cpp @@ -1359,7 +1359,13 @@ static int32_t cc_replay_startrecord(InteractiveConsole& console, const utf8** a auto* replayManager = OpenRCT2::GetContext()->GetReplayManager(); if (replayManager->StartRecording(name, maxTicks)) { - console.WriteFormatLine("Replay recording start"); + OpenRCT2::ReplayRecordInfo info; + replayManager->GetCurrentReplayInfo(info); + + const char* logFmt = "Replay recording started: (%s) %s"; + console.WriteFormatLine(logFmt, info.Name.c_str(), info.FilePath.c_str()); + log_info(logFmt, info.Name.c_str(), info.FilePath.c_str()); + return 1; } @@ -1375,9 +1381,26 @@ static int32_t cc_replay_stoprecord(InteractiveConsole& console, const utf8** ar } auto* replayManager = OpenRCT2::GetContext()->GetReplayManager(); + if (replayManager->IsRecording() == false && replayManager->IsNormalising() == false) + { + console.WriteFormatLine("Replay currently not recording"); + return 0; + } + + OpenRCT2::ReplayRecordInfo info; + replayManager->GetCurrentReplayInfo(info); + if (replayManager->StopRecording()) { - console.WriteFormatLine("Replay recording stopped"); + const char* logFmt = "Replay recording stopped: (%s) %s\n" + " Ticks: %u\n" + " Commands: %u\n" + " Checksums: %u"; + + console.WriteFormatLine( + logFmt, info.Name.c_str(), info.FilePath.c_str(), info.Ticks, info.NumCommands, info.NumChecksums); + log_info(logFmt, info.Name.c_str(), info.FilePath.c_str(), info.Ticks, info.NumCommands, info.NumChecksums); + return 1; } @@ -1403,7 +1426,21 @@ static int32_t cc_replay_start(InteractiveConsole& console, const utf8** argv, i auto* replayManager = OpenRCT2::GetContext()->GetReplayManager(); if (replayManager->StartPlayback(name)) { - console.WriteFormatLine("Started replay"); + OpenRCT2::ReplayRecordInfo info; + replayManager->GetCurrentReplayInfo(info); + + std::time_t ts = info.TimeRecorded; + + const char* recordingDate = std::asctime(std::localtime(&ts)); + const char* logFmt = "Replay playback started: %s\n" + " Date: %s\n" + " Ticks: %u\n" + " Commands: %u\n" + " Checksums: %u"; + + console.WriteFormatLine(logFmt, recordingDate, info.FilePath.c_str(), info.Ticks, info.NumCommands, info.NumChecksums); + log_info(logFmt, recordingDate, info.FilePath.c_str(), info.Ticks, info.NumCommands, info.NumChecksums); + return 1; }