Skip to content

fix(headless): Skip early load abort in headless replay simulations#2730

Open
githubawn wants to merge 2 commits into
TheSuperHackers:mainfrom
githubawn:fix/ci
Open

fix(headless): Skip early load abort in headless replay simulations#2730
githubawn wants to merge 2 commits into
TheSuperHackers:mainfrom
githubawn:fix/ci

Conversation

@githubawn
Copy link
Copy Markdown

@githubawn githubawn commented May 18, 2026

Fixes the CI replay checker instantly completing introduced by #2336.

Bypasses the QuitGameException in GameLogic::updateLoadProgress when running in headless mode. This ensures that any early initialization warnings that set the quitting flag do not prematurely abort the replay simulation during map loading.

Claude helped. Worked locally. Let's see what it does on our CI.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 18, 2026

Greptile Summary

This PR fixes a CI regression introduced in #2336 where headless replay simulations would immediately abort due to WM_CLOSE firing before TheMessageStream was ready, inadvertently setting the quitting flag and causing GameLogic::updateLoadProgress to throw QuitGameException. Changes are applied symmetrically to both the Generals/ and GeneralsMD/ trees.

  • WinMain.cpp (both trees): The WM_CLOSE fallback path that calls setQuitting(TRUE) is gated behind !TheGlobalData->m_headless, so a spurious close message during early engine init no longer poisons the quitting flag for headless runs.
  • GameLogic.cpp (both trees): updateLoadProgress adds the same headless guard as a second line of defence, ensuring that even if the quitting flag is set through any path during map loading, a QuitGameException is not thrown while running headless.

Confidence Score: 5/5

Safe to merge — changes are narrow, well-commented, and symmetric across both game trees.

The fix correctly gates two independent code paths (WndProc and updateLoadProgress) behind the existing m_headless flag. The !TheGlobalData null-guard is the right conservative fallback: when global data is unavailable the behaviour reverts to the original (throw/setQuitting), so non-headless production builds are unaffected. No new state is introduced and the change does not touch any shared data structures.

No files require special attention.

Important Files Changed

Filename Overview
Generals/Code/Main/WinMain.cpp WM_CLOSE handler skips setQuitting(TRUE) in headless mode when TheMessageStream is not ready, preventing early quit-flag poisoning during engine init.
Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp updateLoadProgress now skips QuitGameException in headless mode, acting as a second-line defence against spurious quit flags during map loading.
GeneralsMD/Code/Main/WinMain.cpp Identical fix to Generals/Code/Main/WinMain.cpp applied to the Zero Hour tree; no functional differences.
GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp Identical fix to Generals GameLogic.cpp applied to the Zero Hour tree; no functional differences.

Sequence Diagram

sequenceDiagram
    participant OS as Windows OS
    participant WP as WndProc (WinMain)
    participant GE as GameEngine
    participant GL as GameLogic::updateLoadProgress
    participant CI as CI Replay Checker

    Note over OS,CI: Headless replay startup — engine still initialising

    OS->>WP: WM_CLOSE (early, TheMessageStream not ready)
    alt Non-headless (old behaviour)
        WP->>GE: setQuitting(TRUE)
        GE-->>GL: "getQuitting() == true"
        GL->>CI: throw QuitGameException() — simulation aborted
    else Headless mode (new behaviour)
        WP-->>WP: skip setQuitting (headless guard)
        Note over GL: updateLoadProgress also guards: skip QuitGameException if headless
        GL->>CI: loading continues normally
    end

    Note over CI: Map loading completes, replay runs to end
    CI->>CI: Simulation finishes, process exits cleanly
Loading

Reviews (5): Last reviewed commit: "another try" | Re-trigger Greptile

Comment thread Core/GameEngine/Source/Common/ReplaySimulation.cpp Outdated
@githubawn
Copy link
Copy Markdown
Author

did some triaging and it happens much earlier than found, run 5930 instead of 7153 https://github.com/TheSuperHackers/GeneralsGameCode/actions/runs/22267506812/job/64416452283 basically either the first or second commit of the PR

In headless replay simulation mode, the hidden game window can receive
WM_CLOSE from the OS before ThePlayerList is initialized (isReadyForMessages()
returns false). The new else branch introduced in PR TheSuperHackers#2336 was calling
setQuitting(TRUE) in this case, which caused updateLoadProgress() to throw
QuitGameException on every replay load. The game would then simulate on an
empty world with no map or objects, completing a 99-minute replay in ~4 seconds
instead of the expected ~4 minutes.

Fix: add !TheGlobalData->m_headless guard to the WM_CLOSE fallback branch so
that OS-generated window close messages during headless init are silently ignored.
@xezon
Copy link
Copy Markdown

xezon commented May 19, 2026

As long as the test passes green, it is not working correctly.

@xezon xezon added Blocker Severity: Minor < Major < Critical < Blocker Debug Is mostly debug functionality Bug Something is not working right, typically is user facing labels May 19, 2026
@stephanmeesters
Copy link
Copy Markdown

stephanmeesters commented May 19, 2026

Suggest to first revert #2336 entirely, observe tests (of GitHub CI), then divide and conquer

@githubawn githubawn changed the title fix(replay): Prevent quit menu from blocking headless replay simulation fix(headless): Skip early load abort in headless replay simulations May 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Blocker Severity: Minor < Major < Critical < Blocker Bug Something is not working right, typically is user facing Debug Is mostly debug functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants