Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
c7714f1
bugfix(savegame): Add headless mode checks to prevent UI calls and sk…
bobtista Jan 17, 2026
1856f25
feat(recorder): Add save/load serialization support for RecorderClass
bobtista Jan 15, 2026
eb0fda6
feat(cli): Add command line options for replay checkpoint save/load
bobtista Jan 15, 2026
b5115d2
feat(replay): Add checkpoint save and resume functionality for replays
bobtista Jan 15, 2026
7861b1b
bugfix(recorder): Preserve mode and game info during checkpoint loading
bobtista Jan 19, 2026
ff46132
bugfix(logic): Call prepareForMP_or_Skirmish when loading replay chec…
bobtista Jan 19, 2026
27b8a88
bugfix(savegame): Fix save file path lookup in getSaveGameInfoFromFile
bobtista Jan 19, 2026
a86d604
bugfix(cli): Fix command line parsing order for -loadCheckpoint and -…
bobtista Jan 19, 2026
62c2200
feat(replay): Implement continueReplayFromCheckpoint for headless che…
bobtista Jan 19, 2026
9e47e11
bugfix(recorder): Capture replay file position before serialization
bobtista Jan 20, 2026
919a086
feat(random): Add functions to save/restore full RNG state for checkp…
bobtista Jan 20, 2026
9ef5b7e
feat(object): Add friend methods to set next/prev pointers for list r…
bobtista Jan 20, 2026
e69ce38
feat(savegame): Add TheAI to save block list for checkpoint serializa…
bobtista Jan 20, 2026
5388eca
refactor(replay): Simplify checkpoint save error handling
bobtista Jan 20, 2026
f616095
feat(replay): Add RNG state serialization and object list reversal fo…
bobtista Jan 20, 2026
9944ad5
feat(replay): Reset transient AI state after checkpoint load for CRC …
bobtista Jan 20, 2026
8487cd6
bugfix(replay): Fix shroud state corruption after checkpoint load by …
bobtista Jan 20, 2026
476b10c
bugfix(replay): Initialize m_lastCell for dirty modules after checkpo…
bobtista Jan 20, 2026
25e1493
bugfix(replay): Serialize path timestamp and blocked state for checkp…
bobtista Jan 20, 2026
e9a8939
bugfix(replay): Fix weapon timing corruption by comparing WeaponSet b…
bobtista Jan 20, 2026
7f5fd5a
feat(savegame): Serialize AI, Pathfinder, and AIGroup state for check…
bobtista Jan 20, 2026
cfb0dcd
feat(savegame): Serialize PartitionCell coordinates for checkpoint CR…
bobtista Jan 20, 2026
c367de5
feat(replay): Preload CRC from replay file after checkpoint load for …
bobtista Jan 20, 2026
f1ddebd
refactor(replay): Move RNG restoration to start of update and add aut…
bobtista Jan 20, 2026
8cae187
cleanup(replay): Remove obsolete resetTransientStateForCheckpoint wor…
bobtista Jan 21, 2026
e2bfde0
feat(replay): Call pathfinder loadPostProcess from AI to trigger zone…
bobtista Jan 21, 2026
95654de
cleanup(replay): Remove debug logging added during checkpoint investi…
bobtista Jan 22, 2026
49808c4
feat(replay): Add checkpoint load tracking and sleepy update order re…
bobtista Jan 22, 2026
c9befa8
fix(replay): Call loadPostProcess on modules during checkpoint load
bobtista Jan 22, 2026
b2dac3b
refactor(savegame): Fix weapon timing root cause by syncing template …
bobtista Jan 22, 2026
d230619
bugfix(pathfind): Fix out-of-bounds array access in Pathfinder::crc()
bobtista Jan 22, 2026
08b2beb
bugfix(savegame): Handle absolute paths in getFilePathInSaveDirectory
bobtista Jan 22, 2026
6229a09
bugfix(checkpoint): Add protection for adjustDestination and goal pos…
bobtista Jan 26, 2026
a02ae4f
bugfix(checkpoint): Add 3-frame protection window to justLoadedFromCh…
bobtista Jan 26, 2026
c3cce06
bugfix(checkpoint): Set checkpoint load frame for all units not just …
bobtista Jan 26, 2026
c2f8244
bugfix(checkpoint): Preserve cell flags in Pathfinder::loadPostProces…
bobtista Jan 26, 2026
bd26cd1
bugfix(replay): Fix CRC queue handling and preload tracking after che…
bobtista Jan 26, 2026
f8f6f5f
bugfix(replay): Use isPlaybackMode for CRC message routing instead of…
bobtista Jan 26, 2026
95b53bc
refactor(checkpoint): Remove duplicate pathfinder loadPostProcess cal…
bobtista Jan 26, 2026
66183c4
bugfix(savegame): Delete pre-existing weapons during load when saved …
bobtista Jan 26, 2026
0945aa3
fix(savegame): Serialize script m_frameToEvaluateAt to prevent RNG di…
bobtista Jan 26, 2026
2c36f6d
fix(savegame): Save checkpoint at correct frame to capture state befo…
bobtista Jan 26, 2026
5d0672c
fix(savegame): Support headless mode in BaseHeightMapRenderObjClass::…
bobtista Jan 27, 2026
2321cae
fix(savegame): Include TerrainVisual chunk in headless mode saves
bobtista Jan 27, 2026
7132a83
fix(savegame): Handle null pointers in W3DTerrainVisual::xfer for hea…
bobtista Jan 27, 2026
0cbe148
Fix DozerActionStateMachine not serializing base StateMachine state
bobtista Jan 28, 2026
149dabd
Fix TurretStateMachine not serializing base StateMachine state
bobtista Jan 28, 2026
49cc0ac
fix(savegame): Serialize PathfindCell unit tracking state for checkpo…
bobtista Jan 31, 2026
d530694
fix(savegame): Serialize DockUpdate state for checkpoint determinism
bobtista Jan 31, 2026
30404ab
fix(savegame): Serialize aircraft goal cells for checkpoint determinism
bobtista Jan 31, 2026
7611fb2
fix(savegame): Preserve pending path requests and invalidate CPOP cac…
bobtista Jan 31, 2026
de39539
fix(savegame): Serialize AIDockProcessDockState timing for checkpoint…
bobtista Jan 31, 2026
5d79e2d
fix(savegame): Remove unnecessary checkpoint workaround infrastructur…
bobtista Jan 31, 2026
69628ab
fix(savegame): Serialize VictoryConditions state to prevent re-trigge…
bobtista Feb 2, 2026
6473546
fix(savegame): Skip shroud reveal in startNewGame when loading a save…
bobtista Feb 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Core/GameEngine/Include/Common/RandomValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,10 @@ extern void InitRandom( UnsignedInt seed );
extern UnsignedInt GetGameLogicRandomSeed(); ///< Get the seed (used for replays)
extern UnsignedInt GetGameLogicRandomSeedCRC();///< Get the seed (used for CRCs)

