Skip to content

refactor(saveload): Decouple result handling from game state - #3000

Open
bobtista wants to merge 6 commits into
TheSuperHackers:mainfrom
bobtista:bobtista/refactor/saveload-result-ui
Open

refactor(saveload): Decouple result handling from game state#3000
bobtista wants to merge 6 commits into
TheSuperHackers:mainfrom
bobtista:bobtista/refactor/saveload-result-ui

Conversation

@bobtista

@bobtista bobtista commented Jul 22, 2026

Copy link
Copy Markdown

Fixes #2998

GameState::saveGame and GameState::loadGame present their results directly through TheInGameUI and MessageBoxOk. This couples save serialization to interactive client UI and prevents non-interactive callers from handling results themselves.

Now save and load functions only return SaveCode results, while GUI callers present the existing success and error messages. Save functions can also return the resolved filename when one is generated automatically.

Todo:

  • Test creating and overwriting a normal save
  • Test successful and failed save loading
  • Test mission-save feedback
  • Replicate to Generals

@bobtista bobtista self-assigned this Jul 22, 2026
@bobtista bobtista added the Refactor Edits the code with insignificant behavior changes, is never user facing label Jul 22, 2026
@greptile-apps

greptile-apps Bot commented Jul 22, 2026

Copy link
Copy Markdown

Greptile Summary

Refactors save/load result presentation out of GameState and into GUI-layer helpers.

  • Adds optional resolved-filename outputs for normal and mission saves.
  • Moves existing success and error feedback into SaveLoadFeedback.
  • Updates popup and score-screen callers in both game targets.
  • Registers the new helper files in both GameEngine builds.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failures remain.

Important Files Changed

Filename Overview
Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp Removes direct UI presentation from persistence methods and returns structured save/load results with optional resolved filenames.
Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/SaveLoadFeedback.cpp Centralizes the existing GUI messages and dialogs associated with save/load result codes.
Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp Captures save/load results and delegates their presentation to the new GUI helper.
Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp Presents mission-save results from the score-screen caller.
GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp Mirrors the decoupled persistence result handling for Zero Hour.
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/SaveLoadFeedback.cpp Mirrors the centralized save/load GUI feedback implementation for Zero Hour.

Reviews (2): Last reviewed commit: "refactor(saveload): Decouple result hand..." | Re-trigger Greptile

@xezon xezon left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Some minor things left.

Comment thread Generals/Code/GameEngine/Include/Common/GameState.h Outdated
// engine resets
//
if (TheGameState->loadGame( *selectedGameInfo ) != SC_OK)
AsciiString filename = selectedGameInfo->filename;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

const ref

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

filename is a const copy rather than a const ref because loadGame() resets the engine and frees the listbox item data that selectedGameInfo points at, so the value has to outlive the call right?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ok if that is the case and selectedGameInfo will be dangling afterwards, then reduce the scope of selectedGameInfo.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

How about we copy the struct up front instead? eg

const AvailableGameInfo gameInfo = *getSelectedSaveFileInfo( listboxGames );
const SaveCode result = TheGameState->loadGame( gameInfo );
presentLoadResult( result, gameInfo.filename );

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Looks a bit much. And there are pointers inside which may be dangling then as well.

By reducing the scope of selectedGameInfo, it implicitly documents that the lifetime is capped and the compiler will error if it is dereferenced after the scope end. Alternative is to set selectedGameInfo to null, but then the compiler would still happily compile a dereference afterwards.

Comment thread Core/GameEngine/Source/GameClient/GUI/GUICallbacks/SaveLoadFeedback.cpp Outdated
Comment thread Core/GameEngine/Source/GameClient/GUI/GUICallbacks/SaveLoadFeedback.cpp Outdated
@bobtista
bobtista force-pushed the bobtista/refactor/saveload-result-ui branch from 8fa0016 to 7347dd0 Compare August 10, 2026 15:48
@xezon

xezon commented Aug 17, 2026

Copy link
Copy Markdown

There are 2 test cases left in the TODO description. Does it work?

@bobtista

Copy link
Copy Markdown
Author

There are 2 test cases left in the TODO description. Does it work?

Yes, I checked them

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

Labels

Refactor Edits the code with insignificant behavior changes, is never user facing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GameState save and load functions present results through client UI

2 participants