Salmanko's Analysis of the Security System and Mismatch Mechanism in Generals: Zero Hour (GeneralsMD) #369
Replies: 12 comments 16 replies
-
Tarnished Knight here. Thanks for putting this together, Salmanko. It is nicely put together. I've read through it and would like to share my thoughts with a view on enhancing this already good document.
Side Note: If the program has CPU-dispatch functions then this could be the case, but I don't believe that is something we need to contend with here. CPU-dispatch functions have to be custom written and require a lot of technical knowledge and have quite an impact on the code. It also wouldn't be in the interest of the deterministic simulation to do so.
|
Beta Was this translation helpful? Give feedback.
-
There is some 'we must look' codes: "what if other part of the game's code change from the default setFPMode() to another mode while the game is trying to calculate the state's CRC?" This code is altering the FPU’s behavior in ways that can easily lead to subtle differences in numerical results between platforms or even runs. Here are some issues it can cause:
In summary, the forced settings can lead to non-deterministic behavior across different systems. This is likely why you see mismatches in calculations among players and consistent discrepancies when using Proton/Wine. Addressing these issues might require ensuring consistent floating point settings across all environments or revisiting the game’s reliance on precise floating point determinism. DirectX can mess with FPU configuration
About that directx fpu messing apparently this can be prevented too https://irrlicht.sourceforge.io/forum/viewtopic.php?t=8773 D3DCREATE_FPU_PRESERVE theMultFactor can be one of the mismatch causes
"what uses a lot of GameLogicRandomValueReal? AI" some maybe related URLs: llvm/llvm-project#44218 can softfloat be an alternative? needs testing |
Beta Was this translation helpful? Give feedback.
-
This may also be a useful source of information from the Recoil Engine: https://github.com/beyond-all-reason/spring/wiki/Determinism-In-Engine |
Beta Was this translation helpful? Give feedback.
-
Another consideration we need is floating point contraction, so we will likely need |
Beta Was this translation helpful? Give feedback.
-
With this anti cheat model someone can be able to fetch match data without getting a game hash mismatch ? Because if you only read the memory from the game but not alter the game stack of user instructions, you could in theory play the game and have total knowledge of units positions and total resources of every player. Probably back in the time with one core cpu that could be hard to do without getting caught. But now probably you could just set the game to use specific cores of the cpu and fetch the game data from other cpu cores. |
Beta Was this translation helpful? Give feedback.
-
I consider @augustresende 's idea about using softfloat one of the best for instant remedy. Also the Xfer routine seems to use the pure value stored in the float. Thus, I currently don't even see the "error accumulation" as the likely reason for the desyncs. My personal opinion, is to take one of these approaches, if breaking compat is considered acceptable:
Last step should be easily testable by excluding LSBs of the mantissa. void Xfer::xferReal( Real *realData )
{
uint32_t realDataInt = *(uint32_t*)realData;
realDataInt >> 13;
xferImplementation( &realDataInt, sizeof( Real ) );
} // end xferReal
Personally, I wouldn't bother with trying to get the floats calculate exactly the same on each client. |
Beta Was this translation helpful? Give feedback.
-
@augustresende @Fighter19 I just spoke to Kris Morness (former Generals + Zero Hour engineer) on LinkedIn and he said:
Hope this helps. Oh, and he's VERY generously offered to join us in Discord to help answering questions! I'll send him the link for Community Outpost Discord ASAP... wow! |
Beta Was this translation helpful? Give feedback.
-
Just to expand on this because this hasn't been mentioned here yet. There are more possibilities as to how mismatches can occur:
My guess is that these causes are mainly responsible for the mismatches that happen out there. |
Beta Was this translation helpful? Give feedback.
-
From kris.morness (Discord) "So I do have some general advice about desync fixing:
|
Beta Was this translation helpful? Give feedback.
-
Analysis of the Security System and Mismatch Mechanism in Generals: Zero Hour (GeneralsMD)
Cheat Detection in GeneralsMD
Generals: Zero Hour runs on a deterministic lockstep engine, meaning every computer runs exactly the same game events. If there's even a tiny difference in the game state between players, it's immediately caught as a "mismatch."
There isn't a separate anti-cheat system in the code that scans for cheaters or instantly bans them. Instead, the game heavily relies on checking for any differences in the game state as its main security measure. In the source code, you can see a structure for verifying state integrity using a CRC (Cyclic Redundancy Check). For example, the GameLogic class has variables like
m_CRC
andm_shouldValidateCRCs
to keep track of the CRC and its validation status.When you play online, the CRC check is turned on. Every machine periodically calculates a checksum representing the game state (like unit positions, health, resources, etc.) and compares it with everyone else's values. If any machine's checksum doesn't match, the game figures that something's off, shows a "Game has detected a mismatch" message, and ends the match.
A technical tidbit: the developers mentioned that differences in CPU settings (like enabling SSE2) can sometimes cause tiny calculation differences that end up triggering a mismatch. This shows just how sensitive the engine is to even the smallest discrepancies.
Keeping Game Data in Sync
The game's core idea is all about strict synchronization. Players send each other commands (like building or attacking), and every copy of the game executes the same command at the exact same moment. The game doesn't send the entire "game state" over the network—only the commands are shared. This means everyone needs to have the same initial state and data files to get the same results. For example, the code includes files like
MultiplayerSettings.h
and likely has some settings to ensure everyone is on the same page.There's also a module called
CopyProtection
in the code, which might be used to verify that everyone's using the same version of the game and hasn't messed with the main files (like checking the game disc or ensuring core files haven't been altered). In practice, if one player tries to use different data files (say, by modifying a unit file or using a custom map.ini that others don't have), it usually results in a mismatch as soon as the game starts or during play. For example, if someone tweaks the map.ini for a custom map but the other players don't have the same tweaks, the mismatch shows up immediately. So, the game makes sure that the content is exactly the same for everyone both before and during the match—any difference in data or game version will trigger a mismatch.In short, any tampering with internal values (money, unit health, POWER, etc.) on one machine without the others doing the same will cause that player's game simulation to drift away from everyone else's, and the CRC system catches this instantly, ending the match to keep things fair.
How Synchronization Works and How Mismatches Happen When Cheating
A mismatch happens when the synchronization mechanism fails—that is, when the game's "world state" isn't identical across all devices. Every player's computer runs its own state machine that represents everything happening in the game (units, positions, health, money, research, and so on). All these states start off exactly the same at the beginning of a match and only change when commands (actions) are executed that are shared over the network. The system assumes that as long as the inputs are identical and the engine behaves the same, the game states should remain in sync. If something goes off, it means there's either a cheat or a bug. The code likely calculates a periodic CRC of the game state (maybe every few frames or at specific events) and sends it between players or to the host. If any CRCs don't match, the game immediately ends with an error message.
There's also a hint from the developer community that enabling certain instruction sets (like SSE2) might help prevent mismatches caused by floating-point calculation errors. This suggests that technical issues causing mismatches might not only be from intentional cheating, but also from tiny differences in how different processors or settings (like the old x87 FPU mode versus SSE2) handle calculations. That's why the engine is designed to be as deterministic as possible. For example, traditional games use a shared "seed" for random number generation, so every random event is based on that same seed to ensure consistency. Any deviation—like one machine producing a different random value or order—will break the synchronization.
Reasons for Mismatches and Theoretical Ways to Bypass the System
Technically speaking, mismatches boil down to two main reasons:
Is it possible to bypass this detection system? Practically speaking, it's really hard unless all players are in on it. The power of the sync system is that it doesn't allow one machine to go off on its own; either all machines go off together (so no mismatch is detected) or the game ends. The only way to cheat without getting caught is to keep the cheating synchronized across all clients. For example, if every player (whether by agreement or trickery) uses the same modifications or files, then there won't be any differences in the calculated state—and the engine won't notice anything because everyone's doing the same shady stuff. Of course, this scenario only works if everyone is colluding.
Now, imagine a custom, "modified" map where the creator inserts code that gives them a secret advantage (like a hidden super unit). If all the other players end up playing on that same modified map without knowing it, then they're all basically following the same game rules (even if those rules are unbalanced), and no mismatch occurs because the engine sees everything as normal and in sync. This is more like fooling the game itself rather than outright breaking it. It's an unethical way to cheat, but it stays within the boundaries of the synchronization system—essentially cheating "by the rules" instead of breaking them—so it doesn't get flagged as a technical violation.
On the other hand, trying to bypass the system by modifying the code on one machine while leaving everyone else's untouched is nearly impossible without causing a mismatch, because at least one other machine will spot the CRC difference. There's even the theoretical idea of patching the game engine to disable the CRC check or ignore mismatches, but that isn't practical either. Even if you stop the mismatch alert on your own machine, the other players would notice and shut the match down, or your game would run out of sync on its own. Similarly, changing the calculation method to output the same CRC despite differences would require full knowledge of everyone else's game state—meaning the cheater would have to calculate "what the expected CRC is for the others" and then fake their data to match it. This isn't really doable unless you control all devices or break the communication encryption, if there is any.
So, the game's security is built on a simple yet effective principle: either everyone plays by the same rules and stays synchronized, or the game ends for everyone.
In a Nutshell
The GeneralsMD code includes cheat detection systems that ensure total synchronization among players. Any break in that sync—whether from cheating or a bug—results in a mismatch and ends the match. The only theoretical ways to get around it are either to cheat on all machines simultaneously (which isn't really a one-sided cheat) or to use cheats that don't affect the distributed game state. The latter includes things like map hacks that remove the fog of war for a player locally without changing any in-game objects—this kind of cheat gives you extra information but doesn't alter unit or resource states, so it doesn't trigger a mismatch (since what you see locally isn't part of the engine's calculations). Similarly, tweaks to the user interface or camera that let you see further don't impact synchronization. These methods are possible because GeneralsMD's security focuses on the core game state rather than what's shown on your screen. Still, they're much less impactful compared to direct cheats on resources or units, which the synchronization system quickly catches.
Source Code References
GameLogic.cpp
GameLogicDispatch.cpp
CrateSystem.cpp
PerfTimer.cpp
GameClient.cpp
((important note ))
There may be errors in expression or explanation. My friend Ak69 reviewed the text to make sure the meanings reached the reader well, and I apologize if there was no clarity in understanding or other terms. As for the analysis, it is early, meaning that the analysis was based on previous old experiences and new experiences with the source code. This is still the first part, and there are 3 other parts left, and two other practical experiences in a real match. I hope you will correct any mistakes for me.
Programs used in the experiment:
Relcass
x86dbg
OllyDbg
PE-bear
There are also programs I created that do not have a name -------- > ( 3 )
Beta Was this translation helpful? Give feedback.
All reactions