Skip to content

Conversation

@bobtista
Copy link

Summary

  • Adds CLI options to save and load replay checkpoints for debugging replay sync issues
  • Can create a .sav game with the state at a given frame from a .rep file, then load the .sav (checkpoint) and continue playing the commands from the .rep in headless.
  • -saveAtFrame <frame> -saveTo <filename> saves game state at specified frame
  • -loadCheckpoint <filename> loads a checkpoint and resumes replay playback
  • Checkpoint files are saved to the standard Save directory

Usage

# Save checkpoint at frame 49000
generalszh.exe -headless -replay 00000000.rep -saveAtFrame 49000 -saveTo checkpoint.sav

# Load checkpoint and resume playback
generalszh.exe -headless -replay 00000000.rep -loadCheckpoint checkpoint.sav

## Testing

-[x] Test checkpoint save at specified frame
-[x] Test checkpoint load and resume playback

##  Notes
- Depends on #2137 to avoid ParticleSystem crash during headless replay playback.
- Since game state is saved at the checkpoint moment, any changes that would meaningfully affect game state BEFORE that frame will require a new checkpoint being made. Ideally, fixes are about something that happens after the checkpoint and before the mismatch/crash being fixed.

@greptile-apps
Copy link

greptile-apps bot commented Jan 19, 2026

Greptile Summary

This PR adds checkpoint save/resume functionality for replay debugging, allowing developers to create save points at specific frames during replay playback and resume from those points in headless mode.

Key changes:

  • Extends RecorderClass to implement Snapshot interface for serialization of replay state (filename, file position, CRC queue)
  • Adds CLI options -saveAtFrame, -saveTo, and -loadCheckpoint for checkpoint management
  • Implements continueReplayFromCheckpoint() to load checkpoint and resume replay from saved position
  • Adds headless mode checks in GameState to prevent UI calls and skip visual-only blocks during checkpoint saves
  • Properly handles CRC state restoration by serializing the CRC queue and associated flags

Implementation notes:

  • When loading a checkpoint, the replay filename is restored from the checkpoint itself (not from -replay CLI arg)
  • Visual blocks (CHUNK_TerrainVisual, CHUNK_TacticalView, CHUNK_ParticleSystem, CHUNK_GhostObject) are skipped in headless saves
  • The save/load system handles missing blocks gracefully, making headless checkpoints compatible with the existing save system
  • CRC queue is properly serialized/restored to maintain sync detection after checkpoint resume

Confidence Score: 5/5

  • This PR is safe to merge with proper testing of checkpoint save/load workflow
  • The implementation is well-designed with proper error handling, memory management (CRCInfo cleanup in reset()), and defensive coding. The serialization logic correctly handles all replay state, and the headless mode guards prevent UI crashes. The save/load system's existing tolerance for missing blocks ensures compatibility.
  • No files require special attention. The core implementation in Recorder.cpp and ReplaySimulation.cpp is solid.

Important Files Changed

Filename Overview
Core/GameEngine/Source/Common/ReplaySimulation.cpp Implemented checkpoint save at specified frame and resume functionality with proper error handling
GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp Implemented serialization methods for replay state, added file reopening logic, and proper CRCInfo cleanup in reset()
GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp Added CHUNK_Recorder snapshot block, headless mode checks for UI calls, and logic to skip visual blocks in headless saves

Sequence Diagram

sequenceDiagram
    participant CLI as Command Line
    participant GM as GameMain
    participant RS as ReplaySimulation
    participant GS as GameState
    participant RC as RecorderClass
    participant GL as GameLogic
    participant FS as FileSystem

    Note over CLI,FS: Checkpoint Save Flow
    CLI->>GM: -replay file.rep -saveAtFrame 49000 -saveTo checkpoint.sav
    GM->>RS: simulateReplays([file.rep])
    RS->>RC: playbackFile(file.rep)
    RC->>FS: Open replay file
    FS-->>RC: File handle
    loop Until target frame or end
        RS->>GL: UPDATE()
        GL-->>RS: Current frame
        alt Frame == saveAtFrame
            RS->>GS: saveGame(checkpoint.sav)
            GS->>RC: xfer(XFER_SAVE)
            Note over RC: Saves replay filename,<br/>file position, CRC state
            GS->>FS: Write checkpoint file
            FS-->>GS: Success
            RS->>RC: stopPlayback()
            RC->>FS: Close replay file
        end
    end

    Note over CLI,FS: Checkpoint Resume Flow
    CLI->>GM: -loadCheckpoint checkpoint.sav
    GM->>RS: continueReplayFromCheckpoint(checkpoint.sav)
    RS->>GS: loadGame(checkpoint.sav)
    GS->>FS: Read checkpoint file
    FS-->>GS: Checkpoint data
    GS->>RC: xfer(XFER_LOAD)
    Note over RC: Restores replay filename,<br/>file position, CRC state
    GS->>RC: loadPostProcess()
    RC->>FS: reopenReplayFileAtPosition(position)
    FS-->>RC: File handle at saved position
    loop Until replay end or CRC mismatch
        RS->>GL: UPDATE()
        GL-->>RS: Current frame
        RC->>FS: Read next commands from position
        alt CRC mismatch detected
            RS-->>GM: Exit with error
        end
    end
    RS-->>GM: Exit code
Loading

@bobtista bobtista marked this pull request as draft January 19, 2026 21:37
Comment on lines +1913 to +1916
if ( xfer->getXferMode() == XFER_SAVE && m_file != nullptr )
{
m_currentFilePosition = m_file->position();
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to go before xfer->xferInt( &m_currentFilePosition ). Could probably be put at the start of the function.

Comment on lines +347 to +351
if (TheRecorder->sawCRCMismatch())
{
numErrors++;
break;
}
Copy link

@Caball009 Caball009 Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some issues with the CRC computation for the replay from save game.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants