feat(cli): Play a replay file from the command line - #3227
Conversation
PR Summary by QodoAdd visual replay playback and absolute file loading to CLI
AI Description
Diagram
High-Level Assessment
Files changed (15)
|
|
| Filename | Overview |
|---|---|
| Core/GameEngine/Source/Common/CommandLine.cpp | Registers -loadreplay, validates the replay extension, and queues the supplied path for client startup. |
| Generals/Code/GameEngine/Source/Common/Recorder.cpp | Adds queued replay validation and shared absolute-or-relative replay path resolution for Generals. |
| GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp | Mirrors queued replay validation and path resolution for Zero Hour. |
| Generals/Code/GameEngine/Source/GameClient/GameClient.cpp | Starts a queued replay after the shell becomes available, following the existing queued-save lifecycle point. |
| GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp | Mirrors the post-intro queued replay startup integration for Zero Hour. |
| Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp | Updates replay metadata reads to use the new explicit filename and playback-mode API. |
| GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp | Mirrors the replay metadata API migration for Zero Hour. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
CLI["-loadreplay path"] --> Queue["Store startup replay path"]
Queue --> Init["Initialize client and shell"]
Init --> Preflight["Read replay header"]
Preflight --> Map{"Replay map available?"}
Map -- No --> Error["Show load error and remain in shell"]
Map -- Yes --> Playback["Open replay for playback"]
Playback --> Game["Run replay through normal client lifecycle"]
Game --> Menus["Return to menus"]
Reviews (4): Last reviewed commit: "feat(cli): Play a replay file from the c..." | Re-trigger Greptile
Code Review by Qodo
1. -ignoreReplaySyncErrors is unregistered
|
This is not entirely the case. |
Yeah that's true, replay without -headless already plays visually. Fixed the description. |
a82128f to
cbeeaa5
Compare
cbeeaa5 to
d35d0ff
Compare
d35d0ff to
00e5bc8
Compare
| if (!filename.endsWithNoCase(RecorderClass::getReplayExtention())) | ||
| { | ||
| printf("Invalid replay name \"%s\"\n", filename.str()); | ||
| exit(1); |
There was a problem hiding this comment.
If a replay does not end on .rep but would otherwise work, then it needs to fail and exit here? parseLoadSave does not have this.
Maybe it should also be removed from parseReplay? What happens if we try to load a replay that ends on .rep2 ? Does it load?
-replayalready plays visually when used without-headless, but it enters the replay-simulation workflow before the normal shell is shown and terminates the process when that workflow finishes. This is appropriate for batch simulation and synchronization checking, but not for an operating-system file handler whose playback should return to the menus.-loadreplay <file>instead plays one replay through the normal client lifecycle.loadQueuedReplayruns at the point-loadsavealready uses, once the client has initialized the shell, so the menus the playback returns to are on the stack.Absolute paths are opened in place while relative names still resolve from the Replay directory.
RecorderClass::getReplayPathForReaddoes that, mirroringGameState::getSaveGamePathForReadfrom #3226. Because this resolution is shared, existing-replayalso gains support for absolute paths.A replay that cannot be read, or whose map is unavailable, is rejected up front with the same message boxes the Replay menu shows, and the game stays on the main menu rather than failing deep in map loading.
Verified with failing files as controls so a pass is distinguishable from "the game started anyway":
\\localhost\C$\...)InGame:D9C721A5 Replay:D8A198C0 Frame:110)Todo:
z_generalsandg_generals)