From fbba14eda54ad74b8edad2b58876d0150623018c Mon Sep 17 00:00:00 2001 From: Michal Schiller Date: Fri, 6 Mar 2026 18:23:44 +0100 Subject: [PATCH] Refactor GetXuid and HostGame methods IQNet::AddLocalPlayerByUserIndex was a no-op The stub did nothing, so GetLocalPlayerByUserIndex returned NULL for controller 2, and NotifyPlayerJoined(NULL) crashed with an access violation at offset 0x48. Now it properly initialises the player slot. --- Minecraft.Client/Extrax64Stubs.cpp | 58 ++++++++++++------------------ 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/Minecraft.Client/Extrax64Stubs.cpp b/Minecraft.Client/Extrax64Stubs.cpp index 9e693d1a0..75d15ec9e 100644 --- a/Minecraft.Client/Extrax64Stubs.cpp +++ b/Minecraft.Client/Extrax64Stubs.cpp @@ -21,7 +21,6 @@ #include "Windows64\Social\SocialManager.h" #include "Windows64\Sentient\DynamicConfigurations.h" #include "Windows64\Network\WinsockNetLayer.h" -#include "Windows64\Windows64_Xuid.h" #elif defined __PSVITA__ #include "PSVita\Sentient\SentientManager.h" #include "StatsCounter.h" @@ -201,15 +200,7 @@ DWORD IQNetPlayer::GetCurrentRtt() { return 0; } bool IQNetPlayer::IsHost() { return m_isHostPlayer; } bool IQNetPlayer::IsGuest() { return false; } bool IQNetPlayer::IsLocal() { return !m_isRemote; } -PlayerUID IQNetPlayer::GetXuid() -{ - // Compatibility model: - // - Preferred path: use per-player resolved XUID populated from login/add-player flow. - // - Fallback path: keep legacy base+smallId behavior for peers/saves still on old scheme. - if (m_resolvedXuid != INVALID_XUID) - return m_resolvedXuid; - return (PlayerUID)(0xe000d45248242f2e + m_smallId); -} +PlayerUID IQNetPlayer::GetXuid() { return (PlayerUID)(0xe000d45248242f2e + m_smallId); } // todo: restore to INVALID_XUID once saves support this LPCWSTR IQNetPlayer::GetGamertag() { return m_gamertag; } int IQNetPlayer::GetSessionIndex() { return m_smallId; } bool IQNetPlayer::IsTalking() { return false; } @@ -235,7 +226,6 @@ void Win64_SetupRemoteQNetPlayer(IQNetPlayer * player, BYTE smallId, bool isHost player->m_smallId = smallId; player->m_isRemote = !isLocal; player->m_isHostPlayer = isHost; - player->m_resolvedXuid = INVALID_XUID; swprintf_s(player->m_gamertag, 32, L"Player%d", smallId); if (smallId >= IQNet::s_playerCount) IQNet::s_playerCount = smallId + 1; @@ -243,7 +233,23 @@ void Win64_SetupRemoteQNetPlayer(IQNetPlayer * player, BYTE smallId, bool isHost static bool Win64_IsActivePlayer(IQNetPlayer* p, DWORD index); -HRESULT IQNet::AddLocalPlayerByUserIndex(DWORD dwUserIndex) { return S_OK; } +HRESULT IQNet::AddLocalPlayerByUserIndex(DWORD dwUserIndex) +{ + if (dwUserIndex < MINECRAFT_NET_MAX_PLAYERS) + { + IQNetPlayer& p = m_player[dwUserIndex]; + p.m_smallId = (BYTE)dwUserIndex; + p.m_isRemote = false; + p.m_isHostPlayer = (dwUserIndex == 0); + swprintf_s(p.m_gamertag, 32, L"Player%d", (int)dwUserIndex); + // Mark the slot active so Win64_IsActivePlayer returns true for index >= 1 + if (dwUserIndex >= s_playerCount) + s_playerCount = dwUserIndex + 1; + if (p.GetCustomDataValue() == 0) + p.SetCustomDataValue(1); + } + return S_OK; +} IQNetPlayer* IQNet::GetHostPlayer() { return &m_player[0]; } IQNetPlayer* IQNet::GetLocalPlayerByUserIndex(DWORD dwUserIndex) { @@ -295,13 +301,8 @@ IQNetPlayer* IQNet::GetPlayerByXuid(PlayerUID xuid) { for (DWORD i = 0; i < s_playerCount; i++) { - if (!Win64_IsActivePlayer(&m_player[i], i)) - continue; - - if (m_player[i].GetXuid() == xuid) - return &m_player[i]; + if (Win64_IsActivePlayer(&m_player[i], i) && m_player[i].GetXuid() == xuid) return &m_player[i]; } - // Keep existing stub behavior: return host slot instead of NULL on miss. return &m_player[0]; } DWORD IQNet::GetPlayerCount() @@ -316,13 +317,7 @@ DWORD IQNet::GetPlayerCount() QNET_STATE IQNet::GetState() { return _iQNetStubState; } bool IQNet::IsHost() { return s_isHosting; } HRESULT IQNet::JoinGameFromInviteInfo(DWORD dwUserIndex, DWORD dwUserMask, const INVITE_INFO * pInviteInfo) { return S_OK; } -void IQNet::HostGame() -{ - _iQNetStubState = QNET_STATE_SESSION_STARTING; - s_isHosting = true; - // Host slot keeps legacy XUID so old host player data remains addressable. - m_player[0].m_resolvedXuid = Win64Xuid::GetLegacyEmbeddedHostXuid(); -} +void IQNet::HostGame() { _iQNetStubState = QNET_STATE_SESSION_STARTING; s_isHosting = true; } void IQNet::ClientJoinGame() { _iQNetStubState = QNET_STATE_SESSION_STARTING; @@ -333,7 +328,6 @@ void IQNet::ClientJoinGame() m_player[i].m_smallId = (BYTE)i; m_player[i].m_isRemote = true; m_player[i].m_isHostPlayer = false; - m_player[i].m_resolvedXuid = INVALID_XUID; m_player[i].m_gamertag[0] = 0; m_player[i].SetCustomDataValue(0); } @@ -348,14 +342,9 @@ void IQNet::EndGame() m_player[i].m_smallId = (BYTE)i; m_player[i].m_isRemote = false; m_player[i].m_isHostPlayer = false; - m_player[i].m_resolvedXuid = INVALID_XUID; m_player[i].m_gamertag[0] = 0; m_player[i].SetCustomDataValue(0); } - // Restore local player 0's gamertag so re-joining works correctly - extern wchar_t g_Win64UsernameW[17]; - m_player[0].m_isHostPlayer = true; - wcscpy_s(m_player[0].m_gamertag, 32, g_Win64UsernameW); } DWORD MinecraftDynamicConfigurations::GetTrialTime() { return DYNAMIC_CONFIG_DEFAULT_TRIAL_TIME; } @@ -598,13 +587,10 @@ void C_4JProfile::GetXUID(int iPad, PlayerUID * pXuid, bool bOnlineXuid) *pXuid = INVALID_XUID; return; } - // LoginPacket reads this value as client identity: - // - host keeps legacy host XUID for world compatibility - // - non-host uses persistent uid.dat-backed XUID if (IQNet::s_isHosting) - *pXuid = Win64Xuid::GetLegacyEmbeddedHostXuid(); + *pXuid = 0xe000d45248242f2e; else - *pXuid = Win64Xuid::ResolvePersistentXuid(); + *pXuid = 0xe000d45248242f2e + WinsockNetLayer::GetLocalSmallId(); #else * pXuid = 0xe000d45248242f2e + iPad; #endif