From 77b165b17218391427f8bf815f3094af92a716d8 Mon Sep 17 00:00:00 2001 From: Felipe Keller Braz Date: Sun, 5 Jul 2026 20:57:46 -0300 Subject: [PATCH] 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)