Introduce uid.dat (offline PlayerUIDs), fix multiplayer save data persistence (#536)
* fix: fix multiplayer player data mix between different players bug Fixes a Win64 multiplayer issue where player data (`players/*.dat`) could be mismatched because identity was effectively tied to connection-order `smallId` XUIDs. Introduces a deterministic username-derived persistent XUID and integrates it into the existing XUID-based save pipeline. - Added `Windows64_NameXuid` for deterministic `name -> persistent xuid` resolution - On Win64 login (`PlayerList`), set `ServerPlayer::xuid` from username-based resolver - Aligned local player `xuid` assignment (`Minecraft`) for create/init/respawn paths to use the same resolver - Added Win64 local-self guard in `ClientConnection::handleAddPlayer` using name match to avoid duplicate local remote-player creation - Kept `IQNet::GetPlayerByXuid` compatibility fallback behavior, while extending lookup to also resolve username-based XUIDs - Moved implementation to `Minecraft.Client/Windows64/Windows64_NameXuid.h`; kept legacy `Win64NameXuid.h` as compatibility include Rename migration is intentionally out of scope (same-name identity only). * fix: preserve legacy host xuid (base xuid + 0) for existing world compatibility - Add legacy embedded host XUID helper (base + 0). - When Minecraft.Client is hosting, force only the first host player to use legacy host XUID. - Keep name-based XUID for non-host players. - Prevent old singleplayer/hosted worlds from losing/mismatching host player data. * update: migrate Win64 player uid to `uid.dat`-backed XUID and add XUID based duplicate login guards - Replace Win64 username-derived XUID resolution with persistent `uid.dat`-backed identity (`Windows64_Xuid` / `Win64Xuid`). - Persist a per-client XUID next to the executable, with first-run generation, read/write, and process-local caching. - Keep legacy host compatibility by pinning host self to legacy embedded `base + 0` XUID for existing world/playerdata continuity. - Propagate packet-authoritative XUIDs into QNet player slots via `m_resolvedXuid`, and use it for `GetXuid`/`GetPlayerByXuid` with legacy fallback. - Update Win64 profile/network paths to use persistent XUID for non-host clients and clear resolved identity on disconnect. - Add login-time duplicate checks: reject connections when the same XUID is already connected (in addition to existing duplicate-name checks on Win64). - Add inline compatibility comments around legacy/new identity coexistence paths for easier future maintenance. * update: ensure uid.dat exists at startup in client mode for multiplayer
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
#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"
|
||||
@@ -200,7 +201,15 @@ 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() { return (PlayerUID)(0xe000d45248242f2e + m_smallId); } // todo: restore to INVALID_XUID once saves support this
|
||||
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);
|
||||
}
|
||||
LPCWSTR IQNetPlayer::GetGamertag() { return m_gamertag; }
|
||||
int IQNetPlayer::GetSessionIndex() { return m_smallId; }
|
||||
bool IQNetPlayer::IsTalking() { return false; }
|
||||
@@ -226,6 +235,7 @@ 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;
|
||||
@@ -285,8 +295,13 @@ IQNetPlayer* IQNet::GetPlayerByXuid(PlayerUID xuid)
|
||||
{
|
||||
for (DWORD i = 0; i < s_playerCount; i++)
|
||||
{
|
||||
if (Win64_IsActivePlayer(&m_player[i], i) && m_player[i].GetXuid() == xuid) return &m_player[i];
|
||||
if (!Win64_IsActivePlayer(&m_player[i], i))
|
||||
continue;
|
||||
|
||||
if (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()
|
||||
@@ -301,7 +316,13 @@ 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; }
|
||||
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::ClientJoinGame()
|
||||
{
|
||||
_iQNetStubState = QNET_STATE_SESSION_STARTING;
|
||||
@@ -312,6 +333,7 @@ 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);
|
||||
}
|
||||
@@ -326,6 +348,7 @@ 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);
|
||||
}
|
||||
@@ -575,10 +598,13 @@ 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 = 0xe000d45248242f2e;
|
||||
*pXuid = Win64Xuid::GetLegacyEmbeddedHostXuid();
|
||||
else
|
||||
*pXuid = 0xe000d45248242f2e + WinsockNetLayer::GetLocalSmallId();
|
||||
*pXuid = Win64Xuid::ResolvePersistentXuid();
|
||||
#else
|
||||
* pXuid = 0xe000d45248242f2e + iPad;
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user