Merge branch 'smartcmd:main' into main

This commit is contained in:
Necmi 2026-03-06 18:02:15 +03:00 committed by GitHub
commit 06b774e78f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
127 changed files with 493 additions and 124 deletions

3
.gitignore vendored
View file

@ -433,3 +433,6 @@ build/*
# Local saves # Local saves
Minecraft.Client/Saves/ Minecraft.Client/Saves/
# Visual Studio Per-User Config
*.user

View file

@ -759,6 +759,18 @@ void ClientConnection::handleAddPlayer(shared_ptr<AddPlayerPacket> packet)
return; return;
} }
} }
#ifdef _WINDOWS64
// Win64 keeps local-player identity separate from network smallId; also guard against creating
// a duplicate remote player for a local slot by checking the username directly.
for (unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
{
if (minecraft->localplayers[idx] != NULL && minecraft->localplayers[idx]->name == packet->name)
{
app.DebugPrintf("AddPlayerPacket received for local player name %ls\n", packet->name.c_str());
return;
}
}
#endif
/*#ifdef _WINDOWS64 /*#ifdef _WINDOWS64
// On Windows64 all XUIDs are INVALID_XUID so the XUID check above never fires. // On Windows64 all XUIDs are INVALID_XUID so the XUID check above never fires.
// packet->m_playerIndex is the server-assigned sequential index (set via LoginPacket), // packet->m_playerIndex is the server-assigned sequential index (set via LoginPacket),
@ -800,22 +812,50 @@ void ClientConnection::handleAddPlayer(shared_ptr<AddPlayerPacket> packet)
#ifdef _WINDOWS64 #ifdef _WINDOWS64
{ {
IQNetPlayer* matchedQNetPlayer = NULL;
PlayerUID pktXuid = player->getXuid(); PlayerUID pktXuid = player->getXuid();
const PlayerUID WIN64_XUID_BASE = (PlayerUID)0xe000d45248242f2e; const PlayerUID WIN64_XUID_BASE = (PlayerUID)0xe000d45248242f2e;
// Legacy compatibility path for peers still using embedded smallId XUIDs.
if (pktXuid >= WIN64_XUID_BASE && pktXuid < WIN64_XUID_BASE + MINECRAFT_NET_MAX_PLAYERS) if (pktXuid >= WIN64_XUID_BASE && pktXuid < WIN64_XUID_BASE + MINECRAFT_NET_MAX_PLAYERS)
{ {
BYTE smallId = (BYTE)(pktXuid - WIN64_XUID_BASE); BYTE smallId = (BYTE)(pktXuid - WIN64_XUID_BASE);
INetworkPlayer* np = g_NetworkManager.GetPlayerBySmallId(smallId); INetworkPlayer* np = g_NetworkManager.GetPlayerBySmallId(smallId);
if (np != NULL) if (np != NULL)
{ {
NetworkPlayerXbox* npx = (NetworkPlayerXbox*)np;
matchedQNetPlayer = npx->GetQNetPlayer();
}
}
// Current Win64 path: identify QNet player by name and attach packet XUID.
if (matchedQNetPlayer == NULL)
{
for (BYTE smallId = 0; smallId < MINECRAFT_NET_MAX_PLAYERS; ++smallId)
{
INetworkPlayer* np = g_NetworkManager.GetPlayerBySmallId(smallId);
if (np == NULL)
continue;
NetworkPlayerXbox* npx = (NetworkPlayerXbox*)np; NetworkPlayerXbox* npx = (NetworkPlayerXbox*)np;
IQNetPlayer* qp = npx->GetQNetPlayer(); IQNetPlayer* qp = npx->GetQNetPlayer();
if (qp != NULL && qp->m_gamertag[0] == 0) if (qp != NULL && _wcsicmp(qp->m_gamertag, packet->name.c_str()) == 0)
{ {
wcsncpy_s(qp->m_gamertag, 32, packet->name.c_str(), _TRUNCATE); matchedQNetPlayer = qp;
break;
} }
} }
} }
if (matchedQNetPlayer != NULL)
{
// Store packet-authoritative XUID on this network slot so later lookups by XUID
// (e.g. remove player, display mapping) work for both legacy and uid.dat clients.
matchedQNetPlayer->m_resolvedXuid = pktXuid;
if (matchedQNetPlayer->m_gamertag[0] == 0)
{
wcsncpy_s(matchedQNetPlayer->m_gamertag, 32, packet->name.c_str(), _TRUNCATE);
}
}
} }
#endif #endif
@ -985,6 +1025,8 @@ void ClientConnection::handleRemoveEntity(shared_ptr<RemoveEntitiesPacket> packe
qp->m_smallId = 0; qp->m_smallId = 0;
qp->m_isRemote = false; qp->m_isRemote = false;
qp->m_isHostPlayer = false; qp->m_isHostPlayer = false;
// Clear resolved id to avoid stale XUID -> player matches after disconnect.
qp->m_resolvedXuid = INVALID_XUID;
qp->m_gamertag[0] = 0; qp->m_gamertag[0] = 0;
qp->SetCustomDataValue(0); qp->SetCustomDataValue(0);
} }
@ -3956,4 +3998,4 @@ ClientConnection::DeferredEntityLinkPacket::DeferredEntityLinkPacket(shared_ptr<
{ {
m_recievedTick = GetTickCount(); m_recievedTick = GetTickCount();
m_packet = packet; m_packet = packet;
} }

View file

@ -260,16 +260,13 @@ void SoundEngine::updateMiniAudio()
continue; continue;
} }
float finalVolume = s->info.volume; float finalVolume = s->info.volume * m_MasterEffectsVolume;
if (finalVolume > 1.0f) if (finalVolume > 1.0f)
finalVolume = 1.0f; finalVolume = 1.0f;
ma_sound_set_volume(&s->sound, finalVolume); ma_sound_set_volume(&s->sound, finalVolume);
if (!s->info.bUseSoundsPitchVal) ma_sound_set_pitch(&s->sound, s->info.pitch);
{
ma_sound_set_pitch(&s->sound, s->info.pitch);
}
if (s->info.bIs3D) if (s->info.bIs3D)
{ {
@ -967,14 +964,8 @@ void SoundEngine::playMusicTick()
// AP - moved to a separate function so it can be called from the mixer callback on Vita // AP - moved to a separate function so it can be called from the mixer callback on Vita
void SoundEngine::playMusicUpdate() void SoundEngine::playMusicUpdate()
{ {
//return;
static bool firstCall = true;
static float fMusicVol = 0.0f; static float fMusicVol = 0.0f;
if( firstCall ) fMusicVol = getMasterMusicVolume();
{
fMusicVol = getMasterMusicVolume();
firstCall = false;
}
switch(m_StreamState) switch(m_StreamState)
{ {
@ -1242,7 +1233,7 @@ void SoundEngine::playMusicUpdate()
ma_sound_set_pitch(&m_musicStream, m_StreamingAudioInfo.pitch); ma_sound_set_pitch(&m_musicStream, m_StreamingAudioInfo.pitch);
float finalVolume = m_StreamingAudioInfo.volume * m_MasterMusicVolume; float finalVolume = m_StreamingAudioInfo.volume * getMasterMusicVolume();
ma_sound_set_volume(&m_musicStream, finalVolume); ma_sound_set_volume(&m_musicStream, finalVolume);
ma_result startResult = ma_sound_start(&m_musicStream); ma_result startResult = ma_sound_start(&m_musicStream);
@ -1370,14 +1361,11 @@ void SoundEngine::playMusicUpdate()
} }
// volume change required? // volume change required?
if (fMusicVol != getMasterMusicVolume()) if (m_musicStreamActive)
{ {
if (m_musicStreamActive) float finalVolume = m_StreamingAudioInfo.volume * fMusicVol;
{
float finalVolume = m_StreamingAudioInfo.volume * fMusicVol;
ma_sound_set_volume(&m_musicStream, finalVolume); ma_sound_set_volume(&m_musicStream, finalVolume);
}
} }
} }
} }

View file

@ -2441,10 +2441,10 @@ void CMinecraftApp::ClearGameSettingsChangedFlag(int iPad)
/////////////////////////// ///////////////////////////
// //
// Remove the debug settings in the content package build // Remove the debug settings in the release build
// //
//////////////////////////// ////////////////////////////
#ifndef _DEBUG_MENUS_ENABLED #ifndef _DEBUG
unsigned int CMinecraftApp::GetGameSettingsDebugMask(int iPad,bool bOverridePlayer) //bOverridePlayer is to force the send for the server to get the read options unsigned int CMinecraftApp::GetGameSettingsDebugMask(int iPad,bool bOverridePlayer) //bOverridePlayer is to force the send for the server to get the read options
{ {
return 0; return 0;

View file

@ -5,6 +5,7 @@
#include "..\..\Xbox\Network\NetworkPlayerXbox.h" #include "..\..\Xbox\Network\NetworkPlayerXbox.h"
#ifdef _WINDOWS64 #ifdef _WINDOWS64
#include "..\..\Windows64\Network\WinsockNetLayer.h" #include "..\..\Windows64\Network\WinsockNetLayer.h"
#include "..\..\Windows64\Windows64_Xuid.h"
#include "..\..\Minecraft.h" #include "..\..\Minecraft.h"
#include "..\..\User.h" #include "..\..\User.h"
#include <iostream> #include <iostream>
@ -234,6 +235,7 @@ void CPlatformNetworkManagerStub::DoWork()
qnetPlayer->m_smallId = 0; qnetPlayer->m_smallId = 0;
qnetPlayer->m_isRemote = false; qnetPlayer->m_isRemote = false;
qnetPlayer->m_isHostPlayer = false; qnetPlayer->m_isHostPlayer = false;
qnetPlayer->m_resolvedXuid = INVALID_XUID;
qnetPlayer->m_gamertag[0] = 0; qnetPlayer->m_gamertag[0] = 0;
qnetPlayer->SetCustomDataValue(0); qnetPlayer->SetCustomDataValue(0);
WinsockNetLayer::PushFreeSmallId(disconnectedSmallId); WinsockNetLayer::PushFreeSmallId(disconnectedSmallId);
@ -354,7 +356,9 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame,
#ifdef _WINDOWS64 #ifdef _WINDOWS64
IQNet::m_player[0].m_smallId = 0; IQNet::m_player[0].m_smallId = 0;
IQNet::m_player[0].m_isRemote = false; IQNet::m_player[0].m_isRemote = false;
// world host is pinned to legacy host XUID to keep old player data compatibility.
IQNet::m_player[0].m_isHostPlayer = true; IQNet::m_player[0].m_isHostPlayer = true;
IQNet::m_player[0].m_resolvedXuid = Win64Xuid::GetLegacyEmbeddedHostXuid();
IQNet::s_playerCount = 1; IQNet::s_playerCount = 1;
#endif #endif
@ -411,6 +415,8 @@ int CPlatformNetworkManagerStub::JoinGame(FriendSessionInfo* searchResult, int l
IQNet::m_player[0].m_smallId = 0; IQNet::m_player[0].m_smallId = 0;
IQNet::m_player[0].m_isRemote = true; IQNet::m_player[0].m_isRemote = true;
IQNet::m_player[0].m_isHostPlayer = true; IQNet::m_player[0].m_isHostPlayer = true;
// Remote host still maps to legacy host XUID in mixed old/new sessions.
IQNet::m_player[0].m_resolvedXuid = Win64Xuid::GetLegacyEmbeddedHostXuid();
wcsncpy_s(IQNet::m_player[0].m_gamertag, 32, searchResult->data.hostName, _TRUNCATE); wcsncpy_s(IQNet::m_player[0].m_gamertag, 32, searchResult->data.hostName, _TRUNCATE);
WinsockNetLayer::StopDiscovery(); WinsockNetLayer::StopDiscovery();
@ -426,6 +432,8 @@ int CPlatformNetworkManagerStub::JoinGame(FriendSessionInfo* searchResult, int l
IQNet::m_player[localSmallId].m_smallId = localSmallId; IQNet::m_player[localSmallId].m_smallId = localSmallId;
IQNet::m_player[localSmallId].m_isRemote = false; IQNet::m_player[localSmallId].m_isRemote = false;
IQNet::m_player[localSmallId].m_isHostPlayer = false; IQNet::m_player[localSmallId].m_isHostPlayer = false;
// Local non-host identity is the persistent uid.dat XUID.
IQNet::m_player[localSmallId].m_resolvedXuid = Win64Xuid::ResolvePersistentXuid();
Minecraft* pMinecraft = Minecraft::GetInstance(); Minecraft* pMinecraft = Minecraft::GetInstance();
wcscpy_s(IQNet::m_player[localSmallId].m_gamertag, 32, pMinecraft->user->name.c_str()); wcscpy_s(IQNet::m_player[localSmallId].m_gamertag, 32, pMinecraft->user->name.c_str());

View file

@ -2342,7 +2342,13 @@ void UIController::PlayUISFX(ESoundEffect eSound)
if (time - m_lastUiSfx < 10) { return; } if (time - m_lastUiSfx < 10) { return; }
m_lastUiSfx = time; m_lastUiSfx = time;
Minecraft::GetInstance()->soundEngine->playUI(eSound,1.0f,1.0f); float pitch = 1.0f;
if (eSound == eSFX_Focus)
{
pitch += (m_randomDistribution(m_randomGenerator) - 0.5f) / 10;
}
Minecraft::GetInstance()->soundEngine->playUI(eSound,1.0f,pitch);
} }
void UIController::DisplayGamertag(unsigned int iPad, bool show) void UIController::DisplayGamertag(unsigned int iPad, bool show)

View file

@ -3,6 +3,7 @@ using namespace std;
#include "IUIController.h" #include "IUIController.h"
#include "UIEnums.h" #include "UIEnums.h"
#include "UIGroup.h" #include "UIGroup.h"
#include <random>
class UIAbstractBitmapFont; class UIAbstractBitmapFont;
class UIBitmapFont; class UIBitmapFont;
@ -63,6 +64,9 @@ private:
UITTFFont *m_mcTTFFont; UITTFFont *m_mcTTFFont;
UIBitmapFont *m_moj7, *m_moj11; UIBitmapFont *m_moj7, *m_moj11;
std::mt19937 m_randomGenerator;
std::uniform_real_distribution<float> m_randomDistribution;
public: public:
void setCleanupOnReload(); void setCleanupOnReload();
void updateCurrentFont(); void updateCurrentFont();

View file

@ -23,7 +23,7 @@ UIScene_HelpAndOptionsMenu::UIScene_HelpAndOptionsMenu(int iPad, void *initData,
// We don't have a reinstall content, so remove the button // We don't have a reinstall content, so remove the button
removeControl( &m_buttons[BUTTON_HAO_REINSTALL], false ); removeControl( &m_buttons[BUTTON_HAO_REINSTALL], false );
#ifdef _FINAL_BUILD #ifndef _DEBUG
removeControl( &m_buttons[BUTTON_HAO_DEBUG], false); removeControl( &m_buttons[BUTTON_HAO_DEBUG], false);
#else #else
if(!app.DebugSettingsOn()) removeControl( &m_buttons[BUTTON_HAO_DEBUG], false); if(!app.DebugSettingsOn()) removeControl( &m_buttons[BUTTON_HAO_DEBUG], false);

View file

@ -21,6 +21,7 @@
#include "Windows64\Social\SocialManager.h" #include "Windows64\Social\SocialManager.h"
#include "Windows64\Sentient\DynamicConfigurations.h" #include "Windows64\Sentient\DynamicConfigurations.h"
#include "Windows64\Network\WinsockNetLayer.h" #include "Windows64\Network\WinsockNetLayer.h"
#include "Windows64\Windows64_Xuid.h"
#elif defined __PSVITA__ #elif defined __PSVITA__
#include "PSVita\Sentient\SentientManager.h" #include "PSVita\Sentient\SentientManager.h"
#include "StatsCounter.h" #include "StatsCounter.h"
@ -200,7 +201,15 @@ DWORD IQNetPlayer::GetCurrentRtt() { return 0; }
bool IQNetPlayer::IsHost() { return m_isHostPlayer; } bool IQNetPlayer::IsHost() { return m_isHostPlayer; }
bool IQNetPlayer::IsGuest() { return false; } bool IQNetPlayer::IsGuest() { return false; }
bool IQNetPlayer::IsLocal() { return !m_isRemote; } 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; } LPCWSTR IQNetPlayer::GetGamertag() { return m_gamertag; }
int IQNetPlayer::GetSessionIndex() { return m_smallId; } int IQNetPlayer::GetSessionIndex() { return m_smallId; }
bool IQNetPlayer::IsTalking() { return false; } bool IQNetPlayer::IsTalking() { return false; }
@ -226,6 +235,7 @@ void Win64_SetupRemoteQNetPlayer(IQNetPlayer * player, BYTE smallId, bool isHost
player->m_smallId = smallId; player->m_smallId = smallId;
player->m_isRemote = !isLocal; player->m_isRemote = !isLocal;
player->m_isHostPlayer = isHost; player->m_isHostPlayer = isHost;
player->m_resolvedXuid = INVALID_XUID;
swprintf_s(player->m_gamertag, 32, L"Player%d", smallId); swprintf_s(player->m_gamertag, 32, L"Player%d", smallId);
if (smallId >= IQNet::s_playerCount) if (smallId >= IQNet::s_playerCount)
IQNet::s_playerCount = smallId + 1; IQNet::s_playerCount = smallId + 1;
@ -285,8 +295,13 @@ IQNetPlayer* IQNet::GetPlayerByXuid(PlayerUID xuid)
{ {
for (DWORD i = 0; i < s_playerCount; i++) 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]; return &m_player[0];
} }
DWORD IQNet::GetPlayerCount() DWORD IQNet::GetPlayerCount()
@ -301,7 +316,13 @@ DWORD IQNet::GetPlayerCount()
QNET_STATE IQNet::GetState() { return _iQNetStubState; } QNET_STATE IQNet::GetState() { return _iQNetStubState; }
bool IQNet::IsHost() { return s_isHosting; } bool IQNet::IsHost() { return s_isHosting; }
HRESULT IQNet::JoinGameFromInviteInfo(DWORD dwUserIndex, DWORD dwUserMask, const INVITE_INFO * pInviteInfo) { return S_OK; } 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() void IQNet::ClientJoinGame()
{ {
_iQNetStubState = QNET_STATE_SESSION_STARTING; _iQNetStubState = QNET_STATE_SESSION_STARTING;
@ -312,6 +333,7 @@ void IQNet::ClientJoinGame()
m_player[i].m_smallId = (BYTE)i; m_player[i].m_smallId = (BYTE)i;
m_player[i].m_isRemote = true; m_player[i].m_isRemote = true;
m_player[i].m_isHostPlayer = false; m_player[i].m_isHostPlayer = false;
m_player[i].m_resolvedXuid = INVALID_XUID;
m_player[i].m_gamertag[0] = 0; m_player[i].m_gamertag[0] = 0;
m_player[i].SetCustomDataValue(0); m_player[i].SetCustomDataValue(0);
} }
@ -326,6 +348,7 @@ void IQNet::EndGame()
m_player[i].m_smallId = (BYTE)i; m_player[i].m_smallId = (BYTE)i;
m_player[i].m_isRemote = false; m_player[i].m_isRemote = false;
m_player[i].m_isHostPlayer = false; m_player[i].m_isHostPlayer = false;
m_player[i].m_resolvedXuid = INVALID_XUID;
m_player[i].m_gamertag[0] = 0; m_player[i].m_gamertag[0] = 0;
m_player[i].SetCustomDataValue(0); m_player[i].SetCustomDataValue(0);
} }
@ -575,10 +598,13 @@ void C_4JProfile::GetXUID(int iPad, PlayerUID * pXuid, bool bOnlineXuid)
*pXuid = INVALID_XUID; *pXuid = INVALID_XUID;
return; 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) if (IQNet::s_isHosting)
*pXuid = 0xe000d45248242f2e; *pXuid = Win64Xuid::GetLegacyEmbeddedHostXuid();
else else
*pXuid = 0xe000d45248242f2e + WinsockNetLayer::GetLocalSmallId(); *pXuid = Win64Xuid::ResolvePersistentXuid();
#else #else
* pXuid = 0xe000d45248242f2e + iPad; * pXuid = 0xe000d45248242f2e + iPad;
#endif #endif

View file

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<LastConfigDeployed>Debug</LastConfigDeployed>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LocalDebuggerWorkingDirectory>$(SolutionDir)$(Platform)\$(Configuration)\</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64EC'">
<LocalDebuggerWorkingDirectory>$(SolutionDir)$(Platform)\$(Configuration)\</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LocalDebuggerWorkingDirectory>$(SolutionDir)$(Platform)\$(Configuration)\</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64EC'">
<LocalDebuggerWorkingDirectory>$(SolutionDir)$(Platform)\$(Configuration)\</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>

View file

@ -52,6 +52,7 @@
#include "..\Minecraft.World\net.minecraft.world.level.dimension.h" #include "..\Minecraft.World\net.minecraft.world.level.dimension.h"
#include "..\Minecraft.World\net.minecraft.world.item.h" #include "..\Minecraft.World\net.minecraft.world.item.h"
#include "..\Minecraft.World\Minecraft.World.h" #include "..\Minecraft.World\Minecraft.World.h"
#include "Windows64\Windows64_Xuid.h"
#include "ClientConnection.h" #include "ClientConnection.h"
#include "..\Minecraft.World\HellRandomLevelSource.h" #include "..\Minecraft.World\HellRandomLevelSource.h"
#include "..\Minecraft.World\net.minecraft.world.entity.animal.h" #include "..\Minecraft.World\net.minecraft.world.entity.animal.h"
@ -1038,6 +1039,19 @@ shared_ptr<MultiplayerLocalPlayer> Minecraft::createExtraLocalPlayer(int idx, co
PlayerUID playerXUIDOnline = INVALID_XUID; PlayerUID playerXUIDOnline = INVALID_XUID;
ProfileManager.GetXUID(idx,&playerXUIDOffline,false); ProfileManager.GetXUID(idx,&playerXUIDOffline,false);
ProfileManager.GetXUID(idx,&playerXUIDOnline,true); ProfileManager.GetXUID(idx,&playerXUIDOnline,true);
#ifdef _WINDOWS64
// Compatibility rule for Win64 id migration
// host keeps legacy host XUID, non-host uses persistent uid.dat XUID.
INetworkPlayer *localNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(idx);
if(localNetworkPlayer != NULL && localNetworkPlayer->IsHost())
{
playerXUIDOffline = Win64Xuid::GetLegacyEmbeddedHostXuid();
}
else
{
playerXUIDOffline = Win64Xuid::ResolvePersistentXuid();
}
#endif
localplayers[idx]->setXuid(playerXUIDOffline); localplayers[idx]->setXuid(playerXUIDOffline);
localplayers[idx]->setOnlineXuid(playerXUIDOnline); localplayers[idx]->setOnlineXuid(playerXUIDOnline);
localplayers[idx]->setIsGuest(ProfileManager.IsGuest(idx)); localplayers[idx]->setIsGuest(ProfileManager.IsGuest(idx));
@ -4298,6 +4312,19 @@ void Minecraft::setLevel(MultiPlayerLevel *level, int message /*=-1*/, shared_pt
// player doesn't have an online UID, set it from the player name // player doesn't have an online UID, set it from the player name
playerXUIDOnline.setForAdhoc(); playerXUIDOnline.setForAdhoc();
} }
#endif
#ifdef _WINDOWS64
// On Windows, the implementation has been changed to use a per-client pseudo XUID based on `uid.dat`.
// To maintain player data compatibility with existing worlds, the world host (the first player) will use the previous embedded pseudo XUID.
INetworkPlayer *localNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(iPrimaryPlayer);
if(localNetworkPlayer != NULL && localNetworkPlayer->IsHost())
{
playerXUIDOffline = Win64Xuid::GetLegacyEmbeddedHostXuid();
}
else
{
playerXUIDOffline = Win64Xuid::ResolvePersistentXuid();
}
#endif #endif
player->setXuid(playerXUIDOffline); player->setXuid(playerXUIDOffline);
player->setOnlineXuid(playerXUIDOnline); player->setOnlineXuid(playerXUIDOnline);
@ -4481,6 +4508,18 @@ void Minecraft::respawnPlayer(int iPad, int dimension, int newEntityId)
PlayerUID playerXUIDOnline = INVALID_XUID; PlayerUID playerXUIDOnline = INVALID_XUID;
ProfileManager.GetXUID(iTempPad,&playerXUIDOffline,false); ProfileManager.GetXUID(iTempPad,&playerXUIDOffline,false);
ProfileManager.GetXUID(iTempPad,&playerXUIDOnline,true); ProfileManager.GetXUID(iTempPad,&playerXUIDOnline,true);
#ifdef _WINDOWS64
// Same compatibility rule as create/init paths.
INetworkPlayer *localNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(iTempPad);
if(localNetworkPlayer != NULL && localNetworkPlayer->IsHost())
{
playerXUIDOffline = Win64Xuid::GetLegacyEmbeddedHostXuid();
}
else
{
playerXUIDOffline = Win64Xuid::ResolvePersistentXuid();
}
#endif
player->setXuid(playerXUIDOffline); player->setXuid(playerXUIDOffline);
player->setOnlineXuid(playerXUIDOnline); player->setOnlineXuid(playerXUIDOnline);
player->setIsGuest( ProfileManager.IsGuest(iTempPad) ); player->setIsGuest( ProfileManager.IsGuest(iTempPad) );

