From 75eb646becfbae8090f76b44dd690b88194a4ce6 Mon Sep 17 00:00:00 2001 From: itsRevela Date: Thu, 26 Mar 2026 22:25:58 -0500 Subject: [PATCH] Fix player list map icon colors to match map markers The tab player list and teleport menu now show the correct map marker color for each player. The icon is computed using the same hash as the map renderer (getRandomPlayerMapIcon) and stored by player name, bypassing the unreliable small-ID lookup that produced wrong colors on dedicated servers. --- Minecraft.Client/ClientConnection.cpp | 27 +++++++++++++++ Minecraft.Client/Common/Consoles_App.cpp | 34 +++++++++++++++++++ Minecraft.Client/Common/Consoles_App.h | 4 +++ .../Common/UI/UIScene_InGameInfoMenu.cpp | 2 +- .../Common/UI/UIScene_TeleportMenu.cpp | 4 +-- 5 files changed, 68 insertions(+), 3 deletions(-) diff --git a/Minecraft.Client/ClientConnection.cpp b/Minecraft.Client/ClientConnection.cpp index df2ee6273..7116f311c 100644 --- a/Minecraft.Client/ClientConnection.cpp +++ b/Minecraft.Client/ClientConnection.cpp @@ -66,6 +66,30 @@ #include "..\Minecraft.World\GenericStats.h" #endif +namespace +{ + char mapIconToFrame(char iconSlot) + { + if (iconSlot >= 8) return iconSlot - 4; + return iconSlot; + } + + // Same hash as getRandomPlayerMapIcon in MapItemSavedData.cpp, returning + // the Iggy/SWF frame index (0-7) instead of the raw icon slot. + char computePlayerMapFrame(int entityId, int playerIndex) + { + static const char PLAYER_MAP_ICON_SLOTS[] = { 0, 1, 2, 3, 8, 9, 10, 11 }; + unsigned int seed = static_cast(entityId); + seed ^= static_cast(playerIndex * 0x9E3779B9u); + seed ^= (seed >> 16); + seed *= 0x7FEB352Du; + seed ^= (seed >> 15); + seed *= 0x846CA68Bu; + seed ^= (seed >> 16); + return mapIconToFrame(PLAYER_MAP_ICON_SLOTS[seed % 8]); + } +} + ClientConnection::ClientConnection(Minecraft *minecraft, const wstring& ip, int port) { // 4J Stu - No longer used as we use the socket version below. @@ -377,6 +401,7 @@ void ClientConnection::handleLogin(shared_ptr packet) BYTE networkSmallId = getSocket()->getSmallId(); app.UpdatePlayerInfo(networkSmallId, packet->m_playerIndex, packet->m_uiGamePrivileges); + app.SetPlayerMapIcon(minecraft->player->getName().c_str(), computePlayerMapFrame(packet->clientVersion, packet->m_playerIndex)); minecraft->player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All, packet->m_uiGamePrivileges); // Assume all privileges are on, so that the first message we see only indicates things that have been turned off @@ -447,6 +472,7 @@ void ClientConnection::handleLogin(shared_ptr packet) BYTE networkSmallId = getSocket()->getSmallId(); app.UpdatePlayerInfo(networkSmallId, packet->m_playerIndex, packet->m_uiGamePrivileges); + app.SetPlayerMapIcon(player->getName().c_str(), computePlayerMapFrame(packet->clientVersion, packet->m_playerIndex)); player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All, packet->m_uiGamePrivileges); // Assume all privileges are on, so that the first message we see only indicates things that have been turned off @@ -976,6 +1002,7 @@ void ClientConnection::handleAddPlayer(shared_ptr packet) player->setPlayerIndex( packet->m_playerIndex ); player->setCustomSkin( packet->m_skinId ); player->setCustomCape( packet->m_capeId ); + app.SetPlayerMapIcon(packet->name.c_str(), computePlayerMapFrame(packet->id, packet->m_playerIndex)); player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All, packet->m_uiGamePrivileges); if (!player->customTextureUrl.empty() && player->customTextureUrl.substr(0, 3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(player->customTextureUrl)) diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp index 0a2fd159a..c98837890 100644 --- a/Minecraft.Client/Common/Consoles_App.cpp +++ b/Minecraft.Client/Common/Consoles_App.cpp @@ -187,6 +187,7 @@ CMinecraftApp::CMinecraftApp() #endif ZeroMemory(m_playerColours,MINECRAFT_NET_MAX_PLAYERS); + ZeroMemory(m_playerMapIcons,MINECRAFT_NET_MAX_PLAYERS); m_iDLCOfferC=0; m_bAllDLCContentRetrieved=true; @@ -8463,6 +8464,39 @@ short CMinecraftApp::GetPlayerColour(BYTE networkSmallId) return index; } +void CMinecraftApp::SetPlayerMapIcon(const wchar_t* name, char icon) +{ + if (name == nullptr) return; + // Update existing entry or use first empty slot + int emptySlot = -1; + for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) + { + if (m_playerMapIcons[i].name[0] != 0 && _wcsicmp(m_playerMapIcons[i].name, name) == 0) + { + m_playerMapIcons[i].icon = icon; + return; + } + if (emptySlot < 0 && m_playerMapIcons[i].name[0] == 0) + emptySlot = i; + } + if (emptySlot >= 0) + { + wcsncpy_s(m_playerMapIcons[emptySlot].name, 32, name, _TRUNCATE); + m_playerMapIcons[emptySlot].icon = icon; + } +} + +char CMinecraftApp::GetPlayerMapIconByName(const wchar_t* name) +{ + if (name == nullptr) return 0; + for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) + { + if (m_playerMapIcons[i].name[0] != 0 && _wcsicmp(m_playerMapIcons[i].name, name) == 0) + return m_playerMapIcons[i].icon; + } + return 0; +} + unsigned int CMinecraftApp::GetPlayerPrivileges(BYTE networkSmallId) { diff --git a/Minecraft.Client/Common/Consoles_App.h b/Minecraft.Client/Common/Consoles_App.h index 0c1c261ef..ff7aa8e88 100644 --- a/Minecraft.Client/Common/Consoles_App.h +++ b/Minecraft.Client/Common/Consoles_App.h @@ -747,10 +747,14 @@ public: private: BYTE m_playerColours[MINECRAFT_NET_MAX_PLAYERS]; // An array of QNet small-id's unsigned int m_playerGamePrivileges[MINECRAFT_NET_MAX_PLAYERS]; + struct PlayerMapIconEntry { wchar_t name[32]; char icon; }; + PlayerMapIconEntry m_playerMapIcons[MINECRAFT_NET_MAX_PLAYERS]; public: void UpdatePlayerInfo(BYTE networkSmallId, SHORT playerColourIndex, unsigned int playerGamePrivileges); short GetPlayerColour(BYTE networkSmallId); + void SetPlayerMapIcon(const wchar_t* name, char icon); + char GetPlayerMapIconByName(const wchar_t* name); unsigned int GetPlayerPrivileges(BYTE networkSmallId); wstring getEntityName(eINSTANCEOF type); diff --git a/Minecraft.Client/Common/UI/UIScene_InGameInfoMenu.cpp b/Minecraft.Client/Common/UI/UIScene_InGameInfoMenu.cpp index b8e184445..d3a149df5 100644 --- a/Minecraft.Client/Common/UI/UIScene_InGameInfoMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_InGameInfoMenu.cpp @@ -512,7 +512,7 @@ UIScene_InGameInfoMenu::PlayerInfo *UIScene_InGameInfoMenu::BuildPlayerInfo(INet } info->m_voiceStatus = voiceStatus; - info->m_colorState = app.GetPlayerColour(info->m_smallId); + info->m_colorState = app.GetPlayerMapIconByName(player->GetOnlineName()); info->m_name = playerName; return info; diff --git a/Minecraft.Client/Common/UI/UIScene_TeleportMenu.cpp b/Minecraft.Client/Common/UI/UIScene_TeleportMenu.cpp index 017af93ef..d8390d373 100644 --- a/Minecraft.Client/Common/UI/UIScene_TeleportMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_TeleportMenu.cpp @@ -193,12 +193,12 @@ void UIScene_TeleportMenu::tick() { m_players[i] = player->GetSmallId(); - short icon = app.GetPlayerColour( m_players[i] ); + short icon = static_cast(app.GetPlayerMapIconByName(player->GetOnlineName())); if(icon != m_playersColourState[i]) { m_playersColourState[i] = icon; - m_playerList.setPlayerIcon( i, (int)app.GetPlayerColour( m_players[i] ) ); + m_playerList.setPlayerIcon( i, (int)icon ); } wstring playerName = L"";