From 6853eab85feb41cd92c8949bd24a3f9ecd4e4c62 Mon Sep 17 00:00:00 2001 From: piebot Date: Fri, 14 Aug 2026 07:02:50 +0300 Subject: [PATCH 1/6] update lcemp networking --- Minecraft.Client/Chunk.cpp | 6 +- Minecraft.Client/ClientConnection.cpp | 51 ++--- .../Common/Network/NetworkSocketLayer.cpp | 184 +++++++++++------- .../Common/Network/NetworkSocketLayer.h | 12 +- .../Common/Network/PlatformNetworkManager.cpp | 34 +++- .../Common/UI/UIScene_CreateWorldMenu.cpp | 12 ++ .../Common/UI/UIScene_JoinMenu.cpp | 6 +- .../Common/UI/UIScene_LoadMenu.cpp | 5 + .../Common/UI/UIScene_MainMenu.cpp | 10 +- Minecraft.Client/Extrax64Stubs.cpp | 161 +++++++++++++-- Minecraft.Client/LevelRenderer.cpp | 4 + Minecraft.Client/LevelRenderer.h | 4 +- Minecraft.Client/MinecraftServer.h | 2 +- Minecraft.Client/MultiPlayerGameMode.cpp | 14 ++ Minecraft.Client/Orbis/Orbis_Minecraft.cpp | 5 + Minecraft.Client/PS3/PS3_Minecraft.cpp | 5 + Minecraft.Client/PSVita/PSVita_Minecraft.cpp | 4 + Minecraft.Client/PlayerChunkMap.cpp | 11 +- Minecraft.Client/PlayerConnection.cpp | 9 +- Minecraft.Client/ServerLevel.cpp | 4 +- Minecraft.Client/ServerPlayer.cpp | 19 ++ Minecraft.Client/Xbox/Xbox_Minecraft.cpp | 5 + Minecraft.World/AwardStatPacket.cpp | 2 +- Minecraft.World/BlockRegionUpdatePacket.cpp | 11 +- Minecraft.World/ByteArrayInputStream.cpp | 8 +- Minecraft.World/ByteArrayOutputStream.cpp | 7 +- Minecraft.World/ByteArrayTag.h | 2 + Minecraft.World/ComplexItemDataPacket.cpp | 4 +- Minecraft.World/CompoundTag.h | 3 + Minecraft.World/Connection.cpp | 4 +- Minecraft.World/ContainerSetContentPacket.cpp | 3 + Minecraft.World/ContainerSetSlotPacket.cpp | 2 +- Minecraft.World/CustomPayloadPacket.cpp | 2 +- Minecraft.World/DataInputStream.cpp | 4 + Minecraft.World/DirectoryLevelStorage.cpp | 22 +++ Minecraft.World/ExplodePacket.cpp | 2 + Minecraft.World/GameCommandPacket.cpp | 2 +- Minecraft.World/IntArrayTag.h | 1 + Minecraft.World/LevelChunk.cpp | 1 - Minecraft.World/ListTag.h | 2 + Minecraft.World/Packet.cpp | 38 ++-- Minecraft.World/PreLoginPacket.cpp | 2 + Minecraft.World/RemoveEntitiesPacket.cpp | 4 +- Minecraft.World/Socket.cpp | 5 + Minecraft.World/SynchedEntityData.cpp | 5 +- Minecraft.World/Tag.cpp | 31 ++- Minecraft.World/TextureAndGeometryPacket.cpp | 22 ++- Minecraft.World/TexturePacket.cpp | 24 ++- .../UpdateGameRuleProgressPacket.cpp | 2 +- Minecraft.World/compression.cpp | 27 ++- Minecraft.World/x64headers/extraX64.h | 3 + 51 files changed, 629 insertions(+), 183 deletions(-) diff --git a/Minecraft.Client/Chunk.cpp b/Minecraft.Client/Chunk.cpp index dddb628b..fd27733c 100644 --- a/Minecraft.Client/Chunk.cpp +++ b/Minecraft.Client/Chunk.cpp @@ -402,6 +402,7 @@ void Chunk::rebuild() } Tile *tile = Tile::tiles[tileId]; + if (!tile) continue; if (currentLayer == 0 && tile->isEntityTile()) { shared_ptr et = region->getTileEntity(x, y, z); @@ -739,9 +740,9 @@ void Chunk::rebuild_SPU() { // 4J - get tile from those copied into our local array in earlier optimisation unsigned char tileId = pOutData->getTile(x,y,z); - if (tileId > 0) + if (tileId > 0 && tileId != 0xff) { - if (currentLayer == 0 && Tile::tiles[tileId]->isEntityTile()) + if (currentLayer == 0 && Tile::tiles[tileId] && Tile::tiles[tileId]->isEntityTile()) { shared_ptr et = region.getTileEntity(x, y, z); if (TileEntityRenderDispatcher::instance->hasRenderer(et)) @@ -754,6 +755,7 @@ void Chunk::rebuild_SPU() { Tile *tile = Tile::tiles[tileId]; + if (!tile) continue; int renderLayer = tile->getRenderLayer(); if (renderLayer != currentLayer) diff --git a/Minecraft.Client/ClientConnection.cpp b/Minecraft.Client/ClientConnection.cpp index d44995e9..b7e7f6dd 100644 --- a/Minecraft.Client/ClientConnection.cpp +++ b/Minecraft.Client/ClientConnection.cpp @@ -776,6 +776,7 @@ void ClientConnection::handleAddPlayer(shared_ptr packet) player->yRotp = packet->yRot; player->yHeadRot = packet->yHeadRot * 360 / 256.0f; player->setXuid(packet->xuid); + player->setOnlineXuid(packet->OnlineXuid); #ifdef _DURANGO // On Durango request player display name from network manager @@ -788,11 +789,11 @@ void ClientConnection::handleAddPlayer(shared_ptr packet) #if defined _WINDOWS64 || defined DISABLE_PSN || defined _DISABLE_XBLIVE { - PlayerUID pktXuid = player->getXuid(); + PlayerUID netXuid = packet->OnlineXuid; const PlayerUID WIN64_XUID_BASE = (PlayerUID)0xe000d45248242f2e; - if (pktXuid >= WIN64_XUID_BASE && pktXuid < WIN64_XUID_BASE + MINECRAFT_NET_MAX_PLAYERS) + if (netXuid >= WIN64_XUID_BASE && netXuid < WIN64_XUID_BASE + MINECRAFT_NET_MAX_PLAYERS) { - BYTE smallId = (BYTE)(pktXuid - WIN64_XUID_BASE); + BYTE smallId = (BYTE)(netXuid - WIN64_XUID_BASE); INetworkPlayer *np = g_NetworkManager.GetPlayerBySmallId(smallId); if (np != NULL) { @@ -954,39 +955,6 @@ void ClientConnection::handleMoveEntitySmall(shared_ptr p void ClientConnection::handleRemoveEntity(shared_ptr packet) { -#if defined _WINDOWS64 || defined DISABLE_PSN || defined _DISABLE_XBLIVE - if (!g_NetworkManager.IsHost()) - { - for (int i = 0; i < packet->ids.length; i++) - { - shared_ptr entity = getEntity(packet->ids[i]); - if (entity != NULL && entity->GetType() == eTYPE_PLAYER) - { - shared_ptr player = dynamic_pointer_cast(entity); - if (player != NULL) - { - PlayerUID xuid = player->getXuid(); - INetworkPlayer *np = g_NetworkManager.GetPlayerByXuid(xuid); - if (np != NULL) - { - NetworkPlayerXbox *npx = (NetworkPlayerXbox *)np; - IQNetPlayer *qp = npx->GetQNetPlayer(); - if (qp != NULL) - { - extern CPlatformNetworkManagerStub *g_pPlatformNetworkManager; - g_pPlatformNetworkManager->NotifyPlayerLeaving(qp); - qp->m_smallId = 0; - qp->m_isRemote = false; - qp->m_isHostPlayer = false; - qp->m_gamertag[0] = 0; - qp->SetCustomDataValue(0); - } - } - } - } - } - } -#endif for (int i = 0; i < packet->ids.length; i++) { level->removeEntity(packet->ids[i]); @@ -1068,6 +1036,7 @@ void ClientConnection::handleChunkVisibility(shared_ptr p void ClientConnection::handleChunkTilesUpdate(shared_ptr packet) { // 4J - changed to encode level in packet + if (packet->levelIdx >= minecraft->levels.length) return; MultiPlayerLevel *dimensionLevel = (MultiPlayerLevel *)minecraft->levels[packet->levelIdx]; if( dimensionLevel ) { @@ -1137,6 +1106,7 @@ void ClientConnection::handleChunkTilesUpdate(shared_ptr void ClientConnection::handleBlockRegionUpdate(shared_ptr packet) { // 4J - changed to encode level in packet + if (packet->levelIdx >= minecraft->levels.length) return; MultiPlayerLevel *dimensionLevel = (MultiPlayerLevel *)minecraft->levels[packet->levelIdx]; if( dimensionLevel ) { @@ -1194,6 +1164,8 @@ void ClientConnection::handleTileUpdate(shared_ptr packet) destroyTilePacket = true; } // 4J - changed to encode level in packet + if (packet->levelIdx >= minecraft->levels.length) return; + MultiPlayerLevel *dimensionLevel = (MultiPlayerLevel *)minecraft->levels[packet->levelIdx]; if( dimensionLevel ) { @@ -2350,6 +2322,7 @@ void ClientConnection::handleAddMob(shared_ptr packet) float xRot = packet->xRot * 360 / 256.0f; shared_ptr mob = dynamic_pointer_cast(EntityIO::newById(packet->type, level)); + if (mob == NULL) return; mob->xp = packet->x; mob->yp = packet->y; mob->zp = packet->z; @@ -3593,10 +3566,12 @@ void ClientConnection::handleCustomPayload(shared_ptr custo } #else UIScene *scene = ui.GetTopScene(m_userIndex, eUILayer_Scene); - UIScene_TradingMenu *screen = (UIScene_TradingMenu *)scene; - trader = screen->getMerchant(); + UIScene_TradingMenu *screen = dynamic_cast(scene); + if (screen != NULL) + trader = screen->getMerchant(); #endif + if (trader == NULL) return; MerchantRecipeList *recipeList = MerchantRecipeList::createFromStream(&input); trader->overrideOffers(recipeList); } diff --git a/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp b/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp index a17aae00..2c5acccf 100644 --- a/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp +++ b/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp @@ -39,7 +39,7 @@ BYTE NetworkSocketLayer::s_nextSmallId = 1; CRITICAL_SECTION NetworkSocketLayer::s_sendLock; CRITICAL_SECTION NetworkSocketLayer::s_connectionsLock; -std::vector NetworkSocketLayer::s_connections; +RemoteConnection NetworkSocketLayer::s_connections[NETWORK_LAN_MAX_CLIENTS + 1]; C4JThread* NetworkSocketLayer::s_advertiseThread = NULL; volatile bool NetworkSocketLayer::s_advertising = false; @@ -55,6 +55,9 @@ std::vector NetworkSocketLayer::s_discoveredSessions; CRITICAL_SECTION NetworkSocketLayer::s_disconnectLock; std::vector NetworkSocketLayer::s_disconnectedSmallIds; +CRITICAL_SECTION NetworkSocketLayer::s_pendingJoinLock; +std::vector NetworkSocketLayer::s_pendingJoinSmallIds; + CRITICAL_SECTION NetworkSocketLayer::s_freeSmallIdLock; std::vector NetworkSocketLayer::s_freeSmallIds; @@ -83,7 +86,17 @@ bool NetworkSocketLayer::Initialize() InitializeCriticalSection(&s_advertiseLock); InitializeCriticalSection(&s_discoveryLock); InitializeCriticalSection(&s_disconnectLock); + InitializeCriticalSection(&s_pendingJoinLock); InitializeCriticalSection(&s_freeSmallIdLock); + + for (int i = 0; i < NETWORK_LAN_MAX_CLIENTS + 1; i++) + { + s_connections[i].tcpSocket = INVALID_SOCKET; + s_connections[i].smallId = 0; + s_connections[i].recvThread = NULL; + s_connections[i].active = false; + InitializeCriticalSection(&s_connections[i].sendLock); + } s_locksCreated = true; } @@ -167,7 +180,7 @@ void NetworkSocketLayer::Shutdown() EnterCriticalSection(&s_connectionsLock); - for (size_t i = 0; i < s_connections.size(); i++) + for (int i = 0; i < NETWORK_LAN_MAX_CLIENTS + 1; i++) { s_connections[i].active = false; #if defined _WINDOWS64 || defined _XBOX @@ -177,9 +190,16 @@ void NetworkSocketLayer::Shutdown() #endif { closesocket(s_connections[i].tcpSocket); + s_connections[i].tcpSocket = INVALID_SOCKET; } + if (s_connections[i].recvThread != NULL) + { + s_connections[i].recvThread->WaitForCompletion(2000); + delete s_connections[i].recvThread; + s_connections[i].recvThread = NULL; + } + DeleteCriticalSection(&s_connections[i].sendLock); } - s_connections.clear(); LeaveCriticalSection(&s_connectionsLock); if (s_acceptThread != NULL) @@ -204,6 +224,8 @@ void NetworkSocketLayer::Shutdown() DeleteCriticalSection(&s_discoveryLock); DeleteCriticalSection(&s_disconnectLock); s_disconnectedSmallIds.clear(); + DeleteCriticalSection(&s_pendingJoinLock); + s_pendingJoinSmallIds.clear(); DeleteCriticalSection(&s_freeSmallIdLock); s_freeSmallIds.clear(); s_locksCreated = false; @@ -219,6 +241,14 @@ bool NetworkSocketLayer::HostGame(int port) s_isHost = true; s_localSmallId = 0; s_hostSmallId = 0; + s_connected = false; + s_active = false; + + if (s_hostConnectionSocket != INVALID_SOCKET) + { + closesocket(s_hostConnectionSocket); + s_hostConnectionSocket = INVALID_SOCKET; + } s_nextSmallId = 1; s_hostGamePort = port; @@ -786,9 +816,9 @@ bool NetworkSocketLayer::JoinGame(const char *ip, int port) bool NetworkSocketLayer::SendOnSocket(SOCKET sock, const void *data, int dataSize) { #if defined _WINDOWS64 || defined _XBOX - if (sock == INVALID_SOCKET || dataSize <= 0) return false; + if (sock == INVALID_SOCKET || dataSize <= 0 || dataSize > NETWORK_LAN_MAX_PACKET_SIZE) return false; #elif defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ - if (sock < 0 || dataSize <= 0) return false; + if (sock < 0 || dataSize <= 0 || dataSize > NETWORK_LAN_MAX_PACKET_SIZE) return false; #endif EnterCriticalSection(&s_sendLock); @@ -809,10 +839,7 @@ bool NetworkSocketLayer::SendOnSocket(SOCKET sock, const void *data, int dataSiz #elif defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ if (sent < 0 || sent == 0) #endif - { - LeaveCriticalSection(&s_sendLock); return false; - } totalSent += sent; } @@ -826,14 +853,10 @@ bool NetworkSocketLayer::SendOnSocket(SOCKET sock, const void *data, int dataSiz #elif defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ if (sent < 0 || sent == 0) #endif - { - LeaveCriticalSection(&s_sendLock); return false; - } totalSent += sent; } - LeaveCriticalSection(&s_sendLock); return true; } @@ -843,31 +866,38 @@ bool NetworkSocketLayer::SendToSmallId(BYTE targetSmallId, const void *data, int if (s_isHost) { - SOCKET sock = GetSocketForSmallId(targetSmallId); -#if defined _WINDOWS64 || defined _XBOX - if (sock == INVALID_SOCKET) return false; -#elif defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ - if (sock < 0) return false; -#endif - return SendOnSocket(sock, data, dataSize); + EnterCriticalSection(&s_connectionsLock); + if (targetSmallId >= NETWORK_LAN_MAX_CLIENTS + 1 || !s_connections[targetSmallId].active) + { + LeaveCriticalSection(&s_connectionsLock); + return false; + } + SOCKET sock = s_connections[targetSmallId].tcpSocket; + CRITICAL_SECTION *pLock = &s_connections[targetSmallId].sendLock; + LeaveCriticalSection(&s_connectionsLock); + + EnterCriticalSection(pLock); + bool result = SendOnSocket(sock, data, dataSize); + LeaveCriticalSection(pLock); + return result; } else { - return SendOnSocket(s_hostConnectionSocket, data, dataSize); + EnterCriticalSection(&s_sendLock); + bool result = SendOnSocket(s_hostConnectionSocket, data, dataSize); + LeaveCriticalSection(&s_sendLock); + return result; } } SOCKET NetworkSocketLayer::GetSocketForSmallId(BYTE smallId) { EnterCriticalSection(&s_connectionsLock); - for (size_t i = 0; i < s_connections.size(); i++) + if (smallId < NETWORK_LAN_MAX_CLIENTS + 1 && s_connections[smallId].active) { - if (s_connections[i].smallId == smallId && s_connections[i].active) - { - SOCKET sock = s_connections[i].tcpSocket; - LeaveCriticalSection(&s_connectionsLock); - return sock; - } + SOCKET sock = s_connections[smallId].tcpSocket; + LeaveCriticalSection(&s_connectionsLock); + return sock; } LeaveCriticalSection(&s_connectionsLock); #if defined _WINDOWS64 || defined _XBOX @@ -981,15 +1011,19 @@ int NetworkSocketLayer::AcceptThreadProc(LPVOID param) continue; } - RemoteConnection conn; + RemoteConnection &conn = s_connections[assignedSmallId]; + + EnterCriticalSection(&s_connectionsLock); + + if (conn.recvThread != NULL) + { + conn.recvThread->WaitForCompletion(2000); + delete conn.recvThread; + conn.recvThread = NULL; + } conn.tcpSocket = clientSocket; conn.smallId = assignedSmallId; conn.active = true; - conn.recvThread = NULL; - - EnterCriticalSection(&s_connectionsLock); - s_connections.push_back(conn); - int connIdx = (int)s_connections.size() - 1; LeaveCriticalSection(&s_connectionsLock); app.DebugPrintf("Win64 LAN: Client connected, assigned smallId=%d\n", assignedSmallId); @@ -999,17 +1033,17 @@ int NetworkSocketLayer::AcceptThreadProc(LPVOID param) extern void Win64_SetupRemoteQNetPlayer(IQNetPlayer *player, BYTE smallId, bool isHost, bool isLocal); Win64_SetupRemoteQNetPlayer(qnetPlayer, assignedSmallId, false, false); - extern CPlatformNetworkManagerStub *g_pPlatformNetworkManager; - g_pPlatformNetworkManager->NotifyPlayerJoined(qnetPlayer); + EnterCriticalSection(&s_pendingJoinLock); + s_pendingJoinSmallIds.push_back(assignedSmallId); + LeaveCriticalSection(&s_pendingJoinLock); DWORD *threadParam = new DWORD; - *threadParam = connIdx; + *threadParam = assignedSmallId; C4JThread* hThread = new C4JThread(RecvThreadProc, threadParam, "RecvThreadProc"); hThread->Run(); EnterCriticalSection(&s_connectionsLock); - if (connIdx < (int)s_connections.size()) - s_connections[connIdx].recvThread = hThread; + s_connections[assignedSmallId].recvThread = hThread; LeaveCriticalSection(&s_connectionsLock); } return 0; @@ -1017,17 +1051,16 @@ int NetworkSocketLayer::AcceptThreadProc(LPVOID param) int NetworkSocketLayer::RecvThreadProc(LPVOID param) { - DWORD connIdx = *(DWORD *)param; + BYTE clientSmallId = (BYTE)*(DWORD *)param; delete (DWORD *)param; EnterCriticalSection(&s_connectionsLock); - if (connIdx >= (DWORD)s_connections.size()) + if (clientSmallId >= NETWORK_LAN_MAX_CLIENTS + 1 || !s_connections[clientSmallId].active) { LeaveCriticalSection(&s_connectionsLock); return 0; } - SOCKET sock = s_connections[connIdx].tcpSocket; - BYTE clientSmallId = s_connections[connIdx].smallId; + SOCKET sock = s_connections[clientSmallId].tcpSocket; LeaveCriticalSection(&s_connectionsLock); std::vector recvBuf; @@ -1048,7 +1081,7 @@ int NetworkSocketLayer::RecvThreadProc(LPVOID param) ((uint32_t)header[2] << 8) | ((uint32_t)header[3]); - if (packetSize <= 0 || packetSize > NETWORK_LAN_MAX_PACKET_SIZE) + if (packetSize <= 0 || (unsigned int)packetSize > NETWORK_LAN_MAX_PACKET_SIZE) { app.DebugPrintf("LAN: Invalid packet size %d from client smallId=%d (max=%d)\n", packetSize, @@ -1073,18 +1106,11 @@ int NetworkSocketLayer::RecvThreadProc(LPVOID param) } EnterCriticalSection(&s_connectionsLock); - for (size_t i = 0; i < s_connections.size(); i++) + s_connections[clientSmallId].active = false; + if (s_connections[clientSmallId].tcpSocket != INVALID_SOCKET) { - if (s_connections[i].smallId == clientSmallId) - { - s_connections[i].active = false; - if (s_connections[i].tcpSocket != INVALID_SOCKET) - { - closesocket(s_connections[i].tcpSocket); - s_connections[i].tcpSocket = INVALID_SOCKET; - } - break; - } + closesocket(s_connections[clientSmallId].tcpSocket); + s_connections[clientSmallId].tcpSocket = INVALID_SOCKET; } LeaveCriticalSection(&s_connectionsLock); @@ -1116,18 +1142,34 @@ void NetworkSocketLayer::PushFreeSmallId(BYTE smallId) LeaveCriticalSection(&s_freeSmallIdLock); } +bool NetworkSocketLayer::PopPendingJoinSmallId(BYTE *outSmallId) +{ + bool found = false; + EnterCriticalSection(&s_pendingJoinLock); + if (!s_pendingJoinSmallIds.empty()) + { + *outSmallId = s_pendingJoinSmallIds.back(); + s_pendingJoinSmallIds.pop_back(); + found = true; + } + LeaveCriticalSection(&s_pendingJoinLock); + return found; +} + +bool NetworkSocketLayer::IsSmallIdConnected(BYTE smallId) +{ + if (smallId >= NETWORK_LAN_MAX_CLIENTS + 1) return false; + return s_connections[smallId].active; +} + void NetworkSocketLayer::CloseConnectionBySmallId(BYTE smallId) { EnterCriticalSection(&s_connectionsLock); - for (size_t i = 0; i < s_connections.size(); i++) + if (smallId < NETWORK_LAN_MAX_CLIENTS + 1 && s_connections[smallId].active && s_connections[smallId].tcpSocket != INVALID_SOCKET) { - if (s_connections[i].smallId == smallId && s_connections[i].active && s_connections[i].tcpSocket != INVALID_SOCKET) - { - closesocket(s_connections[i].tcpSocket); - s_connections[i].tcpSocket = INVALID_SOCKET; - app.DebugPrintf("Win64 LAN: Force-closed TCP connection for smallId=%d\n", smallId); - break; - } + closesocket(s_connections[smallId].tcpSocket); + s_connections[smallId].tcpSocket = INVALID_SOCKET; + app.DebugPrintf("Win64 LAN: Force-closed TCP connection for smallId=%d\n", smallId); } LeaveCriticalSection(&s_connectionsLock); } @@ -1150,9 +1192,9 @@ int NetworkSocketLayer::ClientRecvThreadProc(LPVOID param) break; } - int packetSize = (header[0] << 24) | (header[1] << 16) | (header[2] << 8) | header[3]; + int packetSize = ((uint32_t)header[0] << 24) | ((uint32_t)header[1] << 16) | ((uint32_t)header[2] << 8) | (uint32_t)header[3]; - if (packetSize <= 0 || packetSize > NETWORK_LAN_MAX_PACKET_SIZE) + if (packetSize <= 0 || (unsigned int)packetSize > NETWORK_LAN_MAX_PACKET_SIZE) { app.DebugPrintf("LAN: Invalid packet size %d from host\n", packetSize); break; @@ -1161,7 +1203,6 @@ int NetworkSocketLayer::ClientRecvThreadProc(LPVOID param) if ((int)recvBuf.size() < packetSize) { recvBuf.resize(packetSize); - app.DebugPrintf("LAN: Resized client recv buffer to %d bytes\n", packetSize); } if (!RecvExact(s_hostConnectionSocket, &recvBuf[0], packetSize)) @@ -1494,8 +1535,8 @@ std::vector NetworkSocketLayer::GetDiscoveredSessions() int NetworkSocketLayer::DiscoveryThreadProc(LPVOID param) { - app.DebugPrintf("Discovery thread started\n"); char recvBuf[1024]; + const size_t MAX_DISCOVERED_SESSIONS = 64; while (s_discovering) { @@ -1544,6 +1585,11 @@ int NetworkSocketLayer::DiscoveryThreadProc(LPVOID param) if (broadcast->magic != NETWORK_LAN_BROADCAST_MAGIC) continue; + broadcast->hostName[31] = L'\0'; + + for (int pn = 0; pn < 8; pn++) + broadcast->playerNames[pn][XUSER_NAME_SIZE - 1] = '\0'; + char senderIP[64]; #if defined _XBOX unsigned char *ipBytes = (unsigned char *)&senderAddr.sin_addr; @@ -1586,6 +1632,12 @@ int NetworkSocketLayer::DiscoveryThreadProc(LPVOID param) if (!found) { + if (s_discoveredSessions.size() >= MAX_DISCOVERED_SESSIONS) + { + LeaveCriticalSection(&s_discoveryLock); + continue; + } + LANSession session; memset(&session, 0, sizeof(session)); strncpy(session.hostIP, senderIP, sizeof(session.hostIP) - 1); diff --git a/Minecraft.Client/Common/Network/NetworkSocketLayer.h b/Minecraft.Client/Common/Network/NetworkSocketLayer.h index 9949bdfd..ee56e7c7 100644 --- a/Minecraft.Client/Common/Network/NetworkSocketLayer.h +++ b/Minecraft.Client/Common/Network/NetworkSocketLayer.h @@ -35,7 +35,7 @@ #define NETWORK_LAN_DEFAULT_PORT 25565 #define NETWORK_LAN_MAX_CLIENTS 7 #define NETWORK_LAN_RECV_BUFFER_SIZE 65536 -#define NETWORK_LAN_MAX_PACKET_SIZE (4 * 1024 * 1024) +#define NETWORK_LAN_MAX_PACKET_SIZE (3 * 1024 * 1024) #define NETWORK_LAN_DISCOVERY_PORT 25566 #define NETWORK_LAN_BROADCAST_MAGIC 0x4D434C4E @@ -141,6 +141,7 @@ struct RemoteConnection BYTE smallId; C4JThread* recvThread; volatile bool active; + CRITICAL_SECTION sendLock; }; class NetworkSocketLayer @@ -170,6 +171,10 @@ public: static void PushFreeSmallId(BYTE smallId); static void CloseConnectionBySmallId(BYTE smallId); + static bool PopPendingJoinSmallId(BYTE *outSmallId); + + static bool IsSmallIdConnected(BYTE smallId); + static bool StartAdvertising(int gamePort, const wchar_t *hostName, unsigned int gameSettings, unsigned int texPackId, unsigned char subTexId, unsigned short netVer); static void StopAdvertising(); static void UpdateAdvertisePlayerCount(BYTE count); @@ -206,7 +211,7 @@ private: static CRITICAL_SECTION s_sendLock; static CRITICAL_SECTION s_connectionsLock; - static std::vector s_connections; + static RemoteConnection s_connections[NETWORK_LAN_MAX_CLIENTS + 1]; static SOCKET s_advertiseSock; static C4JThread* s_advertiseThread; @@ -224,6 +229,9 @@ private: static CRITICAL_SECTION s_disconnectLock; static std::vector s_disconnectedSmallIds; + static CRITICAL_SECTION s_pendingJoinLock; + static std::vector s_pendingJoinSmallIds; + static CRITICAL_SECTION s_freeSmallIdLock; static std::vector s_freeSmallIds; }; diff --git a/Minecraft.Client/Common/Network/PlatformNetworkManager.cpp b/Minecraft.Client/Common/Network/PlatformNetworkManager.cpp index 125183e4..af454b31 100644 --- a/Minecraft.Client/Common/Network/PlatformNetworkManager.cpp +++ b/Minecraft.Client/Common/Network/PlatformNetworkManager.cpp @@ -233,6 +233,8 @@ void CPlatformNetworkManagerStub::DoWork() BYTE disconnectedSmallId; while (NetworkSocketLayer::PopDisconnectedSmallId(&disconnectedSmallId)) { + if (disconnectedSmallId == 0) continue; + if (NetworkSocketLayer::IsSmallIdConnected(disconnectedSmallId)) continue; IQNetPlayer *qnetPlayer = m_pIQNet->GetPlayerBySmallId(disconnectedSmallId); if (qnetPlayer != NULL && qnetPlayer->m_smallId == disconnectedSmallId) { @@ -248,6 +250,16 @@ void CPlatformNetworkManagerStub::DoWork() } } + BYTE joinedSmallId; + while (NetworkSocketLayer::PopPendingJoinSmallId(&joinedSmallId)) + { + IQNetPlayer *qnetPlayer = m_pIQNet->GetPlayerBySmallId(joinedSmallId); + if (qnetPlayer != NULL && qnetPlayer->m_smallId == joinedSmallId) + { + NotifyPlayerJoined(qnetPlayer); + } + } + for (int i = 1; i < MINECRAFT_NET_MAX_PLAYERS; i++) { IQNetPlayer *qp = &IQNet::m_player[i]; @@ -294,12 +306,27 @@ int CPlatformNetworkManagerStub::GetLocalPlayerMask(int playerIndex) bool CPlatformNetworkManagerStub::AddLocalPlayerByUserIndex( int userIndex ) { + if (m_pIQNet->AddLocalPlayerByUserIndex(userIndex) != S_OK) return false; NotifyPlayerJoined(m_pIQNet->GetLocalPlayerByUserIndex(userIndex)); - return ( m_pIQNet->AddLocalPlayerByUserIndex(userIndex) == S_OK ); + return true; } bool CPlatformNetworkManagerStub::RemoveLocalPlayerByUserIndex( int userIndex ) { + if (userIndex > 0 && userIndex < XUSER_MAX_COUNT) + { + IQNetPlayer *qnetPlayer = m_pIQNet->GetLocalPlayerByUserIndex(userIndex); + if (qnetPlayer != NULL) + { + NotifyPlayerLeaving(qnetPlayer); + qnetPlayer->m_isRemote = true; + qnetPlayer->m_isHostPlayer = false; + qnetPlayer->m_gamertag[0] = 0; + qnetPlayer->SetCustomDataValue(0); + if (IQNet::s_playerCount > 1) + IQNet::s_playerCount--; + } + } return true; } @@ -376,7 +403,11 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame, IQNet::m_player[0].m_smallId = 0; IQNet::m_player[0].m_isRemote = false; IQNet::m_player[0].m_isHostPlayer = true; +#ifdef _WINDOWS64 + extern wchar_t g_Win64UsernameW[17]; + wcscpy_s(IQNet::m_player[0].m_gamertag, 32, g_Win64UsernameW); IQNet::s_playerCount = 1; +#endif if (getNetworkPlayer(&IQNet::m_player[0]) == NULL) NotifyPlayerJoined(&IQNet::m_player[0]); @@ -854,6 +885,7 @@ bool CPlatformNetworkManagerStub::GetGameSessionInfo(int iPad, SessionID session return true; } } + return false; } void CPlatformNetworkManagerStub::SetSessionsUpdatedCallback( void (*SessionsUpdatedCallback)(LPVOID pParam), LPVOID pSearchParam ) diff --git a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp index 9e8a6b92..9cafc1b9 100644 --- a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp @@ -959,11 +959,19 @@ void UIScene_CreateWorldMenu::checkStateAndStartGame() else { //ProfileManager.RequestSignInUI(false, false, false, true, false,&CScene_MultiGameCreate::StartGame_SignInReturned, this,ProfileManager.GetPrimaryPad()); +#if defined(_WINDOWS64) || defined(DISABLE_PSN) || defined(_DISABLE_XBLIVE) + SignInInfo info; + info.Func = &UIScene_CreateWorldMenu::StartGame_SignInReturned; + info.lpParam = this; + info.requireOnline = m_MoreOptionsParams.bOnlineGame; + UIScene_CreateWorldMenu::StartGame_SignInReturned(this, true, ProfileManager.GetPrimaryPad()); +#else SignInInfo info; info.Func = &UIScene_CreateWorldMenu::StartGame_SignInReturned; info.lpParam = this; info.requireOnline = m_MoreOptionsParams.bOnlineGame; ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_QuadrantSignin,&info); +#endif } } else @@ -1342,12 +1350,16 @@ int UIScene_CreateWorldMenu::ConfirmCreateReturned(void *pParam,int iPad,C4JStor if(isClientSide && app.IsLocalMultiplayerAvailable()) { +#if defined(_WINDOWS64) || defined(DISABLE_PSN) || defined(_DISABLE_XBLIVE) + UIScene_CreateWorldMenu::StartGame_SignInReturned(pClass, true, ProfileManager.GetPrimaryPad()); +#else //ProfileManager.RequestSignInUI(false, false, false, true, false,&UIScene_CreateWorldMenu::StartGame_SignInReturned, pClass,ProfileManager.GetPrimaryPad()); SignInInfo info; info.Func = &UIScene_CreateWorldMenu::StartGame_SignInReturned; info.lpParam = pClass; info.requireOnline = pClass->m_MoreOptionsParams.bOnlineGame; ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_QuadrantSignin,&info); +#endif } else { diff --git a/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp b/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp index f6d47048..540d396d 100644 --- a/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp @@ -7,7 +7,7 @@ #include "..\..\MinecraftServer.h" #include "..\..\..\Minecraft.World\net.minecraft.world.level.h" #include "..\..\..\Minecraft.World\net.minecraft.world.h" -#include "..\..\Common\Network\NetworkSocketLayer.h" +#include "..\..\Common\Network\NetworkSocketLayer.h" #define UPDATE_PLAYERS_TIMER_ID 0 #define UPDATE_PLAYERS_TIMER_TIME 30000 @@ -368,7 +368,7 @@ void UIScene_JoinMenu::checkPrivilegeCallback(LPVOID lpParam, bool hasPrivilege, void UIScene_JoinMenu::StartSharedLaunchFlow() { - const bool bManualJoin = (g_MultiplayerJoin == true); + const bool bManualJoin = (g_MultiplayerJoin == true); if (bManualJoin) { JoinGame(this); @@ -431,7 +431,7 @@ void UIScene_JoinMenu::JoinGame(UIScene_JoinMenu* pClass) { return; } - const bool bManualJoin = (g_MultiplayerJoin == true); + const bool bManualJoin = (g_MultiplayerJoin == true); DWORD dwSignedInUsers = 0; bool noPrivileges = false; diff --git a/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp index 7c7eda77..47fe593c 100644 --- a/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp @@ -1446,7 +1446,12 @@ int UIScene_LoadMenu::LoadDataComplete(void *pParam) #endif else { +#if defined(_WINDOWS64) || defined(DISABLE_PSN) || defined(_DISABLE_XBLIVE) + DWORD dwLocalUsersMask = CGameNetworkManager::GetLocalPlayerMask(ProfileManager.GetPrimaryPad()); + StartGameFromSave(pClass, dwLocalUsersMask); +#else pClass->m_bRequestQuadrantSignin = true; +#endif } } } diff --git a/Minecraft.Client/Common/UI/UIScene_MainMenu.cpp b/Minecraft.Client/Common/UI/UIScene_MainMenu.cpp index 1a6f51d5..4d1a97f4 100644 --- a/Minecraft.Client/Common/UI/UIScene_MainMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_MainMenu.cpp @@ -8,7 +8,7 @@ #include "UIScene_MainMenu.h" #ifdef __ORBIS__ #include -#include "..\..\Common\Network\NetworkSocketLayer.h" +#include "..\..\Common\Network\NetworkSocketLayer.h" #endif Random *UIScene_MainMenu::random = new Random(); @@ -319,7 +319,7 @@ void UIScene_MainMenu::handlePress(F64 controlId, F64 childId) //CD - Added for audio ui.PlayUISFX(eSFX_Press); - if(g_MultiplayerJoin) + if(g_MultiplayerJoin) { // request only completes after the platform network timeout. // this needs to be done before hand or else the PS4 crashes ill need to look more into this @@ -386,6 +386,10 @@ void UIScene_MainMenu::handlePress(F64 controlId, F64 childId) ui.NavigateToScene(primaryPad,eUIScene_TrialExitUpsell); } break; +#elif defined _WINDOWS64 + case eControl_Exit: + app.ExitGame(); + break; #endif #ifdef _DURANGO @@ -1108,7 +1112,7 @@ void UIScene_MainMenu::RefreshChatAndContentRestrictionsReturned_PlayGame(void * int primaryPad = ProfileManager.GetPrimaryPad(); UIScene_MainMenu* pClass = (UIScene_MainMenu*)pParam; - const bool bManualJoin = (g_MultiplayerJoin == true); + const bool bManualJoin = (g_MultiplayerJoin == true); if (bManualJoin) { CreateLoad_SignInReturned(pClass, true, primaryPad); diff --git a/Minecraft.Client/Extrax64Stubs.cpp b/Minecraft.Client/Extrax64Stubs.cpp index 299639c4..4c605e7f 100644 --- a/Minecraft.Client/Extrax64Stubs.cpp +++ b/Minecraft.Client/Extrax64Stubs.cpp @@ -355,7 +355,7 @@ DWORD XEnableGuestSignin(BOOL fEnable) { return 0; } /////////////////////////////////////////////// Profile library #ifdef _WINDOWS64 static void *profileData[4]; -static bool s_bProfileIsFullVersion; +static bool s_bProfileIsFullVersion = true; void C_4JProfile::Initialise( DWORD dwTitleID, DWORD dwOfferID, unsigned short usProfileVersion, @@ -374,6 +374,7 @@ void C_4JProfile::Initialise( DWORD dwTitleID, GAME_SETTINGS *pGameSettings = (GAME_SETTINGS *)profileData[i]; pGameSettings->ucMenuSensitivity=100; //eGameSetting_Sensitivity_InMenu pGameSettings->ucInterfaceOpacity=80; //eGameSetting_Sensitivity_InMenu + pGameSettings->ucGamma=100; //eGameSetting_Gamma pGameSettings->usBitmaskValues|=0x0200; //eGameSetting_DisplaySplitscreenGamertags - on pGameSettings->usBitmaskValues|=0x0400; //eGameSetting_Hints - on pGameSettings->usBitmaskValues|=0x1000; //eGameSetting_Autosave - 2 @@ -437,7 +438,95 @@ 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); } +static void Win64_BuildSplitName(int iPad, char *outName, int outSize); + +PlayerUID IQNetPlayer::GetXuid() +{ +#if defined(_WINDOWS64) || defined(DISABLE_PSN) || defined(_DISABLE_XBLIVE) + if (!m_isRemote) + { + int idx = (int)(this - &IQNet::m_player[0]); + if (idx == 0) + { + extern char g_Win64Username[17]; + return Win64_UsernameToXuid(g_Win64Username); + } + if (idx > 0 && idx < XUSER_MAX_COUNT) + { + char splitName[32]; + Win64_BuildSplitName(idx, splitName, sizeof(splitName)); + return Win64_UsernameToXuid(splitName); + } + } +#endif + return (PlayerUID)(0xe000d45248242f2e + m_smallId); +} + +PlayerUID Win64_UsernameToXuid(const char* username) +{ + uint64_t hash = 14695981039346656037ULL; + for (const char* p = username; *p; ++p) + { + hash ^= (uint64_t)(unsigned char)(*p); + hash *= 1099511628211ULL; + } + + const uint64_t WIN64_XUID_BASE = 0xe000d45248242f2e; + if (hash >= WIN64_XUID_BASE && hash <= WIN64_XUID_BASE + MINECRAFT_NET_MAX_PLAYERS) + hash = WIN64_XUID_BASE + MINECRAFT_NET_MAX_PLAYERS + 1; + if (hash == 0) + hash = 1; + return (PlayerUID)hash; +} + +PlayerUID Win64_UsernameToXuid(const wchar_t* username) +{ + char narrow[64]; + int i = 0; + for (; username[i] && i < 63; ++i) + narrow[i] = (char)(unsigned char)(username[i] & 0xFF); + narrow[i] = 0; + return Win64_UsernameToXuid(narrow); +} + +static void Win64_BuildSplitName(int iPad, char *outName, int outSize) +{ + extern char g_Win64Username[17]; + char candidate[32]; + sprintf(candidate, "%s_%d", g_Win64Username, iPad); + for (DWORD i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++) + { + if (!IQNet::m_player[i].m_isRemote) continue; + if (IQNet::m_player[i].m_gamertag[0] == 0) continue; + char remoteName[64]; + int j = 0; + for (; IQNet::m_player[i].m_gamertag[j] && j < 63; ++j) + remoteName[j] = (char)(unsigned char)(IQNet::m_player[i].m_gamertag[j] & 0xFF); + remoteName[j] = 0; +#if defined(_MSC_VER) + if (_stricmp(candidate, remoteName) == 0) +#else + if (strcasecmp(candidate, remoteName) == 0) +#endif + { + sprintf(candidate, "%s_%d_L", g_Win64Username, iPad); + break; + } + } + strncpy(outName, candidate, outSize - 1); + outName[outSize - 1] = 0; +} + +static void Win64_BuildSplitNameW(int iPad, wchar_t *outName, int outSize) +{ + char narrow[32]; + Win64_BuildSplitName(iPad, narrow, sizeof(narrow)); + for (int i = 0; i < outSize - 1 && narrow[i]; ++i) + { + outName[i] = (wchar_t)(unsigned char)narrow[i]; + outName[i + 1] = 0; + } +} LPCWSTR IQNetPlayer::GetGamertag() { return m_gamertag; } int IQNetPlayer::GetSessionIndex() { return m_smallId; } bool IQNetPlayer::IsTalking() { return false; } @@ -470,15 +559,30 @@ void Win64_SetupRemoteQNetPlayer(IQNetPlayer *player, BYTE smallId, bool isHost, static bool Win64_IsActivePlayer(IQNetPlayer *p, DWORD index); -HRESULT IQNet::AddLocalPlayerByUserIndex(DWORD dwUserIndex){ return S_OK; } +HRESULT IQNet::AddLocalPlayerByUserIndex(DWORD dwUserIndex) +{ + // no E_FAIL on PS3. + if (dwUserIndex >= MINECRAFT_NET_MAX_PLAYERS) return (HRESULT)0x80004005L; + m_player[dwUserIndex].m_isRemote = false; + m_player[dwUserIndex].m_smallId = (BYTE)dwUserIndex; + if (dwUserIndex > 0) + { + wchar_t splitNameW[32]; + Win64_BuildSplitNameW((int)dwUserIndex, splitNameW, 32); + wcsncpy(m_player[dwUserIndex].m_gamertag, splitNameW, 32 - 1); + m_player[dwUserIndex].m_gamertag[32 - 1] = 0; + } + if (dwUserIndex >= (DWORD)s_playerCount) + s_playerCount = dwUserIndex + 1; + return S_OK; +} IQNetPlayer *IQNet::GetHostPlayer() { return &m_player[0]; } IQNetPlayer *IQNet::GetLocalPlayerByUserIndex(DWORD dwUserIndex) { if (s_isHosting) { if (dwUserIndex < MINECRAFT_NET_MAX_PLAYERS && - !m_player[dwUserIndex].m_isRemote && - Win64_IsActivePlayer(&m_player[dwUserIndex], dwUserIndex)) + !m_player[dwUserIndex].m_isRemote) return &m_player[dwUserIndex]; return NULL; } @@ -575,7 +679,7 @@ void C_4JProfile::SetTrialTextStringTable(CXuiStringTable *pStringTable,int i void C_4JProfile::SetTrialAwardText(eAwardType AwardType,int iTitle,int iText) {} int C_4JProfile::GetLockedProfile() { return 0; } void C_4JProfile::SetLockedProfile(int iProf) {} -bool C_4JProfile::IsSignedIn(int iQuadrant) { return ( iQuadrant == 0); } +bool C_4JProfile::IsSignedIn(int iQuadrant) { return (iQuadrant >= 0 && iQuadrant < XUSER_MAX_COUNT); } bool C_4JProfile::IsSignedInLive(int iProf) { return true; } bool C_4JProfile::IsGuest(int iQuadrant) { return false; } UINT C_4JProfile::RequestSignInUI(bool bFromInvite,bool bLocalGame,bool bNoGuestsAllowed,bool bMultiplayerSignIn,bool bAddUser, int( *Func)(LPVOID,const bool, const int iPad),LPVOID lpParam,int iQuadrant) { return 0; } @@ -585,16 +689,21 @@ void C_4JProfile::SetPrimaryPlayerChanged(bool bVal) {} bool C_4JProfile::QuerySigninStatus(void) { return true; } void C_4JProfile::GetXUID(int iPad, PlayerUID *pXuid,bool bOnlineXuid) { - if (iPad != 0) + if (iPad == 0) { - // previously INVALID_XUID - *pXuid = 0; - return; + extern char g_Win64Username[17]; + *pXuid = Win64_UsernameToXuid(g_Win64Username); + } + else if (iPad > 0 && iPad < XUSER_MAX_COUNT) + { + char splitName[32]; + Win64_BuildSplitName(iPad, splitName, sizeof(splitName)); + *pXuid = Win64_UsernameToXuid(splitName); } - if (IQNet::s_isHosting) - *pXuid = 0xe000d45248242f2e; else - *pXuid = 0xe000d45248242f2e + NetworkSocketLayer::GetLocalSmallId(); + { + *pXuid = INVALID_XUID; + } } BOOL C_4JProfile::AreXUIDSEqual(PlayerUID xuid1,PlayerUID xuid2) { return xuid1 == xuid2; } BOOL C_4JProfile::XUIDIsGuest(PlayerUID xuid) { return false; } @@ -623,8 +732,30 @@ char fakeGamerTag[32] = "PlayerName"; void SetFakeGamertag(char *name){ strcpy_s(fakeGamerTag, name); } char* C_4JProfile::GetGamertag(int iPad){ return fakeGamerTag; } #else -char* C_4JProfile::GetGamertag(int iPad){ extern char g_Win64Username[17]; return g_Win64Username; } -wstring C_4JProfile::GetDisplayName(int iPad){ extern wchar_t g_Win64UsernameW[17]; return g_Win64UsernameW; } +static char s_win64SplitNames[4][32]; +char* C_4JProfile::GetGamertag(int iPad) +{ + extern char g_Win64Username[17]; + if (iPad == 0) return g_Win64Username; + if (iPad > 0 && iPad < XUSER_MAX_COUNT) + { + Win64_BuildSplitName(iPad, s_win64SplitNames[iPad], sizeof(s_win64SplitNames[iPad])); + return s_win64SplitNames[iPad]; + } + return g_Win64Username; +} +wstring C_4JProfile::GetDisplayName(int iPad) +{ + extern wchar_t g_Win64UsernameW[17]; + if (iPad == 0) return g_Win64UsernameW; + if (iPad > 0 && iPad < XUSER_MAX_COUNT) + { + wchar_t buf[32]; + Win64_BuildSplitNameW(iPad, buf, 32); + return buf; + } + return g_Win64UsernameW; +} #endif bool C_4JProfile::IsFullVersion() { return s_bProfileIsFullVersion; } void C_4JProfile::SetSignInChangeCallback(void ( *Func)(LPVOID, bool, unsigned int),LPVOID lpParam) {} diff --git a/Minecraft.Client/LevelRenderer.cpp b/Minecraft.Client/LevelRenderer.cpp index 8947b759..6e883ccc 100644 --- a/Minecraft.Client/LevelRenderer.cpp +++ b/Minecraft.Client/LevelRenderer.cpp @@ -1964,7 +1964,11 @@ bool LevelRenderer::updateDirtyChunks() { if( (!onlyRebuild) || globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_COMPILED || +#ifdef _WINDOWS64 + ( distSq < 96 * 96 ) ) // Always rebuild really near things or else building (say) at tower up into empty blocks when we are low on memory will not create render data +#else ( distSq < 20 * 20 ) ) // Always rebuild really near things or else building (say) at tower up into empty blocks when we are low on memory will not create render data +#endif { considered++; // Is this chunk nearer than our nearest? diff --git a/Minecraft.Client/LevelRenderer.h b/Minecraft.Client/LevelRenderer.h index bd3af8c8..a3da5426 100644 --- a/Minecraft.Client/LevelRenderer.h +++ b/Minecraft.Client/LevelRenderer.h @@ -52,7 +52,9 @@ public: static const int CHUNK_SIZE = 16; #endif static const int CHUNK_Y_COUNT = Level::maxBuildHeight / CHUNK_SIZE; -#if ( defined _XBOX_ONE || defined _WINDOWS64 ) +#if defined _WINDOWS64 + static const int MAX_COMMANDBUFFER_ALLOCATIONS = 2047 * 1024 * 1024; // whisper - added (wow) +#elif ( defined _XBOX_ONE ) static const int MAX_COMMANDBUFFER_ALLOCATIONS = 512 * 1024 * 1024; // 4J - added #elif defined __ORBIS__ static const int MAX_COMMANDBUFFER_ALLOCATIONS = 448 * 1024 * 1024; // 4J - added - hard limit is 512 so giving a lot of headroom here for fragmentation (have seen 16MB lost to fragmentation in multiplayer crash dump before) diff --git a/Minecraft.Client/MinecraftServer.h b/Minecraft.Client/MinecraftServer.h index 53417bb1..fb2a8640 100644 --- a/Minecraft.Client/MinecraftServer.h +++ b/Minecraft.Client/MinecraftServer.h @@ -236,7 +236,7 @@ public: static void SetTime(__int64 time) { setTimeAtEndOfTick = true; setTime = time; } C4JThread::Event* m_serverPausedEvent; -private: +public: // 4J Added bool m_isServerPaused; diff --git a/Minecraft.Client/MultiPlayerGameMode.cpp b/Minecraft.Client/MultiPlayerGameMode.cpp index bd99a9e6..5bc0fc8a 100644 --- a/Minecraft.Client/MultiPlayerGameMode.cpp +++ b/Minecraft.Client/MultiPlayerGameMode.cpp @@ -85,6 +85,20 @@ bool MultiPlayerGameMode::destroyBlock(int x, int y, int z, int face) if (oldTile == NULL) return false; +#if defined(_WINDOWS64) || defined(DISABLE_PSN) || defined(_DISABLE_XBLIVE) + if (g_NetworkManager.IsHost()) + { + level->levelEvent(LevelEvent::PARTICLES_DESTROY_BLOCK, x, y, z, oldTile->id + (level->getData(x, y, z) << Tile::TILE_NUM_SHIFT)); + int data = level->getData(x, y, z); + bool changed = level->removeTile(x, y, z); + if (changed) + { + oldTile->destroy(level, x, y, z, data); + } + return changed; + } +#endif + level->levelEvent(LevelEvent::PARTICLES_DESTROY_BLOCK, x, y, z, oldTile->id + (level->getData(x, y, z) << Tile::TILE_NUM_SHIFT)); int data = level->getData(x, y, z); diff --git a/Minecraft.Client/Orbis/Orbis_Minecraft.cpp b/Minecraft.Client/Orbis/Orbis_Minecraft.cpp index a0d342b7..6c7e1c4c 100644 --- a/Minecraft.Client/Orbis/Orbis_Minecraft.cpp +++ b/Minecraft.Client/Orbis/Orbis_Minecraft.cpp @@ -80,6 +80,8 @@ DWORD dwProfileSettingsA[NUM_PROFILE_VALUES]= uint8_t * AddRichPresenceString(int iID); void FreeRichPresenceStrings(); +char g_Win64Username[17] = {0}; + BOOL g_bWidescreen = TRUE; @@ -984,6 +986,9 @@ int main(int argc, const char *argv[] ) app.GAME_DEFINED_PROFILE_DATA_BYTES*XUSER_MAX_COUNT, &app.uiGameDefinedDataChangedBitmask); + strncpy(g_Win64Username, ProfileManager.GetGamertag(0), 17); + g_Win64Username[16] = 0; + // register the awards RegisterAwardsWithProfileManager(); diff --git a/Minecraft.Client/PS3/PS3_Minecraft.cpp b/Minecraft.Client/PS3/PS3_Minecraft.cpp index cf47bbb7..27812042 100644 --- a/Minecraft.Client/PS3/PS3_Minecraft.cpp +++ b/Minecraft.Client/PS3/PS3_Minecraft.cpp @@ -158,6 +158,8 @@ extern "C" void* __wrap__malloc_init(size_t a_Boundary, size_t a_Size) // for a long time. //------------------------------------------------------------------------------------- +char g_Win64Username[17] = {0}; + BOOL g_bWidescreen = TRUE; //int g_numberOfSpeakersForMiles = 2; // number of speakers to pass to Miles, this is setup from init_audio_hardware @@ -910,6 +912,9 @@ int main() app.GAME_DEFINED_PROFILE_DATA_BYTES*XUSER_MAX_COUNT, &app.uiGameDefinedDataChangedBitmask); + strncpy(g_Win64Username, ProfileManager.GetGamertag(0), 17); + g_Win64Username[16] = 0; + app.DebugPrintf("+++Main - after ProfileManager.Initialise\n"); // register the awards diff --git a/Minecraft.Client/PSVita/PSVita_Minecraft.cpp b/Minecraft.Client/PSVita/PSVita_Minecraft.cpp index ea26eb59..4301a9f0 100644 --- a/Minecraft.Client/PSVita/PSVita_Minecraft.cpp +++ b/Minecraft.Client/PSVita/PSVita_Minecraft.cpp @@ -133,6 +133,8 @@ extern "C" void* __wrap__malloc_init(size_t a_Boundary, size_t a_Size) // for a long time. //------------------------------------------------------------------------------------- +char g_Win64Username[17] = {0}; + BOOL g_bWidescreen = TRUE; //int g_numberOfSpeakersForMiles = 2; // number of speakers to pass to Miles, this is setup from init_audio_hardware @@ -601,6 +603,8 @@ int main() app.GAME_DEFINED_PROFILE_DATA_BYTES*XUSER_MAX_COUNT, &app.uiGameDefinedDataChangedBitmask); + strncpy(g_Win64Username, ProfileManager.GetGamertag(0), 17); + g_Win64Username[16] = 0; // register the awards RegisterAwardsWithProfileManager(); diff --git a/Minecraft.Client/PlayerChunkMap.cpp b/Minecraft.Client/PlayerChunkMap.cpp index bc5f8fc5..fe9c49ff 100644 --- a/Minecraft.Client/PlayerChunkMap.cpp +++ b/Minecraft.Client/PlayerChunkMap.cpp @@ -539,7 +539,12 @@ void PlayerChunkMap::getChunkAndRemovePlayer(int x, int z, shared_ptr player) { - if( addRequests.size() ) +#ifdef _WINDOWS64 + const int maxPerTick = 10; +#else + const int maxPerTick = 1; +#endif + for (int _processed = 0; _processed < maxPerTick && addRequests.size(); _processed++) { // Find the nearest chunk request to the player int px = (int)player->x; @@ -569,6 +574,10 @@ void PlayerChunkMap::tickAddRequests(shared_ptr player) getChunk(itNearest->x, itNearest->z, true)->add(itNearest->player); addRequests.erase(itNearest); } + else + { + break; + } } } diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp index 2658f8e3..9b5312d8 100644 --- a/Minecraft.Client/PlayerConnection.cpp +++ b/Minecraft.Client/PlayerConnection.cpp @@ -1577,7 +1577,10 @@ bool PlayerConnection::isDisconnected() void PlayerConnection::handleDebugOptions(shared_ptr packet) { //Player player = dynamic_pointer_cast( player->shared_from_this() ); - player->SetDebugOptions(packet->m_uiVal); + if(app.DebugSettingsOn()) + { + player->SetDebugOptions(packet->m_uiVal); + } } void PlayerConnection::handleCraftItem(shared_ptr packet) @@ -1587,6 +1590,10 @@ void PlayerConnection::handleCraftItem(shared_ptr packet) if(iRecipe == -1) return; + int recipeCount = (int)Recipes::getInstance()->getRecipies()->size(); + if(iRecipe < 0 || iRecipe >= recipeCount) + return; + Recipy::INGREDIENTS_REQUIRED *pRecipeIngredientsRequired=Recipes::getInstance()->getRecipeIngredientsArray(); shared_ptr pTempItemInst=pRecipeIngredientsRequired[iRecipe].pRecipy->assemble(nullptr); diff --git a/Minecraft.Client/ServerLevel.cpp b/Minecraft.Client/ServerLevel.cpp index bb78fe3b..1150c78b 100644 --- a/Minecraft.Client/ServerLevel.cpp +++ b/Minecraft.Client/ServerLevel.cpp @@ -1086,7 +1086,9 @@ void ServerLevel::entityRemoved(shared_ptr e) shared_ptr ServerLevel::getEntity(int id) { - return entitiesById[id]; + AUTO_VAR(it, entitiesById.find(id)); + if(it != entitiesById.end()) return it->second; + return nullptr; } bool ServerLevel::addGlobalEntity(shared_ptr e) diff --git a/Minecraft.Client/ServerPlayer.cpp b/Minecraft.Client/ServerPlayer.cpp index ec30e2d8..7a441eb7 100644 --- a/Minecraft.Client/ServerPlayer.cpp +++ b/Minecraft.Client/ServerPlayer.cpp @@ -329,6 +329,10 @@ void ServerPlayer::doTickA() // 4J - split off the chunk sending bit of the tick here from ::doTick so we can do this exactly once per player per server tick void ServerPlayer::doChunkSendingTick(bool dontDelayChunks) { +#if defined(_WINDOWS64) || defined(DISABLE_PSN) || defined(_DISABLE_XBLIVE) + for (int _w64cs = 0; _w64cs < 4; _w64cs++) + { +#endif // printf("[%d] %s: sendChunks: %d, empty: %d\n",tickCount, connection->getNetworkPlayer()->GetUID().getOnlineID(),sendChunks,chunksToSend.empty()); if (!chunksToSend.empty()) { @@ -367,6 +371,17 @@ void ServerPlayer::doChunkSendingTick(bool dontDelayChunks) } else { +#if defined(_WINDOWS64) + if( dontDelayChunks || + ((connection->countDelayedPackets() < 16 )&& + (g_NetworkManager.GetHostPlayer()->GetSendQueueSizeMessages( NULL, true ) < 16 )&& + !connection->done) ) + { + lastBrupSendTickCount = tickCount; + okToSend = true; + MinecraftServer::s_slowQueuePacketSent = true; + } +#else bool canSendToPlayer = MinecraftServer::chunkPacketManagement_CanSendTo(connection->getNetworkPlayer()); // app.DebugPrintf(">>> %d\n", canSendToPlayer); @@ -408,6 +423,7 @@ void ServerPlayer::doChunkSendingTick(bool dontDelayChunks) { // app.DebugPrintf(" - \n"); } +#endif } if (okToSend) @@ -485,6 +501,9 @@ void ServerPlayer::doChunkSendingTick(bool dontDelayChunks) } } } +#if defined(_WINDOWS64) || defined(DISABLE_PSN) || defined(_DISABLE_XBLIVE) + } +#endif } void ServerPlayer::doTickB() diff --git a/Minecraft.Client/Xbox/Xbox_Minecraft.cpp b/Minecraft.Client/Xbox/Xbox_Minecraft.cpp index 30c88dc9..f7489850 100644 --- a/Minecraft.Client/Xbox/Xbox_Minecraft.cpp +++ b/Minecraft.Client/Xbox/Xbox_Minecraft.cpp @@ -94,6 +94,8 @@ D3DSAMPLERSTATETYPE SamplerStateModes[]= //------------------------------------------------------------------------------------- +char g_Win64Username[17] = {0}; + BOOL g_bWidescreen = TRUE; @@ -433,6 +435,9 @@ int __cdecl main() &app.uiGameDefinedDataChangedBitmask ); + strncpy(g_Win64Username, ProfileManager.GetGamertag(0), 17); + g_Win64Username[16] = 0; + // register the awards ProfileManager.RegisterAward(eAward_TakingInventory, ACHIEVEMENT_01, eAwardType_Achievement); ProfileManager.RegisterAward(eAward_GettingWood, ACHIEVEMENT_02, eAwardType_Achievement); diff --git a/Minecraft.World/AwardStatPacket.cpp b/Minecraft.World/AwardStatPacket.cpp index b1e3d9e4..6e856a53 100644 --- a/Minecraft.World/AwardStatPacket.cpp +++ b/Minecraft.World/AwardStatPacket.cpp @@ -47,7 +47,7 @@ void AwardStatPacket::read(DataInputStream *dis) //throws IOException // Read parameter blob. int length = dis->readInt(); - if(length > 0) + if(length > 0 && length <= 65536) { m_paramData = byteArray(length); dis->readFully(m_paramData); diff --git a/Minecraft.World/BlockRegionUpdatePacket.cpp b/Minecraft.World/BlockRegionUpdatePacket.cpp index d5484e5a..52ca2255 100644 --- a/Minecraft.World/BlockRegionUpdatePacket.cpp +++ b/Minecraft.World/BlockRegionUpdatePacket.cpp @@ -103,6 +103,12 @@ void BlockRegionUpdatePacket::read(DataInputStream *dis) //throws IOException levelIdx = ( size >> 30 ) & 3; size &= 0x3fffffff; + const int MAX_COMPRESSED_CHUNK_SIZE = 5 * 1024 * 1024; + if(size < 0 || size > MAX_COMPRESSED_CHUNK_SIZE) + { + size = 0; + } + if(size == 0) { buffer = byteArray(); @@ -131,7 +137,10 @@ void BlockRegionUpdatePacket::read(DataInputStream *dis) //throws IOException delete [] compressedBuffer.data; - assert(buffer.length == outputSize); + if(buffer.length != outputSize) + { + app.DebugPrintf("BlockRegionUpdatePacket: decompressed size mismatch (expected %d, got %d)\n", buffer.length, outputSize); + } } } diff --git a/Minecraft.World/ByteArrayInputStream.cpp b/Minecraft.World/ByteArrayInputStream.cpp index 3fd24933..630013b3 100644 --- a/Minecraft.World/ByteArrayInputStream.cpp +++ b/Minecraft.World/ByteArrayInputStream.cpp @@ -10,8 +10,14 @@ //offset - the offset in the buffer of the first byte to read. //length - the maximum number of bytes to read from the buffer. ByteArrayInputStream::ByteArrayInputStream(byteArray buf, unsigned int offset, unsigned int length) - : pos( offset ), count( min( offset+length, buf.length ) ), mark( offset ) + : pos( offset ), mark( offset ) { + if( offset > buf.length ) + count = buf.length; + else if( length > buf.length - offset ) + count = buf.length; + else + count = offset + length; this->buf = buf; } diff --git a/Minecraft.World/ByteArrayOutputStream.cpp b/Minecraft.World/ByteArrayOutputStream.cpp index 5d971031..53125e21 100644 --- a/Minecraft.World/ByteArrayOutputStream.cpp +++ b/Minecraft.World/ByteArrayOutputStream.cpp @@ -31,7 +31,12 @@ void ByteArrayOutputStream::write(unsigned int b) { // If we will fill the buffer we need to make it bigger if( count + 1 >= buf.length ) - buf.resize( buf.length * 2 ); + { + unsigned int newSize = buf.length * 2; + if( newSize <= buf.length ) + return; + buf.resize( newSize ); + } buf[count] = (byte) b; count++; diff --git a/Minecraft.World/ByteArrayTag.h b/Minecraft.World/ByteArrayTag.h index 7ecc88f4..9d2eae31 100644 --- a/Minecraft.World/ByteArrayTag.h +++ b/Minecraft.World/ByteArrayTag.h @@ -21,6 +21,8 @@ public: void load(DataInput *dis, int tagDepth) { int length = dis->readInt(); + + if (length < 0 || length > 2 * 1024 * 1024) length = 0; if ( data.data ) delete[] data.data; data = byteArray(length); diff --git a/Minecraft.World/ComplexItemDataPacket.cpp b/Minecraft.World/ComplexItemDataPacket.cpp index fdb6e122..ccebc4f5 100644 --- a/Minecraft.World/ComplexItemDataPacket.cpp +++ b/Minecraft.World/ComplexItemDataPacket.cpp @@ -32,7 +32,9 @@ void ComplexItemDataPacket::read(DataInputStream *dis) //throws IOException itemType = dis->readShort(); itemId = dis->readShort(); - data = charArray(dis->readUnsignedShort() & 0xffff); + int dataLength = dis->readUnsignedShort() & 0xffff; + if(dataLength > 32767) dataLength = 0; + data = charArray(dataLength); dis->readFully(data); } diff --git a/Minecraft.World/CompoundTag.h b/Minecraft.World/CompoundTag.h index 3e2dcd1f..313e6830 100644 --- a/Minecraft.World/CompoundTag.h +++ b/Minecraft.World/CompoundTag.h @@ -43,9 +43,12 @@ public: } tags.clear(); Tag *tag; + int tagCount = 0; + const int MAX_COMPOUND_TAGS = 10000; while ((tag = Tag::readNamedTag(dis))->getId() != Tag::TAG_End) { tags[tag->getName()] = tag; + if(++tagCount >= MAX_COMPOUND_TAGS) break; } delete tag; } diff --git a/Minecraft.World/Connection.cpp b/Minecraft.World/Connection.cpp index d3fb5694..bb4e1477 100644 --- a/Minecraft.World/Connection.cpp +++ b/Minecraft.World/Connection.cpp @@ -108,8 +108,8 @@ Connection::Connection(Socket *socket, const wstring& id, PacketListener *packet const char *szId = wstringtofilename(id); char readThreadName[256]; char writeThreadName[256]; - sprintf(readThreadName,"%s read\n",szId); - sprintf(writeThreadName,"%s write\n",szId); + sprintf(readThreadName, "%.240s read\n", szId); + sprintf(writeThreadName, "%.240s write\n", szId); readThread = new C4JThread(runRead, (void*)this, readThreadName, READ_STACK_SIZE); writeThread = new C4JThread(runWrite, this, writeThreadName, WRITE_STACK_SIZE); diff --git a/Minecraft.World/ContainerSetContentPacket.cpp b/Minecraft.World/ContainerSetContentPacket.cpp index 9fb2df06..fd2ab8c5 100644 --- a/Minecraft.World/ContainerSetContentPacket.cpp +++ b/Minecraft.World/ContainerSetContentPacket.cpp @@ -32,6 +32,9 @@ void ContainerSetContentPacket::read(DataInputStream *dis) //throws IOException { containerId = dis->readByte(); int count = dis->readShort(); + + if(count < 0 || count > 256) count = 0; + items = ItemInstanceArray(count); for (int i = 0; i < count; i++) { diff --git a/Minecraft.World/ContainerSetSlotPacket.cpp b/Minecraft.World/ContainerSetSlotPacket.cpp index 39b25112..da98a8dc 100644 --- a/Minecraft.World/ContainerSetSlotPacket.cpp +++ b/Minecraft.World/ContainerSetSlotPacket.cpp @@ -35,7 +35,7 @@ void ContainerSetSlotPacket::read(DataInputStream *dis) //throws IOException // 4J Stu - TU-1 hotfix // Fix for #13142 - Holding down the A button on the furnace ingredient slot causes the UI to display incorrect item counts BYTE byteId = dis->readByte(); - containerId = *(char *)&byteId; + containerId = (char)(signed char)byteId; slot = dis->readShort(); item = readItem(dis); } diff --git a/Minecraft.World/CustomPayloadPacket.cpp b/Minecraft.World/CustomPayloadPacket.cpp index 7d108d11..43c213eb 100644 --- a/Minecraft.World/CustomPayloadPacket.cpp +++ b/Minecraft.World/CustomPayloadPacket.cpp @@ -43,7 +43,7 @@ void CustomPayloadPacket::read(DataInputStream *dis) identifier = readUtf(dis, 20); length = dis->readShort(); - if (length > 0 && length < Short::MAX_VALUE) + if (length > 0 && length <= Short::MAX_VALUE) { if(data.data != NULL) { diff --git a/Minecraft.World/DataInputStream.cpp b/Minecraft.World/DataInputStream.cpp index 9bf61c76..ab82a265 100644 --- a/Minecraft.World/DataInputStream.cpp +++ b/Minecraft.World/DataInputStream.cpp @@ -303,6 +303,10 @@ wstring DataInputStream::readUTF() int b = stream->read(); unsigned short UTFLength = (unsigned short) (((a & 0xff) << 8) | (b & 0xff)); + const unsigned short MAX_UTF_LENGTH = 32767; + if( UTFLength > MAX_UTF_LENGTH ) + return outputString; + //// 4J Stu - I decided while writing DataOutputStream that we didn't need to bother using the UTF8 format //// used in the java libs, and just write in/out as wchar_t all the time diff --git a/Minecraft.World/DirectoryLevelStorage.cpp b/Minecraft.World/DirectoryLevelStorage.cpp index d6def6f9..d939f89f 100644 --- a/Minecraft.World/DirectoryLevelStorage.cpp +++ b/Minecraft.World/DirectoryLevelStorage.cpp @@ -432,6 +432,28 @@ void DirectoryLevelStorage::save(shared_ptr player) CompoundTag *DirectoryLevelStorage::load(shared_ptr player) { CompoundTag *tag = loadPlayerDataTag( player->getXuid() ); + +#if defined(_WINDOWS64) || defined(DISABLE_PSN) || defined(_DISABLE_XBLIVE) + if (tag == NULL) + { + const PlayerUID WIN64_XUID_BASE = (PlayerUID)0xe000d45248242f2e; + for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++) + { + PlayerUID oldXuid = WIN64_XUID_BASE + i; + tag = loadPlayerDataTag(oldXuid); + if (tag != NULL) + { + ConsoleSavePath oldFile = ConsoleSavePath(playerDir.getName() + _toString(oldXuid) + L".dat"); + if (m_saveFile->doesFileExist(oldFile)) + { + m_saveFile->deleteFile(m_saveFile->createFile(oldFile)); + } + app.DebugPrintf("Migrated player data from old XUID %llu to new XUID %llu\n", oldXuid, player->getXuid()); + break; + } + } + } +#endif if (tag != NULL) { player->load(tag); diff --git a/Minecraft.World/ExplodePacket.cpp b/Minecraft.World/ExplodePacket.cpp index edcd755c..1b87d1d8 100644 --- a/Minecraft.World/ExplodePacket.cpp +++ b/Minecraft.World/ExplodePacket.cpp @@ -56,6 +56,8 @@ void ExplodePacket::read(DataInputStream *dis) //throws IOException r = dis->readFloat(); int count = dis->readInt(); + if(count < 0 || count > 32768) count = 0; + int xp = (int)x; int yp = (int)y; int zp = (int)z; diff --git a/Minecraft.World/GameCommandPacket.cpp b/Minecraft.World/GameCommandPacket.cpp index 37b207d7..0bf54624 100644 --- a/Minecraft.World/GameCommandPacket.cpp +++ b/Minecraft.World/GameCommandPacket.cpp @@ -40,7 +40,7 @@ void GameCommandPacket::read(DataInputStream *dis) command = (EGameCommand)dis->readInt(); length = dis->readShort(); - if (length > 0 && length < Short::MAX_VALUE) + if (length > 0 && length <= Short::MAX_VALUE) { if(data.data != NULL) { diff --git a/Minecraft.World/IntArrayTag.h b/Minecraft.World/IntArrayTag.h index 808b9894..74dc341a 100644 --- a/Minecraft.World/IntArrayTag.h +++ b/Minecraft.World/IntArrayTag.h @@ -35,6 +35,7 @@ public: void load(DataInput *dis, int tagDepth) { int length = dis->readInt(); + if (length < 0 || length > 65536) length = 0; if ( data.data ) delete[] data.data; data = intArray(length); diff --git a/Minecraft.World/LevelChunk.cpp b/Minecraft.World/LevelChunk.cpp index b48b4491..295f9b9a 100644 --- a/Minecraft.World/LevelChunk.cpp +++ b/Minecraft.World/LevelChunk.cpp @@ -1197,7 +1197,6 @@ void LevelChunk::addEntity(shared_ptr e) int zc = Mth::floor(e->z / 16); if (xc != this->x || zc != this->z) { - app.DebugPrintf("Wrong location!"); // System.out.println("Wrong location! " + e); // Thread.dumpStack(); } diff --git a/Minecraft.World/ListTag.h b/Minecraft.World/ListTag.h index 888ca3ad..d7f90122 100644 --- a/Minecraft.World/ListTag.h +++ b/Minecraft.World/ListTag.h @@ -37,11 +37,13 @@ public: } type = dis->readByte(); int size = dis->readInt(); + if (size < 0 || size > 10000) size = 0; list.clear(); for (int i = 0; i < size; i++) { Tag *tag = Tag::newTag(type, L""); + if (tag == NULL) break; tag->load(dis, tagDepth); list.push_back(tag); } diff --git a/Minecraft.World/Packet.cpp b/Minecraft.World/Packet.cpp index a58ebe6e..0f530ef1 100644 --- a/Minecraft.World/Packet.cpp +++ b/Minecraft.World/Packet.cpp @@ -268,7 +268,13 @@ void Packet::updatePacketStatsPIX() shared_ptr Packet::getPacket(int id) { // 4J: Removed try/catch - return idToCreateMap[id](); +#ifdef __PS3__ + boost::unordered_map::iterator it = idToCreateMap.find(id); +#else + auto it = idToCreateMap.find(id); +#endif + if (it == idToCreateMap.end()) return shared_ptr(); + return it->second(); } void Packet::writeBytes(DataOutputStream *dataoutputstream, byteArray bytes) @@ -330,13 +336,12 @@ shared_ptr Packet::readPacket(DataInputStream *dis, bool isServer) // th if ((isServer && serverReceivedPackets.find(id) == serverReceivedPackets.end()) || (!isServer && clientReceivedPackets.find(id) == clientReceivedPackets.end())) { //app.DebugPrintf("Bad packet id %d\n", id); - __debugbreak(); - assert(false); + return nullptr; // throw new IOException(wstring(L"Bad packet id ") + _toString(id)); } packet = getPacket(id); - if (packet == NULL) assert(false);//throw new IOException(wstring(L"Bad packet id ") + _toString(id)); + if (packet == NULL) return nullptr;//throw new IOException(wstring(L"Bad packet id ") + _toString(id)); //app.DebugPrintf("%s reading packet %d\n", isServer ? "Server" : "Client", packet->getId()); packet->read(dis); @@ -394,17 +399,9 @@ wstring Packet::readUtf(DataInputStream *dis, int maxLength) // throws IOExcepti { short stringLength = dis->readShort(); - if (stringLength > maxLength) + if (stringLength > maxLength || stringLength < 0) { - wstringstream stream; - stream << L"Received string length longer than maximum allowed (" << stringLength << " > " << maxLength << ")"; - assert(false); - // throw new IOException( stream.str() ); - } - if (stringLength < 0) - { - assert(false); - // throw new IOException(L"Received string length is less than zero! Weird string!"); + return L""; } wstring builder = L""; @@ -507,7 +504,7 @@ shared_ptr Packet::readItem(DataInputStream *dis) { shared_ptr item = nullptr; int id = dis->readShort(); - if (id >= 0) + if (id >= 0 && id < 32000) // validate against Item::ITEM_NUM_COUNT { int count = dis->readByte(); int damage = dis->readShort(); @@ -545,9 +542,16 @@ void Packet::writeItem(shared_ptr item, DataOutputStream *dos) CompoundTag *Packet::readNbt(DataInputStream *dis) { int size = dis->readShort(); - if (size < 0) return NULL; + if (size <= 0) return NULL; + + const int MAX_NBT_SIZE = 32767; + if (size > MAX_NBT_SIZE) return NULL; byteArray buff(size); - dis->readFully(buff); + if (!dis->readFully(buff)) + { + delete [] buff.data; + return NULL; + } CompoundTag *result = (CompoundTag *) NbtIo::decompress(buff); delete [] buff.data; return result; diff --git a/Minecraft.World/PreLoginPacket.cpp b/Minecraft.World/PreLoginPacket.cpp index 8ce838e0..b357e5a0 100644 --- a/Minecraft.World/PreLoginPacket.cpp +++ b/Minecraft.World/PreLoginPacket.cpp @@ -62,6 +62,7 @@ void PreLoginPacket::read(DataInputStream *dis) //throws IOException m_friendsOnlyBits = dis->readByte(); m_ugcPlayersVersion = dis->readInt(); m_dwPlayerCount = dis->readByte(); + if( m_dwPlayerCount > MINECRAFT_NET_MAX_PLAYERS ) m_dwPlayerCount = MINECRAFT_NET_MAX_PLAYERS; if( m_dwPlayerCount > 0 ) { m_playerXuids = new PlayerUID[m_dwPlayerCount]; @@ -74,6 +75,7 @@ void PreLoginPacket::read(DataInputStream *dis) //throws IOException { m_szUniqueSaveName[i]=dis->readByte(); } + m_szUniqueSaveName[m_iSaveNameLen - 1] = 0; m_serverSettings = dis->readInt(); m_hostIndex = dis->readByte(); diff --git a/Minecraft.World/RemoveEntitiesPacket.cpp b/Minecraft.World/RemoveEntitiesPacket.cpp index b6c75e18..8a978025 100644 --- a/Minecraft.World/RemoveEntitiesPacket.cpp +++ b/Minecraft.World/RemoveEntitiesPacket.cpp @@ -21,7 +21,9 @@ RemoveEntitiesPacket::~RemoveEntitiesPacket() void RemoveEntitiesPacket::read(DataInputStream *dis) //throws IOException { - ids = intArray(dis->readByte()); + int count = dis->readByte(); + if(count < 0) count = 0; + ids = intArray(count); for(unsigned int i = 0; i < ids.length; ++i) { ids[i] = dis->readInt(); diff --git a/Minecraft.World/Socket.cpp b/Minecraft.World/Socket.cpp index fc98101a..1f0fc607 100644 --- a/Minecraft.World/Socket.cpp +++ b/Minecraft.World/Socket.cpp @@ -138,6 +138,11 @@ void Socket::pushDataToQueue(const BYTE * pbData, DWORD dwDataSize, bool fromHos } EnterCriticalSection(&m_queueLockNetwork[queueIdx]); + if(m_queueNetwork[queueIdx].size() + dwDataSize > 2 * 1024 * 1024) + { + LeaveCriticalSection(&m_queueLockNetwork[queueIdx]); + return; + } for( unsigned int i = 0; i < dwDataSize; i++ ) { m_queueNetwork[queueIdx].push(*pbData++); diff --git a/Minecraft.World/SynchedEntityData.cpp b/Minecraft.World/SynchedEntityData.cpp index 8ffda5d8..cd5110e9 100644 --- a/Minecraft.World/SynchedEntityData.cpp +++ b/Minecraft.World/SynchedEntityData.cpp @@ -343,8 +343,10 @@ vector > *SynchedEntityData::unpack(Data vector > *result = NULL; int currentHeader = input->readByte(); + int itemCount = 0; + const int MAX_ENTITY_DATA_ITEMS = 256; - while (currentHeader != EOF_MARKER) + while (currentHeader != EOF_MARKER && itemCount < MAX_ENTITY_DATA_ITEMS) { if (result == NULL) @@ -399,6 +401,7 @@ vector > *SynchedEntityData::unpack(Data break; } result->push_back(item); + itemCount++; currentHeader = input->readByte(); } diff --git a/Minecraft.World/Tag.cpp b/Minecraft.World/Tag.cpp index 0051368d..295751e6 100644 --- a/Minecraft.World/Tag.cpp +++ b/Minecraft.World/Tag.cpp @@ -84,27 +84,48 @@ Tag *Tag::readNamedTag(DataInput *dis) Tag *Tag::readNamedTag(DataInput *dis, int tagDepth) { + static __declspec(thread) int depth = 0; + static __declspec(thread) int totalTagCount = 0; + + if (depth == 0) + totalTagCount = 0; + + depth++; + + if (depth > 256) + { + depth--; + return new EndTag(); + } + + totalTagCount++; + const int MAX_TOTAL_TAGS = 32768; + if (totalTagCount > MAX_TOTAL_TAGS) + { + depth--; + return new EndTag(); + } + byte type = dis->readByte(); - if (type == 0) return new EndTag(); + if (type == 0) { depth--; return new EndTag(); } // 4J Stu - readByte can return -1, so if it's that then also mark as the end tag if(type == 255) { - app.DebugPrintf("readNamedTag read a type of 255\n"); -#ifndef _CONTENT_PACKAGE - __debugbreak(); -#endif + depth--; return new EndTag(); } wstring name = dis->readUTF();//new String(bytes, "UTF-8"); Tag *tag = newTag(type, name); + if (tag == NULL) { depth--; return new EndTag(); } // short length = dis.readShort(); // byte[] bytes = new byte[length]; // dis.readFully(bytes); tag->load(dis, tagDepth); + depth--; return tag; } diff --git a/Minecraft.World/TextureAndGeometryPacket.cpp b/Minecraft.World/TextureAndGeometryPacket.cpp index b6fc6ce7..c3046e37 100644 --- a/Minecraft.World/TextureAndGeometryPacket.cpp +++ b/Minecraft.World/TextureAndGeometryPacket.cpp @@ -123,7 +123,16 @@ void TextureAndGeometryPacket::read(DataInputStream *dis) //throws IOException { textureName = dis->readUTF(); dwSkinID = (DWORD)dis->readInt(); - dwTextureBytes = (DWORD)dis->readShort(); + short rawTextureBytes = dis->readShort(); + if(rawTextureBytes <= 0) + { + dwTextureBytes = 0; + } + else + { + dwTextureBytes = (DWORD)(unsigned short)rawTextureBytes; + if(dwTextureBytes > 65536) dwTextureBytes = 0; + } if(dwTextureBytes>0) { @@ -136,7 +145,16 @@ void TextureAndGeometryPacket::read(DataInputStream *dis) //throws IOException } uiAnimOverrideBitmask = dis->readInt(); - dwBoxC = (DWORD)dis->readShort(); + short rawBoxC = dis->readShort(); + if(rawBoxC <= 0) + { + dwBoxC = 0; + } + else + { + dwBoxC = (DWORD)(unsigned short)rawBoxC; + if(dwBoxC > 256) dwBoxC = 0; // sane limit for skin boxes + } if(dwBoxC>0) { diff --git a/Minecraft.World/TexturePacket.cpp b/Minecraft.World/TexturePacket.cpp index c5eebb0d..34e35da3 100644 --- a/Minecraft.World/TexturePacket.cpp +++ b/Minecraft.World/TexturePacket.cpp @@ -37,16 +37,24 @@ void TexturePacket::handle(PacketListener *listener) void TexturePacket::read(DataInputStream *dis) //throws IOException { textureName = dis->readUTF(); - dwBytes = (DWORD)dis->readShort(); - - if(dwBytes>0) + short rawBytes = dis->readShort(); + if(rawBytes <= 0) { - this->pbData= new BYTE [dwBytes]; + dwBytes = 0; + return; + } + dwBytes = (DWORD)(unsigned short)rawBytes; + if(dwBytes > 65536) + { + dwBytes = 0; + return; + } - for(DWORD i=0;ipbData[i] = dis->readByte(); - } + this->pbData= new BYTE [dwBytes]; + + for(DWORD i=0;ipbData[i] = dis->readByte(); } } diff --git a/Minecraft.World/UpdateGameRuleProgressPacket.cpp b/Minecraft.World/UpdateGameRuleProgressPacket.cpp index 29430bb2..8a2c3d4f 100644 --- a/Minecraft.World/UpdateGameRuleProgressPacket.cpp +++ b/Minecraft.World/UpdateGameRuleProgressPacket.cpp @@ -42,7 +42,7 @@ void UpdateGameRuleProgressPacket::read(DataInputStream *dis) //throws IOExcepti m_dataTag = dis->readInt(); int dataLength = dis->readInt(); - if(dataLength > 0) + if(dataLength > 0 && dataLength <= 65536) { m_data = byteArray(dataLength); dis->readFully(m_data); diff --git a/Minecraft.World/compression.cpp b/Minecraft.World/compression.cpp index 9bbf0a8e..419576c2 100644 --- a/Minecraft.World/compression.cpp +++ b/Minecraft.World/compression.cpp @@ -196,9 +196,20 @@ HRESULT Compression::DecompressLZXRLE(void *pDestination, unsigned int *pDestSiz unsigned int rleSize = staticRleSize; unsigned char *dynamicRleBuf = NULL; - if(*pDestSize > rleSize) + unsigned int safeRleSize = max(rleSize, *pDestSize); + + const unsigned int MAX_RLE_ALLOC = 16 * 1024 * 1024; // 16 MB + if(safeRleSize > MAX_RLE_ALLOC) { - rleSize = *pDestSize; + LeaveCriticalSection(&rleDecompressLock); + *pDestSize = 0; + // E_FAIL doesnt exist on PS3 + return (HRESULT)0x80004005L; + } + + if(safeRleSize > staticRleSize) + { + rleSize = safeRleSize; dynamicRleBuf = new unsigned char[rleSize]; Decompress(dynamicRleBuf, &rleSize, pSource, SrcSize); pucIn = (unsigned char *)dynamicRleBuf; @@ -212,16 +223,19 @@ HRESULT Compression::DecompressLZXRLE(void *pDestination, unsigned int *pDestSiz //unsigned char *pucIn = (unsigned char *)rleDecompressBuf; unsigned char *pucEnd = pucIn + rleSize; unsigned char *pucOut = (unsigned char *)pDestination; + unsigned char *pucOutEnd = pucOut + *pDestSize; while( pucIn != pucEnd ) { unsigned char thisOne = *pucIn++; if( thisOne == 255 ) { + if( pucIn >= pucEnd ) break; unsigned int count = *pucIn++; if( count < 3 ) { count++; + if( pucOut + count > pucOutEnd ) { pucOut = pucOutEnd; break; } for( unsigned int i = 0; i < count; i++ ) { *pucOut++ = 255; @@ -230,7 +244,9 @@ HRESULT Compression::DecompressLZXRLE(void *pDestination, unsigned int *pDestSiz else { count++; + if( pucIn >= pucEnd ) break; unsigned char data = *pucIn++; + if( pucOut + count > pucOutEnd ) { pucOut = pucOutEnd; break; } for( unsigned int i = 0; i < count; i++ ) { *pucOut++ = data; @@ -239,6 +255,7 @@ HRESULT Compression::DecompressLZXRLE(void *pDestination, unsigned int *pDestSiz } else { + if( pucOut >= pucOutEnd ) break; *pucOut++ = thisOne; } } @@ -260,16 +277,19 @@ HRESULT Compression::DecompressRLE(void *pDestination, unsigned int *pDestSize, unsigned char *pucIn = (unsigned char *)pSource; unsigned char *pucEnd = pucIn + SrcSize; unsigned char *pucOut = (unsigned char *)pDestination; + unsigned char *pucOutEnd = pucOut + *pDestSize; while( pucIn != pucEnd ) { unsigned char thisOne = *pucIn++; if( thisOne == 255 ) { + if( pucIn >= pucEnd ) break; unsigned int count = *pucIn++; if( count < 3 ) { count++; + if( pucOut + count > pucOutEnd ) { pucOut = pucOutEnd; break; } for( unsigned int i = 0; i < count; i++ ) { *pucOut++ = 255; @@ -278,7 +298,9 @@ HRESULT Compression::DecompressRLE(void *pDestination, unsigned int *pDestSize, else { count++; + if( pucIn >= pucEnd ) break; unsigned char data = *pucIn++; + if( pucOut + count > pucOutEnd ) { pucOut = pucOutEnd; break; } for( unsigned int i = 0; i < count; i++ ) { *pucOut++ = data; @@ -287,6 +309,7 @@ HRESULT Compression::DecompressRLE(void *pDestination, unsigned int *pDestSize, } else { + if( pucOut >= pucOutEnd ) break; *pucOut++ = thisOne; } } diff --git a/Minecraft.World/x64headers/extraX64.h b/Minecraft.World/x64headers/extraX64.h index d384cbe7..884730ba 100644 --- a/Minecraft.World/x64headers/extraX64.h +++ b/Minecraft.World/x64headers/extraX64.h @@ -249,6 +249,9 @@ private: }; void Win64_SetupRemoteQNetPlayer(IQNetPlayer *player, BYTE smallId, bool isHost, bool isLocal); +char GetUsername(); +PlayerUID Win64_UsernameToXuid(const char* username); +PlayerUID Win64_UsernameToXuid(const wchar_t* username); const int QNET_GETSENDQUEUESIZE_SECONDARY_TYPE = 0; const int QNET_GETSENDQUEUESIZE_MESSAGES = 0; From 67a8111974b975c2ab0272746aed6228d667aea4 Mon Sep 17 00:00:00 2001 From: piebot Date: Fri, 14 Aug 2026 11:46:19 +0300 Subject: [PATCH 2/6] LCEMP commit: support more max players --- .../Common/Network/NetworkSocketLayer.cpp | 4 ++-- .../Common/Network/NetworkSocketLayer.h | 5 ++-- .../Common/Network/PlatformNetworkManager.cpp | 2 +- .../Common/UI/UIScene_LoadOrJoinMenu.cpp | 2 +- Minecraft.Client/Extrax64Stubs.cpp | 3 ++- Minecraft.Client/LivingEntityRenderer.cpp | 6 ++--- Minecraft.Client/Minecraft.Client.vcxproj | 3 +-- .../Minecraft.Client.vcxproj.filters | 3 --- Minecraft.Client/PlayerRenderer.cpp | 24 ++++++++++++++++++- Minecraft.Client/Xbox/Network/extra.h | 4 ---- Minecraft.World/stdafx.h | 2 -- 11 files changed, 36 insertions(+), 22 deletions(-) delete mode 100644 Minecraft.Client/Xbox/Network/extra.h diff --git a/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp b/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp index 2c5acccf..fba5a944 100644 --- a/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp +++ b/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp @@ -1309,7 +1309,7 @@ void NetworkSocketLayer::UpdateAdvertisePlayerNames(BYTE count, const char playe EnterCriticalSection(&s_advertiseLock); memset(s_advertiseData.playerNames, 0, sizeof(s_advertiseData.playerNames)); s_advertiseData.playerCount = count; - for (int i = 0; i < count && i < 8; i++) + for (int i = 0; i < count && i < NETWORK_LAN_BROADCAST_PLAYERS; i++) { memcpy(s_advertiseData.playerNames[i], playerNames[i], NETWORK_LAN_PLAYER_NAME_SIZE < (int)XUSER_NAME_SIZE ? NETWORK_LAN_PLAYER_NAME_SIZE : (int)XUSER_NAME_SIZE); @@ -1587,7 +1587,7 @@ int NetworkSocketLayer::DiscoveryThreadProc(LPVOID param) broadcast->hostName[31] = L'\0'; - for (int pn = 0; pn < 8; pn++) + for (int pn = 0; pn < NETWORK_LAN_BROADCAST_PLAYERS; pn++) broadcast->playerNames[pn][XUSER_NAME_SIZE - 1] = '\0'; char senderIP[64]; diff --git a/Minecraft.Client/Common/Network/NetworkSocketLayer.h b/Minecraft.Client/Common/Network/NetworkSocketLayer.h index ee56e7c7..908a4b61 100644 --- a/Minecraft.Client/Common/Network/NetworkSocketLayer.h +++ b/Minecraft.Client/Common/Network/NetworkSocketLayer.h @@ -38,6 +38,7 @@ #define NETWORK_LAN_MAX_PACKET_SIZE (3 * 1024 * 1024) #define NETWORK_LAN_DISCOVERY_PORT 25566 #define NETWORK_LAN_BROADCAST_MAGIC 0x4D434C4E +#define NETWORK_LAN_BROADCAST_PLAYERS 8 #ifdef __PS3__ typedef int SOCKET; @@ -115,7 +116,7 @@ struct LANBroadcast DWORD texturePackParentId; BYTE subTexturePackId; BYTE isJoinable; - char playerNames[8][NETWORK_LAN_PLAYER_NAME_SIZE]; + char playerNames[NETWORK_LAN_BROADCAST_PLAYERS][NETWORK_LAN_PLAYER_NAME_SIZE]; }; #pragma pack(pop) @@ -132,7 +133,7 @@ struct LANSession unsigned char subTexturePackId; bool isJoinable; DWORD lastSeenTick; - char playerNames[8][NETWORK_LAN_PLAYER_NAME_SIZE]; + char playerNames[NETWORK_LAN_BROADCAST_PLAYERS][NETWORK_LAN_PLAYER_NAME_SIZE]; }; struct RemoteConnection diff --git a/Minecraft.Client/Common/Network/PlatformNetworkManager.cpp b/Minecraft.Client/Common/Network/PlatformNetworkManager.cpp index af454b31..968ff578 100644 --- a/Minecraft.Client/Common/Network/PlatformNetworkManager.cpp +++ b/Minecraft.Client/Common/Network/PlatformNetworkManager.cpp @@ -811,7 +811,7 @@ void CPlatformNetworkManagerStub::SearchForGames() memset(info->data.players, 0, sizeof(info->data.players)); memset(info->data.szPlayers, 0, sizeof(info->data.szPlayers)); - for (int p = 0; p < MINECRAFT_NET_MAX_PLAYERS && p < lanSessions[i].playerCount; p++) + for (int p = 0; p < NETWORK_LAN_BROADCAST_PLAYERS && p < lanSessions[i].playerCount; p++) { if (lanSessions[i].playerNames[p][0] != 0) { diff --git a/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp index aeeb871f..98ebbd23 100644 --- a/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp @@ -1597,7 +1597,7 @@ void UIScene_LoadOrJoinMenu::LoadLevelGen(LevelGenerationOptions *levelGen) bool isClientSide = false; bool isPrivate = false; // TODO int maxPlayers = MINECRAFT_NET_MAX_PLAYERS; - int maxPlayers = 8; + int maxPlayers = MINECRAFT_NET_MAX_PLAYERS; if( app.GetTutorialMode() ) { diff --git a/Minecraft.Client/Extrax64Stubs.cpp b/Minecraft.Client/Extrax64Stubs.cpp index 4c605e7f..2d50b945 100644 --- a/Minecraft.Client/Extrax64Stubs.cpp +++ b/Minecraft.Client/Extrax64Stubs.cpp @@ -598,7 +598,8 @@ IQNetPlayer *IQNet::GetLocalPlayerByUserIndex(DWORD dwUserIndex) static bool Win64_IsActivePlayer(IQNetPlayer *p, DWORD index) { if (index == 0) return true; - return (p->GetCustomDataValue() != 0); + if (p->GetCustomDataValue() != 0) return true; + return (p->m_isRemote && p->m_gamertag[0] != 0); } IQNetPlayer *IQNet::GetPlayerByIndex(DWORD dwPlayerIndex) diff --git a/Minecraft.Client/LivingEntityRenderer.cpp b/Minecraft.Client/LivingEntityRenderer.cpp index 7f4a3258..a4d0ecb1 100644 --- a/Minecraft.Client/LivingEntityRenderer.cpp +++ b/Minecraft.Client/LivingEntityRenderer.cpp @@ -531,7 +531,7 @@ void LivingEntityRenderer::renderNameTag(shared_ptr mob, const wst int offs = 0; wstring playerName; - WCHAR wchName[2]; + WCHAR wchName[8]; if(mob->instanceof(eTYPE_PLAYER)) { @@ -551,7 +551,7 @@ void LivingEntityRenderer::renderNameTag(shared_ptr mob, const wst } else { - memset(wchName,0,sizeof(WCHAR)*2); + memset(wchName,0,sizeof(wchName)); swprintf(wchName, 2, L"%d",player->getPlayerIndex()+1); playerName=wchName; player->SetPlayerNameValidState(false); @@ -561,7 +561,7 @@ void LivingEntityRenderer::renderNameTag(shared_ptr mob, const wst playerName=name; break; case Player::ePlayerNameValid_False: - memset(wchName,0,sizeof(WCHAR)*2); + memset(wchName,0,sizeof(wchName)d); swprintf(wchName, 2, L"%d",player->getPlayerIndex()+1); playerName=wchName; break; diff --git a/Minecraft.Client/Minecraft.Client.vcxproj b/Minecraft.Client/Minecraft.Client.vcxproj index d1674507..df0dcdbb 100644 --- a/Minecraft.Client/Minecraft.Client.vcxproj +++ b/Minecraft.Client/Minecraft.Client.vcxproj @@ -16586,7 +16586,6 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false - true true @@ -36696,4 +36695,4 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU - + \ No newline at end of file diff --git a/Minecraft.Client/Minecraft.Client.vcxproj.filters b/Minecraft.Client/Minecraft.Client.vcxproj.filters index f92e8d8d..4b4cb47b 100644 --- a/Minecraft.Client/Minecraft.Client.vcxproj.filters +++ b/Minecraft.Client/Minecraft.Client.vcxproj.filters @@ -3611,9 +3611,6 @@ Orbis\Network - - Xbox\Source Files\Network - Common\Source Files\UI\Scenes diff --git a/Minecraft.Client/PlayerRenderer.cpp b/Minecraft.Client/PlayerRenderer.cpp index 6e6b6626..77ec0fdd 100644 --- a/Minecraft.Client/PlayerRenderer.cpp +++ b/Minecraft.Client/PlayerRenderer.cpp @@ -38,13 +38,35 @@ PlayerRenderer::PlayerRenderer() : LivingEntityRenderer( new HumanoidModel(0), 0 armorParts2 = new HumanoidModel(0.5f); } +static unsigned int HsvToArgb(float h, float s, float v) +{ + float c = v * s; + float x = c * (1.0f - fabsf(fmodf(h / 60.0f, 2.0f) - 1.0f)); + float m = v - c; + float r, g, b; + if (h < 60) { r = c; g = x; b = 0; } + else if (h < 120) { r = x; g = c; b = 0; } + else if (h < 180) { r = 0; g = c; b = x; } + else if (h < 240) { r = 0; g = x; b = c; } + else if (h < 300) { r = x; g = 0; b = c; } + else { r = c; g = 0; b = x; } + unsigned int ri = (unsigned int)((r + m) * 255.0f); + unsigned int gi = (unsigned int)((g + m) * 255.0f); + unsigned int bi = (unsigned int)((b + m) * 255.0f); + return 0xFF000000 | (ri << 16) | (gi << 8) | bi; +} + unsigned int PlayerRenderer::getNametagColour(int index) { if( index >= 0 && index < MINECRAFT_NET_MAX_PLAYERS) { return s_nametagColors[index]; } - return 0xFF000000; + + float hue = fmodf(index * 137.508f, 360.0f); + float sat = 0.65f + (float)(index % 3) * 0.15f; + float val = 0.75f + (float)(index % 4) * 0.08f; + return HsvToArgb(hue, sat, val); } int PlayerRenderer::prepareArmor(shared_ptr _player, int layer, float a) diff --git a/Minecraft.Client/Xbox/Network/extra.h b/Minecraft.Client/Xbox/Network/extra.h deleted file mode 100644 index 70085d5b..00000000 --- a/Minecraft.Client/Xbox/Network/extra.h +++ /dev/null @@ -1,4 +0,0 @@ - -#pragma once - -const int MINECRAFT_NET_MAX_PLAYERS = 8; diff --git a/Minecraft.World/stdafx.h b/Minecraft.World/stdafx.h index 864648d9..b0abc003 100644 --- a/Minecraft.World/stdafx.h +++ b/Minecraft.World/stdafx.h @@ -131,8 +131,6 @@ typedef XUID GameSessionUID; #ifndef _XBOX #include "extraX64.h" -#else -#include "..\Minecraft.Client\xbox\network\extra.h" #endif #include "Definitions.h" From f404e4b4e4444df02ac2c420778c2d3b027d43d5 Mon Sep 17 00:00:00 2001 From: piebot Date: Fri, 14 Aug 2026 13:32:11 +0300 Subject: [PATCH 3/6] LCEMP commit: prepare code for dedicated server support --- Minecraft.Client/Common/Consoles_App.cpp | 25 ++++++++ .../Common/Network/GameNetworkManager.cpp | 3 + .../Common/Network/NetworkSocketLayer.cpp | 49 ++++++++++++++- .../Common/Network/NetworkSocketLayer.h | 4 ++ Minecraft.Client/Minecraft.cpp | 8 +++ Minecraft.Client/MinecraftServer.cpp | 60 ++++++++++++++----- Minecraft.Client/PendingConnection.cpp | 2 + Minecraft.Client/PlayerConnection.cpp | 8 ++- Minecraft.Client/PlayerConnection.h | 1 + Minecraft.Client/PlayerList.cpp | 4 ++ Minecraft.Client/ServerLevel.cpp | 2 + Minecraft.World/Level.cpp | 3 +- Minecraft.World/StrongholdPieces.cpp | 4 +- 13 files changed, 150 insertions(+), 23 deletions(-) diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp index 56a374d7..d5c29b8c 100644 --- a/Minecraft.Client/Common/Consoles_App.cpp +++ b/Minecraft.Client/Common/Consoles_App.cpp @@ -1,5 +1,7 @@  #include "stdafx.h" + +#include #include "..\..\Minecraft.World\net.minecraft.world.entity.item.h" #include "..\..\Minecraft.World\net.minecraft.world.entity.player.h" #include "..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" @@ -236,6 +238,26 @@ void CMinecraftApp::DebugPrintf(const char *szFormat, ...) vsnprintf(buf, sizeof(buf), szFormat, ap); va_end(ap); OutputDebugStringA(buf); +#ifdef _DEDICATED_SERVER + bool hasContent = false; + for (const char *p = buf; *p; p++) { + if (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\r' && *p != '=') { + hasContent = true; + break; + } + } + if (hasContent) + { + size_t len = strlen(buf); + while (len > 0 && (buf[len-1] == '\n' || buf[len-1] == '\r')) + buf[--len] = '\0'; + + time_t now = time(NULL); + struct tm t; + localtime_s(&t, &now); + printf("[%02d:%02d:%02d] [Server thread/INFO]: %s\n", t.tm_hour, t.tm_min, t.tm_sec, buf); + } +#endif #endif } @@ -298,6 +320,9 @@ LPCWSTR CMinecraftApp::GetString(int iID) { //return L"Değişiklikler ve Yenilikler"; //return L"ÕÕÕÕÖÖÖÖ"; +#ifdef _DEDICATED_SERVER + if (!app.m_stringTable) return L""; +#endif return app.m_stringTable->getString(iID); } diff --git a/Minecraft.Client/Common/Network/GameNetworkManager.cpp b/Minecraft.Client/Common/Network/GameNetworkManager.cpp index f422c3a0..bde8e013 100644 --- a/Minecraft.Client/Common/Network/GameNetworkManager.cpp +++ b/Minecraft.Client/Common/Network/GameNetworkManager.cpp @@ -873,12 +873,14 @@ int CGameNetworkManager::JoinFromInvite_SignInReturned(void *pParam,bool bContin void CGameNetworkManager::UpdateAndSetGameSessionData(INetworkPlayer *pNetworkPlayerLeaving) { +#ifndef _DEDICATED_SERVER Minecraft *pMinecraft = Minecraft::GetInstance(); TexturePack *tPack = pMinecraft->skins->getSelected(); s_pPlatformNetworkManager->SetSessionTexturePackParentId( tPack->getDLCParentPackId() ); s_pPlatformNetworkManager->SetSessionSubTexturePackId( tPack->getDLCSubPackId() ); s_pPlatformNetworkManager->UpdateAndSetGameSessionData( pNetworkPlayerLeaving ); +#endif } void CGameNetworkManager::SendInviteGUI(int quadrant) @@ -1540,6 +1542,7 @@ void CGameNetworkManager::CreateSocket( INetworkPlayer *pNetworkPlayer, bool loc // Add this user to the game server if the game is started already if( g_NetworkManager.IsHost() && g_NetworkManager.IsInGameplay() ) { + app.DebugPrintf("Adding incoming socket for smallId=%d\n", pNetworkPlayer->GetSmallId()); Socket::addIncomingSocket(socket); } diff --git a/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp b/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp index fba5a944..be7c203f 100644 --- a/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp +++ b/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp @@ -61,6 +61,9 @@ std::vector NetworkSocketLayer::s_pendingJoinSmallIds; CRITICAL_SECTION NetworkSocketLayer::s_freeSmallIdLock; std::vector NetworkSocketLayer::s_freeSmallIds; +CRITICAL_SECTION NetworkSocketLayer::s_earlyDataLock; +std::vector NetworkSocketLayer::s_earlyDataBuffers[NETWORK_LAN_MAX_CLIENTS + 1]; + // only goes true on a successful Initialize(), and a failed one gets retried static bool s_locksCreated = false; #if defined _WINDOWS64 @@ -88,6 +91,7 @@ bool NetworkSocketLayer::Initialize() InitializeCriticalSection(&s_disconnectLock); InitializeCriticalSection(&s_pendingJoinLock); InitializeCriticalSection(&s_freeSmallIdLock); + InitializeCriticalSection(&s_earlyDataLock); for (int i = 0; i < NETWORK_LAN_MAX_CLIENTS + 1; i++) { @@ -801,6 +805,9 @@ bool NetworkSocketLayer::JoinGame(const char *ip, int port) } s_localSmallId = assignedSmallId; + DWORD noTimeout = 0; + setsockopt(s_hostConnectionSocket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&noTimeout, sizeof(noTimeout)); + app.DebugPrintf("LAN: Connected to %s:%d, assigned smallId=%d\n", ip, port, s_localSmallId); s_active = true; @@ -926,9 +933,13 @@ void NetworkSocketLayer::HandleDataReceived(BYTE fromSmallId, BYTE toSmallId, un if (pPlayerFrom == NULL || pPlayerTo == NULL) { - // dropping here is silent and looks identical to the peer never sending - app.DebugPrintf("LAN: DROPPED %u bytes - from smallId=%d(%s) to smallId=%d(%s)\n", - dataSize, fromSmallId, pPlayerFrom ? "ok" : "NULL", toSmallId, pPlayerTo ? "ok" : "NULL"); + if (s_isHost && fromSmallId > 0 && fromSmallId < NETWORK_LAN_MAX_CLIENTS + 1) + { + EnterCriticalSection(&s_earlyDataLock); + s_earlyDataBuffers[fromSmallId].insert( + s_earlyDataBuffers[fromSmallId].end(), data, data + dataSize); + LeaveCriticalSection(&s_earlyDataLock); + } return; } @@ -937,6 +948,13 @@ void NetworkSocketLayer::HandleDataReceived(BYTE fromSmallId, BYTE toSmallId, un ::Socket *pSocket = pPlayerFrom->GetSocket(); if (pSocket != NULL) pSocket->pushDataToQueue(data, dataSize, false); + else + { + EnterCriticalSection(&s_earlyDataLock); + s_earlyDataBuffers[fromSmallId].insert( + s_earlyDataBuffers[fromSmallId].end(), data, data + dataSize); + LeaveCriticalSection(&s_earlyDataLock); + } } else { @@ -946,6 +964,26 @@ void NetworkSocketLayer::HandleDataReceived(BYTE fromSmallId, BYTE toSmallId, un } } +void NetworkSocketLayer::FlushPendingData() +{ + EnterCriticalSection(&s_earlyDataLock); + for (int i = 1; i < NETWORK_LAN_MAX_CLIENTS + 1; i++) + { + if (s_earlyDataBuffers[i].empty()) continue; + + INetworkPlayer *pPlayer = g_NetworkManager.GetPlayerBySmallId((BYTE)i); + if (pPlayer == NULL) continue; + + ::Socket *pSocket = pPlayer->GetSocket(); + if (pSocket == NULL) continue; + + pSocket->pushDataToQueue(s_earlyDataBuffers[i].data(), + (DWORD)s_earlyDataBuffers[i].size(), false); + s_earlyDataBuffers[i].clear(); + } + LeaveCriticalSection(&s_earlyDataLock); +} + int NetworkSocketLayer::AcceptThreadProc(LPVOID param) { while (s_active) @@ -1172,6 +1210,11 @@ void NetworkSocketLayer::CloseConnectionBySmallId(BYTE smallId) app.DebugPrintf("Win64 LAN: Force-closed TCP connection for smallId=%d\n", smallId); } LeaveCriticalSection(&s_connectionsLock); + + EnterCriticalSection(&s_earlyDataLock); + if (smallId < NETWORK_LAN_MAX_CLIENTS + 1) + s_earlyDataBuffers[smallId].clear(); + LeaveCriticalSection(&s_earlyDataLock); } int NetworkSocketLayer::ClientRecvThreadProc(LPVOID param) diff --git a/Minecraft.Client/Common/Network/NetworkSocketLayer.h b/Minecraft.Client/Common/Network/NetworkSocketLayer.h index 908a4b61..71b4ef8c 100644 --- a/Minecraft.Client/Common/Network/NetworkSocketLayer.h +++ b/Minecraft.Client/Common/Network/NetworkSocketLayer.h @@ -167,6 +167,7 @@ public: static SOCKET GetSocketForSmallId(BYTE smallId); static void HandleDataReceived(BYTE fromSmallId, BYTE toSmallId, unsigned char *data, unsigned int dataSize); + static void FlushPendingData(); static bool PopDisconnectedSmallId(BYTE *outSmallId); static void PushFreeSmallId(BYTE smallId); @@ -235,6 +236,9 @@ private: static CRITICAL_SECTION s_freeSmallIdLock; static std::vector s_freeSmallIds; + + static CRITICAL_SECTION s_earlyDataLock; + static std::vector s_earlyDataBuffers[NETWORK_LAN_MAX_CLIENTS + 1]; }; extern bool g_MultiplayerHost; diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index 96111185..f8dd53b1 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -4963,11 +4963,18 @@ void Minecraft::handleClientTextureReceived(const wstring &textureName) unsigned int Minecraft::getCurrentTexturePackId() { +#ifdef _DEDICATED_SERVER + return 0; +#else return skins->getSelected()->getId(); +#endif } ColourTable *Minecraft::getColourTable() { +#ifdef _DEDICATED_SERVER + return NULL; +#else TexturePack *selected = skins->getSelected(); ColourTable *colours = selected->getColourTable(); @@ -4978,6 +4985,7 @@ ColourTable *Minecraft::getColourTable() } return colours; +#endif } #if defined __ORBIS__ diff --git a/Minecraft.Client/MinecraftServer.cpp b/Minecraft.Client/MinecraftServer.cpp index 626b5d08..09d5405c 100644 --- a/Minecraft.Client/MinecraftServer.cpp +++ b/Minecraft.Client/MinecraftServer.cpp @@ -35,7 +35,11 @@ #include "..\Minecraft.World\net.minecraft.world.entity.h" #include "ProgressRenderer.h" #include "ServerPlayer.h" +#include "PlayerConnection.h" #include "GameRenderer.h" +#if defined(_WINDOWS64) || defined(DISABLE_PSN) || defined(_DISABLE_XBLIVE) +#include "Common\Network\NetworkSocketLayer.h" +#endif #include "..\Minecraft.World\ThreadName.h" #include "..\Minecraft.World\IntCache.h" #include "..\Minecraft.World\CompressedTileStorage.h" @@ -47,6 +51,11 @@ #endif #include "PS3\PS3Extras\ShutdownManager.h" #include "ServerCommandDispatcher.h" + +#ifdef _DEDICATED_SERVER +#include "..\Minecraft.Server\ServerCommands.h" +#endif + #include "..\Minecraft.World\BiomeSource.h" #include "PlayerChunkMap.h" #include "Common\Telemetry\TelemetryManager.h" @@ -150,13 +159,11 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW #endif settings = new Settings(new File(L"server.properties")); - app.DebugPrintf("\n*** SERVER SETTINGS ***\n"); - app.DebugPrintf("ServerSettings: host-friends-only is %s\n",(app.GetGameHostOption(eGameHostOption_FriendsOfFriends)>0)?"on":"off"); - app.DebugPrintf("ServerSettings: game-type is %s\n",(app.GetGameHostOption(eGameHostOption_GameType)==0)?"Survival Mode":"Creative Mode"); - app.DebugPrintf("ServerSettings: pvp is %s\n",(app.GetGameHostOption(eGameHostOption_PvP)>0)?"on":"off"); - app.DebugPrintf("ServerSettings: fire spreads is %s\n",(app.GetGameHostOption(eGameHostOption_FireSpreads)>0)?"on":"off"); - app.DebugPrintf("ServerSettings: tnt explodes is %s\n",(app.GetGameHostOption(eGameHostOption_TNT)>0)?"on":"off"); - app.DebugPrintf("\n"); + app.DebugPrintf("host-friends-only is %s",(app.GetGameHostOption(eGameHostOption_FriendsOfFriends)>0)?"on":"off"); + app.DebugPrintf("game-type is %s",(app.GetGameHostOption(eGameHostOption_GameType)==0)?"Survival Mode":"Creative Mode"); + app.DebugPrintf("pvp is %s",(app.GetGameHostOption(eGameHostOption_PvP)>0)?"on":"off"); + app.DebugPrintf("fire-spreads is %s",(app.GetGameHostOption(eGameHostOption_FireSpreads)>0)?"on":"off"); + app.DebugPrintf("tnt-explodes is %s",(app.GetGameHostOption(eGameHostOption_TNT)>0)?"on":"off"); // TODO 4J Stu - Init a load of settings based on data passed as params //settings->setBooleanAndSave( L"host-friends-only", (app.GetGameHostOption(eGameHostOption_FriendsOfFriends)>0) ); @@ -283,6 +290,14 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW } g_NetworkManager.ServerReady(); // 4J added + +#ifdef _DEDICATED_SERVER + { + extern QNET_STATE _iQNetStubState; + _iQNetStubState = QNET_STATE_GAME_PLAY; + } +#endif + return m_bLoaded; } @@ -405,7 +420,7 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring int gameTypeId = settings->getInt(L"gamemode", app.GetGameHostOption(eGameHostOption_GameType));//LevelSettings::GAMETYPE_SURVIVAL); GameType *gameType = LevelSettings::validateGameType(gameTypeId); - app.DebugPrintf("Default game type: %d\n" , gameTypeId); + app.DebugPrintf("Default game type: %d" , gameTypeId); LevelSettings *levelSettings = new LevelSettings(levelSeed, gameType, app.GetGameHostOption(eGameHostOption_Structures)>0?true:false, isHardcore(), true, pLevelType, initData->xzSize, initData->hellScale); if( app.GetGameHostOption(eGameHostOption_BonusChest ) ) levelSettings->enableStartingBonusItems(); @@ -497,7 +512,7 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring Minecraft *pMinecraft = Minecraft::GetInstance(); // m_lastSentDifficulty = pMinecraft->options->difficulty; levels[i]->difficulty = app.GetGameHostOption(eGameHostOption_Difficulty); //pMinecraft->options->difficulty; - app.DebugPrintf("MinecraftServer::loadLevel - Difficulty = %d\n",levels[i]->difficulty); + app.DebugPrintf("MinecraftServer::loadLevel - Difficulty = %d",levels[i]->difficulty); #if DEBUG_SERVER_DONT_SPAWN_MOBS levels[i]->setSpawnSettings(false, false); @@ -571,6 +586,13 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring csf->closeHandle(fe); } +#ifdef _DEDICATED_SERVER + { + __int64 doneTime = System::currentTimeMillis(); + app.DebugPrintf("Done! For help, type \"help\" or \"?\""); + } +#endif + __int64 lastTime = System::currentTimeMillis(); #ifdef _LARGE_WORLDS if(app.GetGameNewWorldSize() > levels[0]->getLevelData()->getXZSizeOld()) @@ -693,9 +715,6 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring // stronghold position? if(levels[0]->dimension->id==0) { - - app.DebugPrintf("===================================\n"); - if(!levels[0]->getLevelData()->getHasStronghold()) { int x,z; @@ -705,20 +724,19 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring levels[0]->getLevelData()->setZStronghold(z); levels[0]->getLevelData()->setHasStronghold(); - app.DebugPrintf("=== FOUND stronghold in terrain features list\n"); + app.DebugPrintf("FOUND stronghold in terrain features list\n"); } else { // can't find the stronghold position in the terrain feature list. Do we have to run a post-process? - app.DebugPrintf("=== Can't find stronghold in terrain features list\n"); + app.DebugPrintf("Can't find stronghold in terrain features list\n"); } } else { - app.DebugPrintf("=== Leveldata has stronghold position\n"); + app.DebugPrintf("Leveldata has stronghold position\n"); } - app.DebugPrintf("===================================\n"); } // printf("Post processing complete at %dms\n",System::currentTimeMillis() - startTime); @@ -1683,6 +1701,10 @@ void MinecraftServer::tick() } } Entity::tickExtraWandering(); // 4J added +#ifdef _DEDICATED_SERVER + g_NetworkManager.DoWork(); + NetworkSocketLayer::FlushPendingData(); +#endif PIXBeginNamedEvent(0,"Connection tick"); connection->tick(); @@ -1717,6 +1739,12 @@ void MinecraftServer::handleConsoleInputs() AUTO_VAR(it, consoleInput.begin()); ConsoleInput *input = *it; consoleInput.erase(it); +#ifdef _DEDICATED_SERVER + HandleServerCommand(input->msg, input->source, this); + delete input; +#else + delete input; +#endif // commands->handleCommand(input); // 4J - removed - TODO - do we want equivalent of console commands? } } diff --git a/Minecraft.Client/PendingConnection.cpp b/Minecraft.Client/PendingConnection.cpp index 873763e0..f0959c65 100644 --- a/Minecraft.Client/PendingConnection.cpp +++ b/Minecraft.Client/PendingConnection.cpp @@ -85,6 +85,7 @@ void PendingConnection::handlePreLogin(shared_ptr packet) return; } // printf("Server: handlePreLogin\n"); + app.DebugPrintf("PreLogin received from \"%ls\"\n", packet->loginKey.c_str()); name = packet->loginKey; // 4J Stu - Change from the login packet as we know better on client end during the pre-login packet sendPreLoginResponse(); } @@ -139,6 +140,7 @@ void PendingConnection::sendPreLoginResponse() void PendingConnection::handleLogin(shared_ptr packet) { + app.DebugPrintf("Login received from \"%ls\" (protocol %d)\n", name.c_str(), packet->clientVersion); // printf("Server: handleLogin\n"); //name = packet->userName; if (packet->clientVersion != SharedConstants::NETWORK_PROTOCOL_VERSION) diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp index 9b5312d8..ed898895 100644 --- a/Minecraft.Client/PlayerConnection.cpp +++ b/Minecraft.Client/PlayerConnection.cpp @@ -116,7 +116,7 @@ void PlayerConnection::disconnect(DisconnectPacket::eDisconnectReason reason) return; } - app.DebugPrintf("PlayerConnection disconect reason: %d\n", reason ); + app.DebugPrintf("PlayerConnection disconect reason: %d", reason ); player->disconnect(); // 4J Stu - Need to remove the player from the receiving list before their socket is NULLed so that we can find another player on their system @@ -543,10 +543,16 @@ void PlayerConnection::onDisconnect(DisconnectPacket::eDisconnectReason reason, if(getWasKicked()) { server->getPlayers()->broadcastAll( shared_ptr( new ChatPacket(player->name, ChatPacket::e_ChatPlayerKickedFromGame) ) ); +#ifdef _DEDICATED_SERVER + app.DebugPrintf("%ls was kicked from the game", player->name.c_str()); +#endif } else { server->getPlayers()->broadcastAll( shared_ptr( new ChatPacket(player->name, ChatPacket::e_ChatPlayerLeftGame) ) ); +#ifdef _DEDICATED_SERVER + app.DebugPrintf("%ls left the game", player->name.c_str()); +#endif } server->getPlayers()->remove(player); done = true; diff --git a/Minecraft.Client/PlayerConnection.h b/Minecraft.Client/PlayerConnection.h index 39861f8c..835d9bfe 100644 --- a/Minecraft.Client/PlayerConnection.h +++ b/Minecraft.Client/PlayerConnection.h @@ -1,3 +1,4 @@ +#pragma once #include "ConsoleInputSource.h" #include "..\Minecraft.World\PacketListener.h" #include "..\Minecraft.World\JavaIntHash.h" diff --git a/Minecraft.Client/PlayerList.cpp b/Minecraft.Client/PlayerList.cpp index 08b01e62..dd44698e 100644 --- a/Minecraft.Client/PlayerList.cpp +++ b/Minecraft.Client/PlayerList.cpp @@ -243,6 +243,10 @@ void PlayerList::placeNewPlayer(Connection *connection, shared_ptr //server->players->broadcastAll( shared_ptr( new ChatPacket(L"§e" + playerEntity->name + L" joined the game.") ) ); broadcastAll( shared_ptr( new ChatPacket(player->name, ChatPacket::e_ChatPlayerJoinedGame) ) ); +#ifdef _DEDICATED_SERVER + app.DebugPrintf("%ls joined the game", player->name.c_str()); +#endif + MemSect(14); add(player); MemSect(0); diff --git a/Minecraft.Client/ServerLevel.cpp b/Minecraft.Client/ServerLevel.cpp index 1150c78b..a5570dab 100644 --- a/Minecraft.Client/ServerLevel.cpp +++ b/Minecraft.Client/ServerLevel.cpp @@ -1026,6 +1026,7 @@ void ServerLevel::saveToDisc(ProgressListener *progressListener, bool autosave) // 4J-PB - check that saves are enabled if(StorageManager.GetSaveDisabled()) return; +#ifndef _DEDICATED_SERVER // Check if we are using a trial version of a texture pack (which will be the case for going into the mash-up pack world with a trial version) if(!Minecraft::GetInstance()->skins->isUsingDefaultSkin()) { @@ -1039,6 +1040,7 @@ void ServerLevel::saveToDisc(ProgressListener *progressListener, bool autosave) return; } } +#endif if (progressListener != NULL) progressListener->progressStage(IDS_PROGRESS_SAVING_TO_DISC); levelStorage->flushSaveFile(autosave); diff --git a/Minecraft.World/Level.cpp b/Minecraft.World/Level.cpp index ec087554..20740f4d 100644 --- a/Minecraft.World/Level.cpp +++ b/Minecraft.World/Level.cpp @@ -1951,7 +1951,8 @@ AABBList *Level::getCubes(shared_ptr source, AABB *box, bool noEntities/ // 4J - now add in collision for any blocks which have actually been removed, but haven't had their render data updated to reflect this yet. This is to stop the player // being able to move the view position inside a tile which is (visually) still there, and see out of the world. This is particularly a problem when moving upwards in // creative mode as the player can get very close to the edge of tiles whilst looking upwards and can therefore very quickly move inside one. - Minecraft::GetInstance()->levelRenderer->destroyedTileManager->addAABBs( this, box, &boxes); + if(Minecraft::GetInstance()->levelRenderer != NULL) + Minecraft::GetInstance()->levelRenderer->destroyedTileManager->addAABBs( this, box, &boxes); // 4J - added if( noEntities ) return &boxes; diff --git a/Minecraft.World/StrongholdPieces.cpp b/Minecraft.World/StrongholdPieces.cpp index 40bce30f..bdd18c04 100644 --- a/Minecraft.World/StrongholdPieces.cpp +++ b/Minecraft.World/StrongholdPieces.cpp @@ -222,7 +222,7 @@ StructurePiece *StrongholdPieces::generateAndAddPiece(StartPiece *startPiece, li if(piece->pieceClass != EPieceClass_PortalRoom) continue; #ifndef _CONTENT_PACKAGE - printf("Portal room forcing attempt\n"); + app.DebugPrintf("Portal room forcing attempt\n"); #endif StrongholdPiece *strongholdPiece = PortalRoom::createPiece(pieces, random, footX, footY, footZ, direction, depth); if (strongholdPiece != NULL) @@ -235,7 +235,7 @@ StructurePiece *StrongholdPieces::generateAndAddPiece(StartPiece *startPiece, li currentPieces.remove(piece); } #ifndef _CONTENT_PACKAGE - printf("Success\n"); + app.DebugPrintf("Success\n"); #endif return strongholdPiece; } From feeff5592455f2b38f85e5b0266a23090f06b119 Mon Sep 17 00:00:00 2001 From: piebot Date: Fri, 14 Aug 2026 13:42:38 +0300 Subject: [PATCH 4/6] LCEMP Commit: add dedicated server check to sessioninfo --- .../Common/Network/NetworkSocketLayer.cpp | 7 +++++++ Minecraft.Client/Common/Network/NetworkSocketLayer.h | 2 ++ .../Common/Network/PlatformNetworkManager.cpp | 12 ++++++++++-- Minecraft.Client/Common/Network/SessionInfo.h | 2 ++ Minecraft.Client/Extrax64Stubs.cpp | 10 +++++++++- Minecraft.Client/MinecraftServer.cpp | 9 +++++++-- 6 files changed, 37 insertions(+), 5 deletions(-) diff --git a/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp b/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp index be7c203f..b76c5716 100644 --- a/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp +++ b/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp @@ -1286,6 +1286,11 @@ bool NetworkSocketLayer::StartAdvertising(int gamePort, const wchar_t *hostName, s_advertiseData.texturePackParentId = texPackId; s_advertiseData.subTexturePackId = subTexId; s_advertiseData.isJoinable = 0; +#ifdef _DEDICATED_SERVER + s_advertiseData.isDedicatedServer = 1; +#else + s_advertiseData.isDedicatedServer = 0; +#endif s_hostGamePort = gamePort; LeaveCriticalSection(&s_advertiseLock); @@ -1666,6 +1671,7 @@ int NetworkSocketLayer::DiscoveryThreadProc(LPVOID param) s_discoveredSessions[i].texturePackParentId = broadcast->texturePackParentId; s_discoveredSessions[i].subTexturePackId = broadcast->subTexturePackId; s_discoveredSessions[i].isJoinable = (broadcast->isJoinable != 0); + s_discoveredSessions[i].isDedicatedServer = (broadcast->isDedicatedServer != 0); s_discoveredSessions[i].lastSeenTick = now; memcpy(s_discoveredSessions[i].playerNames, broadcast->playerNames, sizeof(broadcast->playerNames)); found = true; @@ -1699,6 +1705,7 @@ int NetworkSocketLayer::DiscoveryThreadProc(LPVOID param) session.texturePackParentId = broadcast->texturePackParentId; session.subTexturePackId = broadcast->subTexturePackId; session.isJoinable = (broadcast->isJoinable != 0); + session.isDedicatedServer = (broadcast->isDedicatedServer != 0); session.lastSeenTick = now; memcpy(session.playerNames, broadcast->playerNames, sizeof(broadcast->playerNames)); s_discoveredSessions.push_back(session); diff --git a/Minecraft.Client/Common/Network/NetworkSocketLayer.h b/Minecraft.Client/Common/Network/NetworkSocketLayer.h index 71b4ef8c..d3221ce7 100644 --- a/Minecraft.Client/Common/Network/NetworkSocketLayer.h +++ b/Minecraft.Client/Common/Network/NetworkSocketLayer.h @@ -116,6 +116,7 @@ struct LANBroadcast DWORD texturePackParentId; BYTE subTexturePackId; BYTE isJoinable; + BYTE isDedicatedServer; char playerNames[NETWORK_LAN_BROADCAST_PLAYERS][NETWORK_LAN_PLAYER_NAME_SIZE]; }; #pragma pack(pop) @@ -132,6 +133,7 @@ struct LANSession unsigned int texturePackParentId; unsigned char subTexturePackId; bool isJoinable; + bool isDedicatedServer; DWORD lastSeenTick; char playerNames[NETWORK_LAN_BROADCAST_PLAYERS][NETWORK_LAN_PLAYER_NAME_SIZE]; }; diff --git a/Minecraft.Client/Common/Network/PlatformNetworkManager.cpp b/Minecraft.Client/Common/Network/PlatformNetworkManager.cpp index 968ff578..bbbe7638 100644 --- a/Minecraft.Client/Common/Network/PlatformNetworkManager.cpp +++ b/Minecraft.Client/Common/Network/PlatformNetworkManager.cpp @@ -354,6 +354,10 @@ bool CPlatformNetworkManagerStub::LeaveGame(bool bMigrateHost) if( m_bLeavingGame ) return true; m_bLeavingGame = true; + + extern bool g_connectedToDedicatedServer; + g_connectedToDedicatedServer = false; + NetworkSocketLayer::StopAdvertising(); // If we are the host wait for the game server to end @@ -403,10 +407,10 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame, IQNet::m_player[0].m_smallId = 0; IQNet::m_player[0].m_isRemote = false; IQNet::m_player[0].m_isHostPlayer = true; -#ifdef _WINDOWS64 + IQNet::s_playerCount = 1; +#if defined _WINDOWS64 && !defined WITH_SERVER_CODE extern wchar_t g_Win64UsernameW[17]; wcscpy_s(IQNet::m_player[0].m_gamertag, 32, g_Win64UsernameW); - IQNet::s_playerCount = 1; #endif if (getNetworkPlayer(&IQNet::m_player[0]) == NULL) @@ -454,6 +458,9 @@ int CPlatformNetworkManagerStub::JoinGame(FriendSessionInfo *searchResult, int l wcsncpy(IQNet::m_player[0].m_gamertag, searchResult->data.hostName, 31); IQNet::m_player[0].m_gamertag[31] = L'\0'; + extern bool g_connectedToDedicatedServer; + g_connectedToDedicatedServer = searchResult->data.isDedicatedServer; + NetworkSocketLayer::StopDiscovery(); if (!NetworkSocketLayer::JoinGame(hostIP, hostPort)) @@ -801,6 +808,7 @@ void CPlatformNetworkManagerStub::SearchForGames() info->data.subTexturePackId = lanSessions[i].subTexturePackId; info->data.isReadyToJoin = lanSessions[i].isJoinable; info->data.isJoinable = lanSessions[i].isJoinable; + info->data.isDedicatedServer = lanSessions[i].isDedicatedServer; strncpy(info->data.hostIP, lanSessions[i].hostIP, sizeof(info->data.hostIP) - 1); info->data.hostIP[sizeof(info->data.hostIP) - 1] = '\0'; info->data.hostPort = lanSessions[i].hostPort; diff --git a/Minecraft.Client/Common/Network/SessionInfo.h b/Minecraft.Client/Common/Network/SessionInfo.h index eb9a42a6..1844950e 100644 --- a/Minecraft.Client/Common/Network/SessionInfo.h +++ b/Minecraft.Client/Common/Network/SessionInfo.h @@ -79,6 +79,7 @@ typedef struct _GameSessionData bool isReadyToJoin; // 1 byte bool isJoinable; // 1 byte + bool isDedicatedServer; // 1 byte char hostIP[64]; // 64 bytes int hostPort; // 4 bytes @@ -97,6 +98,7 @@ typedef struct _GameSessionData subTexturePackId = 0; isReadyToJoin = false; isJoinable = true; + isDedicatedServer = false; memset(hostIP, 0, sizeof(hostIP)); hostPort = 0; memset(hostName, 0, sizeof(hostName)); diff --git a/Minecraft.Client/Extrax64Stubs.cpp b/Minecraft.Client/Extrax64Stubs.cpp index 2d50b945..2d62be5d 100644 --- a/Minecraft.Client/Extrax64Stubs.cpp +++ b/Minecraft.Client/Extrax64Stubs.cpp @@ -547,6 +547,8 @@ bool IQNet::s_isHosting = true; QNET_STATE _iQNetStubState = QNET_STATE_IDLE; +bool g_connectedToDedicatedServer = false; + void Win64_SetupRemoteQNetPlayer(IQNetPlayer *player, BYTE smallId, bool isHost, bool isLocal) { player->m_smallId = smallId; @@ -597,7 +599,13 @@ IQNetPlayer *IQNet::GetLocalPlayerByUserIndex(DWORD dwUserIndex) } static bool Win64_IsActivePlayer(IQNetPlayer *p, DWORD index) { - if (index == 0) return true; + if (index == 0) + { + extern bool g_connectedToDedicatedServer; + if (g_connectedToDedicatedServer && !IQNet::s_isHosting) + return false; + return true; + } if (p->GetCustomDataValue() != 0) return true; return (p->m_isRemote && p->m_gamertag[0] != 0); } diff --git a/Minecraft.Client/MinecraftServer.cpp b/Minecraft.Client/MinecraftServer.cpp index 09d5405c..eae6c64a 100644 --- a/Minecraft.Client/MinecraftServer.cpp +++ b/Minecraft.Client/MinecraftServer.cpp @@ -53,7 +53,7 @@ #include "ServerCommandDispatcher.h" #ifdef _DEDICATED_SERVER -#include "..\Minecraft.Server\ServerCommands.h" +#include "..\Minecraft.Server\Commands\ServerCommands.h" #endif #include "..\Minecraft.World\BiomeSource.h" @@ -1006,12 +1006,16 @@ void MinecraftServer::stopServer(bool didInit) // 4J-PB - If the primary player has signed out, then don't attempt to save anything // also need to check for a profile switch here - primary player signs out, and another player signs in before dismissing the dash -#ifdef _DURANGO +#ifdef _DEDICATED_SERVER + { + { +#elif defined(_DURANGO) // On Durango check if the primary user is signed in OR mid-sign-out if(ProfileManager.GetUser(0, true) != nullptr) #else if((m_bPrimaryPlayerSignedOut==false) && ProfileManager.IsSignedIn(ProfileManager.GetPrimaryPad())) #endif +#ifndef _DEDICATED_SERVER { #if defined(_XBOX_ONE) || defined(__ORBIS__) // Always save on exit! Except if saves are disabled. @@ -1020,6 +1024,7 @@ void MinecraftServer::stopServer(bool didInit) // if trial version or saving is disabled, then don't save anything. Also don't save anything if we didn't actually get through the server initialisation. if(m_saveOnExit && ProfileManager.IsFullVersion() && (!StorageManager.GetSaveDisabled()) && didInit) { +#endif if (players != NULL) { players->saveAll(Minecraft::GetInstance()->progressRenderer, true); From 494235878e735f09db1265e10c89195b474059fd Mon Sep 17 00:00:00 2001 From: piebot Date: Fri, 14 Aug 2026 14:39:13 +0300 Subject: [PATCH 5/6] LCEMP commit: multiple savefile fixes --- .../Common/Network/NetworkSocketLayer.cpp | 2 ++ Minecraft.Client/MinecraftServer.cpp | 27 +++++++++++++++++ Minecraft.Client/MultiPlayerChunkCache.cpp | 5 ++-- Minecraft.Client/ServerChunkCache.cpp | 2 ++ Minecraft.World/OldChunkStorage.cpp | 29 +++++++++---------- Minecraft.World/RandomLevelSource.cpp | 2 +- Minecraft.World/RegionFile.cpp | 7 ++++- Minecraft.World/RegionFileCache.cpp | 13 ++++----- Minecraft.World/RegionFileCache.h | 5 ++-- Minecraft.World/SparseDataStorage.cpp | 5 ++-- Minecraft.World/SparseLightStorage.cpp | 5 ++-- Minecraft.World/StructureFeature.cpp | 23 +++++++++++++++ Minecraft.World/StructureFeature.h | 1 + 13 files changed, 94 insertions(+), 32 deletions(-) diff --git a/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp b/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp index b76c5716..9536e272 100644 --- a/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp +++ b/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp @@ -143,7 +143,9 @@ bool NetworkSocketLayer::Initialize() s_initialized = true; +#ifndef _DEDICATED_SERVER StartDiscovery(); +#endif return true; } diff --git a/Minecraft.Client/MinecraftServer.cpp b/Minecraft.Client/MinecraftServer.cpp index eae6c64a..67f5e027 100644 --- a/Minecraft.Client/MinecraftServer.cpp +++ b/Minecraft.Client/MinecraftServer.cpp @@ -1634,6 +1634,33 @@ void MinecraftServer::tick() tickCount++; +#ifdef _DEDICATED_SERVER + if (tickCount % 6000 == 0 && !s_bServerHalted) + { + app.DebugPrintf("Auto-saving world...\n"); + if (players != NULL) + { + players->saveAll(NULL); + } + for (unsigned int j = 0; j < levels.length; j++) + { + if (s_bServerHalted) break; + ServerLevel *level = levels[levels.length - 1 - j]; + if (level) level->save(false, NULL, true); + } + if (!s_bServerHalted) + { + saveGameRules(); + levels[0]->saveToDisc(NULL, true); + } + while (StorageManager.GetSaveState() != C4JStorage::ESaveGame_Idle) + { + Sleep(10); + } + app.DebugPrintf("Auto-save complete\n"); + } +#endif + // 4J We need to update client difficulty levels based on the servers Minecraft *pMinecraft = Minecraft::GetInstance(); // 4J-PB - sending this on the host changing the difficulty in the menus diff --git a/Minecraft.Client/MultiPlayerChunkCache.cpp b/Minecraft.Client/MultiPlayerChunkCache.cpp index 46c2a8d7..879ebfd9 100644 --- a/Minecraft.Client/MultiPlayerChunkCache.cpp +++ b/Minecraft.Client/MultiPlayerChunkCache.cpp @@ -175,10 +175,11 @@ LevelChunk *MultiPlayerChunkCache::create(int x, int z) if (MinecraftServer::getInstance()->serverHalted()) return NULL; // If we're the host, then don't create the chunk, share data from the server's copy + int dimId = level->dimension->id; #ifdef _LARGE_WORLDS - LevelChunk *serverChunk = MinecraftServer::getInstance()->getLevel(level->dimension->id)->cache->getChunkLoadedOrUnloaded(x,z); + LevelChunk *serverChunk = MinecraftServer::getInstance()->getLevel(dimId)->cache->getChunkLoadedOrUnloaded(x,z); #else - LevelChunk *serverChunk = MinecraftServer::getInstance()->getLevel(level->dimension->id)->cache->getChunk(x,z); + LevelChunk *serverChunk = MinecraftServer::getInstance()->getLevel(dimId)->cache->getChunk(x,z); #endif chunk = new LevelChunk(level, x, z, serverChunk); // Let renderer know that this chunk has been created - it might have made render data from the EmptyChunk if it got to a chunk before the server sent it diff --git a/Minecraft.Client/ServerChunkCache.cpp b/Minecraft.Client/ServerChunkCache.cpp index f26489e8..c8f84836 100644 --- a/Minecraft.Client/ServerChunkCache.cpp +++ b/Minecraft.Client/ServerChunkCache.cpp @@ -151,8 +151,10 @@ LevelChunk *ServerChunkCache::create(int x, int z, bool asyncPostProcess) // 4J if( ( chunk == NULL ) || ( chunk->x != x ) || ( chunk->z != z ) ) { + bool wasLoaded = false; EnterCriticalSection(&m_csLoadCreate); chunk = load(x, z); + wasLoaded = (chunk != NULL); if (chunk == NULL) { if (source == NULL) diff --git a/Minecraft.World/OldChunkStorage.cpp b/Minecraft.World/OldChunkStorage.cpp index bb87e39d..7dbd4a86 100644 --- a/Minecraft.World/OldChunkStorage.cpp +++ b/Minecraft.World/OldChunkStorage.cpp @@ -476,29 +476,28 @@ LevelChunk *OldChunkStorage::load(Level *level, DataInputStream *dis) CompoundTag *tag = NbtIo::read(dis); - loadEntities(levelChunk, level, tag); - - if (tag->contains(L"TileTicks")) + if (tag != NULL) { - PIXBeginNamedEvent(0,"Loading TileTicks"); - ListTag *tileTicks = (ListTag *) tag->getList(L"TileTicks"); + loadEntities(levelChunk, level, tag); - if (tileTicks != NULL) + if (tag->contains(L"TileTicks")) { - for (int i = 0; i < tileTicks->size(); i++) - { - CompoundTag *teTag = tileTicks->get(i); + ListTag *tileTicks = (ListTag *) tag->getList(L"TileTicks"); - level->forceAddTileTick(teTag->getInt(L"x"), teTag->getInt(L"y"), teTag->getInt(L"z"), teTag->getInt(L"i"), teTag->getInt(L"t"), teTag->getInt(L"p")); + if (tileTicks != NULL) + { + for (int i = 0; i < tileTicks->size(); i++) + { + CompoundTag *teTag = tileTicks->get(i); + + level->forceAddTileTick(teTag->getInt(L"x"), teTag->getInt(L"y"), teTag->getInt(L"z"), teTag->getInt(L"i"), teTag->getInt(L"t")); + } } } - PIXEndNamedEvent(); + + delete tag; } - delete tag; - - PIXEndNamedEvent(); - return levelChunk; } diff --git a/Minecraft.World/RandomLevelSource.cpp b/Minecraft.World/RandomLevelSource.cpp index 9c24e4b3..51954be9 100644 --- a/Minecraft.World/RandomLevelSource.cpp +++ b/Minecraft.World/RandomLevelSource.cpp @@ -778,7 +778,7 @@ void RandomLevelSource::postProcess(ChunkSource *parent, int xt, int zt) mineShaftFeature->postProcess(level, pprandom, xt, zt); hasVillage = villageFeature->postProcess(level, pprandom, xt, zt); strongholdFeature->postProcess(level, pprandom, xt, zt); - scatteredFeature->postProcess(level, random, xt, zt); + scatteredFeature->postProcess(level, pprandom, xt, zt); } PIXEndNamedEvent(); diff --git a/Minecraft.World/RegionFile.cpp b/Minecraft.World/RegionFile.cpp index 305337a8..de920fa9 100644 --- a/Minecraft.World/RegionFile.cpp +++ b/Minecraft.World/RegionFile.cpp @@ -38,7 +38,12 @@ RegionFile::RegionFile(ConsoleSaveFile *saveFile, File *path) } */ - fileEntry = m_saveFile->createFile( fileName->getName() ); + wstring saveName = fileName->getPath(); + for (size_t i = 0; i < saveName.size(); i++) + { + if (saveName[i] == L'\\') saveName[i] = L'/'; + } + fileEntry = m_saveFile->createFile( ConsoleSavePath(saveName) ); m_saveFile->setFilePointer( fileEntry, 0, NULL, FILE_END ); if ( fileEntry->getFileSize() < SECTOR_BYTES) diff --git a/Minecraft.World/RegionFileCache.cpp b/Minecraft.World/RegionFileCache.cpp index 6b88f4e0..3b84f773 100644 --- a/Minecraft.World/RegionFileCache.cpp +++ b/Minecraft.World/RegionFileCache.cpp @@ -17,7 +17,7 @@ bool RegionFileCache::useSplitSaves(ESavePlatform platform) }; } -RegionFile *RegionFileCache::_getRegionFile(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ) // 4J - TODO was synchronized +RegionFile *RegionFileCache::_getRegionFile(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ) // 4J - synchronized restored { // 4J Jev - changed back to use of the File class. //char file[MAX_PATH_SIZE]; @@ -26,6 +26,7 @@ RegionFile *RegionFileCache::_getRegionFile(ConsoleSaveFile *saveFile, const wst //File regionDir(basePath, L"region"); //File file(regionDir, wstring(L"r.") + _toString(chunkX>>5) + L"." + _toString(chunkZ>>5) + L".mcr" ); + EnterCriticalSection(&m_cs); MemSect(31); File file; if(useSplitSaves(saveFile->getSavePlatform())) @@ -46,6 +47,7 @@ RegionFile *RegionFileCache::_getRegionFile(ConsoleSaveFile *saveFile, const wst // 4J Jev, put back in. if (ref != NULL) { + LeaveCriticalSection(&m_cs); return ref; } @@ -63,12 +65,14 @@ RegionFile *RegionFileCache::_getRegionFile(ConsoleSaveFile *saveFile, const wst RegionFile *reg = new RegionFile(saveFile, &file); cache[file] = reg; // 4J - this was originally a softReferenc + LeaveCriticalSection(&m_cs); return reg; } void RegionFileCache::_clear() // 4J - TODO was synchronized { + EnterCriticalSection(&m_cs); AUTO_VAR(itEnd, cache.end()); for( AUTO_VAR(it, cache.begin()); it != itEnd; it++ ) { @@ -85,6 +89,7 @@ void RegionFileCache::_clear() // 4J - TODO was synchronized // } } cache.clear(); + LeaveCriticalSection(&m_cs); } int RegionFileCache::_getSizeDelta(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ) @@ -120,9 +125,3 @@ DataOutputStream *RegionFileCache::_getChunkDataOutputStream(ConsoleSaveFile *sa return r->getChunkDataOutputStream(chunkX & 31, chunkZ & 31); } } - - -RegionFileCache::~RegionFileCache() -{ - _clear(); -} diff --git a/Minecraft.World/RegionFileCache.h b/Minecraft.World/RegionFileCache.h index 822e49c9..58af1f03 100644 --- a/Minecraft.World/RegionFileCache.h +++ b/Minecraft.World/RegionFileCache.h @@ -10,13 +10,14 @@ private: static const int MAX_CACHE_SIZE = 256; unordered_map cache; + CRITICAL_SECTION m_cs; static RegionFileCache s_defaultCache; public: // Made public and non-static so we can have a cache for input and output files - RegionFileCache() {} - ~RegionFileCache(); + RegionFileCache() { InitializeCriticalSectionAndSpinCount(&m_cs, 4000); } + ~RegionFileCache() { DeleteCriticalSection(&m_cs); _clear(); } RegionFile *_getRegionFile(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ); // 4J - TODO was synchronized void _clear(); // 4J - TODO was synchronized diff --git a/Minecraft.World/SparseDataStorage.cpp b/Minecraft.World/SparseDataStorage.cpp index dc32a431..0e460214 100644 --- a/Minecraft.World/SparseDataStorage.cpp +++ b/Minecraft.World/SparseDataStorage.cpp @@ -602,9 +602,10 @@ bool SparseDataStorage::isCompressed() void SparseDataStorage::write(DataOutputStream *dos) { - int count = ( dataAndCount >> 48 ) & 0xffff; + __int64 snapshot = dataAndCount; + int count = ( snapshot >> 48 ) & 0xffff; dos->writeInt(count); - unsigned char *dataPointer = (unsigned char *)(dataAndCount & 0x0000ffffffffffff); + unsigned char *dataPointer = (unsigned char *)(snapshot & 0x0000ffffffffffff); byteArray wrapper(dataPointer, count * 128 + 128); dos->write(wrapper); } diff --git a/Minecraft.World/SparseLightStorage.cpp b/Minecraft.World/SparseLightStorage.cpp index 8b848520..7257f29d 100644 --- a/Minecraft.World/SparseLightStorage.cpp +++ b/Minecraft.World/SparseLightStorage.cpp @@ -619,9 +619,10 @@ bool SparseLightStorage::isCompressed() void SparseLightStorage::write(DataOutputStream *dos) { - int count = ( dataAndCount >> 48 ) & 0xffff; + __int64 snapshot = dataAndCount; + int count = ( snapshot >> 48 ) & 0xffff; dos->writeInt(count); - unsigned char *dataPointer = (unsigned char *)(dataAndCount & 0x0000ffffffffffff); + unsigned char *dataPointer = (unsigned char *)(snapshot & 0x0000ffffffffffff); byteArray wrapper(dataPointer, count * 128 + 128); dos->write(wrapper); } diff --git a/Minecraft.World/StructureFeature.cpp b/Minecraft.World/StructureFeature.cpp index ab4e37db..ba10ceec 100644 --- a/Minecraft.World/StructureFeature.cpp +++ b/Minecraft.World/StructureFeature.cpp @@ -9,6 +9,7 @@ StructureFeature::StructureFeature() { + InitializeCriticalSectionAndSpinCount(&m_csCachedStructures, 4000); #ifdef ENABLE_STRUCTURE_SAVING savedData = nullptr; #endif @@ -16,10 +17,13 @@ StructureFeature::StructureFeature() StructureFeature::~StructureFeature() { + EnterCriticalSection(&m_csCachedStructures); for( AUTO_VAR(it, cachedStructures.begin()); it != cachedStructures.end(); it++ ) { delete it->second; } + LeaveCriticalSection(&m_csCachedStructures); + DeleteCriticalSection(&m_csCachedStructures); } void StructureFeature::addFeature(Level *level, int x, int z, int xOffs, int zOffs, byteArray blocks) @@ -28,12 +32,16 @@ void StructureFeature::addFeature(Level *level, int x, int z, int xOffs, int zOf // the chunk being generated, but not all chunks are the sources of // structures + EnterCriticalSection(&m_csCachedStructures); + restoreSavedData(level); if (cachedStructures.find(ChunkPos::hashCode(x, z)) != cachedStructures.end()) { + LeaveCriticalSection(&m_csCachedStructures); return; } + LeaveCriticalSection(&m_csCachedStructures); // clear random key random->nextInt(); @@ -41,7 +49,9 @@ void StructureFeature::addFeature(Level *level, int x, int z, int xOffs, int zOf if (isFeatureChunk(x, z,level->getLevelData()->getGenerator() == LevelType::lvl_flat)) { StructureStart *start = createStructureStart(x, z); + EnterCriticalSection(&m_csCachedStructures); cachedStructures[ChunkPos::hashCode(x, z)] = start; + LeaveCriticalSection(&m_csCachedStructures); saveFeature(x, z, start); } } @@ -58,6 +68,7 @@ bool StructureFeature::postProcess(Level *level, Random *random, int chunkX, int int cz = (chunkZ << 4); // + 8; bool intersection = false; + EnterCriticalSection(&m_csCachedStructures); for( AUTO_VAR(it, cachedStructures.begin()); it != cachedStructures.end(); it++ ) { StructureStart *structureStart = it->second; @@ -76,12 +87,14 @@ bool StructureFeature::postProcess(Level *level, Random *random, int chunkX, int } } } + LeaveCriticalSection(&m_csCachedStructures); return intersection; } bool StructureFeature::isIntersection(int cellX, int cellZ) { + EnterCriticalSection(&m_csCachedStructures); restoreSavedData(level); for( AUTO_VAR(it, cachedStructures.begin()); it != cachedStructures.end(); it++ ) @@ -97,12 +110,14 @@ bool StructureFeature::isIntersection(int cellX, int cellZ) StructurePiece *next = *it2++; if (next->getBoundingBox()->intersects(cellX, cellZ, cellX, cellZ)) { + LeaveCriticalSection(&m_csCachedStructures); return true; } } } } } + LeaveCriticalSection(&m_csCachedStructures); return false; } @@ -114,6 +129,7 @@ bool StructureFeature::isInsideFeature(int cellX, int cellY, int cellZ) StructureStart *StructureFeature::getStructureAt(int cellX, int cellY, int cellZ) { + EnterCriticalSection(&m_csCachedStructures); //for (StructureStart structureStart : cachedStructures.values()) for(AUTO_VAR(it, cachedStructures.begin()); it != cachedStructures.end(); ++it) { @@ -138,12 +154,14 @@ StructureStart *StructureFeature::getStructureAt(int cellX, int cellY, int cellZ StructurePiece* piece = *it2; if ( piece->getBoundingBox()->isInside(cellX, cellY, cellZ) ) { + LeaveCriticalSection(&m_csCachedStructures); return pStructureStart; } } } } } + LeaveCriticalSection(&m_csCachedStructures); return NULL; } @@ -151,14 +169,17 @@ bool StructureFeature::isInsideBoundingFeature(int cellX, int cellY, int cellZ) { restoreSavedData(level); + EnterCriticalSection(&m_csCachedStructures); for(AUTO_VAR(it, cachedStructures.begin()); it != cachedStructures.end(); ++it) { StructureStart *structureStart = it->second; if (structureStart->isValid()) { + LeaveCriticalSection(&m_csCachedStructures); return (structureStart->getBoundingBox()->intersects(cellX, cellZ, cellX, cellZ)); } } + LeaveCriticalSection(&m_csCachedStructures); return false; } @@ -182,6 +203,7 @@ TilePos *StructureFeature::getNearestGeneratedFeature(Level *level, int cellX, i double minDistance = DBL_MAX; TilePos *selected = NULL; + EnterCriticalSection(&m_csCachedStructures); for(AUTO_VAR(it, cachedStructures.begin()); it != cachedStructures.end(); ++it) { StructureStart *pStructureStart = it->second; @@ -205,6 +227,7 @@ TilePos *StructureFeature::getNearestGeneratedFeature(Level *level, int cellX, i } } } + LeaveCriticalSection(&m_csCachedStructures); if (selected != NULL) { return selected; diff --git a/Minecraft.World/StructureFeature.h b/Minecraft.World/StructureFeature.h index 5159343a..16e246cb 100644 --- a/Minecraft.World/StructureFeature.h +++ b/Minecraft.World/StructureFeature.h @@ -27,6 +27,7 @@ private: protected: unordered_map<__int64, StructureStart *> cachedStructures; + CRITICAL_SECTION m_csCachedStructures; public: StructureFeature(); From 9e3077c3bc08c10e67d3c04c34d8bccea84a2ee7 Mon Sep 17 00:00:00 2001 From: piebot Date: Fri, 14 Aug 2026 15:06:42 +0300 Subject: [PATCH 6/6] LCEMP commit: world/server linux support, a ton of fixes, initial ded server support --- .gitmodules | 3 +++ Minecraft.Server | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 Minecraft.Server diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..badb1c24 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Minecraft.Server"] + path = Minecraft.Server + url = https://gitea.str1k3r.xyz/cafeberry/Cafeberry-Server.git diff --git a/Minecraft.Server b/Minecraft.Server new file mode 160000 index 00000000..c356c7b9 --- /dev/null +++ b/Minecraft.Server @@ -0,0 +1 @@ +Subproject commit c356c7b911b45d4bc08bd5120a82007381da361a