Skip to content

fix(logic): Improve handling of MSG_NEW_GAME in GameLogicDispatch#2440

Open
stephanmeesters wants to merge 3 commits intoTheSuperHackers:mainfrom
stephanmeesters:fix/gamelogicdispatch-newgame
Open

fix(logic): Improve handling of MSG_NEW_GAME in GameLogicDispatch#2440
stephanmeesters wants to merge 3 commits intoTheSuperHackers:mainfrom
stephanmeesters:fix/gamelogicdispatch-newgame

Conversation

@stephanmeesters
Copy link

@stephanmeesters stephanmeesters commented Mar 11, 2026

This prevents a possible issue where the game gets stuck in the disconnect screen ("dc bug") when making a call to MSG_NEW_GAME when the game is not ready for it.

Tested with local multiplayer match, skirmish, savegame, replay, campaign, generals challeng. 1k replays passed.

Moved behind RETAIL_COMPATIBLE_CRC in the case of any replays that have a forced dc bug.

Todo

  • Replicate in Generals

@greptile-apps
Copy link

greptile-apps bot commented Mar 11, 2026

Greptile Summary

This PR adds a guard in the MSG_NEW_GAME handler inside GameLogic::logicMessageDispatcher to prevent processing a new-game request when the game is already active, clearing data, or loading a map — blocking a cheat-exploitable "dc bug" (infinite disconnect screen). The fix is wrapped in #if !RETAIL_COMPATIBLE_CRC to preserve CRC compatibility with retail replays.

Key observations:

  • GeneralsMD (Zero Hour): The fix is complete and correct. All three guard methods (isInGame(), isClearingGameData(), isLoadingMap()) are declared in the GeneralsMD GameLogic class.
  • Generals (vanilla): The fix references isClearingGameData() and isLoadingMap(), which do not exist in Generals/Code/GameEngine/Include/GameLogic/GameLogic.h. The Generals version only has isLoadingGame(). This will cause a compile error when RETAIL_COMPATIBLE_CRC=0. This appears to be acknowledged by the open "Replicate in Generals" Todo item, but the current state of the Generals codebase is broken under that configuration.
  • In non-debug release builds, DEBUG_CRASH is a no-op, but the break still correctly prevents the invalid game-state transition — this is intentional and safe.
  • RETAIL_COMPATIBLE_CRC defaults to 1, so the guard is inactive in default builds and only activates when the project is deliberately built without retail CRC compatibility.

Confidence Score: 3/5

  • Safe to merge for GeneralsMD, but the Generals variant will not compile when RETAIL_COMPATIBLE_CRC=0 due to missing methods.
  • The Zero Hour (GeneralsMD) side of the fix is correct and well-tested. The Generals side introduces a compile-time failure when RETAIL_COMPATIBLE_CRC is disabled, because isClearingGameData() and isLoadingMap() are absent from the Generals GameLogic class. The open Todo item acknowledges this, but the code as written is incomplete for the Generals target.
  • Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp requires attention — the guard calls methods not yet present in the Generals GameLogic class.

Important Files Changed

Filename Overview
Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp Adds guard against MSG_NEW_GAME when game is in invalid state, but calls isClearingGameData() and isLoadingMap() which don't exist in the Generals GameLogic class — will cause a compile error when RETAIL_COMPATIBLE_CRC=0.
GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp Same guard added to Zero Hour (GeneralsMD); all three methods (isInGame, isClearingGameData, isLoadingMap) are properly declared in the GeneralsMD GameLogic header — no issues.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[MSG_NEW_GAME received] --> B{RETAIL_COMPATIBLE_CRC == 0?}
    B -- No --> E[Process MSG_NEW_GAME normally]
    B -- Yes --> C{"isInGame() ||\nisClearingGameData() ||\nisLoadingMap()"}
    C -- true --> D[DEBUG_CRASH + break\nMessage silently dropped]
    C -- false --> E
    E --> F[prepareNewGame]
    F --> G[startNewGame]
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp
Line: 417-422

Comment:
**Missing methods in Generals GameLogic**

`isClearingGameData()` and `isLoadingMap()` are not declared in `Generals/Code/GameEngine/Include/GameLogic/GameLogic.h`. The Generals version only has `isLoadingGame()`. This means that when `RETAIL_COMPATIBLE_CRC` is set to `0`, the Generals build will fail to compile.

The GeneralsMD (Zero Hour) version correctly declares both methods (`isLoadingMap()` on line 189 and `isClearingGameData()` on line 191 of its `GameLogic.h`), but the vanilla Generals version does not have these equivalents.

This appears to be tracked by the open PR Todo item ("Replicate in Generals"), but as-is the `Generals` build is broken when `RETAIL_COMPATIBLE_CRC=0`.

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: b5d35b3

@Caball009
Copy link

Moved behind RETAIL_COMPATIBLE_CRC in the case of any replays that have a forced dc bug.

Is this necessary? I was under the impression that it's not possible to recover from the DC bug.

@xezon xezon added Minor Severity: Minor < Major < Critical < Blocker Gen Relates to Generals ZH Relates to Zero Hour Fix Is fixing something, but is not user facing labels Mar 11, 2026
@stephanmeesters
Copy link
Author

Moved behind RETAIL_COMPATIBLE_CRC in the case of any replays that have a forced dc bug.

Is this necessary? I was under the impression that it's not possible to recover from the DC bug.

