bugfix(skirmish): Set consistent seed values for skirmish games to prevent mismatches for restarted skirmish games#2270
Conversation
Greptile Overview
|
| Filename | Overview |
|---|---|
| GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp | Caches skirmish seed before clearGameData and uses it in InitRandom() on restart; adds debug assertion tying TheSkirmishGameInfo presence to GAME_SKIRMISH. Potential issue: release builds can still dereference null if TheSkirmishGameInfo is unexpectedly null. |
| GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/SkirmishGameOptionsMenu.cpp | Moves InitGameLogicRandom() seeding into branches: skirmish uses TheSkirmishGameInfo seed, non-skirmish forces seed 0 for solo campaign maps selectable via skirmish menu. Change appears consistent with intended deterministic non-skirmish behavior. |
Sequence Diagram
sequenceDiagram
participant UI as GUI (Quit/Skirmish Menus)
participant SGI as TheSkirmishGameInfo
participant GL as TheGameLogic
participant RNG as RandomValue (InitRandom/InitGameLogicRandom)
participant REC as TheRecorder
participant MS as TheMessageStream
Note over UI: Starting game from Skirmish menu
UI->>SGI: startGame(0)
UI->>UI: determine isSkirmish via MapMetaData.m_isMultiplayer
alt isSkirmish
UI->>RNG: InitGameLogicRandom(SGI.getSeed())
UI->>MS: append MSG_NEW_GAME(GAME_SKIRMISH, ...)
else non-skirmish scenario map
UI->>RNG: InitGameLogicRandom(0)
UI->>MS: append MSG_NEW_GAME(GAME_SINGLE_PLAYER, ...)
end
Note over UI: Restarting from Quit menu
UI->>UI: gameMode = GL.getGameMode()
UI->>UI: seed = (SGI != nullptr ? SGI.getSeed() : 0)
UI->>GL: clearGameData(FALSE)
UI->>REC: getCurrentReplayFilename()
alt replayFile not empty
UI->>REC: playbackFile(replayFile)
else new game restart
UI->>MS: append MSG_NEW_GAME(gameMode, diff, rankPoints, fps)
UI->>RNG: InitRandom(seed)
end
Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp
Outdated
Show resolved
Hide resolved
Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp
Outdated
Show resolved
Hide resolved
Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp
Show resolved
Hide resolved
Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp
Outdated
Show resolved
Hide resolved
|
I did find a single case where the game would start a new |
6dacb8a to
45fa3ea
Compare
|
EDIT: Maybe I didn't properly explain what I documented here. The findings below explain how the seed value is set for different game modes. I noted the execution flow with the current changes of this PR. Here are the results of my testing with this PR: 1. Campaign ( 2. Challenges ( 3. Skirmish, single player map / scenario ( 4. Skirmish, multiplayer map ( |
|
If we ever to enable replays for campaign / challenge / single player scenarios, we may need to verify that this issue doesn't happen there as well. I don't see anything that could still present an issue in that regard, though. |
|
|
||
| if (isSkirmish) | ||
| { | ||
| InitGameLogicRandom(TheSkirmishGameInfo->getSeed()); |
There was a problem hiding this comment.
Why do this change? Shouldn't it be responsibility of whoever set seed in TheSkirmishGameInfo? Now this responsibility has shifted to this function instead.
There was a problem hiding this comment.
This line was only changed because of the change below this for single player maps.
With regard to the setting of the seed value, that's already part of the GameInfo constructor.
There was a problem hiding this comment.
The restart function sets 0 as seed value for skirmish single player maps, so this function should as well. The restart function cannot use the seed value for skirmish single player maps because TheSkirmishGameInfo is null.
There was a problem hiding this comment.
I do not understand. Does this mean, when !isSkirmish, then TheSkirmishGameInfo not null here, but will be null on mission restart?
If yes, does that not indicate a bug elsewhere?
There was a problem hiding this comment.
TheSkirmishGameInfo is never null in reallyDoStart, otherwise it'd have crashed in the original code.
It's null in restartMissionMenu for skirmish single player mission. I agree that's unexpected, but it works without apparent issue.
There was a problem hiding this comment.
Ok that is very strange. Perhaps that warrants a follow up investigation?
| //else | ||
| // InitGameLogicRandom(GameClientRandomValue(0, INT_MAX - 1)); | ||
|
|
||
| InitRandom(seed); |
There was a problem hiding this comment.
According to your list:
reallyDoStart -> InitGameLogicRandom( TheSkirmishGameInfo->getSeed() )
Wouldn't this mean that this needs to do:
InitGameLogicRandom( TheSkirmishGameInfo->getSeed() ) then?
There was a problem hiding this comment.
Isn't that what I'm doing here?
There was a problem hiding this comment.
Currently:
InitRandom( TheSkirmishGameInfo->getSeed() );
Expected:
InitGameLogicRandom( TheSkirmishGameInfo->getSeed() );
There was a problem hiding this comment.
That's a good point. It's not clear why the former is used and sometimes the latter. The important point, though, is that InitRandom also sets the logical seed values, which are the only seed values that are part of the CRC computation.
I think we should make reallyDoStart use InitRandom, and perhaps check other places if they should also use InitRandom instead of InitGameLogicRandom.
There was a problem hiding this comment.
Ok maybe make a follow up investigation. Is not directly related to this fix.
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp
Show resolved
Hide resolved
3208aa9 to
4b6d3dc
Compare
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp
Show resolved
Hide resolved
| // TheSuperHackers @bugfix Caball009 07/02/2026 Reuse the previous seed value for the new skirmish match to prevent mismatches. | ||
| // Campaign, challenge, and skirmish single-player scenarios all use GAME_SINGLE_PLAYER and are expected to use 0 as seed value. | ||
| DEBUG_ASSERTCRASH((TheSkirmishGameInfo != nullptr) == (gameMode == GAME_SKIRMISH), ("Unexpected game mode on map / mission restart")); | ||
| const UnsignedInt seed = (TheSkirmishGameInfo) ? TheSkirmishGameInfo->getSeed() : 0; |
| //else | ||
| // InitGameLogicRandom(GameClientRandomValue(0, INT_MAX - 1)); | ||
|
|
||
| InitRandom(seed); |
There was a problem hiding this comment.
Ok maybe make a follow up investigation. Is not directly related to this fix.
This PR makes the seed value consistent for started vs. restarted skirmish games, and fixes a bug that causes replays of restarted skirmish game to always mismatch.
TODO: