Skip to content

[GEN][ZH] Add Replay Simulation feature #923

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

Merged
merged 109 commits into from
Jun 22, 2025

Conversation

helmutbuhler
Copy link

@helmutbuhler helmutbuhler commented May 24, 2025

This PR adds the ability to simulate a replay via commandline and check for crashes and mismatches.

To simulate a replay, you must place it in the Replay folder and call the game like this:

generalszh.exe -headless -replay "Golden Replay #1.rep"

The console output will be something like this:

Simulating Replay "Golden Replay #1.rep"
Elapsed Time: 00:13 Game Time: 10:00/99:09
Elapsed Time: 00:28 Game Time: 20:00/99:09
Elapsed Time: 00:44 Game Time: 30:00/99:09
Elapsed Time: 01:01 Game Time: 40:00/99:09
Elapsed Time: 01:29 Game Time: 50:00/99:09
Elapsed Time: 01:49 Game Time: 60:00/99:09
Elapsed Time: 02:06 Game Time: 70:00/99:09
Elapsed Time: 02:25 Game Time: 80:00/99:09
Elapsed Time: 02:59 Game Time: 90:00/99:09
Elapsed Time: 03:14 Game Time: 99:09/99:09

As can be seen from that output, simulating a replay with gametime 99:09 takes about 3 minutes. Replaying it with fast-forward and setting Gentool-Speed to 100x takes about 4:15. For real replays with many players the difference can be much larger.

The exit code will be 1 if an error or mismatch occures and 0 otherwise.

You can also supply -replay multiple times in order to simulate multiple replays one after the other.

This PR also adds another commandline -jobs. When this option is supplied together with multiple replays to simulate, all replays are simulated in multiple worker processes in parallel. The number after -jobs indicates how many processes to run in parallel. Here is an example:

generalszh.exe -headless -jobs 2 -replay "Golden Replay #1.rep" -replay air_air.rep -replay air_china.rep

This will run the first two replays in parallel, and as soon as one is finished, run the third.

Simulating replays in separate processes can speed things up a lot if you want to check many replays. But it can also help prevent mismatches because right now the game doesn't clean everything up perfectly after a game. (Eventually we will want to fix those of course)

There are a few issues with this PR:

  • When calling a gui application via cmd, Windows by default hides the console output and runs the process in the background. This can be worked around with a .bat file like this:
START /B /W generalszh.exe -headless -replay "Golden Replay #1.rep" > sim_replay.log
echo %errorlevel%
PAUSE

Eventually we will want to add a cmake option to compile the game as a Console application.

There are two hacks at the end of SimulateReplayListSingleProcess to prevent crashes during gameshutdown. (It's quite possible that at least one of those will be fixed by #921, but I haven't checked yet)

There will be two followup PRs for this. One will add the ability to add a bunch of replays at once with a csv file. The other one will add a debug button in the ReplayMenu to simulate a replay without graphics (without having to use the commandline).

TODO

  • Replicate to Generals

# Conflicts:
#	GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp
#	GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp
# Conflicts:
#	GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp
# Conflicts:
#	GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp
#	GeneralsMD/Code/Main/WinMain.cpp
# Conflicts:
#	GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp
# Conflicts:
#	GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp
@@ -39,6 +39,10 @@ class ClientInstance
// Returns the instance name of the first game client.
static const char* getFirstInstanceName();

public:
static Bool s_multiInstance; // Allow multiple instances of the game
Copy link

Choose a reason for hiding this comment

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

This is wrong. These variables must not be writable after initialization is done. I have written the functions that need to be added in a previous comment. If you do not have time to do it, I can do that for you.

Copy link

Choose a reason for hiding this comment

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

Fixed

@@ -541,23 +541,35 @@ WindowMsgHandledType ScoreScreenSystem( GameWindow *window, UnsignedInt msg,
{
TheShell->pop();
TheCampaignManager->setCampaign(AsciiString::TheEmptyString);
if (!TheGlobalData->m_simulateReplays.empty())
{
TheWritableGlobalData->m_showReplayContinueButton = FALSE; // Tell GameEngine to quit.
Copy link

Choose a reason for hiding this comment

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

I do not understand this. Isn't TheGameEngine->setQuitting telling game engine to quit?

Copy link

Choose a reason for hiding this comment

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

Fixed

*/
virtual Int seek( Int bytes, seekMode mode = CURRENT ) = NULL; /**< Sets the file position of the next read/write operation. Returns the new file
* position as the number of bytes from the start of the file.
* Returns -1 if an error occured.
* Returns -1 if an error occurred.
Copy link

Choose a reason for hiding this comment

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

This change now touches a few unrelated EA comments. Is this intended?

Copy link

Choose a reason for hiding this comment

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

Fixed.

@xezon
Copy link

xezon commented Jun 20, 2025

Improvements appended.

I tested Score Screen with 2 replays in the Simulator, Continue and Exit button, and it worked correctly.

ClientInstance also worked.

@xezon
Copy link

xezon commented Jun 20, 2025

Zero Hour compile error fixed. Edit: Debug compile error fixed.

Generals fails compiling until the code is replicated to Generals.

@xezon xezon changed the title [ZH] Add Replay Simulation with Commandline [GEN][ZH] Add Replay Simulation feature Jun 20, 2025
@xezon xezon added the Gen Relates to Generals label Jun 20, 2025
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.

From my side things look ok now.

@helmutbuhler
Copy link
Author

I synced with Generals and also reverted one commit that caused the gametime in the last log to be zero.(see commit)

You can merge now.

@xezon
Copy link

xezon commented Jun 22, 2025

Very nice. I updated the command line arguments in the Pull Request description. I think it would be nice to have this feature description moved to our wiki.

@xezon xezon merged commit 5104ace into TheSuperHackers:main Jun 22, 2025
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Debug Is mostly debug functionality Enhancement Is new feature or request 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.

3 participants