Merge branch 'main' of https://github.com/smartcmd/MinecraftConsoles into fix-packet-vulns-v1

This commit is contained in:
lag 2026-03-06 00:32:59 -06:00
commit 1fe9dc9c8a
80 changed files with 444 additions and 115 deletions

3
.gitignore vendored
View file

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

View file

@ -759,6 +759,18 @@ void ClientConnection::handleAddPlayer(shared_ptr<AddPlayerPacket> packet)
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
// 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),
@ -800,22 +812,50 @@ void ClientConnection::handleAddPlayer(shared_ptr<AddPlayerPacket> packet)
#ifdef _WINDOWS64
{
IQNetPlayer* matchedQNetPlayer = NULL;
PlayerUID pktXuid = player->getXuid();
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)
{
BYTE smallId = (BYTE)(pktXuid - WIN64_XUID_BASE);
INetworkPlayer* np = g_NetworkManager.GetPlayerBySmallId(smallId);
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;
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
@ -985,6 +1025,8 @@ void ClientConnection::handleRemoveEntity(shared_ptr<RemoveEntitiesPacket> packe
qp->m_smallId = 0;
qp->m_isRemote = 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->SetCustomDataValue(0);
}
@ -3956,4 +3998,4 @@ ClientConnection::DeferredEntityLinkPacket::DeferredEntityLinkPacket(shared_ptr<
{
m_recievedTick = GetTickCount();
m_packet = packet;
}
}

View file

@ -260,7 +260,7 @@ void SoundEngine::updateMiniAudio()
continue;
}
float finalVolume = s->info.volume;
float finalVolume = s->info.volume * m_MasterEffectsVolume;
if (finalVolume > 1.0f)
finalVolume = 1.0f;
@ -967,14 +967,8 @@ void SoundEngine::playMusicTick()
// AP - moved to a separate function so it can be called from the mixer callback on Vita
void SoundEngine::playMusicUpdate()
{
//return;
static bool firstCall = true;
static float fMusicVol = 0.0f;
if( firstCall )
{
fMusicVol = getMasterMusicVolume();
firstCall = false;
}
fMusicVol = getMasterMusicVolume();
switch(m_StreamState)
{
@ -1242,7 +1236,7 @@ void SoundEngine::playMusicUpdate()
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_result startResult = ma_sound_start(&m_musicStream);
@ -1370,14 +1364,11 @@ void SoundEngine::playMusicUpdate()
}
// 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
{
return 0;

View file

@ -5,6 +5,7 @@
#include "..\..\Xbox\Network\NetworkPlayerXbox.h"
#ifdef _WINDOWS64
#include "..\..\Windows64\Network\WinsockNetLayer.h"
#include "..\..\Windows64\Windows64_Xuid.h"
#include "..\..\Minecraft.h"
#include "..\..\User.h"
#include <iostream>
@ -234,6 +235,7 @@ void CPlatformNetworkManagerStub::DoWork()
qnetPlayer->m_smallId = 0;
qnetPlayer->m_isRemote = false;
qnetPlayer->m_isHostPlayer = false;
qnetPlayer->m_resolvedXuid = INVALID_XUID;
qnetPlayer->m_gamertag[0] = 0;
qnetPlayer->SetCustomDataValue(0);
WinsockNetLayer::PushFreeSmallId(disconnectedSmallId);
@ -354,7 +356,9 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame,
#ifdef _WINDOWS64
IQNet::m_player[0].m_smallId = 0;
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_resolvedXuid = Win64Xuid::GetLegacyEmbeddedHostXuid();
IQNet::s_playerCount = 1;
#endif
@ -411,6 +415,8 @@ int CPlatformNetworkManagerStub::JoinGame(FriendSessionInfo* searchResult, int l
IQNet::m_player[0].m_smallId = 0;
IQNet::m_player[0].m_isRemote = 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);
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_isRemote = 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();
wcscpy_s(IQNet::m_player[localSmallId].m_gamertag, 32, pMinecraft->user->name.c_str());

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
removeControl( &m_buttons[BUTTON_HAO_REINSTALL], false );
#ifdef _FINAL_BUILD
#ifndef _DEBUG
removeControl( &m_buttons[BUTTON_HAO_DEBUG], false);
#else
if(!app.DebugSettingsOn()) removeControl( &m_buttons[BUTTON_HAO_DEBUG], false);

View file

@ -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

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.item.h"
#include "..\Minecraft.World\Minecraft.World.h"
#include "Windows64\Windows64_Xuid.h"
#include "ClientConnection.h"
#include "..\Minecraft.World\HellRandomLevelSource.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;
ProfileManager.GetXUID(idx,&playerXUIDOffline,false);
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]->setOnlineXuid(playerXUIDOnline);
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
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
player->setXuid(playerXUIDOffline);
player->setOnlineXuid(playerXUIDOnline);
@ -4481,6 +4508,18 @@ void Minecraft::respawnPlayer(int iPad, int dimension, int newEntityId)
PlayerUID playerXUIDOnline = INVALID_XUID;
ProfileManager.GetXUID(iTempPad,&playerXUIDOffline,false);
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->setOnlineXuid(playerXUIDOnline);
player->setIsGuest( ProfileManager.IsGuest(iTempPad) );

View file

@ -161,6 +161,23 @@ void PendingConnection::handleLogin(shared_ptr<LoginPacket> packet)
//if (true)// 4J removed !server->onlineMode)
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 )
{
// Do nothing
@ -169,6 +186,12 @@ void PendingConnection::handleLogin(shared_ptr<LoginPacket> packet)
{
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
else if (g_bRejectDuplicateNames)
{

View file

@ -18,6 +18,7 @@
#include "..\Minecraft.World\ArrayWithLength.h"
#include "..\Minecraft.World\net.minecraft.network.packet.h"
#include "..\Minecraft.World\net.minecraft.network.h"
#include "Windows64\Windows64_Xuid.h"
#include "..\Minecraft.World\Pos.h"
#include "..\Minecraft.World\ProgressListener.h"
#include "..\Minecraft.World\HellRandomLevelSource.h"
@ -106,11 +107,18 @@ void PlayerList::placeNewPlayer(Connection *connection, shared_ptr<ServerPlayer>
}
#endif
#ifdef _WINDOWS64
if (networkPlayer != NULL && !networkPlayer->IsLocal())
if (networkPlayer != NULL)
{
NetworkPlayerXbox* nxp = (NetworkPlayerXbox*)networkPlayer;
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
// 4J Stu - TU-1 hotfix
@ -520,12 +528,20 @@ shared_ptr<ServerPlayer> PlayerList::getPlayerForLogin(PendingConnection *pendin
player->setOnlineXuid( onlineXuid ); // 4J Added
#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();
if (np != NULL)
{
PlayerUID realXuid = np->GetUID();
player->setXuid(realXuid);
player->setOnlineXuid(realXuid);
player->setOnlineXuid(np->GetUID());
// 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

View file

@ -42,6 +42,7 @@
#include "..\..\Minecraft.World\OldChunkStorage.h"
#include "Common/PostProcesser.h"
#include "Network\WinsockNetLayer.h"
#include "Windows64_Xuid.h"
#include "Xbox/resource.h"
@ -110,6 +111,7 @@ struct Win64LaunchOptions
{
int screenMode;
bool serverMode;
bool fullscreen;
};
static void CopyWideArgToAnsi(LPCWSTR source, char* dest, size_t destSize)
@ -256,6 +258,8 @@ static Win64LaunchOptions ParseLaunchOptions()
g_Win64MultiplayerPort = (int)port;
}
}
else if (_wcsicmp(argv[i], L"-fullscreen") == 0)
options.fullscreen = true;
}
LocalFree(argv);
@ -1218,6 +1222,12 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
Win64LaunchOptions launchOptions = ParseLaunchOptions();
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 (g_Win64Username[0] == 0)
{
@ -1245,7 +1255,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
}
// Restore fullscreen state from previous session
if (LoadFullscreenOption() && !g_isFullscreen)
if (LoadFullscreenOption() && !g_isFullscreen || launchOptions.fullscreen)
{
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

View file

@ -74,7 +74,7 @@ bool Boat::isPushable()
Boat::Boat(Level *level, double x, double y, double z) : Entity( level )
{
_init();
setPos(x, y + heightOffset, z);
setPos(x, y + heightOffset + 0.1, z);
xd = 0;
yd = 0;
@ -209,30 +209,9 @@ void Boat::tick()
}
double lastSpeed = sqrt(xd * xd + zd * zd);
if (lastSpeed > MAX_COLLISION_SPEED)
if (lastSpeed > MAX_COLLISION_SPEED && waterPercentage > 1)
{
double xa = cos(yRot * PI / 180);
double za = sin(yRot * PI / 180);
for (int i = 0; i < 1 + lastSpeed * 60; i++)
{
double side = (random->nextFloat() * 2 - 1);
double side2 = (random->nextInt(2) * 2 - 1) * 0.7;
if (random->nextBoolean())
{
double xx = x - xa * side * 0.8 + za * side2;
double zz = z - za * side * 0.8 - xa * side2;
level->addParticle(eParticleType_splash, xx, y - 2 / 16.0f, zz, +xd, yd, +zd);
}
else
{
double xx = x + xa + za * side * 0.7;
double zz = z + za - xa * side * 0.7;
level->addParticle(eParticleType_splash, xx, y - 2 / 16.0f, zz, +xd, yd, +zd);
}
}
createSplash(lastSpeed);
}
if (level->isClientSide && doLerp)
@ -254,7 +233,6 @@ void Boat::tick()
}
else
{
#if 1
// Original
//double xt = x + xd;
//double yt = y + yd;
@ -273,51 +251,23 @@ void Boat::tick()
xd *= 0.99f;
yd *= 0.95f;
zd *= 0.99f;
#else
// 4J Stu - Fix for #8280 - Gameplay : Boats behave erratically when exited next to land.
// The client shouldn't change the position of the boat
double xt = x;// + xd;
double yt = y + yd;
double zt = z;// + zd;
this->setPos(xt, yt, zt);
// 4J Stu - Fix for #9579 - GAMEPLAY: Boats with a player in them slowly sink under the water over time, and with no player in them they float into the sky.
// Just make the boats bob up and down rather than any other client-side movement when not receiving packets from server
if (waterPercentage > 0)
{
double bob = waterPercentage * 2 - 1;
yd += 0.04f * bob;
}
else
{
if (yd < 0) yd /= 2;
yd += 0.007f;
}
//if (onGround)
//{
xd *= 0.5f;
yd *= 0.5f;
zd *= 0.5f;
//}
//xd *= 0.99f;
//yd *= 0.95f;
//zd *= 0.99f;
#endif
}
return;
}
// Bob on water
if (waterPercentage > 0)
{
double bob = waterPercentage * 2 - 1;
yd += 0.04f * bob;
}
else
{
yd = 0;
// Reimplement "gravity"
if (level->getTile(x, Mth::floor(y - 0.15), z) == 0 && !onGround) {
yd += 0.04f * -1.0; // -1.0 is what bob should return in this situation, just hardcoded.
}
// Rider Controls
if ( rider.lock() != NULL && rider.lock()->instanceof(eTYPE_LIVINGENTITY) )
{
shared_ptr<LivingEntity> livingRider = dynamic_pointer_cast<LivingEntity>(rider.lock());
@ -334,6 +284,7 @@ void Boat::tick()
double curSpeed = sqrt(xd * xd + zd * zd);
// Speed Clamp
if (curSpeed > MAX_SPEED)
{
double ratio = MAX_SPEED / curSpeed;
@ -354,14 +305,15 @@ void Boat::tick()
if (acceleration < MIN_ACCELERATION) acceleration = MIN_ACCELERATION;
}
// Slow speed on ground
if (onGround)
{
xd *= 0.5f;
yd *= 0.5f;
zd *= 0.5f;
}
move(xd, yd, zd);
// Break boat
if ((horizontalCollision && lastSpeed > 0.20))
{
if (!level->isClientSide && !removed)
@ -401,6 +353,7 @@ void Boat::tick()
yRot += (float) rotDiff;
setRot(yRot, xRot);
// Server only code below
if(level->isClientSide) return;
vector<shared_ptr<Entity> > *entities = level->getEntities(shared_from_this(), bb->grow(0.2f, 0, 0.2f));
@ -444,13 +397,37 @@ void Boat::tick()
}
}
void Boat::createSplash(double particleStrengh) {
double xa = cos(yRot * PI / 180);
double za = sin(yRot * PI / 180);
for (int i = 0; i < 1 + particleStrengh * 60; i++)
{
double side = (random->nextFloat() * 2 - 1);
double side2 = (random->nextInt(2) * 2 - 1) * 0.7;
if (random->nextBoolean())
{
double xx = x - xa * side * 0.8 + za * side2;
double zz = z - za * side * 0.8 - xa * side2;
level->addParticle(eParticleType_splash, xx, y - 2 / 16.0f, zz, +xd, yd, +zd);
}
else
{
double xx = x + xa + za * side * 0.7;
double zz = z + za - xa * side * 0.7;
level->addParticle(eParticleType_splash, xx, y - 2 / 16.0f, zz, +xd, yd, +zd);
}
}
}
void Boat::positionRider()
{
if (rider.lock() == NULL) return;
double xa = cos(yRot * PI / 180) * 0.4;
double za = sin(yRot * PI / 180) * 0.4;
rider.lock()->setPos(x + xa, y + getRideHeight() + rider.lock()->getRidingHeight(), z + za);
rider.lock()->setPos(x + xa, y + getRideHeight() + rider.lock()->getRidingHeight()-0.5, z + za);
}

View file

@ -60,6 +60,7 @@ public:
virtual void lerpTo(double x, double y, double z, float yRot, float xRot, int steps);
virtual void lerpMotion(double xd, double yd, double zd);
virtual void tick();
void createSplash(double particleStrengh);
virtual void positionRider();
protected:

View file

@ -219,6 +219,7 @@ public:
BYTE m_smallId;
bool m_isRemote;
bool m_isHostPlayer;
PlayerUID m_resolvedXuid;
wchar_t m_gamertag[32];
private:
ULONG_PTR m_customData;