diff --git a/Core/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp b/Core/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp index 7a202c7f8f7..37d39b85c81 100644 --- a/Core/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp +++ b/Core/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp @@ -2055,12 +2055,14 @@ void GameLogic::logicMessageDispatcher( GameMessage *msg, void *userData ) break; } - //--------------------------------------------------------------------------------------------- case GameMessage::MSG_LOGIC_CRC: { if (TheNetwork) { + if (TheNetwork->sawCRCMismatch()) + break; + Int slotIndex = -1; for (Int i=0; igetArgument(0)->integer; + const UnsignedInt newCRC = msg->getArgument(0)->integer; //DEBUG_LOG(("Received CRC of %8.8X from %ls on frame %d", newCRC, //msgPlayer->getPlayerDisplayName().str(), m_frame)); + m_cachedCRCs[msgPlayer->getPlayerIndex()] = newCRC; } else if (TheRecorder && TheRecorder->isPlaybackMode()) { - UnsignedInt newCRC = msg->getArgument(0)->integer; - //DEBUG_LOG(("Saw CRC of %X from player %d. Our CRC is %X. Arg count is %d", + if (TheRecorder->sawCRCMismatch()) + break; + + DEBUG_ASSERTCRASH(msg->getArgument(1)->boolean == msgPlayer->isLocalPlayer(), + ("CRC message origin is unexpected; playback message argument doesn't match message player index")); + + const UnsignedInt newCRC = msg->getArgument(0)->integer; + //DEBUG_LOG(("Saw CRC of %X from player %d. Our CRC is %X. Arg count is %d", //newCRC, msgPlayer->getPlayerIndex(), getCRC(), msg->getArgumentCount())); - TheRecorder->handleCRCMessage(newCRC, msgPlayer->getPlayerIndex(), (msg->getArgument(1)->boolean)); + if (msgPlayer->isLocalPlayer()) + { + TheRecorder->handlePlaybackCRCMessage(newCRC); + } + else + { +#if defined(RTS_DEBUG) + // don't even put this in release, cause someone might hack it. + if (!TheDebugIgnoreSyncErrors) +#endif + m_validationModeCRC = CRCMODE_REPLAY; + + TheRecorder->handlePlayerCRCMessage(msgPlayer->getPlayerIndex(), newCRC); + } } break; - } //--------------------------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h index e5133a71386..b30006570ff 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h @@ -367,6 +367,8 @@ class GlobalData : public SubsystemInterface Bool m_afterIntro; ///< we need to tell the game our intro is done Bool m_allowExitOutOfMovies; ///< flag to allow exit out of movies only after the Intro has played + Bool m_replayOnlyCheckLocalPlayer; ///< flag to check only the CRC messages from the player that recorded a replay + Bool m_loadScreenRender; ///< flag to disallow rendering of almost everything during a loadscreen Real m_keyboardScrollFactor; ///< Factor applied to game scrolling speed via keyboard scrolling diff --git a/GeneralsMD/Code/GameEngine/Include/Common/Player.h b/GeneralsMD/Code/GameEngine/Include/Common/Player.h index 612560c1b10..ceff981d092 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/Player.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/Player.h @@ -221,7 +221,7 @@ class Player : public Snapshot void deletePlayerAI(); - UnicodeString getPlayerDisplayName() { return m_playerDisplayName; } + UnicodeString getPlayerDisplayName() const { return m_playerDisplayName; } NameKeyType getPlayerNameKey() const { return m_playerNameKey; } AsciiString getSide() const { return m_side; } diff --git a/GeneralsMD/Code/GameEngine/Include/Common/Recorder.h b/GeneralsMD/Code/GameEngine/Include/Common/Recorder.h index fba73ad5ec3..58e1cc6e712 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/Recorder.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/Recorder.h @@ -67,20 +67,50 @@ class RecorderClass : public SubsystemInterface class CRCInfo { public: + struct MismatchData + { + MismatchData() : + mismatched(false), + playerIndex(0), + queueSize(0), + playbackCRC(0), + playerCRC(0) + {} + + MismatchData(Byte playerIndex, UnsignedShort queueSize, UnsignedInt playbackCRC, UnsignedInt playerCRC) : + mismatched(true), + playerIndex(playerIndex), + queueSize(queueSize), + playbackCRC(playbackCRC), + playerCRC(playerCRC) + {} + + Bool mismatched; + Byte playerIndex; + UnsignedShort queueSize; + UnsignedInt playbackCRC; + UnsignedInt playerCRC; + }; + CRCInfo(); - CRCInfo(UnsignedInt localPlayer, Bool isMultiplayer); - void addCRC(UnsignedInt val); - UnsignedInt readCRC(); - int GetQueueSize() const { return m_data.size(); } - UnsignedInt getLocalPlayer() const { return m_localPlayer; } - void setSawCRCMismatch() { m_sawCRCMismatch = TRUE; } - Bool sawCRCMismatch() const { return m_sawCRCMismatch; } + void init(Bool isMultiplayer, Int localPlayerIndex); + void addPlaybackCRC(UnsignedInt val); + void addPlayerCRC(Int playerIndex, UnsignedInt val); + void setSawCRCMismatch(); + Bool sawCRCMismatch() const; + Byte getLocalPlayerIndex() const; + MismatchData getMismatchData(); protected: - Bool m_sawCRCMismatch; + UnsignedInt getLargestQueueSize() const; + UnsignedInt getPlaybackCRC(); + Bool m_skippedOne; - UnsignedInt m_localPlayer; - std::list m_data; + Bool m_sawCRCMismatch; + Byte m_localPlayerIndex; + std::list m_playbackData; + std::vector m_playerData[MAX_PLAYER_COUNT]; + Bool m_inactivePlayer[MAX_PLAYER_COUNT]; }; public: @@ -110,8 +140,9 @@ class RecorderClass : public SubsystemInterface #endif Bool isPlaybackInProgress() const; -public: - void handleCRCMessage(UnsignedInt newCRC, Int playerIndex, Bool fromPlayback); + void handlePlaybackCRCMessage(UnsignedInt newCRC); + void handlePlayerCRCMessage(Int playerIndex, UnsignedInt newCRC); + void checkForMismatch(); // read in info relating to a replay, conditionally setting up m_file for playback struct ReplayHeader diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/GameLogic.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/GameLogic.h index 03899472123..483e12052f9 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/GameLogic.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/GameLogic.h @@ -298,6 +298,13 @@ class GameLogic : public SubsystemInterface, public Snapshot private: + enum CRCValidationMode CPP_11(: UnsignedByte) + { + CRCMODE_NONE, + CRCMODE_NETWORK, + CRCMODE_REPLAY, + }; + /** overrides to thing template buildable status. doesn't really belong here, but has to go somewhere. (srj) @@ -317,7 +324,7 @@ class GameLogic : public SubsystemInterface, public Snapshot // CRC cache system ----------------------------------------------------------------------------- UnsignedInt m_CRC; ///< Cache of previous CRC value std::map m_cachedCRCs; ///< CRCs we've seen this frame - Bool m_shouldValidateCRCs; ///< Should we validate CRCs this frame? + CRCValidationMode m_validationModeCRC; ///< (How) should we validate CRCs this frame? //----------------------------------------------------------------------------------------------- //Bool m_loadingScene; Bool m_loadingMap; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp index 543576d78fc..9fd573f237e 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp @@ -465,6 +465,13 @@ Int parseJobs(char *args[], int num) return 1; } +Int parseReplayCRC(char* args[], int num) +{ + TheWritableGlobalData->m_replayOnlyCheckLocalPlayer = TRUE; + + return 1; +} + Int parseXRes(char *args[], int num) { if (num > 1) @@ -1166,6 +1173,9 @@ static CommandLineParam paramsForEngineInit[] = // TheSuperHackers @feature xezon 03/08/2025 Force full viewport for 'Control Bar Pro' Addons like GenTool did it. { "-forcefullviewport", parseFullViewport }, + // TheSuperHackers @feature Caball009 03/06/2026 Enable checking only the CRC messages from the player that recorded a replay. + { "-replayLocalPlayerCRC", parseReplayCRC }, + #if defined(RTS_DEBUG) { "-noaudio", parseNoAudio }, { "-map", parseMapName }, diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp index 31d8be1105d..e11b4dba3ea 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp @@ -1014,6 +1014,7 @@ GlobalData::GlobalData() m_playSizzle = TRUE; m_afterIntro = FALSE; m_allowExitOutOfMovies = FALSE; + m_replayOnlyCheckLocalPlayer = FALSE; m_loadScreenRender = FALSE; m_keyboardDefaultScrollFactor = m_keyboardScrollFactor = 0.5f; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp index ad1783583b2..165402bd81f 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp @@ -101,19 +101,35 @@ static FILE* openStatsLogFile() #endif RecorderClass::CRCInfo::CRCInfo() : - m_sawCRCMismatch(FALSE), - m_skippedOne(FALSE), - m_localPlayer(0) -{} + m_skippedOne(false), + m_sawCRCMismatch(false), + m_localPlayerIndex(-1) +{ + static_assert(ARRAY_SIZE(m_playerData) == MAX_PLAYER_COUNT + && ARRAY_SIZE(m_inactivePlayer) == MAX_PLAYER_COUNT, "array sizes must be equal to player count"); + + memset(m_inactivePlayer, 0, sizeof(m_inactivePlayer)); +} -RecorderClass::CRCInfo::CRCInfo(UnsignedInt localPlayer, Bool isMultiplayer) +void RecorderClass::CRCInfo::init(Bool isMultiplayer, Int localPlayerIndex) { - m_sawCRCMismatch = FALSE; + DEBUG_ASSERTCRASH((localPlayerIndex >= 0 && localPlayerIndex < MAX_PLAYER_COUNT) || localPlayerIndex == -1, + ("replay local player index is unexpected")); + m_skippedOne = !isMultiplayer; - m_localPlayer = localPlayer; + m_sawCRCMismatch = false; + m_localPlayerIndex = static_cast(localPlayerIndex); + + m_playbackData.clear(); + + for (size_t i = 0; i < MAX_PLAYER_COUNT; ++i) + { + m_playerData[i].clear(); + m_inactivePlayer[i] = false; + } } -void RecorderClass::CRCInfo::addCRC(UnsignedInt val) +void RecorderClass::CRCInfo::addPlaybackCRC(UnsignedInt val) { // TheSuperHackers @fix helmutbuhler 03/04/2025 // In Multiplayer, the first MSG_LOGIC_CRC message somehow doesn't make it through the network. @@ -126,22 +142,109 @@ void RecorderClass::CRCInfo::addCRC(UnsignedInt val) return; } - m_data.push_back(val); - //DEBUG_LOG(("CRCInfo::addCRC() - crc %8.8X pushes list to %d entries (full=%d)", val, m_data.size(), !m_data.empty())); + m_playbackData.push_back(val); + //DEBUG_LOG(("CRCInfo::addPlaybackCRC() - crc %8.8X pushes list to %d entries", val, m_playbackData.size())); +} + +void RecorderClass::CRCInfo::addPlayerCRC(Int playerIndex, UnsignedInt val) +{ + const UnsignedInt index = static_cast(playerIndex); + if (index < ARRAY_SIZE(m_playerData)) + { + m_playerData[index].push_back(val); + //DEBUG_LOG(("CRCInfo::addPlayerCRC() - crc %8.8X pushes list to %d entries", val, m_playerData[index].size())); + } +} + +void RecorderClass::CRCInfo::setSawCRCMismatch() +{ + m_sawCRCMismatch = TRUE; +} + +Bool RecorderClass::CRCInfo::sawCRCMismatch() const +{ + return m_sawCRCMismatch; } -UnsignedInt RecorderClass::CRCInfo::readCRC() +Byte RecorderClass::CRCInfo::getLocalPlayerIndex() const { - if (m_data.empty()) + return m_localPlayerIndex; +} + +RecorderClass::CRCInfo::MismatchData RecorderClass::CRCInfo::getMismatchData() +{ + CRCInfo::MismatchData mdata; + + for (UnsignedInt largestQueueSize = getLargestQueueSize(), j = 0; j < largestQueueSize; ++j) + { + const UnsignedInt playbackCRC = getPlaybackCRC(); + UnsignedInt playerCount = 0; + UnsignedInt mismatchPlayerCount = 0; + + for (size_t i = 0; i < MAX_PLAYER_COUNT; ++i) + { + if (j >= m_playerData[i].size()) + { + m_inactivePlayer[i] = true; + continue; + } + + ++playerCount; + + const UnsignedInt playerCRC = m_playerData[i][j]; + if (!m_inactivePlayer[i] && playbackCRC == playerCRC) + continue; + + ++mismatchPlayerCount; + + if (!mdata.mismatched) + { + if (const Bool isAllowedToSetCRCMismatch = m_localPlayerIndex >= 0 ? i == m_localPlayerIndex : true) + { + mdata = CRCInfo::MismatchData(static_cast(i), static_cast(m_playbackData.size()), playbackCRC, playerCRC); + } + } + } + + if (playerCount <= 1 || mismatchPlayerCount >= 2) + { + mdata.playerIndex = -1; + } + } + + for (size_t i = 0; i < ARRAY_SIZE(m_playerData); ++i) + { + m_playerData[i].clear(); + } + + return mdata; +} + +UnsignedInt RecorderClass::CRCInfo::getLargestQueueSize() const +{ + UnsignedInt size = 0; + for (size_t i = 0; i < ARRAY_SIZE(m_playerData); ++i) + { + if (m_playerData[i].size() > size) + { + size = m_playerData[i].size(); + } + } + + return size; +} + +UnsignedInt RecorderClass::CRCInfo::getPlaybackCRC() +{ + if (m_playbackData.empty()) { - DEBUG_LOG(("CRCInfo::readCRC() - bailing, full=0, size=%d", m_data.size())); return 0; } - UnsignedInt val = m_data.front(); - m_data.pop_front(); - //DEBUG_LOG(("CRCInfo::readCRC() - returning %8.8X, full=%d, size=%d", val, !m_data.empty(), m_data.size())); - return val; + const UnsignedInt crc = m_playbackData.front(); + m_playbackData.pop_front(); + + return crc; } void RecorderClass::logGameStart(AsciiString options) @@ -984,71 +1087,67 @@ Bool RecorderClass::sawCRCMismatch() const return m_crcInfo.sawCRCMismatch(); } -void RecorderClass::handleCRCMessage(UnsignedInt newCRC, Int playerIndex, Bool fromPlayback) +void RecorderClass::handlePlaybackCRCMessage(UnsignedInt newCRC) { - if (fromPlayback) - { - //DEBUG_LOG(("RecorderClass::handleCRCMessage() - Adding CRC of %X from %d to m_crcInfo", newCRC, playerIndex)); - m_crcInfo.addCRC(newCRC); - return; - } + m_crcInfo.addPlaybackCRC(newCRC); + + //DEBUG_LOG(("RecorderClass::handlePlaybackCRCMessage() - Adding CRC of %X from playback to m_crcInfo", newCRC)); +} - Int localPlayerIndex = m_crcInfo.getLocalPlayer(); - Bool samePlayer = FALSE; - AsciiString playerName; - playerName.format("player%d", localPlayerIndex); - const Player *p = ThePlayerList->getNthPlayer(playerIndex); - if (!p || (p->getPlayerNameKey() == NAMEKEY(playerName))) - samePlayer = TRUE; - if (samePlayer || (localPlayerIndex < 0)) +void RecorderClass::handlePlayerCRCMessage(Int playerIndex, UnsignedInt newCRC) +{ + m_crcInfo.addPlayerCRC(playerIndex, newCRC); + + //DEBUG_LOG(("RecorderClass::handlePlayerCRCMessage() - Adding CRC of %X from %d to m_crcInfo", newCRC, playerIndex)); +} + +void RecorderClass::checkForMismatch() +{ + const CRCInfo::MismatchData mdata = m_crcInfo.getMismatchData(); + if (mdata.mismatched) { - UnsignedInt playbackCRC = m_crcInfo.readCRC(); - //DEBUG_LOG(("RecorderClass::handleCRCMessage() - Comparing CRCs of InGame:%8.8X Replay:%8.8X Frame:%d from Player %d", - // playbackCRC, newCRC, TheGameLogic->getFrame()-m_crcInfo.GetQueueSize()-1, playerIndex)); - if (TheGameLogic->getFrame() > 0 && newCRC != playbackCRC && !m_crcInfo.sawCRCMismatch()) + // 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 + // tail end of patch season, let's just disable the message, and hope the users believe the + // problem is fixed. -MDC 3/20/2003 + // + // TheSuperHackers @tweak helmutbuhler 03/04/2025 + // More than 20 years later, but finally fixed and re-enabled! + TheInGameUI->message("GUI:CRCMismatch"); + + // TheSuperHackers @info helmutbuhler 03/04/2025 + // Note: We subtract the queue size from the frame number. This way we calculate the correct frame + // the mismatch first happened in case the NetCRCInterval is set to 1 during the game. + const UnsignedInt mismatchFrame = TheGameLogic->getFrame() - mdata.queueSize - 1; + + // Now also prints a UI message for it. + const Player* player = ThePlayerList->getNthPlayer(mdata.playerIndex); + const UnicodeString mismatchDetailsStr = TheGameText->FETCH_OR_SUBSTITUTE("GUI:CRCMismatchDetails", + L"InGame:%8.8X Replay:%8.8X Frame:%d Player:%ls"); + TheInGameUI->message(mismatchDetailsStr, mdata.playbackCRC, mdata.playerCRC, mismatchFrame, + player ? player->getPlayerDisplayName().str() : L"Unknown"); + + DEBUG_LOG(("Replay has gone out of sync!\nInGame:%8.8X Replay:%8.8X\nFrame:%d\nPlayer:%ls", + mdata.playbackCRC, mdata.playerCRC, mismatchFrame, player ? player->getPlayerDisplayName().str() : L"Unknown")); + + // Print mismatch in case we are simulating replays from console. + printf("CRC Mismatch in Frame %d, Local PlayerIndex %d, Mismatch PlayerIndex %d\n", + mismatchFrame, m_crcInfo.getLocalPlayerIndex(), mdata.playerIndex); + + // TheSuperHackers @tweak Pause the game on mismatch. + // But not when a window with focus is opened, because that can make resuming difficult. + if (TheWindowManager->winGetFocus() == nullptr) { - //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 - // tail end of patch season, let's just disable the message, and hope the users believe the - // problem is fixed. -MDC 3/20/2003 - // - // TheSuperHackers @tweak helmutbuhler 03/04/2025 - // More than 20 years later, but finally fixed and re-enabled! - TheInGameUI->message("GUI:CRCMismatch"); - - // TheSuperHackers @info helmutbuhler 03/04/2025 - // Note: We subtract the queue size from the frame number. This way we calculate the correct frame - // the mismatch first happened in case the NetCRCInterval is set to 1 during the game. - const UnsignedInt mismatchFrame = TheGameLogic->getFrame() - m_crcInfo.GetQueueSize() - 1; - - // Now also prints a UI message for it. - const UnicodeString mismatchDetailsStr = TheGameText->FETCH_OR_SUBSTITUTE("GUI:CRCMismatchDetails", L"InGame:%8.8X Replay:%8.8X Frame:%d"); - TheInGameUI->message(mismatchDetailsStr, playbackCRC, newCRC, mismatchFrame); - - DEBUG_LOG(("Replay has gone out of sync!\nInGame:%8.8X Replay:%8.8X\nFrame:%d", - playbackCRC, newCRC, mismatchFrame)); - - // Print Mismatch in case we are simulating replays from console. - printf("CRC Mismatch in Frame %d\n", mismatchFrame); - - // TheSuperHackers @tweak Pause the game on mismatch. - // But not when a window with focus is opened, because that can make resuming difficult. - if (TheWindowManager->winGetFocus() == nullptr) - { - Bool pause = TRUE; - Bool pauseMusic = FALSE; - Bool pauseInput = FALSE; - TheGameLogic->setGamePaused(pause, pauseMusic, pauseInput); + const Bool pause = TRUE; + const Bool pauseMusic = FALSE; + const Bool pauseInput = FALSE; + TheGameLogic->setGamePaused(pause, pauseMusic, pauseInput); - // Mark this mismatch as seen when we had the chance to pause once. - m_crcInfo.setSawCRCMismatch(); - } + // Mark this mismatch as seen when we had the chance to pause once. + m_crcInfo.setSawCRCMismatch(); } - return; } - - //DEBUG_LOG(("RecorderClass::handleCRCMessage() - Skipping CRC of %8.8X from %d (our index is %d)", newCRC, playerIndex, localPlayerIndex)); } /** @@ -1164,9 +1263,9 @@ Bool RecorderClass::playbackFile(AsciiString filename) #endif Bool isMultiplayer = m_gameInfo.getSlot(header.localPlayerIndex)->getIP() != 0; - m_crcInfo = CRCInfo(header.localPlayerIndex, isMultiplayer); + m_crcInfo.init(isMultiplayer, TheGlobalData->m_replayOnlyCheckLocalPlayer ? header.localPlayerIndex : -1); REPLAY_CRC_INTERVAL = m_gameInfo.getCRCInterval(); - DEBUG_LOG(("Player index is %d, replay CRC interval is %d", m_crcInfo.getLocalPlayer(), REPLAY_CRC_INTERVAL)); + DEBUG_LOG(("Player index is %d, replay CRC interval is %d", header.localPlayerIndex, REPLAY_CRC_INTERVAL)); Int difficulty = 0; m_file->read(&difficulty, sizeof(difficulty)); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index 622aeb5da10..efe4f7c4607 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -256,7 +256,7 @@ GameLogic::GameLogic() m_progressCompleteTimeout[i] = 0; } - m_shouldValidateCRCs = FALSE; + m_validationModeCRC = CRCMODE_NONE; m_startNewGame = FALSE; @@ -2626,76 +2626,80 @@ void GameLogic::processDestroyList() //in the request for a new deletion (sub-object), the new object was added to the end of this list. } -//------------------------------------------------------------------------------------------------- -/** Process the command list passed to the logic from the network */ -//------------------------------------------------------------------------------------------------- -void GameLogic::processCommandList( CommandList *list ) +static void checkForMismatch(const std::map& cachedCRCs) { - m_cachedCRCs.clear(); - m_shouldValidateCRCs = FALSE; - - GameMessage* msg; + Bool sawCRCMismatch = FALSE; + Int numPlayers = 0; - for( msg = list->getFirstMessage(); msg; msg = msg->next() ) + for (Int i=0; iisPlayerConnected(i)) + ++numPlayers; } - if (m_shouldValidateCRCs && !TheNetwork->sawCRCMismatch()) + if (cachedCRCs.size() < numPlayers) + { + DEBUG_CRASH(("Not enough CRCs!")); + sawCRCMismatch = TRUE; + } + else { - Bool sawCRCMismatch = FALSE; - Int numPlayers = 0; - DEBUG_ASSERTCRASH(TheNetwork, ("No Network!")); - if (TheNetwork) + //DEBUG_LOG(("Comparing %d CRCs on frame %d", cachedCRCs.size(), m_frame)); + std::map::const_iterator crcIt = cachedCRCs.begin(); + Int validatorCRC = crcIt->second; + //DEBUG_LOG(("Validator CRC from player %d is %8.8X", crcIt->first, validatorCRC)); + while (++crcIt != cachedCRCs.end()) { - for (Int i=0; isecond; + //DEBUG_LOG(("CRC to validate is from player %d: %8.8X", crcIt->first, validatedCRC)); + if (validatorCRC != validatedCRC) { - if (TheNetwork->isPlayerConnected(i)) - ++numPlayers; - } - - if (m_cachedCRCs.size() < numPlayers) - { - DEBUG_CRASH(("Not enough CRCs!")); + DEBUG_CRASH(("CRC mismatch!")); sawCRCMismatch = TRUE; } - else - { - //DEBUG_LOG(("Comparing %d CRCs on frame %d", m_cachedCRCs.size(), m_frame)); - std::map::const_iterator crcIt = m_cachedCRCs.begin(); - Int validatorCRC = crcIt->second; - //DEBUG_LOG(("Validator CRC from player %d is %8.8X", crcIt->first, validatorCRC)); - while (++crcIt != m_cachedCRCs.end()) - { - Int validatedCRC = crcIt->second; - //DEBUG_LOG(("CRC to validate is from player %d: %8.8X", crcIt->first, validatedCRC)); - if (validatorCRC != validatedCRC) - { - DEBUG_CRASH(("CRC mismatch!")); - sawCRCMismatch = TRUE; - } - } - } } + } - if (sawCRCMismatch) - { + if (sawCRCMismatch) + { #ifdef DEBUG_LOGGING - DEBUG_LOG(("CRC Mismatch - saw %d CRCs from %d players", m_cachedCRCs.size(), numPlayers)); - for (std::map::const_iterator crcIt = m_cachedCRCs.begin(); crcIt != m_cachedCRCs.end(); ++crcIt) - { - Player *player = ThePlayerList->getNthPlayer(crcIt->first); - DEBUG_LOG(("CRC from player %d (%ls) = %X", crcIt->first, - player?player->getPlayerDisplayName().str():L"", crcIt->second)); - } -#endif // DEBUG_LOGGING - TheNetwork->setSawCRCMismatch(); + DEBUG_LOG(("CRC Mismatch - saw %d CRCs from %d players", cachedCRCs.size(), numPlayers)); + for (std::map::const_iterator crcIt = cachedCRCs.begin(); crcIt != cachedCRCs.end(); ++crcIt) + { + Player* player = ThePlayerList->getNthPlayer(crcIt->first); + DEBUG_LOG(("CRC from player %d (%ls) = %X", crcIt->first, + player ? player->getPlayerDisplayName().str() : L"", crcIt->second)); } +#endif // DEBUG_LOGGING + + TheNetwork->setSawCRCMismatch(); } +} +//------------------------------------------------------------------------------------------------- +/** Process the command list passed to the logic from the network */ +//------------------------------------------------------------------------------------------------- +void GameLogic::processCommandList( CommandList *list ) +{ + m_cachedCRCs.clear(); + m_validationModeCRC = CRCMODE_NONE; + + for( GameMessage* msg = list->getFirstMessage(); msg; msg = msg->next() ) + { +#ifdef RTS_DEBUG + DEBUG_ASSERTCRASH(msg != nullptr && msg != (GameMessage*)0xdeadbeef, ("bad msg")); +#endif + logicMessageDispatcher( msg, nullptr ); + } + + if (m_validationModeCRC == CRCMODE_NETWORK) + { + checkForMismatch(m_cachedCRCs); + } + else if (m_validationModeCRC == CRCMODE_REPLAY) + { + TheRecorder->checkForMismatch(); + } } // ------------------------------------------------------------------------------------------------