// TheSuperHackers @info bobtista 19/01/2026
// Functions to save/restore the full RNG state for checkpoints.
// The state array must have room for 6 UnsignedInts.
extern void GetGameLogicRandomState( UnsignedInt* state, UnsignedInt* baseSeed );
extern void SetGameLogicRandomState( const UnsignedInt* state, UnsignedInt baseSeed );

//--------------------------------------------------------------------------------------------------------------
3 changes: 3 additions & 0 deletions Core/GameEngine/Include/Common/ReplaySimulation.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ class ReplaySimulation
// Returns exit code 0 if all replays were successfully simulated without mismatches
static int simulateReplays(const std::vector<AsciiString> &filenames, int maxProcesses);

// Continue a replay from a checkpoint file in headless mode
static int continueReplayFromCheckpoint(const AsciiString &checkpointFile);

static void stop() { s_isRunning = false; }

static Bool isRunning() { return s_isRunning; }
Expand Down
1 change: 1 addition & 0 deletions Core/GameEngine/Include/Common/Snapshot.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class Snapshot
{

friend class GameState;
friend class GameLogic; // TheSuperHackers @bugfix bobtista 22/01/2026 Allow GameLogic to call loadPostProcess on objects
friend class XferLoad;
friend class XferSave;
friend class XferCRC;
Expand Down
9 changes: 9 additions & 0 deletions Core/GameEngine/Include/GameLogic/AIPathfind.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ class Path : public MemoryPoolObject, public Snapshot
/// Given a location, return closest location on path, and along-path dist to end as function result
void markOptimized() {m_isOptimized = true;}

/// TheSuperHackers @bugfix bobtista 31/01/2026 Invalidate CPOP cache after checkpoint load.
/// This forces computePointOnPath() to search from the beginning of the path and find the
/// segment closest to the unit's current position, rather than using a stale cached segment.
void invalidateCpopCache(void) {m_cpopValid = false; m_cpopRecentStart = nullptr;}

protected:
// snapshot interface
virtual void crc( Xfer *xfer );
Expand Down Expand Up @@ -304,9 +309,11 @@ class PathfindCell

Bool setTypeAsObstacle( Object *obstacle, Bool isFence, const ICoord2D &pos ); ///< flag this cell as an obstacle, from the given one
Bool removeObstacle( Object *obstacle ); ///< unflag this cell as an obstacle, from the given one
void resetForCheckpointLoad( void ); ///< reset cell type and obstacle info, but preserve flags and zone
void setType( CellType type ); ///< set the cell type
CellType getType() const { return (CellType)m_type; } ///< get the cell type
CellFlags getFlags() const { return (CellFlags)m_flags; } ///< get the cell type
void setFlags( CellFlags flags ) { m_flags = flags; } ///< set the cell flags (TheSuperHackers @bobtista 29/01/2026)
Bool isAircraftGoal() const {return m_aircraftGoal != 0;}

Bool isObstaclePresent( ObjectID objID ) const; ///< return true if the given object ID is registered as an obstacle in this cell
Expand Down Expand Up @@ -417,6 +424,7 @@ typedef PathfindCell *PathfindCellP;
*/
class PathfindLayer
{
friend class Pathfinder; ///< TheSuperHackers @info bobtista 21/01/2026 Allows Pathfinder::xfer to serialize layer state
public:
PathfindLayer();
~PathfindLayer();
Expand Down Expand Up @@ -527,6 +535,7 @@ typedef ZoneBlock *ZoneBlockP;
*/
class PathfindZoneManager
{
friend class Pathfinder; ///< TheSuperHackers @info bobtista 20/01/2026 Allows Pathfinder::xfer to serialize zone state
public:
enum {INITIAL_ZONES = 256};
enum {ZONE_BLOCK_SIZE = 10}; // Zones are calculated in blocks of 20x20. This way, the raw zone numbers can be used to
Expand Down
34 changes: 34 additions & 0 deletions Core/GameEngine/Source/Common/RandomValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,40 @@
// of the effects displayed on the GameClient.
//

UnsignedInt GetGameLogicRandomSeed( void )

Check failure on line 185 in Core/GameEngine/Source/Common/RandomValue.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-debug+t+e

function 'UnsignedInt GetGameLogicRandomSeed(void)' already has a body

Check failure on line 185 in Core/GameEngine/Source/Common/RandomValue.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg-debug+t+e

function 'UnsignedInt GetGameLogicRandomSeed(void)' already has a body

Check failure on line 185 in Core/GameEngine/Source/Common/RandomValue.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-profile+t+e

function 'UnsignedInt GetGameLogicRandomSeed(void)' already has a body

Check failure on line 185 in Core/GameEngine/Source/Common/RandomValue.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32+t+e

function 'UnsignedInt GetGameLogicRandomSeed(void)' already has a body

Check failure on line 185 in Core/GameEngine/Source/Common/RandomValue.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg-profile+t+e

function 'UnsignedInt GetGameLogicRandomSeed(void)' already has a body

Check failure on line 185 in Core/GameEngine/Source/Common/RandomValue.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg+t+e

function 'UnsignedInt GetGameLogicRandomSeed(void)' already has a body
{

Check failure on line 186 in Core/GameEngine/Source/Common/RandomValue.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6-debug+t+e

function 'unsigned int __cdecl GetGameLogicRandomSeed(void)' already has a body

Check failure on line 186 in Core/GameEngine/Source/Common/RandomValue.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6-profile+t+e

function 'unsigned int __cdecl GetGameLogicRandomSeed(void)' already has a body

Check failure on line 186 in Core/GameEngine/Source/Common/RandomValue.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6+t+e

function 'unsigned int __cdecl GetGameLogicRandomSeed(void)' already has a body
return theGameLogicBaseSeed;
}

UnsignedInt GetGameLogicRandomSeedCRC( void )

Check failure on line 190 in Core/GameEngine/Source/Common/RandomValue.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-debug+t+e

function 'UnsignedInt GetGameLogicRandomSeedCRC(void)' already has a body

Check failure on line 190 in Core/GameEngine/Source/Common/RandomValue.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg-debug+t+e

function 'UnsignedInt GetGameLogicRandomSeedCRC(void)' already has a body

Check failure on line 190 in Core/GameEngine/Source/Common/RandomValue.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-profile+t+e

function 'UnsignedInt GetGameLogicRandomSeedCRC(void)' already has a body

Check failure on line 190 in Core/GameEngine/Source/Common/RandomValue.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32+t+e

function 'UnsignedInt GetGameLogicRandomSeedCRC(void)' already has a body

Check failure on line 190 in Core/GameEngine/Source/Common/RandomValue.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg-profile+t+e

function 'UnsignedInt GetGameLogicRandomSeedCRC(void)' already has a body

Check failure on line 190 in Core/GameEngine/Source/Common/RandomValue.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg+t+e

function 'UnsignedInt GetGameLogicRandomSeedCRC(void)' already has a body
{

Check failure on line 191 in Core/GameEngine/Source/Common/RandomValue.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6-debug+t+e

function 'unsigned int __cdecl GetGameLogicRandomSeedCRC(void)' already has a body

Check failure on line 191 in Core/GameEngine/Source/Common/RandomValue.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6-profile+t+e

function 'unsigned int __cdecl GetGameLogicRandomSeedCRC(void)' already has a body

Check failure on line 191 in Core/GameEngine/Source/Common/RandomValue.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6+t+e

function 'unsigned int __cdecl GetGameLogicRandomSeedCRC(void)' already has a body
CRC c;
c.computeCRC(theGameLogicSeed, 6*sizeof(UnsignedInt));
return c.get();
}

// TheSuperHackers @info bobtista 19/01/2026
// Get the full RNG state for serialization in checkpoints.
void GetGameLogicRandomState( UnsignedInt* state, UnsignedInt* baseSeed )
{
for (int i = 0; i < 6; ++i)
{
state[i] = theGameLogicSeed[i];
}
*baseSeed = theGameLogicBaseSeed;
}

// TheSuperHackers @info bobtista 19/01/2026
// Restore the full RNG state after loading a checkpoint.
void SetGameLogicRandomState( const UnsignedInt* state, UnsignedInt baseSeed )
{
for (int i = 0; i < 6; ++i)
{
theGameLogicSeed[i] = state[i];
}
theGameLogicBaseSeed = baseSeed;
}

//
// Integer random value
//
Expand Down
112 changes: 112 additions & 0 deletions Core/GameEngine/Source/Common/ReplaySimulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "Common/ReplaySimulation.h"

#include "Common/GameEngine.h"
#include "Common/GameState.h"
#include "Common/LocalFileSystem.h"
#include "Common/Recorder.h"
#include "Common/WorkerProcess.h"
Expand Down Expand Up @@ -99,8 +100,27 @@
fflush(stdout);
}
TheGameLogic->UPDATE();

// Check for checkpoint save BEFORE checking for CRC mismatch
// so we can save checkpoints even if the replay will eventually fail
if (TheGlobalData->m_replaySaveAtFrame != 0 &&

Check failure on line 106 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6-debug+t+e

'm_replaySaveAtFrame' : is not a member of 'GlobalData'

Check failure on line 106 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6-profile+t+e

'm_replaySaveAtFrame' : is not a member of 'GlobalData'

Check failure on line 106 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6+t+e

'm_replaySaveAtFrame' : is not a member of 'GlobalData'

Check failure on line 106 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-debug+t+e

'm_replaySaveAtFrame': is not a member of 'GlobalData'

Check failure on line 106 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg-debug+t+e

'm_replaySaveAtFrame': is not a member of 'GlobalData'

Check failure on line 106 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-profile+t+e

'm_replaySaveAtFrame': is not a member of 'GlobalData'

Check failure on line 106 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32+t+e

'm_replaySaveAtFrame': is not a member of 'GlobalData'

Check failure on line 106 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg-profile+t+e

'm_replaySaveAtFrame': is not a member of 'GlobalData'

Check failure on line 106 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg+t+e

'm_replaySaveAtFrame': is not a member of 'GlobalData'
TheGameLogic->getFrame() == TheGlobalData->m_replaySaveAtFrame &&

Check failure on line 107 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6-debug+t+e

'm_replaySaveAtFrame' : is not a member of 'GlobalData'

Check failure on line 107 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6-profile+t+e

'm_replaySaveAtFrame' : is not a member of 'GlobalData'

Check failure on line 107 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6+t+e

'm_replaySaveAtFrame' : is not a member of 'GlobalData'

Check failure on line 107 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-debug+t+e

'm_replaySaveAtFrame': is not a member of 'GlobalData'

Check failure on line 107 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg-debug+t+e

'm_replaySaveAtFrame': is not a member of 'GlobalData'

Check failure on line 107 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-profile+t+e

'm_replaySaveAtFrame': is not a member of 'GlobalData'

Check failure on line 107 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32+t+e

'm_replaySaveAtFrame': is not a member of 'GlobalData'

Check failure on line 107 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg-profile+t+e

'm_replaySaveAtFrame': is not a member of 'GlobalData'

Check failure on line 107 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg+t+e

'm_replaySaveAtFrame': is not a member of 'GlobalData'
!TheGlobalData->m_replaySaveTo.isEmpty())

Check failure on line 108 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6-debug+t+e

left of '.isEmpty' must have class/struct/union type

Check failure on line 108 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6-debug+t+e

'm_replaySaveTo' : is not a member of 'GlobalData'

Check failure on line 108 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6-profile+t+e

left of '.isEmpty' must have class/struct/union type

Check failure on line 108 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6-profile+t+e

'm_replaySaveTo' : is not a member of 'GlobalData'

Check failure on line 108 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6+t+e

left of '.isEmpty' must have class/struct/union type

Check failure on line 108 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6+t+e

'm_replaySaveTo' : is not a member of 'GlobalData'

Check failure on line 108 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-debug+t+e

'm_replaySaveTo': is not a member of 'GlobalData'

Check failure on line 108 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg-debug+t+e

'm_replaySaveTo': is not a member of 'GlobalData'

Check failure on line 108 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-profile+t+e

'm_replaySaveTo': is not a member of 'GlobalData'

Check failure on line 108 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32+t+e

'm_replaySaveTo': is not a member of 'GlobalData'

Check failure on line 108 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg-profile+t+e

'm_replaySaveTo': is not a member of 'GlobalData'

Check failure on line 108 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg+t+e

'm_replaySaveTo': is not a member of 'GlobalData'
{
// TheSuperHackers @info bobtista 19/01/2026
// Pass just the filename to saveGame() - it will be saved to the Save directory.
SaveCode result = TheGameState->saveGame(TheGlobalData->m_replaySaveTo, UnicodeString::TheEmptyString, SAVE_FILE_TYPE_NORMAL);

Check failure on line 112 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6-debug+t+e

'm_replaySaveTo' : is not a member of 'GlobalData'

Check failure on line 112 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6-profile+t+e

'm_replaySaveTo' : is not a member of 'GlobalData'

Check failure on line 112 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6+t+e

'm_replaySaveTo' : is not a member of 'GlobalData'

Check failure on line 112 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-debug+t+e

'm_replaySaveTo': is not a member of 'GlobalData'

Check failure on line 112 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg-debug+t+e

'm_replaySaveTo': is not a member of 'GlobalData'

Check failure on line 112 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-profile+t+e

'm_replaySaveTo': is not a member of 'GlobalData'

Check failure on line 112 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32+t+e

'm_replaySaveTo': is not a member of 'GlobalData'

Check failure on line 112 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg-profile+t+e

'm_replaySaveTo': is not a member of 'GlobalData'

Check failure on line 112 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg+t+e

'm_replaySaveTo': is not a member of 'GlobalData'
if (result != SC_OK)
{
numErrors++;
}
TheRecorder->stopPlayback();
break;
}

if (TheRecorder->sawCRCMismatch())
{
DEBUG_LOG(("CRC mismatch at frame %d", TheGameLogic->getFrame()));
numErrors++;
break;
}
Expand Down Expand Up @@ -253,3 +273,95 @@
else
return simulateReplaysInWorkerProcesses(filenamesResolved, maxProcesses);
}

int ReplaySimulation::continueReplayFromCheckpoint(const AsciiString &checkpointFile)
{
int numErrors = 0;

// TheSuperHackers @info bobtista 19/01/2026
// We need to initialize the replay BEFORE loading the checkpoint because:
// 1. loadGame() calls startNewGame() which creates team prototypes based on the map
// 2. During replay playback, startNewGame() uses TheRecorder->getGameInfo() to get the correct team setup
// 3. But CHUNK_Recorder is loaded AFTER startNewGame() runs
// So we must initialize the replay first to provide the correct game info.
if (TheGlobalData->m_simulateReplays.empty())
{
DEBUG_LOG(("No replay file specified for checkpoint loading"));
return 1;
}

AsciiString replayFile = TheGlobalData->m_simulateReplays[0];
DEBUG_LOG(("Initializing replay from %s before loading checkpoint", replayFile.str()));

// Initialize the recorder from the replay file so that when loadGame() calls
// startNewGame(), it will have the correct game info from the replay header.
if (!TheRecorder->initializeReplayForCheckpointLoad(replayFile))

Check failure on line 298 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6-debug+t+e

'initializeReplayForCheckpointLoad' : is not a member of 'RecorderClass'

Check failure on line 298 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6-profile+t+e

'initializeReplayForCheckpointLoad' : is not a member of 'RecorderClass'

Check failure on line 298 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / vc6+t+e

'initializeReplayForCheckpointLoad' : is not a member of 'RecorderClass'

Check failure on line 298 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-debug+t+e

'initializeReplayForCheckpointLoad': is not a member of 'RecorderClass'

Check failure on line 298 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg-debug+t+e

'initializeReplayForCheckpointLoad': is not a member of 'RecorderClass'

Check failure on line 298 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-profile+t+e

'initializeReplayForCheckpointLoad': is not a member of 'RecorderClass'

Check failure on line 298 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32+t+e

'initializeReplayForCheckpointLoad': is not a member of 'RecorderClass'

Check failure on line 298 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg-profile+t+e

'initializeReplayForCheckpointLoad': is not a member of 'RecorderClass'

Check failure on line 298 in Core/GameEngine/Source/Common/ReplaySimulation.cpp

View workflow job for this annotation

GitHub Actions / Build Generals / win32-vcpkg+t+e

'initializeReplayForCheckpointLoad': is not a member of 'RecorderClass'
{
DEBUG_LOG(("Failed to initialize replay from %s", replayFile.str()));
return 1;
}

DEBUG_LOG(("Loading checkpoint from %s", checkpointFile.str()));

AvailableGameInfo gameInfo;
gameInfo.filename = checkpointFile;
TheGameState->getSaveGameInfoFromFile(checkpointFile, &gameInfo.saveGameInfo);

SaveCode result = TheGameState->loadGame(gameInfo);
if (result != SC_OK)
{
DEBUG_LOG(("Failed to load checkpoint (error %d)", result));
return 1;
}

if (!TheRecorder->isPlaybackMode())
{
DEBUG_LOG(("Checkpoint was not saved during replay playback"));
return 1;
}

DEBUG_LOG(("Resuming replay from frame %d", TheGameLogic->getFrame()));

#ifdef _DEBUG
DWORD startTimeMillis = GetTickCount();
UnsignedInt totalTimeSec = TheRecorder->getPlaybackFrameCount() / LOGICFRAMES_PER_SECOND;
#endif

while (TheRecorder->isPlaybackInProgress())
{
TheGameClient->updateHeadless();

#ifdef _DEBUG
const int progressFrameInterval = 10*60*LOGICFRAMES_PER_SECOND;
if (TheGameLogic->getFrame() != 0 && TheGameLogic->getFrame() % progressFrameInterval == 0)
{
UnsignedInt gameTimeSec = TheGameLogic->getFrame() / LOGICFRAMES_PER_SECOND;
UnsignedInt realTimeSec = (GetTickCount()-startTimeMillis) / 1000;
DEBUG_LOG(("Elapsed Time: %02d:%02d Game Time: %02d:%02d/%02d:%02d",
realTimeSec/60, realTimeSec%60, gameTimeSec/60, gameTimeSec%60, totalTimeSec/60, totalTimeSec%60));
}
#endif
TheGameLogic->UPDATE();
if (TheRecorder->sawCRCMismatch())
{
numErrors++;
break;
}
}

#ifdef _DEBUG
{
UnsignedInt gameTimeSec = TheGameLogic->getFrame() / LOGICFRAMES_PER_SECOND;
UnsignedInt realTimeSec = (GetTickCount()-startTimeMillis) / 1000;
DEBUG_LOG(("Elapsed Time: %02d:%02d Game Time: %02d:%02d/%02d:%02d",
realTimeSec/60, realTimeSec%60, gameTimeSec/60, gameTimeSec%60, totalTimeSec/60, totalTimeSec%60));

if (TheRecorder->sawCRCMismatch())
DEBUG_LOG(("CRC Mismatch detected!"));
else
DEBUG_LOG(("Replay completed successfully"));
}
#endif

return numErrors != 0 ? 1 : 0;
}
Loading
Loading