feat(replay): Check CRC messages from all players in replays#2649
feat(replay): Check CRC messages from all players in replays#2649Caball009 wants to merge 5 commits into
Conversation
6b0ab7d to
b2e84cd
Compare
ea2f91d to
2bf436a
Compare
94b7715 to
9526f3f
Compare
beaa007 to
3a28eae
Compare
|
| Filename | Overview |
|---|---|
| GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp | Core of the feature: splits CRCInfo into separate playback and per-player queues, adds generateMismatchData() to detect and attribute mismatches across all players; logic appears correct. |
| GeneralsMD/Code/GameEngine/Include/Common/Recorder.h | Adds MismatchData struct with Byte-backed sentinel values (PLAYER_PLAYBACK=-1, PLAYER_UNKNOWN=-2); replaces single queue API with separate handlePlaybackCRCMessage / handlePlayerCRCMessage / checkForMismatch; well-designed interface. |
| Core/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp | Routes local-player CRCs to handlePlaybackCRCMessage and non-local to handlePlayerCRCMessage, introduces CRCMODE_REPLAY, adds early-exit guard for already-detected mismatches; correct under the confirmed invariant that all replay-file messages have isLocalPlayer()==false. |
| GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp | Replaces Bool m_shouldValidateCRCs with a CRCValidationMode enum (NONE/NETWORK/REPLAY), extracts checkForMismatch() for the network path, and dispatches to TheRecorder->checkForMismatch() for replays; clean refactor. |
| GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp | Adds -replayLocalPlayerCRC flag that sets m_replayLocalPlayerCRC; straightforward opt-out for developers who rely on old per-recorder-player behaviour. |
| GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h | Adds Bool m_replayLocalPlayerCRC field; correctly initialised to FALSE in GlobalData.cpp. |
| GeneralsMD/Code/GameEngine/Include/Common/Player.h | Makes getPlayerDisplayName() const-qualified to allow calling from const Player* contexts; safe, non-functional change. |
| GeneralsMD/Code/GameEngine/Include/GameLogic/GameLogic.h | Adds CRCValidationMode enum and checkForMismatch() declaration; replaces Bool flag with typed enum for clearer semantics. |
| GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp | Initialises m_replayLocalPlayerCRC to FALSE; trivial, correct change. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant GL as GameLogic::update()
participant CL as TheCommandList
participant D as onLogicCrc()
participant R as TheRecorder (CRCInfo)
participant UI as TheInGameUI
GL->>CL: "appendMessage(MSG_LOGIC_CRC, liveCRC, isPlayback=true)"
Note over CL: Replay file messages also in list
GL->>GL: processCommandList()
loop Each MSG_LOGIC_CRC in list
GL->>D: logicMessageDispatcher(msg)
alt msgPlayer.isLocalPlayer() — live observer CRC
D->>R: handlePlaybackCRCMessage(liveCRC)
Note over R: pushes to m_playbackData
else recorded player CRC from replay file
D->>R: handlePlayerCRCMessage(playerIndex, recordedCRC)
Note over R: pushes to m_playerData[playerIndex]
D->>GL: "set m_validationModeCRC = CRCMODE_REPLAY"
end
end
alt "m_validationModeCRC == CRCMODE_REPLAY"
GL->>R: checkForMismatch()
R->>R: generateMismatchData()
Note over R: pops m_playbackData, compares vs each m_playerData[i]
alt mismatch detected
R->>UI: message(GUI:CRCMismatch)
R->>UI: message(details: frame, playbackCRC, playerCRC, playerName)
R->>GL: setGamePaused()
R->>R: setSawCRCMismatch()
end
end
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant GL as GameLogic::update()
participant CL as TheCommandList
participant D as onLogicCrc()
participant R as TheRecorder (CRCInfo)
participant UI as TheInGameUI
GL->>CL: "appendMessage(MSG_LOGIC_CRC, liveCRC, isPlayback=true)"
Note over CL: Replay file messages also in list
GL->>GL: processCommandList()
loop Each MSG_LOGIC_CRC in list
GL->>D: logicMessageDispatcher(msg)
alt msgPlayer.isLocalPlayer() — live observer CRC
D->>R: handlePlaybackCRCMessage(liveCRC)
Note over R: pushes to m_playbackData
else recorded player CRC from replay file
D->>R: handlePlayerCRCMessage(playerIndex, recordedCRC)
Note over R: pushes to m_playerData[playerIndex]
D->>GL: "set m_validationModeCRC = CRCMODE_REPLAY"
end
end
alt "m_validationModeCRC == CRCMODE_REPLAY"
GL->>R: checkForMismatch()
R->>R: generateMismatchData()
Note over R: pops m_playbackData, compares vs each m_playerData[i]
alt mismatch detected
R->>UI: message(GUI:CRCMismatch)
R->>UI: message(details: frame, playbackCRC, playerCRC, playerName)
R->>GL: setGamePaused()
R->>R: setSawCRCMismatch()
end
end
Reviews (8): Last reviewed commit: "Added '-replayLocalPlayerCRC' command li..." | Re-trigger Greptile
|
@greptileai re-review this pull request now that the previous review concerns are addressed. |
3a28eae to
8907b92
Compare
| #if RETAIL_COMPATIBLE_CRC | ||
| // TheSuperHackers @tweak Caball009 21/06/2026 Playback argument serves no purpose anymore | ||
| // other than to be able play replays from newer retail compatible builds on older builds or retail. | ||
| const bool isPlayback = (TheRecorder && TheRecorder->isPlaybackMode()); | ||
| msg->appendBooleanArgument(isPlayback); | ||
| #endif |
There was a problem hiding this comment.
Perhaps this deserves special attention during reviewing due to potential issues wrt backward compatibility of the replay format.
cbdbfa6 to
f521203
Compare
f521203 to
eec0f3d
Compare
The original behavior wrt replays is that only the CRC messages from the player that recorded the replay are checked. This means that players can experience a 'live' mismatch, but the game won't show the mismatch for their replay if they didn't cause it.
That's not a big deal for regular players but still unexpected behavior. For developers it can be very useful to get the exact frame the mismatch happens, regardless of which player recorded the replay or which player caused the mismatch.
This PR changes the default behavior so the CRC messages from all players are checked. I also added an opt-out command line
-replayLocalPlayerCRCbecause developers with large replay collections may want to rely on the original behavior. As a small bonus the name of the mismatching player is now displayed on screen if it's possible to determine which player is responsible.See commits for cleaner diff.
Before:
replay_crc_before.mp4
After:
replay_crc_after.mp4
TODO: