Skip to content

Commit b2e84cd

Browse files
committed
feat(replay): Check CRC messages from all players in replays.
1 parent 471dc26 commit b2e84cd

9 files changed

Lines changed: 359 additions & 198 deletions

File tree

GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,8 @@ class GlobalData : public SubsystemInterface
367367
Bool m_afterIntro; ///< we need to tell the game our intro is done
368368
Bool m_allowExitOutOfMovies; ///< flag to allow exit out of movies only after the Intro has played
369369

370+
Bool m_replayOnlyCheckLocalPlayer; ///< flag to check only the CRC messages from the player that recorded a replay
371+
370372
Bool m_loadScreenRender; ///< flag to disallow rendering of almost everything during a loadscreen
371373

372374
Real m_keyboardScrollFactor; ///< Factor applied to game scrolling speed via keyboard scrolling

GeneralsMD/Code/GameEngine/Include/Common/Player.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ class Player : public Snapshot
221221

222222
void deletePlayerAI();
223223

224-
UnicodeString getPlayerDisplayName() { return m_playerDisplayName; }
224+
UnicodeString getPlayerDisplayName() const { return m_playerDisplayName; }
225225
NameKeyType getPlayerNameKey() const { return m_playerNameKey; }
226226

227227
AsciiString getSide() const { return m_side; }

GeneralsMD/Code/GameEngine/Include/Common/Recorder.h

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,61 @@ enum RecorderModeType CPP_11(: Int) {
5353
RECORDERMODETYPE_NONE // this is a valid state to be in on the shell map, or in saved games
5454
};
5555

56-
class CRCInfo;
56+
// TheSuperHackers @info helmutbuhler 03/04/2025
57+
// Some info about CRC:
58+
// In each game, each peer periodically calculates a CRC from the local gamestate and sends that
59+
// in a message to all peers (including itself) so that everyone can check that the crc is synchronous.
60+
// In a network game, there is a delay between sending the CRC message and receiving it. This is
61+
// necessary because if you were to wait each frame for all messages from all peers, things would go
62+
// horribly slow.
63+
// But this delay is not a problem for CRC checking because everyone receives the CRC in the same frame
64+
// and every peer just makes sure all the received CRCs are equal.
65+
// While playing replays, this is a problem however: The CRC messages in the replays appear on the frame
66+
// they were received, which can be a few frames delayed if it was a network game. And if we were to
67+
// compare those with the local gamestate, they wouldn't sync up.
68+
// So, in order to fix this, we need to queue up our local CRCs,
69+
// so that we can check it with the crc messages that come later.
70+
// This class is basically that queue.
71+
class CRCInfo
72+
{
73+
public:
74+
struct MismatchData
75+
{
76+
MismatchData() :
77+
mismatched(FALSE),
78+
playerIndex(0),
79+
queueSize(0),
80+
playbackCRC(0),
81+
playerCRC(0)
82+
{}
83+
84+
Bool mismatched;
85+
Byte playerIndex;
86+
UnsignedShort queueSize;
87+
UnsignedInt playbackCRC;
88+
UnsignedInt playerCRC;
89+
};
90+
91+
CRCInfo();
92+
void init(Bool isMultiplayer, Int localPlayerIndex);
93+
void addPlaybackCRC(UnsignedInt val);
94+
void addPlayerCRC(Int playerIndex, UnsignedInt val);
95+
void setSawCRCMismatch();
96+
Bool sawCRCMismatch() const;
97+
Byte getLocalPlayerIndex() const;
98+
MismatchData getMismatchData();
99+
100+
static Int getPlayerIndexOffset();
101+
102+
protected:
103+
UnsignedInt getLargestPlayerQueueSize() const;
104+
105+
Bool m_skippedOne;
106+
Bool m_sawCRCMismatch;
107+
Byte m_localPlayerIndex;
108+
std::list<UnsignedInt> m_playbackData;
109+
std::list<UnsignedInt> m_playerData[MAX_SLOTS];
110+
};
57111

58112
class RecorderClass : public SubsystemInterface {
59113
public:
@@ -84,10 +138,12 @@ class RecorderClass : public SubsystemInterface {
84138
#endif
85139
Bool isPlaybackInProgress() const;
86140

87-
public:
88-
void handleCRCMessage(UnsignedInt newCRC, Int playerIndex, Bool fromPlayback);
141+
void handlePlaybackCRCMessage(UnsignedInt newCRC);
142+
void handlePlayerCRCMessage(Int playerIndex, UnsignedInt newCRC);
143+
void checkForMismatch();
144+
89145
protected:
90-
CRCInfo *m_crcInfo;
146+
CRCInfo m_crcInfo;
91147
public:
92148

93149
// read in info relating to a replay, conditionally setting up m_file for playback

GeneralsMD/Code/GameEngine/Include/GameLogic/GameLogic.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,13 @@ class GameLogic : public SubsystemInterface, public Snapshot
293293

294294
private:
295295

296+
enum CRCValidationMode CPP_11(: UnsignedByte)
297+
{
298+
CRCMODE_NONE,
299+
CRCMODE_NETWORK,
300+
CRCMODE_REPLAY,
301+
};
302+
296303
/**
297304
overrides to thing template buildable status. doesn't really belong here,
298305
but has to go somewhere. (srj)
@@ -312,7 +319,7 @@ class GameLogic : public SubsystemInterface, public Snapshot
312319
// CRC cache system -----------------------------------------------------------------------------
313320
UnsignedInt m_CRC; ///< Cache of previous CRC value
314321
std::map<Int, UnsignedInt> m_cachedCRCs; ///< CRCs we've seen this frame
315-
Bool m_shouldValidateCRCs; ///< Should we validate CRCs this frame?
322+
CRCValidationMode m_shouldValidateCRCs; ///< Should we validate CRCs this frame?
316323
//-----------------------------------------------------------------------------------------------
317324
//Bool m_loadingScene;
318325
Bool m_loadingMap;

GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,13 @@ Int parseJobs(char *args[], int num)
465465
return 1;
466466
}
467467

468+
Int parseReplayCRC(char* args[], int num)
469+
{
470+
TheWritableGlobalData->m_replayOnlyCheckLocalPlayer = TRUE;
471+
472+
return 1;
473+
}
474+
468475
Int parseXRes(char *args[], int num)
469476
{
470477
if (num > 1)
@@ -1177,6 +1184,9 @@ static CommandLineParam paramsForEngineInit[] =
11771184
// TheSuperHackers @feature xezon 03/08/2025 Force full viewport for 'Control Bar Pro' Addons like GenTool did it.
11781185
{ "-forcefullviewport", parseFullViewport },
11791186

1187+
// TheSuperHackers @feature Caball009 24/04/2026 Enable checking only the CRC messages from the player that recorded a replay.
1188+
{ "-replayLocalPlayerCRC", parseReplayCRC },
1189+
11801190
#if defined(RTS_DEBUG)
11811191
{ "-noaudio", parseNoAudio },
11821192
{ "-map", parseMapName },

GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,7 @@ GlobalData::GlobalData()
10091009
m_playSizzle = TRUE;
10101010
m_afterIntro = FALSE;
10111011
m_allowExitOutOfMovies = FALSE;
1012+
m_replayOnlyCheckLocalPlayer = FALSE;
10121013
m_loadScreenRender = FALSE;
10131014

10141015
m_keyboardDefaultScrollFactor = m_keyboardScrollFactor = 0.5f;

0 commit comments

Comments
 (0)