From 56a5126e7266bfe4c842b310caefbfb5be1d21d3 Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Tue, 23 Jul 2024 15:16:06 -0400 Subject: [PATCH] Fixup pending research states when loading savegames --- src/game.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++++++ src/multiplay.cpp | 13 +++++++--- src/multiplay.h | 4 ++- 3 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index efa924a0ca4..5b2c2ef70c9 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2262,6 +2262,8 @@ static bool writeCompListFile(const char *pFileName); static bool loadSaveStructTypeList(const char *pFileName); static bool writeStructTypeListFile(const char *pFileName); +static void loadFixupResearchPendingStates(); + static bool loadSaveResearch(const char *pFileName); static bool writeResearchFile(char *pFileName); @@ -3300,6 +3302,11 @@ bool loadGame(const char *pGameToLoad, bool keepObjects, bool freeMem, bool User } } + if (UserSaveGame) + { + loadFixupResearchPendingStates(); + } + // Load labels aFileName[fileExten] = '\0'; strcat(aFileName, "labels.json"); @@ -7434,6 +7441,61 @@ static bool writeStructTypeListFile(const char *pFileName) return true; } +void loadFixupResearchPendingStates() +{ + const unsigned int players = static_cast(game.maxPlayers); + for (int statInc = 0; statInc < asResearch.size(); statInc++) + { + for (unsigned int plr = 0; plr < players; plr++) + { + PLAYER_RESEARCH *pPlayerRes = &asPlayerResList[plr][statInc]; + + // Handle "pending" states + // - i.e. starting/cancelling research was queued, but the game tick was not processed before the save occurred + // - (Ideally the game would prevent this from happening by ensuring the save is queued to happen after the next tick...) + if (pPlayerRes->ResearchStatus & CANCELLED_RESEARCH_PENDING) + { + STRUCTURE *psLab = findResearchingFacilityByResearchIndex(apsStructLists, plr, statInc); + if (psLab == nullptr) + { + // check the mission list + psLab = findResearchingFacilityByResearchIndex(mission.apsStructLists, plr, statInc); + } + + if (psLab != nullptr) + { + // Process the pending cancellation with immediate effect + debug(LOG_INFO, "Processing CANCELLED_RESEARCH_PENDING for: %s", asResearch[statInc].id.toUtf8().c_str()); + ::cancelResearch(psLab, ModeImmediate); + } + else + { + // did not find in *either* list - log and convert the CANCELLED_RESEARCH_PENDING status appropriately + if (pPlayerRes->currentPoints == 0) + { + // Reset this topic as not having been researched + debug(LOG_INFO, "Resetting CANCELLED_RESEARCH_PENDING to 0 for: %s", asResearch[statInc].id.toUtf8().c_str()); + ResetResearchStatus(pPlayerRes); + } + else + { + // Set the cancelled flag + debug(LOG_INFO, "Resetting CANCELLED_RESEARCH_PENDING to CANCELLED_RESEARCH for: %s", asResearch[statInc].id.toUtf8().c_str()); + MakeResearchCancelled(pPlayerRes); + } + } + } + else if (pPlayerRes->ResearchStatus & STARTED_RESEARCH_PENDING) + { + // Possible Future TODO: Could try to "recover", and queue this research item in an available research facility (however, the save doesn't contain which facility the user queued it in) + // For now: Just clear the STARTED_RESEARCH_PENDING bit, so that the user can re-start the research after loading the save + debug(LOG_INFO, "Resetting STARTED_RESEARCH_PENDING to 0 for: %s", asResearch[statInc].id.toUtf8().c_str()); + pPlayerRes->ResearchStatus &= ~STARTED_RESEARCH_PENDING; + } + } + } +} + // ----------------------------------------------------------------------------------------- // load up saved research file bool loadSaveResearch(const char *pFileName) diff --git a/src/multiplay.cpp b/src/multiplay.cpp index 47aff9dd3e3..932937c6bb9 100644 --- a/src/multiplay.cpp +++ b/src/multiplay.cpp @@ -1576,14 +1576,14 @@ bool sendResearchStatus(const STRUCTURE *psBuilding, uint32_t index, uint8_t pla return true; } -STRUCTURE *findResearchingFacilityByResearchIndex(unsigned player, unsigned index) +STRUCTURE *findResearchingFacilityByResearchIndex(const PerPlayerStructureLists& pList, unsigned player, unsigned index) { // Go through the structs to find the one doing this topic - for (STRUCTURE *psBuilding : apsStructLists[player]) + for (STRUCTURE *psBuilding : pList[player]) { if (psBuilding->pStructureType->type == REF_RESEARCH - && ((RESEARCH_FACILITY *)psBuilding->pFunctionality)->psSubject - && ((RESEARCH_FACILITY *)psBuilding->pFunctionality)->psSubject->ref - STAT_RESEARCH == index) + && ((RESEARCH_FACILITY *)psBuilding->pFunctionality)->psSubject + && ((RESEARCH_FACILITY *)psBuilding->pFunctionality)->psSubject->ref - STAT_RESEARCH == index) { return psBuilding; } @@ -1591,6 +1591,11 @@ STRUCTURE *findResearchingFacilityByResearchIndex(unsigned player, unsigned inde return nullptr; // Not found. } +STRUCTURE *findResearchingFacilityByResearchIndex(unsigned player, unsigned index) +{ + return findResearchingFacilityByResearchIndex(apsStructLists, player, index); +} + bool recvResearchStatus(NETQUEUE queue) { STRUCTURE *psBuilding; diff --git a/src/multiplay.h b/src/multiplay.h index 0bea0594839..efcec425ded 100644 --- a/src/multiplay.h +++ b/src/multiplay.h @@ -36,6 +36,7 @@ #include "levels.h" #include "console.h" #include "multirecv.h" +#include "objmem.h" #include #include #include @@ -323,7 +324,8 @@ bool sendBeacon(int32_t locX, int32_t locY, int32_t forPlayer, int32_t sender, c void startMultiplayerGame(); void resetReadyStatus(bool bSendOptions, bool ignoreReadyReset = false); -STRUCTURE *findResearchingFacilityByResearchIndex(unsigned player, unsigned index); +STRUCTURE *findResearchingFacilityByResearchIndex(const PerPlayerStructureLists& pList, unsigned player, unsigned index); +STRUCTURE *findResearchingFacilityByResearchIndex(unsigned player, unsigned index); // checks apsStructLists void sendSyncRequest(int32_t req_id, int32_t x, int32_t y, const BASE_OBJECT *psObj, const BASE_OBJECT *psObj2);