@@ -924,14 +924,30 @@ AsciiString RecorderClass::getCurrentReplayFilename( void )
924924 return AsciiString::TheEmptyString;
925925}
926926
927+ // TheSuperHackers @info helmutbuhler 03/04/2025
928+ // Some info about CRC:
929+ // In each game, each peer periodically calculates a CRC from the local gamestate and sends that
930+ // in a message to all peers (including itself) so that everyone can check that the crc is synchronous.
931+ // In a network game, there is a delay between sending the CRC message and receiving it. This is
932+ // necessary because if you were to wait each frame for all messages from all peers, things would go
933+ // horribly slow.
934+ // But this delay is not a problem for CRC checking because everyone receives the CRC in the same frame
935+ // and every peer just makes sure all the received CRCs are equal.
936+ // While playing replays, this is a problem however: The CRC messages in the replays appear on the frame
937+ // they were received, which can be a few frames delayed if it was a network game. And if we were to
938+ // compare those with the local gamestate, they wouldn't sync up.
939+ // So, in order to fix this, we need to queue up our local CRCs,
940+ // so that we can check it with the crc messages that come later.
941+ // This class is basically that queue.
927942class CRCInfo
928943{
929944public:
930- CRCInfo ();
945+ CRCInfo (UnsignedInt localPlayer, Bool isMultiplayer );
931946 void addCRC (UnsignedInt val);
932947 UnsignedInt readCRC (void );
933948
934- void setLocalPlayer (UnsignedInt index) { m_localPlayer = index; }
949+ int GetQueueSize () const { return m_data.size (); }
950+
935951 UnsignedInt getLocalPlayer (void ) { return m_localPlayer; }
936952
937953 void setSawCRCMismatch (void ) { m_sawCRCMismatch = TRUE ; }
@@ -945,20 +961,25 @@ class CRCInfo
945961 UnsignedInt m_localPlayer;
946962};
947963
948- CRCInfo::CRCInfo ()
964+ CRCInfo::CRCInfo (UnsignedInt localPlayer, Bool isMultiplayer )
949965{
950- m_localPlayer = ~ 0 ;
951- m_skippedOne = FALSE ;
966+ m_localPlayer = localPlayer ;
967+ m_skippedOne = !isMultiplayer ;
952968 m_sawCRCMismatch = FALSE ;
953969}
954970
955971void CRCInfo::addCRC (UnsignedInt val)
956972{
957- // if (!m_skippedOne)
958- // {
959- // m_skippedOne = TRUE;
960- // return;
961- // }
973+ // TheSuperHackers @fix helmutbuhler 03/04/2025
974+ // In Multiplayer, the first MSG_LOGIC_CRC message somehow doesn't make it through the network.
975+ // Perhaps this happens because the network is not yet set up on frame 0.
976+ // So we also don't queue up the first local crc message, otherwise the crc
977+ // messages wouldn't match up anymore and we'd desync immediately during playback.
978+ if (!m_skippedOne)
979+ {
980+ m_skippedOne = TRUE ;
981+ return ;
982+ }
962983
963984 m_data.push_back (val);
964985 // DEBUG_LOG(("CRCInfo::addCRC() - crc %8.8X pushes list to %d entries (full=%d)\n", val, m_data.size(), !m_data.empty()));
@@ -968,7 +989,7 @@ UnsignedInt CRCInfo::readCRC(void)
968989{
969990 if (m_data.empty ())
970991 {
971- // DEBUG_LOG(("CRCInfo::readCRC() - bailing, full=0, size=%d\n", m_data.size()));
992+ DEBUG_LOG ((" CRCInfo::readCRC() - bailing, full=0, size=%d\n " , m_data.size ()));
972993 return 0 ;
973994 }
974995
@@ -997,7 +1018,8 @@ void RecorderClass::handleCRCMessage(UnsignedInt newCRC, Int playerIndex, Bool f
9971018 if (samePlayer || (localPlayerIndex < 0 ))
9981019 {
9991020 UnsignedInt playbackCRC = m_crcInfo->readCRC ();
1000- // DEBUG_LOG(("RecorderClass::handleCRCMessage() - Comparing CRCs of %8.8X/%8.8X from %d\n", newCRC, playbackCRC, playerIndex));
1021+ // DEBUG_LOG(("RecorderClass::handleCRCMessage() - Comparing CRCs of InGame:%8.8X Replay:%8.8X Frame:%d from Player %d\n",
1022+ // playbackCRC, newCRC, TheGameLogic->getFrame()-m_crcInfo->GetQueueSize()-1, playerIndex));
10011023 if (TheGameLogic->getFrame () > 0 && newCRC != playbackCRC && !m_crcInfo->sawCRCMismatch ())
10021024 {
10031025 m_crcInfo->setSawCRCMismatch ();
@@ -1007,9 +1029,16 @@ void RecorderClass::handleCRCMessage(UnsignedInt newCRC, Int playerIndex, Bool f
10071029 // virtually every replay, the assumption is our CRC checking is faulty. Since we're at the
10081030 // tail end of patch season, let's just disable the message, and hope the users believe the
10091031 // problem is fixed. -MDC 3/20/2003
1010- // TheInGameUI->message("GUI:CRCMismatch");
1011- DEBUG_CRASH ((" Replay has gone out of sync! All bets are off!\n Old:%8.8X New:%8.8X\n Frame:%d" ,
1012- playbackCRC, newCRC, TheGameLogic->getFrame ()));
1032+ //
1033+ // TheSuperHackers @tweak helmutbuhler 03/04/2025
1034+ // More than 20 years later, but finally fixed and reenabled!
1035+ TheInGameUI->message (" GUI:CRCMismatch" );
1036+
1037+ // TheSuperHackers @info helmutbuhler 03/04/2025
1038+ // Note: We subtract the queue size from the frame no. This way we calculate the correct frame
1039+ // the mismatch first happened in case the NetCRCInterval is set to 1 during the game.
1040+ DEBUG_CRASH ((" Replay has gone out of sync! All bets are off!\n InGame:%8.8X Replay:%8.8X\n Frame:%d" ,
1041+ playbackCRC, newCRC, TheGameLogic->getFrame ()-m_crcInfo->GetQueueSize ()-1 ));
10131042 }
10141043 return ;
10151044 }
@@ -1124,8 +1153,8 @@ Bool RecorderClass::playbackFile(AsciiString filename)
11241153 }
11251154#endif
11261155
1127- m_crcInfo = NEW CRCInfo ;
1128- m_crcInfo-> setLocalPlayer (header.localPlayerIndex );
1156+ Bool isMultiplayer = m_gameInfo. getSlot (header. localPlayerIndex )-> getIP () != 0 ;
1157+ m_crcInfo = NEW CRCInfo (header.localPlayerIndex , isMultiplayer );
11291158 REPLAY_CRC_INTERVAL = m_gameInfo.getCRCInterval ();
11301159 DEBUG_LOG ((" Player index is %d, replay CRC interval is %d\n " , m_crcInfo->getLocalPlayer (), REPLAY_CRC_INTERVAL));
11311160
@@ -1141,6 +1170,11 @@ Bool RecorderClass::playbackFile(AsciiString filename)
11411170 fread (&maxFPS, sizeof (maxFPS), 1 , m_file);
11421171
11431172 DEBUG_LOG ((" RecorderClass::playbackFile() - original game was mode %d\n " , m_originalGameMode));
1173+
1174+ // TheSuperHackers @fix helmutbuhler 03/04/2025
1175+ // In case we restart a replay, we need to clear the command list.
1176+ // Otherwise a crc message remains and messes up the crc calculation on the restarted replay.
1177+ TheCommandList->reset ();
11441178
11451179 readNextFrame ();
11461180
0 commit comments