From 486b8138d6aa970e9b8c19fbba29c44948368559 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Thu, 27 Aug 2026 12:30:10 -0400 Subject: [PATCH 1/3] feat(cli): Add -ignoreReplaySyncErrors for diagnostic replay playback --- Core/GameEngine/Include/Common/CRCDebug.h | 1 + Core/GameEngine/Source/Common/CommandLine.cpp | 11 +++++++++++ Generals/Code/GameEngine/Source/Common/Recorder.cpp | 12 ++++++++++++ .../Code/GameEngine/Source/Common/Recorder.cpp | 12 ++++++++++++ 4 files changed, 36 insertions(+) diff --git a/Core/GameEngine/Include/Common/CRCDebug.h b/Core/GameEngine/Include/Common/CRCDebug.h index b4bda92b2c6..0a4511b33de 100644 --- a/Core/GameEngine/Include/Common/CRCDebug.h +++ b/Core/GameEngine/Include/Common/CRCDebug.h @@ -126,3 +126,4 @@ extern Int NET_CRC_INTERVAL; extern Int REPLAY_CRC_INTERVAL; extern Bool TheDebugIgnoreSyncErrors; +extern Bool TheDebugIgnoreReplaySyncErrors; diff --git a/Core/GameEngine/Source/Common/CommandLine.cpp b/Core/GameEngine/Source/Common/CommandLine.cpp index 6e25414102b..c2fcb5de13b 100644 --- a/Core/GameEngine/Source/Common/CommandLine.cpp +++ b/Core/GameEngine/Source/Common/CommandLine.cpp @@ -40,6 +40,7 @@ Bool TheDebugIgnoreSyncErrors = FALSE; +Bool TheDebugIgnoreReplaySyncErrors = FALSE; extern Int DX8Wrapper_PreserveFPU; #ifdef DEBUG_CRC @@ -758,6 +759,15 @@ Int parseLoadReplay(char *args[], int num) return 1; } +// TheSuperHackers @feature bobtista 08/08/2026 Let diagnostic replay playback continue past a CRC +// mismatch without the UI report and pause that normal playback uses. +Int parseIgnoreReplaySyncErrors(char *args[], int) +{ + TheDebugIgnoreReplaySyncErrors = true; + + return 1; +} + //============================================================================= //============================================================================= @@ -1210,6 +1220,7 @@ static CommandLineParam paramsForEngineInit[] = // TheSuperHackers @feature bobtista 08/08/2026 Play a replay file from the command line. { "-loadreplay", parseLoadReplay }, + { "-ignoreReplaySyncErrors", parseIgnoreReplaySyncErrors }, // TheSuperHackers @feature xezon 03/08/2025 Force full viewport for 'Control Bar Pro' Addons like GenTool did it. { "-forcefullviewport", parseFullViewport }, diff --git a/Generals/Code/GameEngine/Source/Common/Recorder.cpp b/Generals/Code/GameEngine/Source/Common/Recorder.cpp index 91c44c437c8..f1785eb645d 100644 --- a/Generals/Code/GameEngine/Source/Common/Recorder.cpp +++ b/Generals/Code/GameEngine/Source/Common/Recorder.cpp @@ -1000,6 +1000,18 @@ void RecorderClass::handleCRCMessage(UnsignedInt newCRC, Int playerIndex, Bool f // playbackCRC, newCRC, TheGameLogic->getFrame()-m_crcInfo.GetQueueSize()-1, playerIndex)); if (TheGameLogic->getFrame() > 0 && newCRC != playbackCRC && !m_crcInfo.sawCRCMismatch()) { + // TheSuperHackers @feature bobtista 08/08/2026 Diagnostic playback continues past a mismatch + // without the UI report and the pause that normal playback uses. + if (TheDebugIgnoreReplaySyncErrors) + { + const UnsignedInt ignoredFrame = TheGameLogic->getFrame() - m_crcInfo.GetQueueSize() - 1; + DEBUG_LOG(("Replay CRC mismatch ignored\nInGame:%8.8X Replay:%8.8X\nFrame:%d", + playbackCRC, newCRC, ignoredFrame)); + printf("CRC Mismatch in Frame %d (ignored)\n", ignoredFrame); + m_crcInfo.setSawCRCMismatch(); + return; + } + // Since we don't seem to have any *visible* desyncs when replaying games, but get this warning // virtually every replay, the assumption is our CRC checking is faulty. Since we're at the // tail end of patch season, let's just disable the message, and hope the users believe the diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp index 8b9ecc5acbb..8f44e3b3cb1 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp @@ -1002,6 +1002,18 @@ void RecorderClass::handleCRCMessage(UnsignedInt newCRC, Int playerIndex, Bool f // playbackCRC, newCRC, TheGameLogic->getFrame()-m_crcInfo.GetQueueSize()-1, playerIndex)); if (TheGameLogic->getFrame() > 0 && newCRC != playbackCRC && !m_crcInfo.sawCRCMismatch()) { + // TheSuperHackers @feature bobtista 08/08/2026 Diagnostic playback continues past a mismatch + // without the UI report and the pause that normal playback uses. + if (TheDebugIgnoreReplaySyncErrors) + { + const UnsignedInt ignoredFrame = TheGameLogic->getFrame() - m_crcInfo.GetQueueSize() - 1; + DEBUG_LOG(("Replay CRC mismatch ignored\nInGame:%8.8X Replay:%8.8X\nFrame:%d", + playbackCRC, newCRC, ignoredFrame)); + printf("CRC Mismatch in Frame %d (ignored)\n", ignoredFrame); + m_crcInfo.setSawCRCMismatch(); + return; + } + //Kris: Patch 1.01 November 10, 2003 (integrated changes from Matt Campbell) // Since we don't seem to have any *visible* desyncs when replaying games, but get this warning // virtually every replay, the assumption is our CRC checking is faulty. Since we're at the From 84ff4ca18b7c84e71382e524612cfec7f3ae4d72 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Thu, 17 Sep 2026 01:42:24 -0400 Subject: [PATCH 2/3] fix(cli): Keep ignored CRC mismatches from ending the replay simulation --- GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp index 8f44e3b3cb1..ab94bc391b8 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp @@ -1003,14 +1003,14 @@ void RecorderClass::handleCRCMessage(UnsignedInt newCRC, Int playerIndex, Bool f if (TheGameLogic->getFrame() > 0 && newCRC != playbackCRC && !m_crcInfo.sawCRCMismatch()) { // TheSuperHackers @feature bobtista 08/08/2026 Diagnostic playback continues past a mismatch - // without the UI report and the pause that normal playback uses. + // without the UI report and the pause that normal playback uses. The mismatch is not marked + // as seen, so every later mismatch is logged too and the playback does not count as failed. if (TheDebugIgnoreReplaySyncErrors) { const UnsignedInt ignoredFrame = TheGameLogic->getFrame() - m_crcInfo.GetQueueSize() - 1; DEBUG_LOG(("Replay CRC mismatch ignored\nInGame:%8.8X Replay:%8.8X\nFrame:%d", playbackCRC, newCRC, ignoredFrame)); printf("CRC Mismatch in Frame %d (ignored)\n", ignoredFrame); - m_crcInfo.setSawCRCMismatch(); return; } From c7014c86f3ed8de375a52d875c01ef9dd29f5fb7 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Thu, 17 Sep 2026 01:42:24 -0400 Subject: [PATCH 3/3] fix(cli): Keep ignored CRC mismatches from ending the replay simulation (Generals) --- Generals/Code/GameEngine/Source/Common/Recorder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Generals/Code/GameEngine/Source/Common/Recorder.cpp b/Generals/Code/GameEngine/Source/Common/Recorder.cpp index f1785eb645d..62870a42873 100644 --- a/Generals/Code/GameEngine/Source/Common/Recorder.cpp +++ b/Generals/Code/GameEngine/Source/Common/Recorder.cpp @@ -1001,14 +1001,14 @@ void RecorderClass::handleCRCMessage(UnsignedInt newCRC, Int playerIndex, Bool f if (TheGameLogic->getFrame() > 0 && newCRC != playbackCRC && !m_crcInfo.sawCRCMismatch()) { // TheSuperHackers @feature bobtista 08/08/2026 Diagnostic playback continues past a mismatch - // without the UI report and the pause that normal playback uses. + // without the UI report and the pause that normal playback uses. The mismatch is not marked + // as seen, so every later mismatch is logged too and the playback does not count as failed. if (TheDebugIgnoreReplaySyncErrors) { const UnsignedInt ignoredFrame = TheGameLogic->getFrame() - m_crcInfo.GetQueueSize() - 1; DEBUG_LOG(("Replay CRC mismatch ignored\nInGame:%8.8X Replay:%8.8X\nFrame:%d", playbackCRC, newCRC, ignoredFrame)); printf("CRC Mismatch in Frame %d (ignored)\n", ignoredFrame); - m_crcInfo.setSawCRCMismatch(); return; }