mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-08-20 09:57:09 +00:00
Server chunk optimizations for large player counts, server full notification fix, added to server.properties.
This commit is contained in:
parent
6664493793
commit
38d24bff0c
|
|
@ -8,6 +8,8 @@
|
|||
#include "..\..\Windows64\Windows64_Xuid.h"
|
||||
#include "..\..\Minecraft.h"
|
||||
#include "..\..\User.h"
|
||||
#include "..\..\MinecraftServer.h"
|
||||
#include "..\..\PlayerList.h"
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
|
|
@ -238,15 +240,33 @@ void CPlatformNetworkManagerStub::DoWork()
|
|||
qnetPlayer->m_resolvedXuid = INVALID_XUID;
|
||||
qnetPlayer->m_gamertag[0] = 0;
|
||||
qnetPlayer->SetCustomDataValue(0);
|
||||
WinsockNetLayer::PushFreeSmallId(disconnectedSmallId);
|
||||
if (IQNet::s_playerCount > 1)
|
||||
IQNet::s_playerCount--;
|
||||
}
|
||||
// Always return smallId to the free pool so it can be reused (game may have already cleared the slot).
|
||||
WinsockNetLayer::PushFreeSmallId(disconnectedSmallId);
|
||||
// Clear O(1) socket lookup so GetSocketForSmallId stays fast (s_connections never shrinks).
|
||||
WinsockNetLayer::ClearSocketForSmallId(disconnectedSmallId);
|
||||
// Clear chunk visibility flags for this system so rejoin gets fresh chunk state.
|
||||
SystemFlagRemoveBySmallId((int)disconnectedSmallId);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerStub::CanAcceptMoreConnections()
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
MinecraftServer* server = MinecraftServer::getInstance();
|
||||
if (server == NULL) return true;
|
||||
PlayerList* list = server->getPlayerList();
|
||||
if (list == NULL) return true;
|
||||
return (unsigned int)list->players.size() < (unsigned int)list->getMaxPlayers();
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
int CPlatformNetworkManagerStub::GetPlayerCount()
|
||||
{
|
||||
return m_pIQNet->GetPlayerCount();
|
||||
|
|
@ -581,6 +601,7 @@ CPlatformNetworkManagerStub::PlayerFlags::PlayerFlags(INetworkPlayer *pNetworkPl
|
|||
this->flags = new unsigned char [ count / 8 ];
|
||||
memset( this->flags, 0, count / 8 );
|
||||
this->count = count;
|
||||
this->m_smallId = (pNetworkPlayer && pNetworkPlayer->IsLocal()) ? 256 : (pNetworkPlayer ? (int)pNetworkPlayer->GetSmallId() : -1);
|
||||
}
|
||||
CPlatformNetworkManagerStub::PlayerFlags::~PlayerFlags()
|
||||
{
|
||||
|
|
@ -618,6 +639,23 @@ void CPlatformNetworkManagerStub::SystemFlagRemovePlayer(INetworkPlayer *pNetwor
|
|||
}
|
||||
}
|
||||
|
||||
// Clear chunk flags for a system when they disconnect (by smallId). Call even when we don't find the player,
|
||||
// so we always clear and the smallId can be reused without stale "chunk seen" state.
|
||||
void CPlatformNetworkManagerStub::SystemFlagRemoveBySmallId(int smallId)
|
||||
{
|
||||
if (smallId < 0) return;
|
||||
for (unsigned int i = 0; i < m_playerFlags.size(); i++)
|
||||
{
|
||||
if (m_playerFlags[i]->m_smallId == smallId)
|
||||
{
|
||||
delete m_playerFlags[i];
|
||||
m_playerFlags[i] = m_playerFlags.back();
|
||||
m_playerFlags.pop_back();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerStub::SystemFlagReset()
|
||||
{
|
||||
for( unsigned int i = 0; i < m_playerFlags.size(); i++ )
|
||||
|
|
|
|||
|
|
@ -98,12 +98,14 @@ private:
|
|||
INetworkPlayer *m_pNetworkPlayer;
|
||||
unsigned char *flags;
|
||||
unsigned int count;
|
||||
int m_smallId;
|
||||
PlayerFlags(INetworkPlayer *pNetworkPlayer, unsigned int count);
|
||||
~PlayerFlags();
|
||||
};
|
||||
vector<PlayerFlags *> m_playerFlags;
|
||||
void SystemFlagAddPlayer(INetworkPlayer *pNetworkPlayer);
|
||||
void SystemFlagRemovePlayer(INetworkPlayer *pNetworkPlayer);
|
||||
void SystemFlagRemoveBySmallId(int smallId);
|
||||
void SystemFlagReset();
|
||||
public:
|
||||
virtual void SystemFlagSet(INetworkPlayer *pNetworkPlayer, int index);
|
||||
|
|
@ -161,6 +163,9 @@ public:
|
|||
virtual void GetFullFriendSessionInfo( FriendSessionInfo *foundSession, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam );
|
||||
virtual void ForceFriendsSessionRefresh();
|
||||
|
||||
// Win64: used by accept thread to reject connections when server is at max players (so we don't assign smallId > max).
|
||||
bool CanAcceptMoreConnections();
|
||||
|
||||
public:
|
||||
void NotifyPlayerJoined( IQNetPlayer *pQNetPlayer );
|
||||
void NotifyPlayerLeaving(IQNetPlayer* pQNetPlayer);
|
||||
|
|
|
|||
|
|
@ -532,6 +532,48 @@ void UIScene_JoinMenu::JoinGame(UIScene_JoinMenu* pClass)
|
|||
break;
|
||||
}
|
||||
|
||||
if( exitReasonStringId == -1 )
|
||||
{
|
||||
Minecraft* pMinecraft = Minecraft::GetInstance();
|
||||
int primaryPad = ProfileManager.GetPrimaryPad();
|
||||
if( pMinecraft->m_connectionFailed[primaryPad] )
|
||||
{
|
||||
switch( pMinecraft->m_connectionFailedReason[primaryPad] )
|
||||
{
|
||||
case DisconnectPacket::eDisconnect_LoginTooLong:
|
||||
exitReasonStringId = IDS_DISCONNECTED_LOGIN_TOO_LONG;
|
||||
break;
|
||||
case DisconnectPacket::eDisconnect_ServerFull:
|
||||
exitReasonStringId = IDS_DISCONNECTED_SERVER_FULL;
|
||||
break;
|
||||
case DisconnectPacket::eDisconnect_Kicked:
|
||||
exitReasonStringId = IDS_DISCONNECTED_KICKED;
|
||||
break;
|
||||
case DisconnectPacket::eDisconnect_NoUGC_AllLocal:
|
||||
exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL;
|
||||
break;
|
||||
case DisconnectPacket::eDisconnect_NoUGC_Single_Local:
|
||||
exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL;
|
||||
break;
|
||||
case DisconnectPacket::eDisconnect_NoFlying:
|
||||
exitReasonStringId = IDS_DISCONNECTED_FLYING;
|
||||
break;
|
||||
case DisconnectPacket::eDisconnect_Quitting:
|
||||
exitReasonStringId = IDS_DISCONNECTED_SERVER_QUIT;
|
||||
break;
|
||||
case DisconnectPacket::eDisconnect_OutdatedServer:
|
||||
exitReasonStringId = IDS_DISCONNECTED_SERVER_OLD;
|
||||
break;
|
||||
case DisconnectPacket::eDisconnect_OutdatedClient:
|
||||
exitReasonStringId = IDS_DISCONNECTED_CLIENT_OLD;
|
||||
break;
|
||||
default:
|
||||
exitReasonStringId = IDS_CONNECTION_LOST_SERVER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( exitReasonStringId == -1 )
|
||||
{
|
||||
ui.NavigateBack(pClass->m_iPad);
|
||||
|
|
|
|||
|
|
@ -682,6 +682,12 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW
|
|||
}
|
||||
#endif
|
||||
setPlayers(new PlayerList(this));
|
||||
#ifdef _WINDOWS64
|
||||
{
|
||||
int maxP = getPlayerList()->getMaxPlayers();
|
||||
WinsockNetLayer::UpdateAdvertiseMaxPlayers((BYTE)(maxP > 255 ? 255 : maxP));
|
||||
}
|
||||
#endif
|
||||
|
||||
// 4J-JEV: Need to wait for levelGenerationOptions to load.
|
||||
while ( app.getLevelGenerationOptions() != NULL && !app.getLevelGenerationOptions()->hasLoadedData() )
|
||||
|
|
@ -2347,15 +2353,19 @@ void MinecraftServer::chunkPacketManagement_PostTick()
|
|||
}
|
||||
|
||||
#else
|
||||
// 4J Added
|
||||
// 4J Added - round-robin chunk sends by player index. Compare vs the player at the current queue index,
|
||||
// not GetSessionIndex() (smallId), so reused smallIds after many connect/disconnects still get chunk sends.
|
||||
bool MinecraftServer::chunkPacketManagement_CanSendTo(INetworkPlayer *player)
|
||||
{
|
||||
if( player == NULL ) return false;
|
||||
|
||||
int time = GetTickCount();
|
||||
if( player->GetSessionIndex() == s_slowQueuePlayerIndex && (time - s_slowQueueLastTime) > MINECRAFT_SERVER_SLOW_QUEUE_DELAY )
|
||||
DWORD currentPlayerCount = g_NetworkManager.GetPlayerCount();
|
||||
if( currentPlayerCount == 0 ) return false;
|
||||
int index = s_slowQueuePlayerIndex % (int)currentPlayerCount;
|
||||
INetworkPlayer *queuePlayer = g_NetworkManager.GetPlayerByIndex( index );
|
||||
if( queuePlayer != NULL && (player == queuePlayer || player->IsSameSystem(queuePlayer)) && (time - s_slowQueueLastTime) > MINECRAFT_SERVER_SLOW_QUEUE_DELAY )
|
||||
{
|
||||
// app.DebugPrintf("Slow queue OK for player #%d\n", player->GetSessionIndex());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,11 @@ class LevelType;
|
|||
class ProgressRenderer;
|
||||
class CommandDispatcher;
|
||||
|
||||
#if defined(_WINDOWS64)
|
||||
#define MINECRAFT_SERVER_SLOW_QUEUE_DELAY 0 // Removed slow queue because at large player counts, chunks stopped appearing
|
||||
#else
|
||||
#define MINECRAFT_SERVER_SLOW_QUEUE_DELAY 250
|
||||
#endif
|
||||
|
||||
#if defined _XBOX_ONE || defined _XBOX || defined __ORBIS__ || defined __PS3__ || defined __PSVITA__
|
||||
#define _ACK_CHUNK_SEND_THROTTLING
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include "..\Minecraft.World\ArrayWithLength.h"
|
||||
#include "..\Minecraft.World\System.h"
|
||||
#include "PlayerList.h"
|
||||
#include <unordered_set>
|
||||
|
||||
PlayerChunkMap::PlayerChunk::PlayerChunk(int x, int z, PlayerChunkMap *pcm) : pos(x,z)
|
||||
{
|
||||
|
|
@ -204,106 +205,53 @@ void PlayerChunkMap::PlayerChunk::prioritiseTileChanges()
|
|||
prioritised = true;
|
||||
}
|
||||
|
||||
// One system id per machine so we send at most once per system. Local = 256, remote = GetSmallId().
|
||||
static int getSystemIdForSentTo(INetworkPlayer* np)
|
||||
{
|
||||
if (np == NULL) return -1;
|
||||
return np->IsLocal() ? 256 : (int)np->GetSmallId();
|
||||
}
|
||||
|
||||
void PlayerChunkMap::PlayerChunk::broadcast(shared_ptr<Packet> packet)
|
||||
{
|
||||
vector< shared_ptr<ServerPlayer> > sentTo;
|
||||
for (unsigned int i = 0; i < players.size(); i++)
|
||||
std::unordered_set<int> sentToSystemIds; // O(1) "already sent to this system" check instead of O(N) scan
|
||||
for (unsigned int i = 0; i < players.size(); i++)
|
||||
{
|
||||
shared_ptr<ServerPlayer> player = players[i];
|
||||
|
||||
// 4J - don't send to a player we've already sent this data to that shares the same machine. TileUpdatePacket,
|
||||
// ChunkTilesUpdatePacket and SignUpdatePacket all used to limit themselves to sending once to each machine
|
||||
// by only sending to the primary player on each machine. This was causing trouble for split screen
|
||||
// as updates were only coming in for the region round this one player. Now these packets can be sent to any
|
||||
// player, but we try to restrict the network impact this has by not resending to the one machine
|
||||
bool dontSend = false;
|
||||
if( sentTo.size() )
|
||||
{
|
||||
INetworkPlayer *thisPlayer = player->connection->getNetworkPlayer();
|
||||
if( thisPlayer == NULL )
|
||||
{
|
||||
dontSend = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(unsigned int j = 0; j < sentTo.size(); j++ )
|
||||
{
|
||||
shared_ptr<ServerPlayer> player2 = sentTo[j];
|
||||
INetworkPlayer *otherPlayer = player2->connection->getNetworkPlayer();
|
||||
if( otherPlayer != NULL && thisPlayer->IsSameSystem(otherPlayer) )
|
||||
{
|
||||
dontSend = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if( dontSend )
|
||||
{
|
||||
shared_ptr<ServerPlayer> player = players[i];
|
||||
INetworkPlayer* thisPlayer = player->connection->getNetworkPlayer();
|
||||
if (thisPlayer == NULL) continue;
|
||||
int sysId = getSystemIdForSentTo(thisPlayer);
|
||||
if (sysId >= 0 && sentToSystemIds.find(sysId) != sentToSystemIds.end())
|
||||
continue;
|
||||
}
|
||||
|
||||
// 4J Changed to get the flag index for the player before we send a packet. This flag is updated when we queue
|
||||
// for send the first BlockRegionUpdatePacket for this chunk to that player/players system. Therefore there is no need to
|
||||
// send tile updates or other updates until that has been sent
|
||||
int flagIndex = ServerPlayer::getFlagIndexForChunk(pos, parent->dimension);
|
||||
if (player->seenChunks.find(pos) != player->seenChunks.end() && (player->connection->isLocal() || g_NetworkManager.SystemFlagGet(player->connection->getNetworkPlayer(),flagIndex) ))
|
||||
if (player->seenChunks.find(pos) != player->seenChunks.end() && (player->connection->isLocal() || g_NetworkManager.SystemFlagGet(thisPlayer, flagIndex)))
|
||||
{
|
||||
player->connection->send(packet);
|
||||
sentTo.push_back(player);
|
||||
}
|
||||
}
|
||||
// Now also check round all the players that are involved in this game. We also want to send the packet
|
||||
// to them if their system hasn't received it already, but they have received the first BlockRegionUpdatePacket for this
|
||||
// chunk
|
||||
|
||||
// Make sure we are only doing this for BlockRegionUpdatePacket, ChunkTilesUpdatePacket and TileUpdatePacket.
|
||||
// We'll be potentially sending to players who aren't on the same level as this packet is intended for,
|
||||
// and only these 3 packets have so far been updated to be able to encode the level so they are robust
|
||||
// enough to cope with this
|
||||
if(!( ( packet->getId() == 51 ) || ( packet->getId() == 52 ) || ( packet->getId() == 53 ) ) )
|
||||
{
|
||||
return;
|
||||
player->connection->send(packet);
|
||||
if (sysId >= 0) sentToSystemIds.insert(sysId);
|
||||
}
|
||||
}
|
||||
// Also send to other server players who have this chunk (may not be in this chunk's players list)
|
||||
if (!((packet->getId() == 51) || (packet->getId() == 52) || (packet->getId() == 53)))
|
||||
return;
|
||||
|
||||
for( int i = 0; i < parent->level->getServer()->getPlayers()->players.size(); i++ )
|
||||
const vector<shared_ptr<ServerPlayer> >& allPlayers = parent->level->getServer()->getPlayers()->players;
|
||||
for (size_t i = 0; i < allPlayers.size(); i++)
|
||||
{
|
||||
shared_ptr<ServerPlayer> player = parent->level->getServer()->getPlayers()->players[i];
|
||||
// Don't worry about local players, they get all their updates through sharing level with the server anyway
|
||||
if ( player->connection == NULL ) continue;
|
||||
if( player->connection->isLocal() ) continue;
|
||||
shared_ptr<ServerPlayer> player = allPlayers[i];
|
||||
if (player->connection == NULL || player->connection->isLocal()) continue;
|
||||
|
||||
// Don't worry about this player if they haven't had this chunk yet (this flag will be the
|
||||
// same for all players on the same system)
|
||||
int flagIndex = ServerPlayer::getFlagIndexForChunk(pos,parent->dimension);
|
||||
if(!g_NetworkManager.SystemFlagGet(player->connection->getNetworkPlayer(),flagIndex)) continue;
|
||||
INetworkPlayer* thisPlayer = player->connection->getNetworkPlayer();
|
||||
if (thisPlayer == NULL) continue;
|
||||
int sysId = getSystemIdForSentTo(thisPlayer);
|
||||
if (sysId >= 0 && sentToSystemIds.find(sysId) != sentToSystemIds.end())
|
||||
continue;
|
||||
|
||||
// From here on the same rules as in the loop above - don't send it if we've already sent to the same system
|
||||
bool dontSend = false;
|
||||
if( sentTo.size() )
|
||||
{
|
||||
INetworkPlayer *thisPlayer = player->connection->getNetworkPlayer();
|
||||
if( thisPlayer == NULL )
|
||||
{
|
||||
dontSend = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(unsigned int j = 0; j < sentTo.size(); j++ )
|
||||
{
|
||||
shared_ptr<ServerPlayer> player2 = sentTo[j];
|
||||
INetworkPlayer *otherPlayer = player2->connection->getNetworkPlayer();
|
||||
if( otherPlayer != NULL && thisPlayer->IsSameSystem(otherPlayer) )
|
||||
{
|
||||
dontSend = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if( !dontSend )
|
||||
{
|
||||
player->connection->send(packet);
|
||||
sentTo.push_back(player);
|
||||
}
|
||||
int flagIndex = ServerPlayer::getFlagIndexForChunk(pos, parent->dimension);
|
||||
if (!g_NetworkManager.SystemFlagGet(thisPlayer, flagIndex)) continue;
|
||||
|
||||
player->connection->send(packet);
|
||||
if (sysId >= 0) sentToSystemIds.insert(sysId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,11 +56,8 @@ PlayerList::PlayerList(MinecraftServer *server)
|
|||
|
||||
//int viewDistance = server->settings->getInt(L"view-distance", 10);
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
maxPlayers = MINECRAFT_NET_MAX_PLAYERS;
|
||||
#else
|
||||
maxPlayers = server->settings->getInt(L"max-players", 20);
|
||||
#endif
|
||||
int rawMax = server->settings->getInt(L"max-players", 8);
|
||||
maxPlayers = (unsigned int)Mth::clamp(rawMax, 1, MINECRAFT_NET_MAX_PLAYERS);
|
||||
doWhiteList = false;
|
||||
InitializeCriticalSection(&m_kickPlayersCS);
|
||||
InitializeCriticalSection(&m_closePlayersCS);
|
||||
|
|
@ -521,11 +518,7 @@ if (player->riding != NULL)
|
|||
|
||||
shared_ptr<ServerPlayer> PlayerList::getPlayerForLogin(PendingConnection *pendingConnection, const wstring& userName, PlayerUID xuid, PlayerUID onlineXuid)
|
||||
{
|
||||
#ifdef _WINDOWS64
|
||||
if (players.size() >= (unsigned int)MINECRAFT_NET_MAX_PLAYERS)
|
||||
#else
|
||||
if (players.size() >= (unsigned int)maxPlayers)
|
||||
#endif
|
||||
{
|
||||
pendingConnection->disconnect(DisconnectPacket::eDisconnect_ServerFull);
|
||||
return shared_ptr<ServerPlayer>();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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())
|
||||
|
|
@ -455,6 +513,7 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
|
|||
{
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#define WIN64_NET_DEFAULT_PORT 25565
|
||||
#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();
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue