-
Notifications
You must be signed in to change notification settings - Fork 151
fix(mismatch): Fix uninitialized variable in AIPathfind and possible source of Mismatches in Retail-compatible builds #1748
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?
Conversation
|
So, I did some more investigation, but it doesn't look good.
Because the variables in question are so high up in the stack, none of these callers set that stackregion to a defined value. That's only done by callers of addObjectToPathfindMap and removeObjectFromPathfindMap, and that's a lot, around 30. So in order to set these variables to a defined value that remains compatible, we'd have to analyze all those callsites to find out how that stackregion is set in practice. That's too much work for me. If we merge this PR, the mismatches due to these uninitialized variables will completely stop for SH players. But some replays won't play back correctly anymore. I tested on 1800 replays, and 6 will mismatch with this merged. Assuming those replays are representative, this would then possibly increase the mismatch likelihood in games with both retail and SH players by 0.3%. If we don't merge this, replay playback will remain compatible, but the underlying issue will still result in mismatches in live games. It's hard to say for how many mismatches this issue is responsible, but I suspect that it's responsible for most of them. In all other cases where we fixed uninitialized variables, it was pretty well defined what value got used in the retail version. Here it's all over the place. If someone is interested, I can provide the replays that mismatch with this PR and I can also provide the code to debug this issue and the code to set the variables depending on the callsite (it's a bit tricky because the tiniest changes trigger incompatiblities due to different stacklayout and different float-calculations) If noone is interested in further improving the heuristics, I'd be in favor of merging this. For the reasons mentioned above, and also to easily rule this issue out when a mismatch happens in the future. |
|
Is there a way to estimate how many mismatches these uninitialized variable would cause originally? |
|
I cannot think of any way. |
|
If we have a way to detect all our clients in a Network match, then we could conditionally opt-in the fix on runtime. Same with the Pathfinder fixes. |
The only real experience we have so far are the Legi streams (Generals Online uses a non-vc6 build, so it doesn't count I think). He rarely mismatches. Often it is a player that didn't restart their game or used to play with mods. I don't think these uninitialized variables are that much of an issue. |
Greptile OverviewGreptile SummaryThis PR updates the initialization values for Key Changes
Technical AnalysisThe large initialization values mean Minor Issues
Confidence Score: 4/5
Important Files ChangedFile Analysis
Sequence DiagramsequenceDiagram
participant Caller
participant classifyFence
participant updateZonesForModify
Caller->>classifyFence: "Object, insert flag"
classifyFence->>classifyFence: "Initialize cellBounds.lo from pos"
alt RETAIL_COMPATIBLE_CRC
classifyFence->>classifyFence: "Set cellBounds.hi = {253961804, 4202797}"
Note over classifyFence: Empirical values to match retail stack
else Modern Build
classifyFence->>classifyFence: "Calculate cellBounds.hi from pos"
end
Note over classifyFence: Iterate fence cells
classifyFence->>classifyFence: "For each cell: calculate cx, cy"
classifyFence->>classifyFence: "Update obstacle map"
classifyFence->>classifyFence: "Update cellBounds.lo (min tracking)"
classifyFence->>classifyFence: "Update cellBounds.hi (max tracking)"
Note over classifyFence: With large init values, hi rarely updates
alt didAnything
classifyFence->>updateZonesForModify: "cellBounds, m_extent"
updateZonesForModify->>updateZonesForModify: "Clamp cellBounds to globalBounds"
Note over updateZonesForModify: Large values safely clamped to map extent
updateZonesForModify->>updateZonesForModify: "Process affected zone blocks"
updateZonesForModify-->>classifyFence: "Complete"
end
classifyFence-->>Caller: "Return"
|
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, 1 comment
| // To keep retail compatibility they need to be uninitialized in VC6 builds. | ||
| #if !(defined(_MSC_VER) && _MSC_VER < 1300) | ||
| #if RETAIL_COMPATIBLE_CRC | ||
| //CRCDEBUG_LOG(("Pathfinder::classifyFence - (%d,%d)", cellBounds.hi.x, cellBounds.hi.y)); |
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.
remove commented-out debug log or document if it should be kept for future investigation
| //CRCDEBUG_LOG(("Pathfinder::classifyFence - (%d,%d)", cellBounds.hi.x, cellBounds.hi.y)); |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
This is a comment left during a code review.
Path: GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp
Line: 3870:3870
Comment:
remove commented-out debug log or document if it should be kept for future investigation
```suggestion
```
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
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.
Yes can be removed.
Good effort; 6 out of 1800 seems acceptable to me, and making the behavior for SH clients fully deterministic is a good thing. |
xezon
left a 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.
I think this is the best course of action for this fix (and the Pathfinding + AIGroup fixes). Detect Game Version from all Peers in Network and Online Lobby, and then set the deterministic behavior if they all use Patched Clients. This state would also need to be written in the Replay however, so that the playback is correct as well. |
We should make the behavior for our clients fully deterministic, and try to use values that are least likely to cause a mismatch with retail. Anything more than that is a waste of time and effort, I think, especially stuff that needs to be sent across the network and logged in the replay file. |
|
Are we ok with 0.3% more mismatches? It is no ideal... |
Follow-up for #632
Some variables in AIPathfind are kept uninitialized to retain compatibility. But they also might be a source of mismatches. This PR sets them to a value found empirically that keeps compatibility with a lot of replays.
Still, a few replays still mismatch with the current value. I'll try to investigate more. Ideally we reverse-engineer the original binary and find out the source of the values that occupy the stack.