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.
This commit is contained in:
Kevin
2026-03-06 19:23:32 -06:00
committed by GitHub
parent 16446265d5
commit 13960a93b2
16 changed files with 294 additions and 145 deletions

View File

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