View file

@ -161,6 +161,23 @@ void PendingConnection::handleLogin(shared_ptr<LoginPacket> packet)
//if (true)// 4J removed !server->onlineMode) //if (true)// 4J removed !server->onlineMode)
bool sentDisconnect = false; bool sentDisconnect = false;
// Use the same Xuid choice as handleAcceptedLogin (offline first, online fallback).
//
PlayerUID loginXuid = packet->m_offlineXuid;
if (loginXuid == INVALID_XUID) loginXuid = packet->m_onlineXuid;
bool duplicateXuid = false;
if (loginXuid != INVALID_XUID && server->getPlayers()->getPlayer(loginXuid) != nullptr)
{
duplicateXuid = true;
}
else if (packet->m_onlineXuid != INVALID_XUID &&
packet->m_onlineXuid != loginXuid &&
server->getPlayers()->getPlayer(packet->m_onlineXuid) != nullptr)
{
duplicateXuid = true;
}
if( sentDisconnect ) if( sentDisconnect )
{ {
// Do nothing // Do nothing
@ -169,6 +186,12 @@ void PendingConnection::handleLogin(shared_ptr<LoginPacket> packet)
{ {
disconnect(DisconnectPacket::eDisconnect_Banned); disconnect(DisconnectPacket::eDisconnect_Banned);
} }
else if (duplicateXuid)
{
// if same XUID already in use by another player so disconnect this one.
app.DebugPrintf("Rejecting duplicate xuid for name: %ls\n", name.c_str());
disconnect(DisconnectPacket::eDisconnect_Banned);
}
#ifdef _WINDOWS64 #ifdef _WINDOWS64
else if (g_bRejectDuplicateNames) else if (g_bRejectDuplicateNames)
{ {

View file

@ -1093,6 +1093,10 @@ void PlayerConnection::handleContainerClose(shared_ptr<ContainerClosePacket> pac
#ifndef _CONTENT_PACKAGE #ifndef _CONTENT_PACKAGE
void PlayerConnection::handleContainerSetSlot(shared_ptr<ContainerSetSlotPacket> packet) void PlayerConnection::handleContainerSetSlot(shared_ptr<ContainerSetSlotPacket> packet)
{ {
if(player->gameMode->isSurvival()){ // Still allow creative players to change slots manually with packets(?) -- might want this different.
server->warn(L"Player " + player->getName() + L" just tried to set a slot in a container in survival mode");
return;
}
if (packet->containerId == AbstractContainerMenu::CONTAINER_ID_CARRIED ) if (packet->containerId == AbstractContainerMenu::CONTAINER_ID_CARRIED )
{ {
player->inventory->setCarried(packet->item); player->inventory->setCarried(packet->item);
@ -1589,6 +1593,10 @@ void PlayerConnection::handleCraftItem(shared_ptr<CraftItemPacket> packet)
Recipy::INGREDIENTS_REQUIRED *pRecipeIngredientsRequired=Recipes::getInstance()->getRecipeIngredientsArray(); Recipy::INGREDIENTS_REQUIRED *pRecipeIngredientsRequired=Recipes::getInstance()->getRecipeIngredientsArray();
shared_ptr<ItemInstance> pTempItemInst=pRecipeIngredientsRequired[iRecipe].pRecipy->assemble(nullptr); shared_ptr<ItemInstance> pTempItemInst=pRecipeIngredientsRequired[iRecipe].pRecipy->assemble(nullptr);
size_t recipeCount = Recipes::getInstance()->getRecipies()->size();
if (iRecipe < 0 || iRecipe >= (int)recipeCount)
return;
if(app.DebugSettingsOn() && (player->GetDebugOptions()&(1L<<eDebugSetting_CraftAnything))) if(app.DebugSettingsOn() && (player->GetDebugOptions()&(1L<<eDebugSetting_CraftAnything)))
{ {
pTempItemInst->onCraftedBy(player->level, dynamic_pointer_cast<Player>( player->shared_from_this() ), pTempItemInst->count ); pTempItemInst->onCraftedBy(player->level, dynamic_pointer_cast<Player>( player->shared_from_this() ), pTempItemInst->count );
@ -1607,9 +1615,21 @@ void PlayerConnection::handleCraftItem(shared_ptr<CraftItemPacket> packet)
{ {
// TODO 4J Stu - Assume at the moment that the client can work this out for us... Recipy::INGREDIENTS_REQUIRED &req = pRecipeIngredientsRequired[iRecipe];
//if(pRecipeIngredientsRequired[iRecipe].bCanMake) if (req.iType == RECIPE_TYPE_3x3 && dynamic_cast<CraftingMenu *>(player->containerMenu) == NULL)
//{ {
server->warn(L"Player " + player->getName() + L" tried to craft a 3x3 recipe without a crafting bench");
return;
}
for (int i = 0; i < req.iIngC; i++){
int need = req.iIngValA[i];
int have = player->inventory->countResource(req.iIngIDA[i], req.iIngAuxValA[i]);
if (have < need){
server->warn(L"Player " + player->getName() + L" just tried to craft item " + to_wstring(pTempItemInst->id) + L" with insufficient ingredients");
return;
}
}
pTempItemInst->onCraftedBy(player->level, dynamic_pointer_cast<Player>( player->shared_from_this() ), pTempItemInst->count ); pTempItemInst->onCraftedBy(player->level, dynamic_pointer_cast<Player>( player->shared_from_this() ), pTempItemInst->count );
// and remove those resources from your inventory // and remove those resources from your inventory

View file

@ -18,6 +18,7 @@
#include "..\Minecraft.World\ArrayWithLength.h" #include "..\Minecraft.World\ArrayWithLength.h"
#include "..\Minecraft.World\net.minecraft.network.packet.h" #include "..\Minecraft.World\net.minecraft.network.packet.h"
#include "..\Minecraft.World\net.minecraft.network.h" #include "..\Minecraft.World\net.minecraft.network.h"
#include "Windows64\Windows64_Xuid.h"
#include "..\Minecraft.World\Pos.h" #include "..\Minecraft.World\Pos.h"
#include "..\Minecraft.World\ProgressListener.h" #include "..\Minecraft.World\ProgressListener.h"
#include "..\Minecraft.World\HellRandomLevelSource.h" #include "..\Minecraft.World\HellRandomLevelSource.h"
@ -106,11 +107,18 @@ void PlayerList::placeNewPlayer(Connection *connection, shared_ptr<ServerPlayer>
} }
#endif #endif
#ifdef _WINDOWS64 #ifdef _WINDOWS64
if (networkPlayer != NULL && !networkPlayer->IsLocal()) if (networkPlayer != NULL)
{ {
NetworkPlayerXbox* nxp = (NetworkPlayerXbox*)networkPlayer; NetworkPlayerXbox* nxp = (NetworkPlayerXbox*)networkPlayer;
IQNetPlayer* qnp = nxp->GetQNetPlayer(); IQNetPlayer* qnp = nxp->GetQNetPlayer();
wcsncpy_s(qnp->m_gamertag, 32, player->name.c_str(), _TRUNCATE); if (qnp != NULL)
{
if (!networkPlayer->IsLocal())
{
wcsncpy_s(qnp->m_gamertag, 32, player->name.c_str(), _TRUNCATE);
}
qnp->m_resolvedXuid = player->getXuid();
}
} }
#endif #endif
// 4J Stu - TU-1 hotfix // 4J Stu - TU-1 hotfix
@ -520,12 +528,20 @@ shared_ptr<ServerPlayer> PlayerList::getPlayerForLogin(PendingConnection *pendin
player->setOnlineXuid( onlineXuid ); // 4J Added player->setOnlineXuid( onlineXuid ); // 4J Added
#ifdef _WINDOWS64 #ifdef _WINDOWS64
{ {
// Use packet-supplied identity from LoginPacket.
// Do not recompute from name here: mixed-version clients must stay compatible.
INetworkPlayer* np = pendingConnection->connection->getSocket()->getPlayer(); INetworkPlayer* np = pendingConnection->connection->getSocket()->getPlayer();
if (np != NULL) if (np != NULL)
{ {
PlayerUID realXuid = np->GetUID(); player->setOnlineXuid(np->GetUID());
player->setXuid(realXuid);
player->setOnlineXuid(realXuid); // Backward compatibility: when Minecraft.Client is hosting, keep the first
// host player on the legacy embedded host XUID (base + 0).
// This preserves pre-migration host playerdata in existing worlds.
if (np->IsHost())
{
player->setXuid(Win64Xuid::GetLegacyEmbeddedHostXuid());
}
} }
} }
#endif #endif

View file

@ -42,6 +42,7 @@
#include "..\..\Minecraft.World\OldChunkStorage.h" #include "..\..\Minecraft.World\OldChunkStorage.h"
#include "Common/PostProcesser.h" #include "Common/PostProcesser.h"
#include "Network\WinsockNetLayer.h" #include "Network\WinsockNetLayer.h"
#include "Windows64_Xuid.h"
#include "Xbox/resource.h" #include "Xbox/resource.h"
@ -110,6 +111,7 @@ struct Win64LaunchOptions
{ {
int screenMode; int screenMode;
bool serverMode; bool serverMode;
bool fullscreen;
}; };
static void CopyWideArgToAnsi(LPCWSTR source, char* dest, size_t destSize) static void CopyWideArgToAnsi(LPCWSTR source, char* dest, size_t destSize)
@ -256,6 +258,8 @@ static Win64LaunchOptions ParseLaunchOptions()
g_Win64MultiplayerPort = (int)port; g_Win64MultiplayerPort = (int)port;
} }
} }
else if (_wcsicmp(argv[i], L"-fullscreen") == 0)
options.fullscreen = true;
} }
LocalFree(argv); LocalFree(argv);
@ -1218,6 +1222,12 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
Win64LaunchOptions launchOptions = ParseLaunchOptions(); Win64LaunchOptions launchOptions = ParseLaunchOptions();
ApplyScreenMode(launchOptions.screenMode); ApplyScreenMode(launchOptions.screenMode);
// Ensure uid.dat exists from startup in client mode (before any multiplayer/login path).
if (!launchOptions.serverMode)
{
Win64Xuid::ResolvePersistentXuid();
}
// If no username, let's fall back // If no username, let's fall back
if (g_Win64Username[0] == 0) if (g_Win64Username[0] == 0)
{ {
@ -1245,7 +1255,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
} }
// Restore fullscreen state from previous session // Restore fullscreen state from previous session
if (LoadFullscreenOption() && !g_isFullscreen) if (LoadFullscreenOption() && !g_isFullscreen || launchOptions.fullscreen)
{ {
ToggleFullscreen(); ToggleFullscreen();
} }

View file

@ -0,0 +1,214 @@
#pragma once
#ifdef _WINDOWS64
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cerrno>
#include <cstring>
#include <Windows.h>
namespace Win64Xuid
{
inline PlayerUID GetLegacyEmbeddedBaseXuid()
{
return (PlayerUID)0xe000d45248242f2eULL;
}
inline PlayerUID GetLegacyEmbeddedHostXuid()
{
// Legacy behavior used "embedded base + smallId"; host was always smallId 0.
// We intentionally keep this value for host/self compatibility with pre-migration worlds.
return GetLegacyEmbeddedBaseXuid();
}
inline bool IsLegacyEmbeddedRange(PlayerUID xuid)
{
// Old Win64 XUIDs were not persistent and always lived in this narrow base+smallId range.
// Treat them as legacy/non-persistent so uid.dat values never collide with old slot IDs.
const PlayerUID base = GetLegacyEmbeddedBaseXuid();
return xuid >= base && xuid < (base + MINECRAFT_NET_MAX_PLAYERS);
}
inline bool IsPersistedUidValid(PlayerUID xuid)
{
return xuid != INVALID_XUID && !IsLegacyEmbeddedRange(xuid);
}
// ./uid.dat
inline bool BuildUidFilePath(char* outPath, size_t outPathSize)
{
if (outPath == NULL || outPathSize == 0)
return false;
outPath[0] = 0;
char exePath[MAX_PATH] = {};
DWORD len = GetModuleFileNameA(NULL, exePath, MAX_PATH);
if (len == 0 || len >= MAX_PATH)
return false;
char* lastSlash = strrchr(exePath, '\\');
if (lastSlash != NULL)
{
*(lastSlash + 1) = 0;
}
if (strcpy_s(outPath, outPathSize, exePath) != 0)
return false;
if (strcat_s(outPath, outPathSize, "uid.dat") != 0)
return false;
return true;
}
inline bool ReadUid(PlayerUID* outXuid)
{
if (outXuid == NULL)
return false;
char path[MAX_PATH] = {};
if (!BuildUidFilePath(path, MAX_PATH))
return false;
FILE* f = NULL;
if (fopen_s(&f, path, "rb") != 0 || f == NULL)
return false;
char buffer[128] = {};
size_t readBytes = fread(buffer, 1, sizeof(buffer) - 1, f);
fclose(f);
if (readBytes == 0)
return false;
// Compatibility: earlier experiments may have written raw 8-byte uid.dat.
if (readBytes == sizeof(unsigned __int64))
{
unsigned __int64 raw = 0;
memcpy(&raw, buffer, sizeof(raw));
PlayerUID parsed = (PlayerUID)raw;
if (IsPersistedUidValid(parsed))
{
*outXuid = parsed;
return true;
}
}
buffer[readBytes] = 0;
char* begin = buffer;
while (*begin == ' ' || *begin == '\t' || *begin == '\r' || *begin == '\n')
{
++begin;
}
errno = 0;
char* end = NULL;
unsigned __int64 raw = _strtoui64(begin, &end, 0);
if (begin == end || errno != 0)
return false;
while (*end == ' ' || *end == '\t' || *end == '\r' || *end == '\n')
{
++end;
}
if (*end != 0)
return false;
PlayerUID parsed = (PlayerUID)raw;
if (!IsPersistedUidValid(parsed))
return false;
*outXuid = parsed;
return true;
}
inline bool WriteUid(PlayerUID xuid)
{
char path[MAX_PATH] = {};
if (!BuildUidFilePath(path, MAX_PATH))
return false;
FILE* f = NULL;
if (fopen_s(&f, path, "wb") != 0 || f == NULL)
return false;
int written = fprintf_s(f, "0x%016llX\n", (unsigned long long)xuid);
fclose(f);
return written > 0;
}
inline unsigned __int64 Mix64(unsigned __int64 x)
{
x += 0x9E3779B97F4A7C15ULL;
x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9ULL;
x = (x ^ (x >> 27)) * 0x94D049BB133111EBULL;
return x ^ (x >> 31);
}
inline PlayerUID GeneratePersistentUid()
{
// Avoid rand_s dependency: mix several Win64 runtime values into a 64-bit seed.
FILETIME ft = {};
GetSystemTimeAsFileTime(&ft);
unsigned __int64 t = (((unsigned __int64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
LARGE_INTEGER qpc = {};
QueryPerformanceCounter(&qpc);
unsigned __int64 seed = t;
seed ^= (unsigned __int64)qpc.QuadPart;
seed ^= ((unsigned __int64)GetCurrentProcessId() << 32);
seed ^= (unsigned __int64)GetCurrentThreadId();
seed ^= (unsigned __int64)GetTickCount();
seed ^= (unsigned __int64)(size_t)&qpc;
seed ^= (unsigned __int64)(size_t)GetModuleHandleA(NULL);
unsigned __int64 raw = Mix64(seed) ^ Mix64(seed + 0xA0761D6478BD642FULL);
raw ^= 0x8F4B2D6C1A93E705ULL;
raw |= 0x8000000000000000ULL;
PlayerUID xuid = (PlayerUID)raw;
if (!IsPersistedUidValid(xuid))
{
raw ^= 0x0100000000000001ULL;
xuid = (PlayerUID)raw;
}
if (!IsPersistedUidValid(xuid))
{
// Last-resort deterministic fallback for pathological cases.
xuid = (PlayerUID)0xD15EA5E000000001ULL;
}
return xuid;
}
inline PlayerUID ResolvePersistentXuid()
{
// Process-local cache: uid.dat is immutable during runtime and this path is hot.
static bool s_cached = false;
static PlayerUID s_xuid = INVALID_XUID;
if (s_cached)
return s_xuid;
PlayerUID fileXuid = INVALID_XUID;
if (ReadUid(&fileXuid))
{
s_xuid = fileXuid;
s_cached = true;
return s_xuid;
}
// First launch on this client: generate once and persist to uid.dat.
s_xuid = GeneratePersistentUid();
WriteUid(s_xuid);
s_cached = true;
return s_xuid;
}
}
#endif

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show more