diff options
| author | Kevin <115616336+lag@users.noreply.github.com> | 2026-03-06 19:23:32 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-06 19:23:32 -0600 |
| commit | 13960a93b2a7c114446c109de059db305db4555d (patch) | |
| tree | 1b681d91fd38f0d2da73024041e968160c22552b /Minecraft.Client/Windows64/Network | |
| parent | 16446265d555d21f564b5989611a05918728d643 (diff) | |
Max players from 8 -> 255 + small connection optimizations (#722)
* Multiplayer 8 to max byte increase.
Made-with: Cursor
* Server chunk optimizations for large player counts, server full notification fix, added to server.properties.
Diffstat (limited to 'Minecraft.Client/Windows64/Network')
| -rw-r--r-- | Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp | 100 | ||||
| -rw-r--r-- | Minecraft.Client/Windows64/Network/WinsockNetLayer.h | 12 |
2 files changed, 95 insertions, 17 deletions
diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp index ca1d62af..c76bc2fe 100644 --- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp +++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp @@ -8,6 +8,11 @@ #include "WinsockNetLayer.h" #include "..\..\Common\Network\PlatformNetworkManagerStub.h" #include "..\..\..\Minecraft.World\Socket.h" +#include "..\..\..\Minecraft.World\DisconnectPacket.h" +#include "..\..\Minecraft.h" +#include "..\4JLibs\inc\4J_Profile.h" + +static bool RecvExact(SOCKET sock, BYTE* buf, int len); SOCKET WinsockNetLayer::s_listenSocket = INVALID_SOCKET; SOCKET WinsockNetLayer::s_hostConnectionSocket = INVALID_SOCKET; @@ -21,7 +26,7 @@ bool WinsockNetLayer::s_initialized = false; BYTE WinsockNetLayer::s_localSmallId = 0; BYTE WinsockNetLayer::s_hostSmallId = 0; -BYTE WinsockNetLayer::s_nextSmallId = 1; +unsigned int WinsockNetLayer::s_nextSmallId = 1; CRITICAL_SECTION WinsockNetLayer::s_sendLock; CRITICAL_SECTION WinsockNetLayer::s_connectionsLock; @@ -46,6 +51,8 @@ std::vector<BYTE> WinsockNetLayer::s_disconnectedSmallIds; CRITICAL_SECTION WinsockNetLayer::s_freeSmallIdLock; std::vector<BYTE> WinsockNetLayer::s_freeSmallIds; +SOCKET WinsockNetLayer::s_smallIdToSocket[256]; +CRITICAL_SECTION WinsockNetLayer::s_smallIdToSocketLock; bool g_Win64MultiplayerHost = false; bool g_Win64MultiplayerJoin = false; @@ -73,6 +80,9 @@ bool WinsockNetLayer::Initialize() InitializeCriticalSection(&s_discoveryLock); InitializeCriticalSection(&s_disconnectLock); InitializeCriticalSection(&s_freeSmallIdLock); + InitializeCriticalSection(&s_smallIdToSocketLock); + for (int i = 0; i < 256; i++) + s_smallIdToSocket[i] = INVALID_SOCKET; s_initialized = true; @@ -137,6 +147,7 @@ void WinsockNetLayer::Shutdown() s_disconnectedSmallIds.clear(); DeleteCriticalSection(&s_freeSmallIdLock); s_freeSmallIds.clear(); + DeleteCriticalSection(&s_smallIdToSocketLock); WSACleanup(); s_initialized = false; } @@ -155,6 +166,10 @@ bool WinsockNetLayer::HostGame(int port, const char* bindIp) EnterCriticalSection(&s_freeSmallIdLock); s_freeSmallIds.clear(); LeaveCriticalSection(&s_freeSmallIdLock); + EnterCriticalSection(&s_smallIdToSocketLock); + for (int i = 0; i < 256; i++) + s_smallIdToSocket[i] = INVALID_SOCKET; + LeaveCriticalSection(&s_smallIdToSocketLock); struct addrinfo hints = {}; struct addrinfo* result = NULL; @@ -289,6 +304,27 @@ bool WinsockNetLayer::JoinGame(const char* ip, int port) continue; } + if (assignBuf[0] == WIN64_SMALLID_REJECT) + { + BYTE rejectBuf[5]; + if (!RecvExact(s_hostConnectionSocket, rejectBuf, 5)) + { + app.DebugPrintf("Failed to receive reject reason from host\n"); + closesocket(s_hostConnectionSocket); + s_hostConnectionSocket = INVALID_SOCKET; + Sleep(200); + continue; + } + // rejectBuf[0] = packet id (255), rejectBuf[1..4] = 4-byte big-endian reason + int reason = ((rejectBuf[1] & 0xff) << 24) | ((rejectBuf[2] & 0xff) << 16) | + ((rejectBuf[3] & 0xff) << 8) | (rejectBuf[4] & 0xff); + Minecraft::GetInstance()->connectionDisconnected(ProfileManager.GetPrimaryPad(), (DisconnectPacket::eDisconnectReason)reason); + closesocket(s_hostConnectionSocket); + s_hostConnectionSocket = INVALID_SOCKET; + freeaddrinfo(result); + return false; + } + assignedSmallId = assignBuf[0]; connected = true; break; @@ -370,18 +406,31 @@ bool WinsockNetLayer::SendToSmallId(BYTE targetSmallId, const void* data, int da SOCKET WinsockNetLayer::GetSocketForSmallId(BYTE smallId) { - EnterCriticalSection(&s_connectionsLock); - for (size_t i = 0; i < s_connections.size(); i++) - { - if (s_connections[i].smallId == smallId && s_connections[i].active) - { - SOCKET sock = s_connections[i].tcpSocket; - LeaveCriticalSection(&s_connectionsLock); - return sock; - } - } - LeaveCriticalSection(&s_connectionsLock); - return INVALID_SOCKET; + EnterCriticalSection(&s_smallIdToSocketLock); + SOCKET sock = s_smallIdToSocket[smallId]; + LeaveCriticalSection(&s_smallIdToSocketLock); + return sock; +} + +void WinsockNetLayer::ClearSocketForSmallId(BYTE smallId) +{ + EnterCriticalSection(&s_smallIdToSocketLock); + s_smallIdToSocket[smallId] = INVALID_SOCKET; + LeaveCriticalSection(&s_smallIdToSocketLock); +} + +// Send reject handshake: sentinel 0xFF + DisconnectPacket wire format (1 byte id 255 + 4 byte big-endian reason). Then caller closes socket. +static void SendRejectWithReason(SOCKET clientSocket, DisconnectPacket::eDisconnectReason reason) +{ + BYTE buf[6]; + buf[0] = WIN64_SMALLID_REJECT; + buf[1] = (BYTE)255; // DisconnectPacket packet id + int r = (int)reason; + buf[2] = (BYTE)((r >> 24) & 0xff); + buf[3] = (BYTE)((r >> 16) & 0xff); + buf[4] = (BYTE)((r >> 8) & 0xff); + buf[5] = (BYTE)(r & 0xff); + send(clientSocket, (const char*)buf, sizeof(buf), 0); } static bool RecvExact(SOCKET sock, BYTE* buf, int len) @@ -440,6 +489,15 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param) continue; } + extern CPlatformNetworkManagerStub* g_pPlatformNetworkManager; + if (g_pPlatformNetworkManager != NULL && !g_pPlatformNetworkManager->CanAcceptMoreConnections()) + { + app.DebugPrintf("Win64 LAN: Rejecting connection, server at max players\n"); + SendRejectWithReason(clientSocket, DisconnectPacket::eDisconnect_ServerFull); + closesocket(clientSocket); + continue; + } + BYTE assignedSmallId; EnterCriticalSection(&s_freeSmallIdLock); if (!s_freeSmallIds.empty()) @@ -447,14 +505,15 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param) assignedSmallId = s_freeSmallIds.back(); s_freeSmallIds.pop_back(); } - else if (s_nextSmallId < MINECRAFT_NET_MAX_PLAYERS) + else if (s_nextSmallId < (unsigned int)MINECRAFT_NET_MAX_PLAYERS) { - assignedSmallId = s_nextSmallId++; + assignedSmallId = (BYTE)s_nextSmallId++; } else { LeaveCriticalSection(&s_freeSmallIdLock); app.DebugPrintf("Win64 LAN: Server full, rejecting connection\n"); + SendRejectWithReason(clientSocket, DisconnectPacket::eDisconnect_ServerFull); closesocket(clientSocket); continue; } @@ -482,6 +541,10 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param) app.DebugPrintf("Win64 LAN: Client connected, assigned smallId=%d\n", assignedSmallId); + EnterCriticalSection(&s_smallIdToSocketLock); + s_smallIdToSocket[assignedSmallId] = clientSocket; + LeaveCriticalSection(&s_smallIdToSocketLock); + IQNetPlayer* qnetPlayer = &IQNet::m_player[assignedSmallId]; extern void Win64_SetupRemoteQNetPlayer(IQNetPlayer * player, BYTE smallId, bool isHost, bool isLocal); @@ -724,6 +787,13 @@ void WinsockNetLayer::UpdateAdvertisePlayerCount(BYTE count) LeaveCriticalSection(&s_advertiseLock); } +void WinsockNetLayer::UpdateAdvertiseMaxPlayers(BYTE maxPlayers) +{ + EnterCriticalSection(&s_advertiseLock); + s_advertiseData.maxPlayers = maxPlayers; + LeaveCriticalSection(&s_advertiseLock); +} + void WinsockNetLayer::UpdateAdvertiseJoinable(bool joinable) { EnterCriticalSection(&s_advertiseLock); diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.h b/Minecraft.Client/Windows64/Network/WinsockNetLayer.h index fd1280f7..f30240d3 100644 --- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.h +++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.h @@ -12,7 +12,8 @@ #pragma comment(lib, "Ws2_32.lib") #define WIN64_NET_DEFAULT_PORT 25565 -#define WIN64_NET_MAX_CLIENTS 7 +#define WIN64_NET_MAX_CLIENTS 255 +#define WIN64_SMALLID_REJECT 0xFF #define WIN64_NET_RECV_BUFFER_SIZE 65536 #define WIN64_NET_MAX_PACKET_SIZE (4 * 1024 * 1024) #define WIN64_LAN_DISCOVERY_PORT 25566 @@ -89,6 +90,7 @@ public: 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); + static void UpdateAdvertiseMaxPlayers(BYTE maxPlayers); static void UpdateAdvertiseJoinable(bool joinable); static bool StartDiscovery(); @@ -116,7 +118,7 @@ private: static BYTE s_localSmallId; static BYTE s_hostSmallId; - static BYTE s_nextSmallId; + static unsigned int s_nextSmallId; static CRITICAL_SECTION s_sendLock; static CRITICAL_SECTION s_connectionsLock; @@ -141,6 +143,12 @@ private: static CRITICAL_SECTION s_freeSmallIdLock; static std::vector<BYTE> s_freeSmallIds; + // O(1) smallId -> socket lookup so we don't scan s_connections (which never shrinks) on every send + static SOCKET s_smallIdToSocket[256]; + static CRITICAL_SECTION s_smallIdToSocketLock; + +public: + static void ClearSocketForSmallId(BYTE smallId); }; extern bool g_Win64MultiplayerHost; |
