-
Notifications
You must be signed in to change notification settings - Fork 151
bugfix(savegame): Fix crashes when saving a game in headless mode #2139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
bugfix(savegame): Fix crashes when saving a game in headless mode #2139
Conversation
|
| Filename | Overview |
|---|---|
| GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp | Guards UI calls with headless checks, skips visual-only blocks during save/load in headless mode, handles null snapshots |
Sequence Diagram
sequenceDiagram
participant Headless as Headless Replay
participant SaveGame as GameState::saveGame()
participant XferSave as GameState::xferSaveData()
participant UI as UI Components
participant Visual as Visual Subsystems
Headless->>SaveGame: Trigger save in headless mode
SaveGame->>SaveGame: Check TheGlobalData && !m_headless
alt Headless Mode Active
SaveGame-->>UI: Skip error message
else Normal Mode
SaveGame->>UI: Show error/success messages
end
SaveGame->>XferSave: xferSaveData(xfer, SNAPSHOT_SAVELOAD)
loop For each snapshot block
XferSave->>XferSave: Check if snapshot == nullptr
alt Snapshot is null
XferSave-->>XferSave: Skip block (headless scenario)
end
XferSave->>XferSave: Check TheGlobalData && m_headless
alt Headless Mode + Visual Block
Note over XferSave: Skip CHUNK_TerrainVisual,<br/>CHUNK_TacticalView,<br/>CHUNK_ParticleSystem,<br/>CHUNK_GhostObject
XferSave-->>XferSave: Skip visual-only block
else Save block normally
XferSave->>Visual: Save visual data
end
end
XferSave-->>SaveGame: Save complete
SaveGame->>SaveGame: Check TheGlobalData && !m_headless
alt Headless Mode
SaveGame-->>Headless: Return SC_OK (no UI message)
else Normal Mode
SaveGame->>UI: Show "Game Save Complete" message
SaveGame-->>Headless: Return SC_OK
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additional Comments (1)
-
GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp, line 1494 (link)logic: Loading path needs nullptr check for
blockInfo->snapshot. If a save created in headless mode (missing visual blocks) is loaded in normal mode where visual subsystems exist but their snapshot pointers could be nullptr, this could crash.
1 file reviewed, 1 comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 file reviewed, 6 comments
GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp
Outdated
Show resolved
Hide resolved
| if( TheGlobalData->m_headless && | ||
| (blockName.compareNoCase( "CHUNK_TerrainVisual" ) == 0 || | ||
| blockName.compareNoCase( "CHUNK_TacticalView" ) == 0 || | ||
| blockName.compareNoCase( "CHUNK_ParticleSystem" ) == 0 || | ||
| blockName.compareNoCase( "CHUNK_GhostObject" ) == 0) ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: Check if TheGlobalData is null before dereferencing. Also, CHUNK_GhostObject being skipped may cause issues when loading in normal mode.
| if( TheGlobalData->m_headless && | |
| (blockName.compareNoCase( "CHUNK_TerrainVisual" ) == 0 || | |
| blockName.compareNoCase( "CHUNK_TacticalView" ) == 0 || | |
| blockName.compareNoCase( "CHUNK_ParticleSystem" ) == 0 || | |
| blockName.compareNoCase( "CHUNK_GhostObject" ) == 0) ) | |
| // Skip visual-only blocks when saving in headless mode | |
| if( TheGlobalData && TheGlobalData->m_headless && | |
| (blockName.compareNoCase( "CHUNK_TerrainVisual" ) == 0 || | |
| blockName.compareNoCase( "CHUNK_TacticalView" ) == 0 || | |
| blockName.compareNoCase( "CHUNK_ParticleSystem" ) == 0 || | |
| blockName.compareNoCase( "CHUNK_GhostObject" ) == 0) ) |
Are ghost objects purely visual, or do they contain gameplay state that would be needed when loading the save in normal mode?
Prompt To Fix With AI
This is a comment left during a code review.
Path: GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp
Line: 1387:1391
Comment:
**logic:** Check if `TheGlobalData` is null before dereferencing. Also, `CHUNK_GhostObject` being skipped may cause issues when loading in normal mode.
```suggestion
// Skip visual-only blocks when saving in headless mode
if( TheGlobalData && TheGlobalData->m_headless &&
(blockName.compareNoCase( "CHUNK_TerrainVisual" ) == 0 ||
blockName.compareNoCase( "CHUNK_TacticalView" ) == 0 ||
blockName.compareNoCase( "CHUNK_ParticleSystem" ) == 0 ||
blockName.compareNoCase( "CHUNK_GhostObject" ) == 0) )
```
Are ghost objects purely visual, or do they contain gameplay state that would be needed when loading the save in normal mode?
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ghost objects are purely visual in terms of debugging a game crash or mismatch - our CI uses headless replays to check for these. If a player wants to load a checkpoint save created in headless, in theory yes they may be missing ghost objects. We could construct them on load if needed, but I think that's out of scope for this PR.
| if( blockInfo->snapshot == nullptr ) | ||
| { | ||
| DEBUG_LOG(("Skipping block '%s' because snapshot is nullptr", blockInfo->blockName.str())); | ||
| Int dataSize = xfer->beginBlock(); | ||
| xfer->skip( dataSize ); | ||
| continue; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Verify the save file is backwards-compatible when visual blocks are omitted in headless saves. The load path handles missing blocks gracefully, but ensure normal-mode loads of headless-created saves work correctly (as noted in the PR TODO).
Prompt To Fix With AI
This is a comment left during a code review.
Path: GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp
Line: 1488:1494
Comment:
**style:** Verify the save file is backwards-compatible when visual blocks are omitted in headless saves. The load path handles missing blocks gracefully, but ensure normal-mode loads of headless-created saves work correctly (as noted in the PR TODO).
How can I resolve this? If you propose a fix, please make it concise.|
Added the nullcheck that greptile requested. |
Summary
Testing
Todo