From 69b6b388d684b047ac3e79f5c3ccce2f8921e73a Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Thu, 9 Dec 2021 17:40:15 +0000 Subject: [PATCH] On dedicated servers, save copy of last autosave on crash This is to avoid all autosaves being overwritten when the server is auto-restarted with a new map --- src/crashlog.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++++---- src/fios.cpp | 7 ++++++- src/fios.h | 2 ++ src/openttd.cpp | 9 +++++++-- 4 files changed, 62 insertions(+), 7 deletions(-) diff --git a/src/crashlog.cpp b/src/crashlog.cpp index 547ecd7523..44f42d4bd0 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -690,6 +690,32 @@ bool CrashLog::WriteScreenshot(char *filename, const char *filename_last, const return res; } +#ifdef DEDICATED +static bool CopyAutosave(const std::string &old_name, const std::string &new_name) +{ + FILE *old_fh = FioFOpenFile(old_name, "rb", AUTOSAVE_DIR); + if (old_fh == nullptr) return false; + auto guard1 = scope_guard([=]() { + FioFCloseFile(old_fh); + }); + FILE *new_fh = FioFOpenFile(new_name, "wb", AUTOSAVE_DIR); + if (new_fh == nullptr) return false; + auto guard2 = scope_guard([=]() { + FioFCloseFile(new_fh); + }); + + char buffer[4096 * 4]; + size_t length; + do { + length = fread(buffer, 1, lengthof(buffer), old_fh); + if (fwrite(buffer, 1, length, new_fh) != length) { + return false; + } + } while (length == lengthof(buffer)); + return true; +} +#endif + /** * Makes the crash log, writes it to a file and then subsequently tries * to make a crash dump and crash savegame. It uses DEBUG to write @@ -703,6 +729,27 @@ bool CrashLog::MakeCrashLog(char *buffer, const char *last) if (crashlogged) return false; crashlogged = true; + char *name_buffer_date = this->name_buffer + seprintf(this->name_buffer, lastof(this->name_buffer), "crash-"); + time_t cur_time = time(nullptr); + strftime(name_buffer_date, lastof(this->name_buffer) - name_buffer_date, "%Y%m%dT%H%M%SZ", gmtime(&cur_time)); + +#ifdef DEDICATED + if (!_settings_client.gui.keep_all_autosave) { + extern FiosNumberedSaveName &GetAutoSaveFiosNumberedSaveName(); + FiosNumberedSaveName &autosave = GetAutoSaveFiosNumberedSaveName(); + int num = autosave.GetLastNumber(); + if (num >= 0) { + std::string old_file = autosave.FilenameUsingNumber(num, ""); + char save_suffix[MAX_PATH]; + seprintf(save_suffix, lastof(save_suffix), "-(%s)", this->name_buffer); + std::string new_file = autosave.FilenameUsingNumber(num, save_suffix); + if (CopyAutosave(old_file, new_file)) { + printf("Saving copy of last autosave: %s -> %s\n\n", old_file.c_str(), new_file.c_str()); + } + } + } +#endif + if (!VideoDriver::EmergencyAcquireGameLock(20, 2)) { printf("Failed to acquire gamelock before filling crash log\n\n"); } @@ -710,10 +757,6 @@ bool CrashLog::MakeCrashLog(char *buffer, const char *last) char filename[MAX_PATH]; bool ret = true; - char *name_buffer_date = this->name_buffer + seprintf(this->name_buffer, lastof(this->name_buffer), "crash-"); - time_t cur_time = time(nullptr); - strftime(name_buffer_date, lastof(this->name_buffer) - name_buffer_date, "%Y%m%dT%H%M%SZ", gmtime(&cur_time)); - printf("Crash encountered, generating crash log...\n"); this->FillCrashLog(buffer, last); printf("%s\n", buffer); diff --git a/src/fios.cpp b/src/fios.cpp index e111546a0a..0516a4749a 100644 --- a/src/fios.cpp +++ b/src/fios.cpp @@ -791,7 +791,12 @@ FiosNumberedSaveName::FiosNumberedSaveName(const std::string &prefix) : prefix(p std::string FiosNumberedSaveName::Filename() { if (++this->number >= _settings_client.gui.max_num_autosaves) this->number = 0; - return stdstr_fmt("%s%u.sav", this->prefix.c_str(), this->number); + return this->FilenameUsingNumber(this->number, ""); +} + +std::string FiosNumberedSaveName::FilenameUsingNumber(int num, const char *suffix) const +{ + return stdstr_fmt("%s%u%s.sav", this->prefix.c_str(), num, suffix); } /** diff --git a/src/fios.h b/src/fios.h index c529c9b522..064ca798e8 100644 --- a/src/fios.h +++ b/src/fios.h @@ -136,7 +136,9 @@ FiosType FiosGetSavegameListCallback(SaveLoadOperation fop, const std::string &f struct FiosNumberedSaveName { FiosNumberedSaveName(const std::string &prefix); std::string Filename(); + std::string FilenameUsingNumber(int num, const char *suffix) const; std::string Extension(); + int GetLastNumber() const { return this->number; } private: std::string prefix; int number; diff --git a/src/openttd.cpp b/src/openttd.cpp index 8e12068a66..7a4016d0e8 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -1929,14 +1929,19 @@ void StateGameLoop() assert(IsLocalCompany()); } +FiosNumberedSaveName &GetAutoSaveFiosNumberedSaveName() +{ + static FiosNumberedSaveName _autosave_ctr("autosave"); + return _autosave_ctr; +} + /** * Create an autosave. The default name is "autosave#.sav". However with * the setting 'keep_all_autosave' the name defaults to company-name + date */ static void DoAutosave() { - static FiosNumberedSaveName _autosave_ctr("autosave"); - DoAutoOrNetsave(_autosave_ctr, true); + DoAutoOrNetsave(GetAutoSaveFiosNumberedSaveName(), true); } /**