From 77b165b17218391427f8bf815f3094af92a716d8 Mon Sep 17 00:00:00 2001 From: Felipe Keller Braz Date: Sun, 5 Jul 2026 20:57:46 -0300 Subject: [PATCH 1/2] fix(network): Pre-compute player slot indices for fast CRC network validation --- .../GameEngine/Include/Common/PlayerList.h | 5 ++ .../Source/Common/RTS/PlayerList.cpp | 47 ++++++++++++++++++- .../Source/GameLogic/System/GameLogic.cpp | 4 +- .../GameEngine/Include/Common/PlayerList.h | 5 ++ .../Source/Common/RTS/PlayerList.cpp | 47 ++++++++++++++++++- .../Source/GameLogic/System/GameLogic.cpp | 4 +- 6 files changed, 108 insertions(+), 4 deletions(-) diff --git a/Generals/Code/GameEngine/Include/Common/PlayerList.h b/Generals/Code/GameEngine/Include/Common/PlayerList.h index f2c2b3622f7..6ba00561123 100644 --- a/Generals/Code/GameEngine/Include/Common/PlayerList.h +++ b/Generals/Code/GameEngine/Include/Common/PlayerList.h @@ -113,6 +113,10 @@ class PlayerList : public SubsystemInterface, */ Player *findPlayerWithNameKey(NameKeyType key); + void setSlotIndex(Int playerIndex, Byte slotIndex); + Byte getSlotIndex(Int playerIndex) const; + void resolveSlotIndices(); + /** Return the "local" player (ie, the human playing the game). This will never return null. @@ -162,6 +166,7 @@ class PlayerList : public SubsystemInterface, Player *m_local; Int m_playerCount; Player *m_players[MAX_PLAYER_COUNT]; + Byte m_slotIndices[MAX_PLAYER_COUNT]; }; diff --git a/Generals/Code/GameEngine/Source/Common/RTS/PlayerList.cpp b/Generals/Code/GameEngine/Source/Common/RTS/PlayerList.cpp index 3aa5b628db3..de16a22ac2a 100644 --- a/Generals/Code/GameEngine/Source/Common/RTS/PlayerList.cpp +++ b/Generals/Code/GameEngine/Source/Common/RTS/PlayerList.cpp @@ -59,7 +59,7 @@ #endif #include "GameLogic/SidesList.h" #include "GameNetwork/NetworkDefs.h" - +#include "GameNetwork/GameInfo.h" //----------------------------------------------------------------------------- /*extern*/ PlayerList *ThePlayerList = nullptr; @@ -110,6 +110,49 @@ Player *PlayerList::findPlayerWithNameKey(NameKeyType key) return nullptr; } +//----------------------------------------------------------------------------- +void PlayerList::setSlotIndex(Int playerIndex, Byte slotIndex) +{ + if (playerIndex >= 0 && playerIndex < ARRAY_SIZE(m_slotIndices)) + { + m_slotIndices[playerIndex] = slotIndex; + } +} + +Byte PlayerList::getSlotIndex(Int playerIndex) const +{ + if (playerIndex >= 0 && playerIndex < ARRAY_SIZE(m_slotIndices)) + { + return m_slotIndices[playerIndex]; + } + + return -1; +} + +void PlayerList::resolveSlotIndices() +{ + if (!TheGameInfo) + return; + + AsciiString playerName; + + for (Int i = 0; i < MAX_SLOTS; ++i) + { + const GameSlot* slot = TheGameInfo->getSlot(i); + if (!slot || !slot->isOccupied()) + continue; + + playerName.format("player%d", i); + + Player* player = findPlayerWithNameKey(TheNameKeyGenerator->nameToKey(playerName)); + if (player) + { + setSlotIndex(player->getPlayerIndex(), i); + } + } +} + + //----------------------------------------------------------------------------- void PlayerList::reset() { @@ -226,6 +269,7 @@ void PlayerList::newGame() p->setDefaultTeam(); } + resolveSlotIndices(); } //----------------------------------------------------------------------------- @@ -233,6 +277,7 @@ void PlayerList::init() { m_playerCount = 1; m_players[0]->init(nullptr); + memset(m_slotIndices, -1, sizeof(m_slotIndices)); for (int i = 1; i < MAX_PLAYER_COUNT; i++) m_players[i]->init(nullptr); diff --git a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index 3ba325d078f..abf7d19da23 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -2345,9 +2345,11 @@ void GameLogic::processCommandList( CommandList *list ) { // TheSuperHackers @bugfix Caball009 14/06/2026 Check if player is still connected, // to avoid spurious mismatches at low CRC intervals, e.g. every frame. - if (!TheNetwork->isPlayerConnected(it->first)) + const Int slotIndex = ThePlayerList->getSlotIndex(it->first); + if (slotIndex >= 0 && !TheNetwork->isPlayerConnected(slotIndex)) continue; + const UnsignedInt crc = it->second; if (!hasReferenceCRC) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/PlayerList.h b/GeneralsMD/Code/GameEngine/Include/Common/PlayerList.h index 8fcbe0a91d9..f947d47ea49 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/PlayerList.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/PlayerList.h @@ -113,6 +113,10 @@ class PlayerList : public SubsystemInterface, */ Player *findPlayerWithNameKey(NameKeyType key); + void setSlotIndex(Int playerIndex, Byte slotIndex); + Byte getSlotIndex(Int playerIndex) const; + void resolveSlotIndices(); + /** Return the "local" player (ie, the human playing the game). This will never return null. @@ -162,6 +166,7 @@ class PlayerList : public SubsystemInterface, Player *m_local; Int m_playerCount; Player *m_players[MAX_PLAYER_COUNT]; + Byte m_slotIndices[MAX_PLAYER_COUNT]; }; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/RTS/PlayerList.cpp b/GeneralsMD/Code/GameEngine/Source/Common/RTS/PlayerList.cpp index 177075bc89a..7bf1bd22d64 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/RTS/PlayerList.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/RTS/PlayerList.cpp @@ -59,7 +59,7 @@ #endif #include "GameLogic/SidesList.h" #include "GameNetwork/NetworkDefs.h" - +#include "GameNetwork/GameInfo.h" //----------------------------------------------------------------------------- /*extern*/ PlayerList *ThePlayerList = nullptr; @@ -110,6 +110,49 @@ Player *PlayerList::findPlayerWithNameKey(NameKeyType key) return nullptr; } +//----------------------------------------------------------------------------- +void PlayerList::setSlotIndex(Int playerIndex, Byte slotIndex) +{ + if (playerIndex >= 0 && playerIndex < ARRAY_SIZE(m_slotIndices)) + { + m_slotIndices[playerIndex] = slotIndex; + } +} + +Byte PlayerList::getSlotIndex(Int playerIndex) const +{ + if (playerIndex >= 0 && playerIndex < ARRAY_SIZE(m_slotIndices)) + { + return m_slotIndices[playerIndex]; + } + + return -1; +} + +void PlayerList::resolveSlotIndices() +{ + if (!TheGameInfo) + return; + + AsciiString playerName; + + for (Int i = 0; i < MAX_SLOTS; ++i) + { + const GameSlot* slot = TheGameInfo->getSlot(i); + if (!slot || !slot->isOccupied()) + continue; + + playerName.format("player%d", i); + + Player* player = findPlayerWithNameKey(TheNameKeyGenerator->nameToKey(playerName)); + if (player) + { + setSlotIndex(player->getPlayerIndex(), i); + } + } +} + + //----------------------------------------------------------------------------- void PlayerList::reset() { @@ -226,6 +269,7 @@ void PlayerList::newGame() p->setDefaultTeam(); } + resolveSlotIndices(); } //----------------------------------------------------------------------------- @@ -233,6 +277,7 @@ void PlayerList::init() { m_playerCount = 1; m_players[0]->init(nullptr); + memset(m_slotIndices, -1, sizeof(m_slotIndices)); for (int i = 1; i < MAX_PLAYER_COUNT; i++) m_players[i]->init(nullptr); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index debe213997a..6a851d07557 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -2671,9 +2671,11 @@ void GameLogic::processCommandList( CommandList *list ) { // TheSuperHackers @bugfix Caball009 14/06/2026 Check if player is still connected, // to avoid spurious mismatches at low CRC intervals, e.g. every frame. - if (!TheNetwork->isPlayerConnected(it->first)) + const Int slotIndex = ThePlayerList->getSlotIndex(it->first); + if (slotIndex >= 0 && !TheNetwork->isPlayerConnected(slotIndex)) continue; + const UnsignedInt crc = it->second; if (!hasReferenceCRC) From c455d16dfb964ba170dbda5edda2a9e9dcf3ffbe Mon Sep 17 00:00:00 2001 From: Felipe Keller Braz Date: Wed, 8 Jul 2026 20:19:10 -0300 Subject: [PATCH 2/2] fix(network): resolve PR #2857 code review comments --- .../GameEngine/Include/Common/PlayerList.h | 10 +- .../Source/Common/RTS/PlayerList.cpp | 92 +++++++++---------- .../Source/GameLogic/System/GameLogic.cpp | 1 - .../GameEngine/Include/Common/PlayerList.h | 10 +- .../Source/Common/RTS/PlayerList.cpp | 92 +++++++++---------- .../Source/GameLogic/System/GameLogic.cpp | 1 - 6 files changed, 102 insertions(+), 104 deletions(-) diff --git a/Generals/Code/GameEngine/Include/Common/PlayerList.h b/Generals/Code/GameEngine/Include/Common/PlayerList.h index 6ba00561123..7b77b205c42 100644 --- a/Generals/Code/GameEngine/Include/Common/PlayerList.h +++ b/Generals/Code/GameEngine/Include/Common/PlayerList.h @@ -113,10 +113,6 @@ class PlayerList : public SubsystemInterface, */ Player *findPlayerWithNameKey(NameKeyType key); - void setSlotIndex(Int playerIndex, Byte slotIndex); - Byte getSlotIndex(Int playerIndex) const; - void resolveSlotIndices(); - /** Return the "local" player (ie, the human playing the game). This will never return null. @@ -154,6 +150,8 @@ class PlayerList : public SubsystemInterface, */ PlayerMaskType getPlayersWithRelationship( Int srcPlayerIndex, UnsignedInt allowedRelationships ); + Int getSlotIndex(Int playerIndex) const; + protected: // snapshot methods @@ -162,11 +160,13 @@ class PlayerList : public SubsystemInterface, virtual void loadPostProcess() override; private: + void resolveSlotIndices(); + void setSlotIndex(Int playerIndex, Int slotIndex); Player *m_local; Int m_playerCount; Player *m_players[MAX_PLAYER_COUNT]; - Byte m_slotIndices[MAX_PLAYER_COUNT]; + Int m_slotIndices[MAX_PLAYER_COUNT]; }; diff --git a/Generals/Code/GameEngine/Source/Common/RTS/PlayerList.cpp b/Generals/Code/GameEngine/Source/Common/RTS/PlayerList.cpp index de16a22ac2a..7881c948205 100644 --- a/Generals/Code/GameEngine/Source/Common/RTS/PlayerList.cpp +++ b/Generals/Code/GameEngine/Source/Common/RTS/PlayerList.cpp @@ -58,8 +58,8 @@ #include "GameLogic/Object.h" #endif #include "GameLogic/SidesList.h" -#include "GameNetwork/NetworkDefs.h" #include "GameNetwork/GameInfo.h" +#include "GameNetwork/NetworkDefs.h" //----------------------------------------------------------------------------- /*extern*/ PlayerList *ThePlayerList = nullptr; @@ -110,49 +110,6 @@ Player *PlayerList::findPlayerWithNameKey(NameKeyType key) return nullptr; } -//----------------------------------------------------------------------------- -void PlayerList::setSlotIndex(Int playerIndex, Byte slotIndex) -{ - if (playerIndex >= 0 && playerIndex < ARRAY_SIZE(m_slotIndices)) - { - m_slotIndices[playerIndex] = slotIndex; - } -} - -Byte PlayerList::getSlotIndex(Int playerIndex) const -{ - if (playerIndex >= 0 && playerIndex < ARRAY_SIZE(m_slotIndices)) - { - return m_slotIndices[playerIndex]; - } - - return -1; -} - -void PlayerList::resolveSlotIndices() -{ - if (!TheGameInfo) - return; - - AsciiString playerName; - - for (Int i = 0; i < MAX_SLOTS; ++i) - { - const GameSlot* slot = TheGameInfo->getSlot(i); - if (!slot || !slot->isOccupied()) - continue; - - playerName.format("player%d", i); - - Player* player = findPlayerWithNameKey(TheNameKeyGenerator->nameToKey(playerName)); - if (player) - { - setSlotIndex(player->getPlayerIndex(), i); - } - } -} - - //----------------------------------------------------------------------------- void PlayerList::reset() { @@ -277,11 +234,12 @@ void PlayerList::init() { m_playerCount = 1; m_players[0]->init(nullptr); - memset(m_slotIndices, -1, sizeof(m_slotIndices)); for (int i = 1; i < MAX_PLAYER_COUNT; i++) m_players[i]->init(nullptr); + std::fill(m_slotIndices, m_slotIndices + ARRAY_SIZE(m_slotIndices), -1); + // call setLocalPlayer so that becomingLocalPlayer() gets called appropriately setLocalPlayer(m_players[0]); @@ -428,7 +386,6 @@ Player *PlayerList::getEachPlayerFromMask( PlayerMaskType& maskToAdjust ) return nullptr; // mask not found } - //------------------------------------------------------------------------------------------------- PlayerMaskType PlayerList::getPlayersWithRelationship( Int srcPlayerIndex, UnsignedInt allowedRelationships ) { @@ -524,6 +481,49 @@ void PlayerList::xfer( Xfer *xfer ) // ------------------------------------------------------------------------------------------------ void PlayerList::loadPostProcess() { + std::fill(m_slotIndices, m_slotIndices + ARRAY_SIZE(m_slotIndices), -1); +} +//----------------------------------------------------------------------------- +void PlayerList::setSlotIndex(Int playerIndex, Int slotIndex) +{ + if (playerIndex >= 0 && playerIndex < ARRAY_SIZE(m_slotIndices)) + { + m_slotIndices[playerIndex] = slotIndex; + } } +//----------------------------------------------------------------------------- +Int PlayerList::getSlotIndex(Int playerIndex) const +{ + if (playerIndex >= 0 && playerIndex < ARRAY_SIZE(m_slotIndices)) + { + return m_slotIndices[playerIndex]; + } + + return -1; +} + +//----------------------------------------------------------------------------- +void PlayerList::resolveSlotIndices() +{ + if (!TheGameInfo) + return; + + AsciiString playerName; + + for (Int i = 0; i < MAX_SLOTS; ++i) + { + const GameSlot* slot = TheGameInfo->getSlot(i); + if (!slot || !slot->isOccupied()) + continue; + + playerName.format("player%d", i); + + Player* player = findPlayerWithNameKey(TheNameKeyGenerator->nameToKey(playerName)); + if (player) + { + setSlotIndex(player->getPlayerIndex(), i); + } + } +} diff --git a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index abf7d19da23..a8b98ea7bba 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -2349,7 +2349,6 @@ void GameLogic::processCommandList( CommandList *list ) if (slotIndex >= 0 && !TheNetwork->isPlayerConnected(slotIndex)) continue; - const UnsignedInt crc = it->second; if (!hasReferenceCRC) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/PlayerList.h b/GeneralsMD/Code/GameEngine/Include/Common/PlayerList.h index f947d47ea49..88051a96758 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/PlayerList.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/PlayerList.h @@ -113,10 +113,6 @@ class PlayerList : public SubsystemInterface, */ Player *findPlayerWithNameKey(NameKeyType key); - void setSlotIndex(Int playerIndex, Byte slotIndex); - Byte getSlotIndex(Int playerIndex) const; - void resolveSlotIndices(); - /** Return the "local" player (ie, the human playing the game). This will never return null. @@ -154,6 +150,8 @@ class PlayerList : public SubsystemInterface, */ PlayerMaskType getPlayersWithRelationship( Int srcPlayerIndex, UnsignedInt allowedRelationships ); + Int getSlotIndex(Int playerIndex) const; + protected: // snapshot methods @@ -162,11 +160,13 @@ class PlayerList : public SubsystemInterface, virtual void loadPostProcess() override; private: + void resolveSlotIndices(); + void setSlotIndex(Int playerIndex, Int slotIndex); Player *m_local; Int m_playerCount; Player *m_players[MAX_PLAYER_COUNT]; - Byte m_slotIndices[MAX_PLAYER_COUNT]; + Int m_slotIndices[MAX_PLAYER_COUNT]; }; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/RTS/PlayerList.cpp b/GeneralsMD/Code/GameEngine/Source/Common/RTS/PlayerList.cpp index 7bf1bd22d64..a3c92afb054 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/RTS/PlayerList.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/RTS/PlayerList.cpp @@ -58,8 +58,8 @@ #include "GameLogic/Object.h" #endif #include "GameLogic/SidesList.h" -#include "GameNetwork/NetworkDefs.h" #include "GameNetwork/GameInfo.h" +#include "GameNetwork/NetworkDefs.h" //----------------------------------------------------------------------------- /*extern*/ PlayerList *ThePlayerList = nullptr; @@ -110,49 +110,6 @@ Player *PlayerList::findPlayerWithNameKey(NameKeyType key) return nullptr; } -//----------------------------------------------------------------------------- -void PlayerList::setSlotIndex(Int playerIndex, Byte slotIndex) -{ - if (playerIndex >= 0 && playerIndex < ARRAY_SIZE(m_slotIndices)) - { - m_slotIndices[playerIndex] = slotIndex; - } -} - -Byte PlayerList::getSlotIndex(Int playerIndex) const -{ - if (playerIndex >= 0 && playerIndex < ARRAY_SIZE(m_slotIndices)) - { - return m_slotIndices[playerIndex]; - } - - return -1; -} - -void PlayerList::resolveSlotIndices() -{ - if (!TheGameInfo) - return; - - AsciiString playerName; - - for (Int i = 0; i < MAX_SLOTS; ++i) - { - const GameSlot* slot = TheGameInfo->getSlot(i); - if (!slot || !slot->isOccupied()) - continue; - - playerName.format("player%d", i); - - Player* player = findPlayerWithNameKey(TheNameKeyGenerator->nameToKey(playerName)); - if (player) - { - setSlotIndex(player->getPlayerIndex(), i); - } - } -} - - //----------------------------------------------------------------------------- void PlayerList::reset() { @@ -277,11 +234,12 @@ void PlayerList::init() { m_playerCount = 1; m_players[0]->init(nullptr); - memset(m_slotIndices, -1, sizeof(m_slotIndices)); for (int i = 1; i < MAX_PLAYER_COUNT; i++) m_players[i]->init(nullptr); + std::fill(m_slotIndices, m_slotIndices + ARRAY_SIZE(m_slotIndices), -1); + // call setLocalPlayer so that becomingLocalPlayer() gets called appropriately setLocalPlayer(m_players[0]); @@ -428,7 +386,6 @@ Player *PlayerList::getEachPlayerFromMask( PlayerMaskType& maskToAdjust ) return nullptr; // mask not found } - //------------------------------------------------------------------------------------------------- PlayerMaskType PlayerList::getPlayersWithRelationship( Int srcPlayerIndex, UnsignedInt allowedRelationships ) { @@ -524,6 +481,49 @@ void PlayerList::xfer( Xfer *xfer ) // ------------------------------------------------------------------------------------------------ void PlayerList::loadPostProcess() { + std::fill(m_slotIndices, m_slotIndices + ARRAY_SIZE(m_slotIndices), -1); +} +//----------------------------------------------------------------------------- +void PlayerList::setSlotIndex(Int playerIndex, Int slotIndex) +{ + if (playerIndex >= 0 && playerIndex < ARRAY_SIZE(m_slotIndices)) + { + m_slotIndices[playerIndex] = slotIndex; + } } +//----------------------------------------------------------------------------- +Int PlayerList::getSlotIndex(Int playerIndex) const +{ + if (playerIndex >= 0 && playerIndex < ARRAY_SIZE(m_slotIndices)) + { + return m_slotIndices[playerIndex]; + } + + return -1; +} + +//----------------------------------------------------------------------------- +void PlayerList::resolveSlotIndices() +{ + if (!TheGameInfo) + return; + + AsciiString playerName; + + for (Int i = 0; i < MAX_SLOTS; ++i) + { + const GameSlot* slot = TheGameInfo->getSlot(i); + if (!slot || !slot->isOccupied()) + continue; + + playerName.format("player%d", i); + + Player* player = findPlayerWithNameKey(TheNameKeyGenerator->nameToKey(playerName)); + if (player) + { + setSlotIndex(player->getPlayerIndex(), i); + } + } +} diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index 6a851d07557..b7d16b86e08 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -2675,7 +2675,6 @@ void GameLogic::processCommandList( CommandList *list ) if (slotIndex >= 0 && !TheNetwork->isPlayerConnected(slotIndex)) continue; - const UnsignedInt crc = it->second; if (!hasReferenceCRC)