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"";