The DC bug is game ending, but once someone leaves the other guy wins, and that game may then carry on for some more frames and be recorded in the replay. Since this PR changes something there it may then mismatch right at the end, I didn't test specifically though.

@xezon xezon added Major Severity: Minor < Major < Critical < Blocker and removed Minor Severity: Minor < Major < Critical < Blocker labels Mar 11, 2026
Copy link

@xezon xezon left a comment

Choose a reason for hiding this comment

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

Makes logical sense.

I suggest test all game modes and verify they all properly launch.

Comment can be written better.

Needs to be replicated in Generals.

#if !RETAIL_COMPATIBLE_CRC
// TheSuperHackers @fix stephanmeesters 11/03/2026
// Make sure we're ready to start a new game. This prevents an issue where an infinite disconnect screen
// ("DC bug") can be triggered in an online match.
Copy link

Choose a reason for hiding this comment

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

("DC bug") can be omitted. I suggest clarify that this can only be done by hacking, not in regular ways.

@stephanmeesters
Copy link
Author

stephanmeesters commented Mar 11, 2026

I suggest test all game modes and verify they all properly launch.

Think I got all of them except for Generals challenge and WOL (see ticket description). I could try spinning up x64's server code

@xezon
Copy link

xezon commented Mar 11, 2026

Can ignore the WOL for now.

Comment on lines +417 to +422
if ( isInGame() || isClearingGameData() || isLoadingMap() )
{
DEBUG_CRASH( ("Called MSG_NEW_GAME while game is not ready (inGame=%d, clearingData=%d, loadingMap=%d)",
isInGame(), isClearingGameData(), isLoadingMap()) );
break;
}
Copy link

Choose a reason for hiding this comment

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

Missing methods in Generals GameLogic

isClearingGameData() and isLoadingMap() are not declared in Generals/Code/GameEngine/Include/GameLogic/GameLogic.h. The Generals version only has isLoadingGame(). This means that when RETAIL_COMPATIBLE_CRC is set to 0, the Generals build will fail to compile.

The GeneralsMD (Zero Hour) version correctly declares both methods (isLoadingMap() on line 189 and isClearingGameData() on line 191 of its GameLogic.h), but the vanilla Generals version does not have these equivalents.

This appears to be tracked by the open PR Todo item ("Replicate in Generals"), but as-is the Generals build is broken when RETAIL_COMPATIBLE_CRC=0.

Prompt To Fix With AI
This is a comment left during a code review.
Path: Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp
Line: 417-422

Comment:
**Missing methods in Generals GameLogic**

`isClearingGameData()` and `isLoadingMap()` are not declared in `Generals/Code/GameEngine/Include/GameLogic/GameLogic.h`. The Generals version only has `isLoadingGame()`. This means that when `RETAIL_COMPATIBLE_CRC` is set to `0`, the Generals build will fail to compile.

The GeneralsMD (Zero Hour) version correctly declares both methods (`isLoadingMap()` on line 189 and `isClearingGameData()` on line 191 of its `GameLogic.h`), but the vanilla Generals version does not have these equivalents.

This appears to be tracked by the open PR Todo item ("Replicate in Generals"), but as-is the `Generals` build is broken when `RETAIL_COMPATIBLE_CRC=0`.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Author

Choose a reason for hiding this comment

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

May need a unify PR for this

Copy link

Choose a reason for hiding this comment

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

Is this right? It will fail to compile with that configuration?

Copy link

Choose a reason for hiding this comment

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

Yes, that's correct. The Generals build will fail to compile when RETAIL_COMPATIBLE_CRC=0.

Verified:

  • Generals GameLogic.h only has isLoadingGame()
  • GeneralsMD GameLogic.h has isLoadingMap() (line 189) and isClearingGameData() (line 191)

When RETAIL_COMPATIBLE_CRC=0, the compiler will encounter undefined method calls to isClearingGameData() and isLoadingMap() in the Generals build, causing compilation errors.

The methods need to be added to Generals/Code/GameEngine/Include/GameLogic/GameLogic.h (likely following the same pattern as GeneralsMD) to make this work.

@Skyaero42
Copy link

Moved behind RETAIL_COMPATIBLE_CRC in the case of any replays that have a forced dc bug.

Is this necessary? I was under the impression that it's not possible to recover from the DC bug.

The DC bug is game ending, but once someone leaves the other guy wins, and that game may then carry on for some more frames and be recorded in the replay. Since this PR changes something there it may then mismatch right at the end, I didn't test specifically though.

This should be an acceptable mismatch imho. The game has basically ended.

@stephanmeesters
Copy link
Author

Moved behind RETAIL_COMPATIBLE_CRC in the case of any replays that have a forced dc bug.

Is this necessary? I was under the impression that it's not possible to recover from the DC bug.

The DC bug is game ending, but once someone leaves the other guy wins, and that game may then carry on for some more frames and be recorded in the replay. Since this PR changes something there it may then mismatch right at the end, I didn't test specifically though.

This should be an acceptable mismatch imho. The game has basically ended.

The concern was that we would be wasting time on investigating replay mismatches due to anti-cheat fixes, see #2408. If you don't think that concern applies here then there will be no need for the RETAIL_COMPATIBLE_CRC

@xezon
Copy link

xezon commented Mar 12, 2026

Can you fix the Generals compile error?

Edit: Nevermind, I see you did a unify first.

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

Labels

Fix Is fixing something, but is not user facing Gen Relates to Generals Major Severity: Minor < Major < Critical < Blocker ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants