Whisper Yelled At me 😢

This commit is contained in:
coah
2026-03-04 04:37:07 -06:00
parent 7f59a61704
commit edfc280090
35 changed files with 506 additions and 461 deletions

View File

@@ -40,10 +40,6 @@
#include "..\Minecraft.World\DurangoStats.h"
#endif
#ifdef _WINDOWS64
#include "..\..\Windows64\Network\P2PConnectionManagerWin.h"
#endif
// Global instance
CGameNetworkManager g_NetworkManager;
CPlatformNetworkManager *CGameNetworkManager::s_pPlatformNetworkManager;
@@ -62,10 +58,6 @@ CGameNetworkManager::CGameNetworkManager()
m_pUpsell = NULL;
m_pInviteInfo = NULL;
#endif
#ifdef _WINDOWS64
m_p2pManager = NULL;
#endif
}
void CGameNetworkManager::Initialise()
@@ -91,9 +83,6 @@ void CGameNetworkManager::Terminate()
{
if( m_bInitialised )
{
#ifdef _WINDOWS64
TeardownP2PMesh();
#endif
s_pPlatformNetworkManager->Terminate();
}
}
@@ -129,10 +118,6 @@ void CGameNetworkManager::DoWork()
#endif
s_pPlatformNetworkManager->DoWork();
#ifdef _WINDOWS64
TickP2P();
#endif
#ifdef __ORBIS__
if (m_pUpsell != NULL && m_pUpsell->hasResponse())
{
@@ -181,16 +166,14 @@ bool CGameNetworkManager::_RunNetworkGame(LPVOID lpParameter)
return true;
}
}
else
{
// Client needs QNET_STATE_GAME_PLAY so that IsInGameplay() returns true
s_pPlatformNetworkManager->SetGamePlayState();
}
if( g_NetworkManager.IsLeavingGame() ) return false;
#ifdef _WINDOWS64
if (!g_NetworkManager.IsLocalGame())
{
g_NetworkManager.EstablishP2PMesh();
}
#endif
app.SetGameStarted(true);
// 4J-PB - if this is the trial game, start the trial timer
@@ -1283,15 +1266,7 @@ bool CGameNetworkManager::SystemFlagGet(INetworkPlayer *pNetworkPlayer, int inde
wstring CGameNetworkManager::GatherStats()
{
wstring stats = s_pPlatformNetworkManager->GatherStats();
#ifdef _WINDOWS64
if (m_p2pManager != NULL)
{
stats += L"\n";
stats += GatherP2PStats();
}
#endif
return stats;
return s_pPlatformNetworkManager->GatherStats();
}
void CGameNetworkManager::renderQueueMeter()
@@ -1421,7 +1396,10 @@ void CGameNetworkManager::CreateSocket( INetworkPlayer *pNetworkPlayer, bool loc
Minecraft *pMinecraft = Minecraft::GetInstance();
Socket *socket = NULL;
shared_ptr<MultiplayerLocalPlayer> mpPlayer = pMinecraft->localplayers[pNetworkPlayer->GetUserIndex()];
shared_ptr<MultiplayerLocalPlayer> mpPlayer = nullptr;
int userIdx = pNetworkPlayer->GetUserIndex();
if (userIdx >= 0 && userIdx < XUSER_MAX_COUNT)
mpPlayer = pMinecraft->localplayers[userIdx];
if( localPlayer && mpPlayer != NULL && mpPlayer->connection != NULL)
{
// If we already have a MultiplayerLocalPlayer here then we are doing a session type change
@@ -1518,7 +1496,7 @@ void CGameNetworkManager::PlayerJoining( INetworkPlayer *pNetworkPlayer )
else
{
if( !pNetworkPlayer->IsHost() )
{
{
for(int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
{
if(Minecraft::GetInstance()->localplayers[idx] != NULL)
@@ -1529,29 +1507,10 @@ void CGameNetworkManager::PlayerJoining( INetworkPlayer *pNetworkPlayer )
}
}
#endif
#ifdef _WINDOWS64
if (m_p2pManager != NULL && !pNetworkPlayer->IsLocal())
{
INetworkPlayer* localPlayer = GetLocalPlayerByUserIndex(0);
if (localPlayer != NULL)
{
m_p2pManager->EstablishDirectConnection(localPlayer, pNetworkPlayer);
}
}
#endif
}
void CGameNetworkManager::PlayerLeaving( INetworkPlayer *pNetworkPlayer )
{
#ifdef _WINDOWS64
// Disconnect P2P with the leaving player
if (m_p2pManager != NULL && !pNetworkPlayer->IsLocal())
{
m_p2pManager->DisconnectPeer(pNetworkPlayer);
}
#endif
if( pNetworkPlayer->IsLocal() )
{
ProfileManager.SetCurrentGameActivity(pNetworkPlayer->GetUserIndex(),CONTEXT_PRESENCE_IDLE,false);
@@ -2047,101 +2006,3 @@ void CGameNetworkManager::startAdhocMatching()
}
#endif
#ifdef _WINDOWS64
///////////////////////////////////// P2P Networking /////////////////////////////////////
void CGameNetworkManager::InitializeP2PConnections()
{
if (m_p2pManager != NULL)
return; // Already initialized
CP2PConnectionManagerWin* winP2P = new CP2PConnectionManagerWin();
if (winP2P->Initialize())
{
m_p2pManager = winP2P;
// Wire up the P2P manager to the socket layer and packet router
Socket::SetP2PManager(m_p2pManager);
g_PacketRouter.Initialize(m_p2pManager);
app.DebugPrintf("P2P: Network manager initialized\n");
}
else
{
delete winP2P;
app.DebugPrintf("P2P: Failed to initialize network manager\n");
}
}
void CGameNetworkManager::EstablishP2PMesh()
{
if (m_p2pManager == NULL)
InitializeP2PConnections();
if (m_p2pManager == NULL)
return;
// Discover our public endpoint
m_p2pManager->DiscoverPublicEndpoint();
INetworkPlayer* localPlayer = GetLocalPlayerByUserIndex(0);
if (localPlayer == NULL)
return;
int playerCount = GetPlayerCount();
for (int i = 0; i < playerCount; i++)
{
INetworkPlayer* player = GetPlayerByIndex(i);
if (player != NULL && !player->IsLocal() && player != localPlayer)
{
m_p2pManager->EstablishDirectConnection(localPlayer, player);
}
}
app.DebugPrintf("P2P: Mesh establishment initiated for %d players\n", playerCount);
}
void CGameNetworkManager::TeardownP2PMesh()
{
if (m_p2pManager == NULL)
return;
// Disconnect the P2P manager from socket layer and packet router
Socket::SetP2PManager(NULL);
g_PacketRouter.Shutdown();
// Shutdown and delete
m_p2pManager->Shutdown();
delete m_p2pManager;
m_p2pManager = NULL;
app.DebugPrintf("P2P: Mesh torn down\n");
}
void CGameNetworkManager::TickP2P()
{
if (m_p2pManager != NULL)
{
m_p2pManager->Tick();
}
}
EP2PConnectionState CGameNetworkManager::GetP2PConnectionState(INetworkPlayer* player)
{
if (m_p2pManager == NULL || player == NULL)
return P2P_STATE_DISCONNECTED;
return m_p2pManager->GetConnectionState(player);
}
wstring CGameNetworkManager::GatherP2PStats()
{
if (m_p2pManager == NULL)
return L"P2P: Not active\n";
return m_p2pManager->GetDebugStats();
}
#endif // _WINDOWS64

View File

@@ -15,11 +15,6 @@ using namespace std;
#endif
#include "SessionInfo.h"
#ifdef _WINDOWS64
#include "P2PConnectionManager.h"
#include "PacketRouter.h"
#endif
#ifdef __ORBIS__
#include "..\..\Orbis\Network\PsPlusUpsellWrapper_Orbis.h"
#endif
@@ -164,17 +159,6 @@ public:
void renderQueueMeter();
wstring GatherRTTStats();
#ifdef _WINDOWS64
void InitializeP2PConnections();
void EstablishP2PMesh();
void TeardownP2PMesh();
void TickP2P();
IP2PConnectionManager* GetP2PManager() { return m_p2pManager; }
EP2PConnectionState GetP2PConnectionState(INetworkPlayer* player);
bool IsP2PActive() { return m_p2pManager != NULL; }
wstring GatherP2PStats();
#endif
// GUI debug output
// Used for debugging output
@@ -228,10 +212,6 @@ private:
int GetJoiningReadyPercentage();
bool m_bLastDisconnectWasLostRoomOnly;
bool m_bFullSessionMessageOnNextSessionChange;
#ifdef _WINDOWS64
IP2PConnectionManager* m_p2pManager;
#endif
#if defined __PS3__ || defined __PSVITA__ || defined __ORBIS__
bool m_bSignedOutofPSN;
#endif

View File

@@ -1,105 +0,0 @@
#include "stdafx.h"
#include "PacketRouter.h"
PacketRouter g_PacketRouter;
PacketRouter::PacketRouter()
{
m_p2pManager = NULL;
m_p2pEnabled = false;
m_p2pRoutedCount = 0;
m_hostRoutedCount = 0;
}
void PacketRouter::Initialize(IP2PConnectionManager* p2pManager)
{
m_p2pManager = p2pManager;
m_p2pEnabled = (p2pManager != NULL);
}
void PacketRouter::Shutdown()
{
m_p2pManager = NULL;
m_p2pEnabled = false;
ResetStats();
}
ERouteDecision PacketRouter::GetRouteDecision(shared_ptr<Packet> packet, INetworkPlayer* targetPlayer)
{
// If P2P is disabled or no manager, always use host
if (!m_p2pEnabled || m_p2pManager == NULL || targetPlayer == NULL)
return ROUTE_VIA_HOST;
// Local players don't need P2P
if (targetPlayer->IsLocal())
return ROUTE_VIA_HOST;
int packetId = packet->getId();
EPacketRoutingMode routingMode = Packet::GetRoutingMode(packetId);
switch (routingMode)
{
case ROUTING_HOST_ONLY:
return ROUTE_VIA_HOST;
case ROUTING_PREFER_DIRECT:
// Use P2P if we have a direct connection to this player
if (m_p2pManager->IsDirectConnectionAvailable(targetPlayer))
return ROUTE_VIA_P2P;
return ROUTE_VIA_HOST;
case ROUTING_DIRECT_ONLY:
if (m_p2pManager->IsDirectConnectionAvailable(targetPlayer))
return ROUTE_VIA_P2P;
return ROUTE_VIA_HOST; // Fallback to host if no direct link
case ROUTING_BROADCAST:
return ROUTE_VIA_HOST; // Broadcasts always go through host
default:
return ROUTE_VIA_HOST;
}
}
bool PacketRouter::RoutePacket(shared_ptr<Packet> packet, INetworkPlayer* targetPlayer,
const void* serializedData, int dataSize)
{
ERouteDecision decision = GetRouteDecision(packet, targetPlayer);
if (decision == ROUTE_VIA_P2P && m_p2pManager != NULL)
{
EP2PChannel channel = GetChannelForPacket(packet->getId());
m_p2pManager->SendDirect(targetPlayer, serializedData, dataSize, channel);
m_p2pRoutedCount++;
return true;
}
m_hostRoutedCount++;
return false; // Caller should use existing host path
}
EP2PChannel PacketRouter::GetChannelForPacket(int packetId)
{
// Movement packets
if ((packetId >= 10 && packetId <= 13) || // MovePlayerPacket variants
(packetId >= 30 && packetId <= 34) || // MoveEntityPacket variants
(packetId >= 162 && packetId <= 165)) // MoveEntityPacketSmall variants
{
return P2P_CHANNEL_MOVEMENT;
}
// Animation packets
if (packetId == 18 || packetId == 35) // AnimatePacket, RotateHeadPacket
{
return P2P_CHANNEL_ANIMATION;
}
// Effects/sounds
if (packetId == 62) // LevelSoundPacket
{
return P2P_CHANNEL_EFFECTS;
}
// Default
return P2P_CHANNEL_MOVEMENT;
}

View File

@@ -1,49 +0,0 @@
#pragma once
#include "..\..\..\Minecraft.World\Packet.h"
#include "P2PConnectionManager.h"
#include "NetworkPlayerInterface.h"
// Packet routing decision
enum ERouteDecision
{
ROUTE_VIA_HOST = 0, // Send through host TCP (existing path)
ROUTE_VIA_P2P, // Send directly via P2P UDP
ROUTE_VIA_BOTH // Send via both paths (redundancy for critical packets)
};
class PacketRouter
{
public:
PacketRouter();
// Initialize with P2P manager reference
void Initialize(IP2PConnectionManager* p2pManager);
void Shutdown();
// Determine how to route a packet to a specific player
ERouteDecision GetRouteDecision(shared_ptr<Packet> packet, INetworkPlayer* targetPlayer);
// Route a serialized packet - returns true if sent via P2P
bool RoutePacket(shared_ptr<Packet> packet, INetworkPlayer* targetPlayer,
const void* serializedData, int dataSize);
// Enable/disable P2P routing (global toggle)
void SetP2PEnabled(bool enabled) { m_p2pEnabled = enabled; }
bool IsP2PEnabled() const { return m_p2pEnabled; }
// Map packet IDs to P2P channels
static EP2PChannel GetChannelForPacket(int packetId);
// Stats
int GetP2PRoutedCount() const { return m_p2pRoutedCount; }
int GetHostRoutedCount() const { return m_hostRoutedCount; }
void ResetStats() { m_p2pRoutedCount = 0; m_hostRoutedCount = 0; }
private:
IP2PConnectionManager* m_p2pManager;
bool m_p2pEnabled;
int m_p2pRoutedCount;
int m_hostRoutedCount;
};
extern PacketRouter g_PacketRouter;

View File

@@ -71,6 +71,7 @@ public:
virtual void HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS, unsigned char privateSlots = 0) = 0;
virtual int JoinGame(FriendSessionInfo *searchResult, int dwLocalUsersMask, int dwPrimaryUserIndex ) = 0;
virtual void CancelJoinGame() {};
virtual void SetGamePlayState() {};
virtual bool SetLocalGame(bool isLocal) = 0;
virtual bool IsLocalGame() = 0;
virtual void SetPrivateGame(bool isPrivate) = 0;

View File

@@ -480,6 +480,14 @@ void CPlatformNetworkManagerStub::HandleSignInChange()
return;
}
void CPlatformNetworkManagerStub::SetGamePlayState()
{
#ifdef _WINDOWS64
extern QNET_STATE _iQNetStubState;
_iQNetStubState = QNET_STATE_GAME_PLAY;
#endif
}
bool CPlatformNetworkManagerStub::_RunNetworkGame()
{
#ifdef _WINDOWS64

View File

@@ -55,6 +55,7 @@ public:
virtual void HandleSignInChange();
virtual bool _RunNetworkGame();
virtual void SetGamePlayState();
private:
bool isSystemPrimaryPlayer(IQNetPlayer *pQNetPlayer);

View File

@@ -2,11 +2,6 @@
#include "UI.h"
#include "UIScene_LoadOrJoinMenu.h"
#ifdef _WINDOWS64
#include "..\Network\P2PConnectionManager.h"
#include "..\Network\GameNetworkManager.h"
#endif
#include "..\..\..\Minecraft.World\StringHelpers.h"
#include "..\..\..\Minecraft.World\net.minecraft.world.item.h"
#include "..\..\..\Minecraft.World\net.minecraft.world.level.h"
@@ -288,13 +283,8 @@ UIScene_LoadOrJoinMenu::~UIScene_LoadOrJoinMenu()
g_NetworkManager.SetSessionsUpdatedCallback( NULL, NULL );
app.SetLiveLinkRequired( false );
if(m_currentSessions)
{
for(AUTO_VAR(it, m_currentSessions->begin()); it < m_currentSessions->end(); ++it)
{
delete (*it);
}
}
delete m_currentSessions;
m_currentSessions = NULL;
#if TO_BE_IMPLEMENTED
// Reset the background downloading, in case we changed it by attempting to download a texture pack
@@ -1739,24 +1729,6 @@ void UIScene_LoadOrJoinMenu::UpdateGamesList()
}
}
#ifdef _WINDOWS64
if (g_NetworkManager.IsP2PActive() && g_NetworkManager.IsInGameplay())
{
int directPeers = g_NetworkManager.GetP2PManager()->GetDirectPeerCount();
int totalOnline = g_NetworkManager.GetOnlinePlayerCount();
if (directPeers > 0 && totalOnline > 1)
{
wchar_t labelWithP2P[256];
swprintf_s(labelWithP2P, 256, L"%s [P2P:%d/%d]", sessionInfo->displayLabel, directPeers, totalOnline - 1);
m_buttonListGames.addItem(labelWithP2P, textureName);
}
else
{
m_buttonListGames.addItem(sessionInfo->displayLabel, textureName);
}
}
else
#endif
m_buttonListGames.addItem( sessionInfo->displayLabel, textureName );
if(memcmp( &selectedSessionId, &sessionInfo->sessionId, sizeof(SessionID) ) == 0)

View File

@@ -105,7 +105,6 @@ private:
bool m_bSaveTransferCancelled;
#endif
bool m_bUpdateSaveSize;
wstring m_directConnectAddress;
public:
UIScene_LoadOrJoinMenu(int iPad, void *initData, UILayer *parentLayer);
@@ -154,7 +153,6 @@ public:
static int DeleteSaveDataReturned(LPVOID lpParam,bool bRes);
static int RenameSaveDataReturned(LPVOID lpParam,bool bRes);
static int KeyboardCompleteWorldNameCallback(LPVOID lpParam,bool bRes);
static int KeyboardCompleteDirectConnectCallback(LPVOID lpParam,bool bRes);
protected:
void handlePress(F64 controlId, F64 childId);
void LoadLevelGen(LevelGenerationOptions *levelGen);

View File

@@ -21562,7 +21562,6 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
<ClCompile Include="Common\Network\GameNetworkManager.cpp" />
<ClCompile Include="Common\Network\NATTraversal.cpp" />
<ClCompile Include="Common\Network\LANSessionManager.cpp" />
<ClCompile Include="Common\Network\PacketRouter.cpp" />
<ClCompile Include="Common\Network\STUNClient.cpp" />
<ClCompile Include="ConnectScreen.cpp" />
<ClCompile Include="KeyboardMouseInput.cpp" />

View File

@@ -161,12 +161,17 @@ bool CGameNetworkManager::_RunNetworkGame(LPVOID lpParameter)
success = s_pPlatformNetworkManager->_RunNetworkGame();
if(!success)
{
{
app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_ExitWorld,(void *)TRUE);
return true;
}
}
else
{
// Client needs QNET_STATE_GAME_PLAY so that IsInGameplay() returns true
s_pPlatformNetworkManager->SetGamePlayState();
}
if( g_NetworkManager.IsLeavingGame() ) return false;
app.SetGameStarted(true);
@@ -1391,7 +1396,10 @@ void CGameNetworkManager::CreateSocket( INetworkPlayer *pNetworkPlayer, bool loc
Minecraft *pMinecraft = Minecraft::GetInstance();
Socket *socket = NULL;
shared_ptr<MultiplayerLocalPlayer> mpPlayer = pMinecraft->localplayers[pNetworkPlayer->GetUserIndex()];
shared_ptr<MultiplayerLocalPlayer> mpPlayer = nullptr;
int userIdx = pNetworkPlayer->GetUserIndex();
if (userIdx >= 0 && userIdx < XUSER_MAX_COUNT)
mpPlayer = pMinecraft->localplayers[userIdx];
if( localPlayer && mpPlayer != NULL && mpPlayer->connection != NULL)
{
// If we already have a MultiplayerLocalPlayer here then we are doing a session type change

View File

@@ -71,6 +71,7 @@ public:
virtual void HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS, unsigned char privateSlots = 0) = 0;
virtual int JoinGame(FriendSessionInfo *searchResult, int dwLocalUsersMask, int dwPrimaryUserIndex ) = 0;
virtual void CancelJoinGame() {};
virtual void SetGamePlayState() {};
virtual bool SetLocalGame(bool isLocal) = 0;
virtual bool IsLocalGame() = 0;
virtual void SetPrivateGame(bool isPrivate) = 0;

View File

@@ -313,6 +313,12 @@ void CPlatformNetworkManagerStub::HandleSignInChange()
return;
}
void CPlatformNetworkManagerStub::SetGamePlayState()
{
extern QNET_STATE _iQNetStubState;
_iQNetStubState = QNET_STATE_GAME_PLAY;
}
bool CPlatformNetworkManagerStub::_RunNetworkGame()
{
return true;

View File

@@ -55,6 +55,7 @@ public:
virtual void HandleSignInChange();
virtual bool _RunNetworkGame();
virtual void SetGamePlayState();
private:
bool isSystemPrimaryPlayer(IQNetPlayer *pQNetPlayer);

View File

@@ -1353,7 +1353,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
PIXEndNamedEvent();
PIXBeginNamedEvent(0,"Network manager do work #1");
// g_NetworkManager.DoWork();
g_NetworkManager.DoWork();
PIXEndNamedEvent();
// LeaderboardManager::Instance()->Tick();

View File

@@ -142,36 +142,6 @@ void Packet::staticCtor()
//map(253, true, false, ServerAuthDataPacket.class);
map(254, false, true, false, false, typeid(GetInfoPacket), GetInfoPacket::create); // TODO New for 1.8.2 - Needs sendToAny?
map(255, true, true, true, false, typeid(DisconnectPacket), DisconnectPacket::create);
#ifdef _WINDOWS64
// P2P Routing Modes: Mark movement/visual packets as P2P-eligible
// These packets are safe for direct peer delivery (visual only, no game state)
// Player movement - high frequency, latency-sensitive
SetRoutingMode(10, ROUTING_PREFER_DIRECT); // MovePlayerPacket
SetRoutingMode(11, ROUTING_PREFER_DIRECT); // MovePlayerPacket::Pos
SetRoutingMode(12, ROUTING_PREFER_DIRECT); // MovePlayerPacket::Rot
SetRoutingMode(13, ROUTING_PREFER_DIRECT); // MovePlayerPacket::PosRot
// Entity movement - visual interpolation
SetRoutingMode(30, ROUTING_PREFER_DIRECT); // MoveEntityPacket
SetRoutingMode(31, ROUTING_PREFER_DIRECT); // MoveEntityPacket::Pos
SetRoutingMode(32, ROUTING_PREFER_DIRECT); // MoveEntityPacket::Rot
SetRoutingMode(33, ROUTING_PREFER_DIRECT); // MoveEntityPacket::PosRot
SetRoutingMode(162, ROUTING_PREFER_DIRECT); // MoveEntityPacketSmall
SetRoutingMode(163, ROUTING_PREFER_DIRECT); // MoveEntityPacketSmall::Pos
SetRoutingMode(164, ROUTING_PREFER_DIRECT); // MoveEntityPacketSmall::Rot
SetRoutingMode(165, ROUTING_PREFER_DIRECT); // MoveEntityPacketSmall::PosRot
// Animations and visual effects
SetRoutingMode(18, ROUTING_PREFER_DIRECT); // AnimatePacket
SetRoutingMode(35, ROUTING_PREFER_DIRECT); // RotateHeadPacket
// Non-critical audio
SetRoutingMode(62, ROUTING_PREFER_DIRECT); // LevelSoundPacket
// All other packets default to ROUTING_HOST_ONLY (game state, inventory, spawns, etc.)
#endif
}
IllegalArgumentException::IllegalArgumentException(const wstring& information)
@@ -195,21 +165,6 @@ unordered_set<int> Packet::clientReceivedPackets = unordered_set<int>();
unordered_set<int> Packet::serverReceivedPackets = unordered_set<int>();
unordered_set<int> Packet::sendToAnyClientPackets = unordered_set<int>();
unordered_map<int, EPacketRoutingMode> Packet::packetRoutingModes = unordered_map<int, EPacketRoutingMode>();
EPacketRoutingMode Packet::GetRoutingMode(int packetId)
{
auto it = packetRoutingModes.find(packetId);
if (it != packetRoutingModes.end())
return it->second;
return ROUTING_HOST_ONLY; // Default: everything goes through host
}
void Packet::SetRoutingMode(int packetId, EPacketRoutingMode mode)
{
packetRoutingModes[packetId] = mode;
}
// 4J Added
unordered_map<int, Packet::PacketStatistics *> Packet::outgoingStatistics = unordered_map<int, Packet::PacketStatistics *>();
vector<Packet::PacketStatistics *> Packet::renderableStats = vector<Packet::PacketStatistics *>();

View File

@@ -8,14 +8,6 @@ class DataOutputStream;
#define PACKET_ENABLE_STAT_TRACKING 0
enum EPacketRoutingMode
{
ROUTING_HOST_ONLY = 0, // Must go through host (game state, actions, spawns)
ROUTING_PREFER_DIRECT, // Use P2P if available, fall back to host
ROUTING_DIRECT_ONLY, // Only send via P2P (keepalives, P2P-specific)
ROUTING_BROADCAST // Send to all peers (host broadcasts)
};
class Packet;
typedef shared_ptr<Packet> (*packetCreateFn)();
@@ -61,10 +53,6 @@ public:
static unordered_set<int> serverReceivedPackets;
static unordered_set<int> sendToAnyClientPackets;
static unordered_map<int, EPacketRoutingMode> packetRoutingModes;
static EPacketRoutingMode GetRoutingMode(int packetId);
static void SetRoutingMode(int packetId, EPacketRoutingMode mode);
// 4J Stu - Added the sendToAnyClient param so we can limit some packets to be only sent to one player on a system
// 4J Stu - Added renderStats param for use in debugging
static void map(int id, bool receiveOnClient, bool receiveOnServer, bool sendToAnyClient, bool renderStats, const type_info& clazz, packetCreateFn );

View File

@@ -6,10 +6,6 @@
#include "..\Minecraft.Client\ServerConnection.h"
#include <algorithm>
#include "..\Minecraft.Client\PS3\PS3Extras\ShutdownManager.h"
#ifdef _WINDOWS64
#include "..\Minecraft.Client\Common\Network\PacketRouter.h"
#include "..\Minecraft.Client\Common\Network\P2PConnectionManager.h"
#endif
// This current socket implementation is for the creation of a single local link. 2 sockets can be created, one for either end of this local
// link, the end (0 or 1) is passed as a parameter to the ctor.
@@ -20,10 +16,6 @@ Socket::SocketOutputStreamLocal *Socket::s_hostOutStream[2];
Socket::SocketInputStreamLocal *Socket::s_hostInStream[2];
ServerConnection *Socket::s_serverConnection = NULL;
#ifdef _WINDOWS64
IP2PConnectionManager* Socket::s_p2pManager = NULL;
#endif
void Socket::Initialise(ServerConnection *serverConnection)
{
s_serverConnection = serverConnection;
@@ -508,36 +500,29 @@ void Socket::SocketOutputStreamNetwork::writeWithFlags(byteArray b, unsigned int
return;
}
#ifdef _WINDOWS64
// Only attempt P2P for server->client sends (host broadcasting to clients)
if( m_queueIdx == SOCKET_SERVER_END && s_p2pManager != NULL && g_PacketRouter.IsP2PEnabled() )
{
// Try to identify the packet ID from the serialized data
// Packet format: first byte(s) contain the packet ID
if( length >= 1 )
{
int packetId = (int)b[offset];
EPacketRoutingMode routingMode = Packet::GetRoutingMode(packetId);
if( routingMode == ROUTING_PREFER_DIRECT || routingMode == ROUTING_DIRECT_ONLY )
{
if( s_p2pManager->IsDirectConnectionAvailable(socketPlayer) )
{
EP2PChannel channel = PacketRouter::GetChannelForPacket(packetId);
s_p2pManager->SendDirect(socketPlayer, buffer.pbyData, buffer.dwDataSize, channel);
return; // Sent via P2P, skip QNet
}
}
}
}
#endif
if( m_queueIdx == SOCKET_SERVER_END )
{
//printf( "Sent %u bytes of data from \"%ls\" to \"%ls\"\n",
//buffer.dwDataSize,
//hostPlayer->GetGamertag(),
//m_socket->networkPlayer->GetGamertag());
hostPlayer->SendData(socketPlayer, buffer.pbyData, buffer.dwDataSize, QNET_SENDDATA_RELIABLE | QNET_SENDDATA_SEQUENTIAL | flags);
// DWORD queueSize = hostPlayer->GetSendQueueSize( NULL, QNET_GETSENDQUEUESIZE_BYTES );
// if( queueSize > 24000 )
// {
// //printf("Queue size is: %d, forcing doWork()\n",queueSize);
// g_NetworkManager.DoWork();
// }
}
else
{
//printf( "Sent %u bytes of data from \"%ls\" to \"%ls\"\n",
//buffer.dwDataSize,
//m_socket->networkPlayer->GetGamertag(),
//hostPlayer->GetGamertag());
socketPlayer->SendData(hostPlayer, buffer.pbyData, buffer.dwDataSize, QNET_SENDDATA_RELIABLE | QNET_SENDDATA_SEQUENTIAL | flags);
}
}

View File

@@ -8,10 +8,6 @@
#define SOCKET_CLIENT_END 0
#define SOCKET_SERVER_END 1
// Forward declare P2P types
class IP2PConnectionManager;
enum EP2PChannel;
class SocketAddress;
class ServerConnection;
@@ -135,11 +131,4 @@ public:
bool isClosing() { return m_endClosed[SOCKET_CLIENT_END] || m_endClosed[SOCKET_SERVER_END]; }
BYTE getSmallId() { return networkPlayerSmallId; }
#ifdef _WINDOWS64
// P2P direct connection support
static IP2PConnectionManager* s_p2pManager;
static void SetP2PManager(IP2PConnectionManager* manager) { s_p2pManager = manager; }
static IP2PConnectionManager* GetP2PManager() { return s_p2pManager; }
#endif
};

View File

@@ -0,0 +1,4 @@
@echo off
call "C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvarsall.bat" x64
cd /d "C:\Users\cole\Documents\GitHub\client\tools\save_converter"
cl /LD /O2 kernelx_shim.c /link /DEF:kernelx.def /OUT:kernelx.dll

View File

@@ -0,0 +1,403 @@
"""
Xbox 360 Minecraft LCE savegame.dat -> Windows .mcs converter
Usage: python convert_x360_save.py <savegame.dat> [output.mcs]
This tool:
1. Strips the 4-byte STFS size prefix from the Xbox 360 save
2. Decompresses LZXRLE data (LZX via xcompress.dll, then custom RLE)
3. Byte-swaps the FileHeader and FileEntry structures (big-endian -> little-endian)
4. Byte-swaps region file data (chunk offsets and timestamps)
5. Re-compresses as ZLIBRLE (zlib + RLE) for the Windows build
6. Writes a .mcs file loadable by the LCE Windows build
"""
import ctypes
import struct
import zlib
import os
import sys
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
# --- LZX Decompression via xcompress.dll ---
def load_xcompress():
"""Load xcompress.dll with the kernelx.dll shim."""
os.add_dll_directory(SCRIPT_DIR)
ctypes.WinDLL(os.path.join(SCRIPT_DIR, "kernelx.dll"))
return ctypes.WinDLL(os.path.join(SCRIPT_DIR, "xcompress.dll"))
def lzx_decompress(xcomp, compressed_data, decompressed_size):
"""Decompress LZX data using XMemDecompress."""
XMEMCODEC_LZX = 1
class XMEMCODEC_PARAMETERS_LZX(ctypes.Structure):
_fields_ = [
("Flags", ctypes.c_uint32),
("WindowSize", ctypes.c_uint32),
("CompressionPartitionSize", ctypes.c_uint32),
]
params = XMEMCODEC_PARAMETERS_LZX()
params.Flags = 0
params.WindowSize = 128 * 1024
params.CompressionPartitionSize = 128 * 1024
context = ctypes.c_void_p()
hr = xcomp.XMemCreateDecompressionContext(
XMEMCODEC_LZX,
ctypes.byref(params),
0,
ctypes.byref(context),
)
if hr != 0:
raise RuntimeError(f"XMemCreateDecompressionContext failed: 0x{hr:08X}")
src_buf = (ctypes.c_ubyte * len(compressed_data))(*compressed_data)
dst_buf = (ctypes.c_ubyte * decompressed_size)()
dst_size = ctypes.c_size_t(decompressed_size)
hr = xcomp.XMemDecompress(
context,
dst_buf,
ctypes.byref(dst_size),
src_buf,
ctypes.c_size_t(len(compressed_data)),
)
xcomp.XMemDestroyDecompressionContext(context)
if hr != 0:
raise RuntimeError(f"XMemDecompress failed: 0x{hr:08X}")
return bytes(dst_buf[: dst_size.value])
# --- RLE Decompression (from compression.cpp DecompressLZXRLE) ---
def rle_decompress(data):
"""
Decompress the custom RLE format used by LCE.
Format:
0-254: literal byte
255 followed by 0,1,2: 1,2,3 literal 255s
255 followed by 3-255 followed by byte: run of (count+1) copies of byte
"""
result = bytearray()
i = 0
length = len(data)
while i < length:
b = data[i]
i += 1
if b == 255:
if i >= length:
break
count = data[i]
i += 1
if count < 3:
result.extend(b"\xff" * (count + 1))
else:
if i >= length:
break
val = data[i]
i += 1
result.extend(bytes([val]) * (count + 1))
else:
result.append(b)
return bytes(result)
# --- RLE Compression (from compression.cpp CompressRLE / CompressLZXRLE) ---
def rle_compress(data):
"""
Compress with the custom RLE format used by LCE.
"""
result = bytearray()
i = 0
length = len(data)
while i < length:
b = data[i]
i += 1
count = 1
while i < length and data[i] == b and count < 256:
i += 1
count += 1
if count <= 3:
if b == 255:
result.append(255)
result.append(count - 1)
else:
result.extend(bytes([b]) * count)
else:
result.append(255)
result.append(count - 1)
result.append(b)
return bytes(result)
# --- Endianness Conversion ---
def swap16(data, offset):
"""Swap a 16-bit value in-place."""
data[offset], data[offset + 1] = data[offset + 1], data[offset]
def swap32(data, offset):
"""Swap a 32-bit value in-place."""
data[offset : offset + 4] = data[offset : offset + 4][::-1]
def swap64(data, offset):
"""Swap a 64-bit value in-place."""
data[offset : offset + 8] = data[offset : offset + 8][::-1]
def swap_wchar_array(data, offset, count):
"""Swap an array of wchar_t (2 bytes each)."""
for i in range(count):
swap16(data, offset + i * 2)
def convert_file_header(data):
"""
Convert the FileHeader from big-endian (Xbox 360) to little-endian (Windows).
Layout of decompressed save data:
Bytes 0-3: headerOffset (uint32)
Bytes 4-7: headerSize (uint32) = number of file entries
Bytes 8-9: originalSaveVersion (int16)
Bytes 10-11: saveVersion (int16)
Bytes 12+: file data
At headerOffset: file table (headerSize entries, each 144 bytes)
"""
buf = bytearray(data)
# Read header values in big-endian
header_offset = struct.unpack(">I", buf[0:4])[0]
header_size = struct.unpack(">I", buf[4:8])[0]
orig_version = struct.unpack(">h", buf[8:10])[0]
save_version = struct.unpack(">h", buf[10:12])[0]
print(f" Header offset: {header_offset}")
print(f" File count: {header_size}")
print(f" Original version: {orig_version}")
print(f" Save version: {save_version}")
# Swap the header fields to little-endian
swap32(buf, 0) # headerOffset
swap32(buf, 4) # headerSize
swap16(buf, 8) # originalSaveVersion
swap16(buf, 10) # saveVersion
# Swap each FileEntry in the file table
# FileEntrySaveDataV2 = 144 bytes:
# wchar_t filename[64] = 128 bytes
# uint32 length = 4 bytes
# uint32 startOffset = 4 bytes
# int64 lastModified = 8 bytes
ENTRY_SIZE = 144
print(f"\n Files in save:")
for i in range(header_size):
entry_offset = header_offset + i * ENTRY_SIZE
# Read filename before swapping (big-endian wchar)
raw_name = buf[entry_offset : entry_offset + 128]
name_chars = []
for j in range(64):
c = struct.unpack(">H", raw_name[j * 2 : j * 2 + 2])[0]
if c == 0:
break
name_chars.append(chr(c))
filename = "".join(name_chars)
length = struct.unpack(">I", buf[entry_offset + 128 : entry_offset + 132])[0]
start = struct.unpack(">I", buf[entry_offset + 132 : entry_offset + 136])[0]
print(f" {filename!r}: offset={start}, length={length}")
# Swap the fields
swap_wchar_array(buf, entry_offset, 64) # filename
swap32(buf, entry_offset + 128) # length
swap32(buf, entry_offset + 132) # startOffset
swap64(buf, entry_offset + 136) # lastModifiedTime
return bytes(buf), header_size, header_offset, save_version
def convert_region_files(data, header_offset, header_size):
"""
Convert region file data from big-endian to little-endian.
Region files (.mcr) contain:
- Sector 0: offset table (1024 uint32 entries)
- Sector 1: timestamp table (1024 uint32 entries)
- Sectors 2+: chunk data with per-chunk headers
This converts the offset/timestamp tables and chunk headers.
"""
buf = bytearray(data)
ENTRY_SIZE = 144
SECTOR_SIZE = 4096
SECTOR_INTS = 1024
for i in range(header_size):
entry_offset = header_offset + i * ENTRY_SIZE
# Read filename (now in little-endian after header conversion)
name_chars = []
for j in range(64):
c = struct.unpack("<H", buf[entry_offset + j * 2 : entry_offset + j * 2 + 2])[0]
if c == 0:
break
name_chars.append(chr(c))
filename = "".join(name_chars)
if not filename.endswith(".mcr"):
continue
file_start = struct.unpack("<I", buf[entry_offset + 132 : entry_offset + 136])[0]
file_length = struct.unpack("<I", buf[entry_offset + 128 : entry_offset + 132])[0]
print(f" Converting region file: {filename} (offset={file_start}, size={file_length})")
if file_length < 2 * SECTOR_SIZE:
print(f" Region file too small, skipping")
continue
# Swap offset table (sector 0: 1024 uint32 values)
for j in range(SECTOR_INTS):
swap32(buf, file_start + j * 4)
# Swap timestamp table (sector 1: 1024 uint32 values)
for j in range(SECTOR_INTS):
swap32(buf, file_start + SECTOR_SIZE + j * 4)
# Swap chunk data headers
# Each chunk entry in the offset table encodes: (sector_count & 0xFF) | (sector_offset << 8)
# The chunk data at each sector has an 8-byte header:
# uint32 compressed_length (MSB = RLE flag)
# uint32 decompressed_length
for j in range(SECTOR_INTS):
offset_entry = struct.unpack("<I", buf[file_start + j * 4 : file_start + j * 4 + 4])[0]
if offset_entry == 0:
continue
sector_count = offset_entry & 0xFF
sector_offset = offset_entry >> 8
if sector_offset < 2:
continue
chunk_data_offset = file_start + sector_offset * SECTOR_SIZE
if chunk_data_offset + 8 > len(buf):
continue
# Swap the chunk header (compressed_length and decompressed_length)
swap32(buf, chunk_data_offset)
swap32(buf, chunk_data_offset + 4)
return bytes(buf)
# --- Main Conversion ---
def convert_save(input_path, output_path):
print(f"Loading {input_path}...")
with open(input_path, "rb") as f:
raw_data = f.read()
file_size = len(raw_data)
print(f"File size: {file_size} bytes")
# Check for 4-byte STFS size prefix
# The first 4 bytes (BE) should match approximately (file_size - padding - 4)
prefix_size = struct.unpack(">I", raw_data[0:4])[0]
comp_flag = struct.unpack(">I", raw_data[4:8])[0]
if comp_flag == 0 and prefix_size > 0 and prefix_size < file_size:
print(f"Detected 4-byte STFS prefix (size={prefix_size})")
save_data = raw_data[4:]
else:
save_data = raw_data
# Parse compression header
compressed_flag = struct.unpack(">I", save_data[0:4])[0]
if compressed_flag != 0:
print("Save data does not appear to be compressed (flag != 0)")
print("Treating as raw uncompressed save data")
decompressed = save_data
else:
decomp_size = struct.unpack(">I", save_data[4:8])[0]
compressed_data = save_data[8:]
print(f"Compressed save: decompressed size = {decomp_size} ({decomp_size/1024/1024:.1f} MB)")
print(f"Compressed data size: {len(compressed_data)} bytes")
# LZX decompress (whole-file level is pure LZX, not LZXRLE)
# The RLE layer is only used per-chunk inside region files
print("\nStep 1: LZX decompression...")
xcomp = load_xcompress()
decompressed = lzx_decompress(xcomp, compressed_data, decomp_size)
print(f" LZX decompressed: {len(decompressed)} bytes")
# Step 2: Inspect the raw save structure (big-endian)
print("\nStep 2: Inspecting raw save data (big-endian Xbox 360 format)...")
header_offset = struct.unpack(">I", decompressed[0:4])[0]
file_count = struct.unpack(">I", decompressed[4:8])[0]
orig_version = struct.unpack(">h", decompressed[8:10])[0]
save_version = struct.unpack(">h", decompressed[10:12])[0]
print(f" Header offset: {header_offset}")
print(f" File count: {file_count}")
print(f" Original version: {orig_version}, Save version: {save_version}")
# List files in the save
ENTRY_SIZE = 144
print(f"\n Files in save:")
for i in range(file_count):
entry_off = header_offset + i * ENTRY_SIZE
name_chars = []
for j in range(64):
c = struct.unpack(">H", decompressed[entry_off + j * 2 : entry_off + j * 2 + 2])[0]
if c == 0:
break
name_chars.append(chr(c))
filename = "".join(name_chars)
length = struct.unpack(">I", decompressed[entry_off + 128 : entry_off + 132])[0]
start = struct.unpack(">I", decompressed[entry_off + 132 : entry_off + 136])[0]
print(f" {filename}: offset={start}, size={length}")
# Step 3: Write raw decompressed data
# The game code will handle endianness conversion when loaded with plat=X360
# Write as uncompressed (first 4 bytes != 0 tells the game it's uncompressed)
print(f"\nStep 3: Writing {output_path}...")
with open(output_path, "wb") as f:
f.write(decompressed)
print(f"\nDone! Output: {output_path} ({len(decompressed)} bytes)")
print(f"\nTo use this save with the LCE source code:")
print(f" 1. Ensure xcompress.dll is available to the Windows build")
print(f" 2. Replace XMemDecompress stubs with real implementation")
print(f" 3. Load with: ConsoleSaveFileOriginal(name, data, size, false, SAVE_FILE_PLATFORM_X360)")
print(f" 4. Call pSave->ConvertToLocalPlatform() after loading")
if __name__ == "__main__":
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} <savegame.dat> [output.mcs]")
sys.exit(1)
input_file = sys.argv[1]
if len(sys.argv) >= 3:
output_file = sys.argv[2]
else:
base = os.path.splitext(input_file)[0]
output_file = base + ".mcs"
convert_save(input_file, output_file)

Binary file not shown.

View File

@@ -0,0 +1,14 @@
LIBRARY kernelx
EXPORTS
XMemAlloc
XMemFree
SetUnhandledExceptionFilter = kernel32.SetUnhandledExceptionFilter
UnhandledExceptionFilter = kernel32.UnhandledExceptionFilter
GetTickCount = kernel32.GetTickCount
GetSystemTimeAsFileTime = kernel32.GetSystemTimeAsFileTime
GetCurrentThreadId = kernel32.GetCurrentThreadId
GetCurrentProcessId = kernel32.GetCurrentProcessId
QueryPerformanceCounter = kernel32.QueryPerformanceCounter
Sleep = kernel32.Sleep
TerminateProcess = kernel32.TerminateProcess
GetCurrentProcess = kernel32.GetCurrentProcess

Binary file not shown.

View File

@@ -0,0 +1,25 @@
// kernelx.dll shim - forwards standard Win32 functions to kernel32.dll
// and provides stub implementations for Xbox-specific XMemAlloc/XMemFree
#include <windows.h>
#include <stdlib.h>
// Forward declarations for exports
__declspec(dllexport) void* __stdcall XMemAlloc(SIZE_T dwSize, DWORD dwAllocAttributes);
__declspec(dllexport) void __stdcall XMemFree(void* pAddress, DWORD dwAllocAttributes);
// Xbox-specific memory functions - map to standard heap
__declspec(dllexport) void* __stdcall XMemAlloc(SIZE_T dwSize, DWORD dwAllocAttributes) {
(void)dwAllocAttributes;
return malloc(dwSize);
}
__declspec(dllexport) void __stdcall XMemFree(void* pAddress, DWORD dwAllocAttributes) {
(void)dwAllocAttributes;
free(pAddress);
}
// All other functions are forwarded to kernel32.dll via the .def file
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
(void)hinstDLL; (void)fdwReason; (void)lpReserved;
return TRUE;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.