Skip to content

Commit

Permalink
Fixup pending research states when loading savegames
Browse files Browse the repository at this point in the history
  • Loading branch information
past-due committed Jul 23, 2024
1 parent 2c42edf commit 56a5126
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 5 deletions.
62 changes: 62 additions & 0 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -7434,6 +7441,61 @@ static bool writeStructTypeListFile(const char *pFileName)
return true;
}

void loadFixupResearchPendingStates()
{
const unsigned int players = static_cast<unsigned int>(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)
Expand Down
13 changes: 9 additions & 4 deletions src/multiplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1576,21 +1576,26 @@ 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;
}
}
return nullptr; // Not found.
}

STRUCTURE *findResearchingFacilityByResearchIndex(unsigned player, unsigned index)
{
return findResearchingFacilityByResearchIndex(apsStructLists, player, index);
}

bool recvResearchStatus(NETQUEUE queue)
{
STRUCTURE *psBuilding;
Expand Down
4 changes: 3 additions & 1 deletion src/multiplay.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "levels.h"
#include "console.h"
#include "multirecv.h"
#include "objmem.h"
#include <vector>
#include <string>
#include <chrono>
Expand Down Expand Up @@ -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);

Expand Down

0 comments on commit 56a5126

Please sign in to comment.