Skip to content

Commit 94b7715

Browse files
committed
feat(replay): Check CRC messages from all players in replays.
1 parent 091ab3d commit 94b7715

9 files changed

Lines changed: 329 additions & 154 deletions

File tree

Core/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2383,6 +2383,9 @@ bool GameLogic::onLogicCrc(MAYBE_UNUSED GameMessage *msg)
23832383
Player *msgPlayer = getMessagePlayer(msg);
23842384
if (TheNetwork)
23852385
{
2386+
if (TheNetwork->sawCRCMismatch())
2387+
return false;
2388+
23862389
Int slotIndex = -1;
23872390
for (Int i=0; i<MAX_SLOTS; ++i)
23882391
{
@@ -2401,26 +2404,42 @@ bool GameLogic::onLogicCrc(MAYBE_UNUSED GameMessage *msg)
24012404
#if defined(RTS_DEBUG)
24022405
// don't even put this in release, cause someone might hack it.
24032406
if (!TheDebugIgnoreSyncErrors)
2404-
{
2405-
#endif
2406-
m_shouldValidateCRCs = TRUE;
2407-
#if defined(RTS_DEBUG)
2408-
}
24092407
#endif
2408+
m_validationModeCRC = CRCMODE_NETWORK;
24102409
}
24112410

2412-
UnsignedInt newCRC = msg->getArgument(0)->integer;
2411+
const UnsignedInt newCRC = msg->getArgument(0)->integer;
24132412
//DEBUG_LOG(("Received CRC of %8.8X from %ls on frame %d", newCRC,
24142413
//msgPlayer->getPlayerDisplayName().str(), m_frame));
2414+
24152415
m_cachedCRCs[msgPlayer->getPlayerIndex()] = newCRC;
24162416
}
24172417
else if (TheRecorder && TheRecorder->isPlaybackMode())
24182418
{
2419-
UnsignedInt newCRC = msg->getArgument(0)->integer;
2420-
//DEBUG_LOG(("Saw CRC of %X from player %d. Our CRC is %X. Arg count is %d",
2419+
if (TheRecorder->sawCRCMismatch())
2420+
return false;
2421+
2422+
DEBUG_ASSERTCRASH(msg->getArgument(1)->boolean == msgPlayer->isLocalPlayer(),
2423+
("CRC message origin is unexpected; playback message argument doesn't match message player index"));
2424+
2425+
const UnsignedInt newCRC = msg->getArgument(0)->integer;
2426+
//DEBUG_LOG(("Saw CRC of %X from player %d. Our CRC is %X. Arg count is %d",
24212427
//newCRC, msgPlayer->getPlayerIndex(), getCRC(), msg->getArgumentCount()));
24222428

2423-
TheRecorder->handleCRCMessage(newCRC, msgPlayer->getPlayerIndex(), (msg->getArgument(1)->boolean));
2429+
if (msgPlayer->isLocalPlayer())
2430+
{
2431+
TheRecorder->handlePlaybackCRCMessage(newCRC);
2432+
}
2433+
else
2434+
{
2435+
#if defined(RTS_DEBUG)
2436+
// don't even put this in release, cause someone might hack it.
2437+
if (!TheDebugIgnoreSyncErrors)
2438+
#endif
2439+
m_validationModeCRC = CRCMODE_REPLAY;
2440+
2441+
TheRecorder->handlePlayerCRCMessage(msgPlayer->getPlayerIndex(), newCRC);
2442+
}
24242443
}
24252444

24262445
return true;

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

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

371+
Bool m_replayLocalPlayerCRC; ///< flag to validate CRC messages only from the player who recorded a replay
372+
371373
Bool m_loadScreenRender; ///< flag to disallow rendering of almost everything during a loadscreen
372374

373375
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: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,20 +67,50 @@ class RecorderClass : public SubsystemInterface
6767
class CRCInfo
6868
{
6969
public:
70+
struct MismatchData
71+
{
72+
MismatchData() :
73+
mismatched(false),
74+
playerIndex(0),
75+
queueSize(0),
76+
playbackCRC(0),
77+
playerCRC(0)
78+
{}
79+
80+
MismatchData(Byte playerIndex, UnsignedShort queueSize, UnsignedInt playbackCRC, UnsignedInt playerCRC) :
81+
mismatched(true),
82+
playerIndex(playerIndex),
83+
queueSize(queueSize),
84+
playbackCRC(playbackCRC),
85+
playerCRC(playerCRC)
86+
{}
87+
88+
Bool mismatched;
89+
Byte playerIndex;
90+
UnsignedShort queueSize;
91+
UnsignedInt playbackCRC;
92+
UnsignedInt playerCRC;
93+
};
94+
7095
CRCInfo();
71-
CRCInfo(UnsignedInt localPlayer, Bool isMultiplayer);
72-
void addCRC(UnsignedInt val);
73-
UnsignedInt readCRC();
74-
int GetQueueSize() const { return m_data.size(); }
75-
UnsignedInt getLocalPlayer() const { return m_localPlayer; }
76-
void setSawCRCMismatch() { m_sawCRCMismatch = TRUE; }
77-
Bool sawCRCMismatch() const { return m_sawCRCMismatch; }
96+
void init(Bool isMultiplayer, Int localPlayerIndex);
97+
void addPlaybackCRC(UnsignedInt val);
98+
void addPlayerCRC(Int playerIndex, UnsignedInt val);
99+
void setSawCRCMismatch();
100+
Bool sawCRCMismatch() const;
101+
Byte getLocalPlayerIndex() const;
102+
MismatchData getMismatchData();
78103

79104
protected:
80-
Bool m_sawCRCMismatch;
105+
UnsignedInt getLargestQueueSize() const;
106+
UnsignedInt getPlaybackCRC();
107+
81108
Bool m_skippedOne;
82-
UnsignedInt m_localPlayer;
83-
std::list<UnsignedInt> m_data;
109+
Bool m_sawCRCMismatch;
110+
Byte m_localPlayerIndex;
111+
std::list<UnsignedInt> m_playbackData;
112+
std::vector<UnsignedInt> m_playerData[MAX_PLAYER_COUNT];
113+
Bool m_inactivePlayer[MAX_PLAYER_COUNT];
84114
};
85115

86116
public:
@@ -110,8 +140,9 @@ class RecorderClass : public SubsystemInterface
110140
#endif
111141
Bool isPlaybackInProgress() const;
112142

113-
public:
114-
void handleCRCMessage(UnsignedInt newCRC, Int playerIndex, Bool fromPlayback);
143+
void handlePlaybackCRCMessage(UnsignedInt newCRC);
144+
void handlePlayerCRCMessage(Int playerIndex, UnsignedInt newCRC);
145+
void checkForMismatch();
115146

116147
// read in info relating to a replay, conditionally setting up m_file for playback
117148
struct ReplayHeader

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

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

373373
private:
374374

375+
enum CRCValidationMode CPP_11(: UnsignedByte)
376+
{
377+
CRCMODE_NONE,
378+
CRCMODE_NETWORK,
379+
CRCMODE_REPLAY,
380+
};
381+
375382
/**
376383
overrides to thing template buildable status. doesn't really belong here,
377384
but has to go somewhere. (srj)
@@ -391,7 +398,7 @@ class GameLogic : public SubsystemInterface, public Snapshot
391398
// CRC cache system -----------------------------------------------------------------------------
392399
UnsignedInt m_CRC; ///< Cache of previous CRC value
393400
std::map<Int, UnsignedInt> m_cachedCRCs; ///< CRCs we've seen this frame
394-
Bool m_shouldValidateCRCs; ///< Should we validate CRCs this frame?
401+
CRCValidationMode m_validationModeCRC; ///< (How) should we validate CRCs this frame?
395402
//-----------------------------------------------------------------------------------------------
396403
//Bool m_loadingScene;
397404
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 parseReplayLocalPlayerCRC(char* args[], int num)
469+
{
470+
TheWritableGlobalData->m_replayLocalPlayerCRC = TRUE;
471+
472+
return 1;
473+
}
474+
468475
Int parseXRes(char *args[], int num)
469476
{
470477
if (num > 1)
@@ -1166,6 +1173,9 @@ static CommandLineParam paramsForEngineInit[] =
11661173
// TheSuperHackers @feature xezon 03/08/2025 Force full viewport for 'Control Bar Pro' Addons like GenTool did it.
11671174
{ "-forcefullviewport", parseFullViewport },
11681175

1176+
// TheSuperHackers @feature Caball009 03/06/2026 Validate CRC messages only from the player who recorded a replay.
1177+
{ "-replayLocalPlayerCRC", parseReplayLocalPlayerCRC },
1178+
11691179
#if defined(RTS_DEBUG)
11701180
{ "-noaudio", parseNoAudio },
11711181
{ "-map", parseMapName },

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,7 @@ GlobalData::GlobalData()
10141014
m_playSizzle = TRUE;
10151015
m_afterIntro = FALSE;
10161016
m_allowExitOutOfMovies = FALSE;
1017+
m_replayLocalPlayerCRC = FALSE;
10171018
m_loadScreenRender = FALSE;
10181019

10191020
m_keyboardDefaultScrollFactor = m_keyboardScrollFactor = 0.5f;

0 commit comments

Comments
 (0)