forked from cafeberry/cafeberry
feat: add smartcmd dedicated server
major bug: when connecting to the server a second time, it gets softlocked and keeps repeating ticking connection A\n in debug logs until it times out.
This commit is contained in:
@@ -1,8 +1,17 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <string>
|
||||
#include "NetworkSocketLayer.h"
|
||||
#include "..\..\Common\Network\PlatformNetworkManager.h"
|
||||
#include "..\..\..\Minecraft.World\Socket.h"
|
||||
#if defined(MINECRAFT_SERVER_BUILD)
|
||||
#include "..\..\..\Minecraft.Server\Access\Access.h"
|
||||
#include "..\..\..\Minecraft.Server\ServerLogManager.h"
|
||||
#endif
|
||||
|
||||
#if defined(MINECRAFT_SERVER_BUILD)
|
||||
static bool TryGetNumericRemoteIp(const sockaddr_in &remoteAddress, std::string *outIp);
|
||||
#endif
|
||||
|
||||
SOCKET NetworkSocketLayer::s_listenSocket = INVALID_SOCKET;
|
||||
SOCKET NetworkSocketLayer::s_hostConnectionSocket = INVALID_SOCKET;
|
||||
@@ -55,23 +64,26 @@ std::vector<LANSession> NetworkSocketLayer::s_discoveredSessions;
|
||||
CRITICAL_SECTION NetworkSocketLayer::s_disconnectLock;
|
||||
std::vector<BYTE> NetworkSocketLayer::s_disconnectedSmallIds;
|
||||
|
||||
CRITICAL_SECTION NetworkSocketLayer::s_pendingJoinLock;
|
||||
std::vector<BYTE> NetworkSocketLayer::s_pendingJoinSmallIds;
|
||||
|
||||
CRITICAL_SECTION NetworkSocketLayer::s_freeSmallIdLock;
|
||||
std::vector<BYTE> NetworkSocketLayer::s_freeSmallIds;
|
||||
|
||||
// only goes true on a successful Initialize(), and a failed one gets retried
|
||||
static bool s_locksCreated = false;
|
||||
#if defined _WINDOWS64
|
||||
#if !defined _WINDOWS64
|
||||
bool g_MultiplayerHost = true;
|
||||
bool g_MultiplayerJoin = false;
|
||||
#else
|
||||
bool g_MultiplayerHost = false;
|
||||
bool g_MultiplayerJoin = true;
|
||||
#endif
|
||||
|
||||
#ifdef MINECRAFT_SERVER_BUILD
|
||||
int g_DedicatedServerPort = NETWORK_LAN_DEFAULT_PORT;
|
||||
char g_DedicatedServerBindIP[256] = "";
|
||||
bool g_DedicatedServerLanAdvertise = true;
|
||||
#endif
|
||||
int g_MultiplayerPort = NETWORK_LAN_DEFAULT_PORT;
|
||||
char g_MultiplayerIP[256] = "192.168.0.69";
|
||||
char g_MultiplayerIP[256] = "192.168.8.27";
|
||||
|
||||
|
||||
bool NetworkSocketLayer::Initialize()
|
||||
@@ -86,7 +98,6 @@ 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++)
|
||||
@@ -139,7 +150,10 @@ bool NetworkSocketLayer::Initialize()
|
||||
|
||||
s_initialized = true;
|
||||
|
||||
// Dedicated Server does not use LAN session discovery and therefore does not initiate discovery.
|
||||
#ifndef MINECRAFT_SERVER_BUILD
|
||||
StartDiscovery();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -224,8 +238,6 @@ 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;
|
||||
@@ -799,6 +811,12 @@ bool NetworkSocketLayer::JoinGame(const char *ip, int port)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clear the recv timeout now that the handshake is complete.
|
||||
// The recv thread should block indefinitely waiting for data.
|
||||
DWORD noTimeout = 0;
|
||||
setsockopt(s_hostConnectionSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&noTimeout, sizeof(noTimeout));
|
||||
|
||||
s_localSmallId = assignedSmallId;
|
||||
|
||||
app.DebugPrintf("LAN: Connected to %s:%d, assigned smallId=%d\n", ip, port, s_localSmallId);
|
||||
@@ -907,6 +925,20 @@ SOCKET NetworkSocketLayer::GetSocketForSmallId(BYTE smallId)
|
||||
#endif
|
||||
}
|
||||
|
||||
// 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] = NETWORK_LAN_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)
|
||||
{
|
||||
int totalRecv = 0;
|
||||
@@ -919,6 +951,27 @@ static bool RecvExact(SOCKET sock, BYTE *buf, int len)
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(MINECRAFT_SERVER_BUILD)
|
||||
static bool TryGetNumericRemoteIp(const sockaddr_in &remoteAddress, std::string *outIp)
|
||||
{
|
||||
if (outIp == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
outIp->clear();
|
||||
char ipBuffer[64] = {};
|
||||
const char *ip = inet_ntop(AF_INET, (void *)&remoteAddress.sin_addr, ipBuffer, sizeof(ipBuffer));
|
||||
if (ip == nullptr || ip[0] == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*outIp = ip;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void NetworkSocketLayer::HandleDataReceived(BYTE fromSmallId, BYTE toSmallId, unsigned char *data, unsigned int dataSize)
|
||||
{
|
||||
INetworkPlayer *pPlayerFrom = g_NetworkManager.GetPlayerBySmallId(fromSmallId);
|
||||
@@ -951,9 +1004,15 @@ int NetworkSocketLayer::AcceptThreadProc(LPVOID param)
|
||||
while (s_active)
|
||||
{
|
||||
#ifdef __PSVITA__
|
||||
SOCKET clientSocket = accept(s_listenSocket, (SceNetSockaddr *)NULL, NULL);
|
||||
sockaddr_in remoteAddress;
|
||||
ZeroMemory(&remoteAddress, sizeof(remoteAddress));
|
||||
int remoteAddressLength = sizeof(remoteAddress);
|
||||
SOCKET clientSocket = accept(s_listenSocket, (SceNetSockaddr *)&remoteAddress, &remoteAddressLength);
|
||||
#else
|
||||
SOCKET clientSocket = accept(s_listenSocket, NULL, NULL);
|
||||
sockaddr_in remoteAddress;
|
||||
ZeroMemory(&remoteAddress, sizeof(remoteAddress));
|
||||
int remoteAddressLength = sizeof(remoteAddress);
|
||||
SOCKET clientSocket = accept(s_listenSocket, (sockaddr*)&remoteAddress, &remoteAddressLength);
|
||||
#endif
|
||||
#if defined _WINDOWS64 || defined _XBOX
|
||||
if (clientSocket == INVALID_SOCKET)
|
||||
@@ -974,10 +1033,28 @@ int NetworkSocketLayer::AcceptThreadProc(LPVOID param)
|
||||
int noDelay = 1;
|
||||
setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, (const char *)&noDelay, sizeof(noDelay));
|
||||
|
||||
extern QNET_STATE _iQNetStubState;
|
||||
if (_iQNetStubState != QNET_STATE_GAME_PLAY && _iQNetStubState != QNET_STATE_SESSION_STARTING)
|
||||
#if defined(MINECRAFT_SERVER_BUILD)
|
||||
std::string remoteIp;
|
||||
const bool hasRemoteIp = TryGetNumericRemoteIp(remoteAddress, &remoteIp);
|
||||
const char *remoteIpForLog = hasRemoteIp ? remoteIp.c_str() : "unknown";
|
||||
ServerRuntime::ServerLogManager::OnIncomingTcpConnection(remoteIpForLog);
|
||||
if (hasRemoteIp && ServerRuntime::Access::IsIpBanned(remoteIp))
|
||||
{
|
||||
ServerRuntime::ServerLogManager::OnRejectedTcpConnection(remoteIpForLog, ServerRuntime::ServerLogManager::eTcpRejectReason_BannedIp);
|
||||
SendRejectWithReason(clientSocket, DisconnectPacket::eDisconnect_Banned);
|
||||
closesocket(clientSocket);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern QNET_STATE _iQNetStubState;
|
||||
if (_iQNetStubState != QNET_STATE_GAME_PLAY)
|
||||
{
|
||||
#if defined(MINECRAFT_SERVER_BUILD)
|
||||
ServerRuntime::ServerLogManager::OnRejectedTcpConnection(remoteIpForLog, ServerRuntime::ServerLogManager::eTcpRejectReason_GameNotReady);
|
||||
#else
|
||||
app.DebugPrintf("Win64 LAN: Rejecting connection, game not ready\n");
|
||||
#endif
|
||||
closesocket(clientSocket);
|
||||
continue;
|
||||
}
|
||||
@@ -996,7 +1073,11 @@ int NetworkSocketLayer::AcceptThreadProc(LPVOID param)
|
||||
else
|
||||
{
|
||||
LeaveCriticalSection(&s_freeSmallIdLock);
|
||||
#if defined(MINECRAFT_SERVER_BUILD)
|
||||
ServerRuntime::ServerLogManager::OnRejectedTcpConnection(remoteIpForLog, ServerRuntime::ServerLogManager::eTcpRejectReason_ServerFull);
|
||||
#else
|
||||
app.DebugPrintf("Win64 LAN: Server full, rejecting connection\n");
|
||||
#endif
|
||||
closesocket(clientSocket);
|
||||
continue;
|
||||
}
|
||||
@@ -1026,16 +1107,19 @@ int NetworkSocketLayer::AcceptThreadProc(LPVOID param)
|
||||
conn.active = true;
|
||||
LeaveCriticalSection(&s_connectionsLock);
|
||||
|
||||
#if defined(MINECRAFT_SERVER_BUILD)
|
||||
ServerRuntime::ServerLogManager::OnAcceptedTcpConnection(assignedSmallId, remoteIpForLog);
|
||||
#else
|
||||
app.DebugPrintf("Win64 LAN: Client connected, assigned smallId=%d\n", assignedSmallId);
|
||||
#endif
|
||||
|
||||
IQNetPlayer *qnetPlayer = &IQNet::m_player[assignedSmallId];
|
||||
IQNetPlayer* qnetPlayer = &IQNet::m_player[assignedSmallId];
|
||||
|
||||
extern void Win64_SetupRemoteQNetPlayer(IQNetPlayer *player, BYTE smallId, bool isHost, bool isLocal);
|
||||
extern void Win64_SetupRemoteQNetPlayer(IQNetPlayer * player, BYTE smallId, bool isHost, bool isLocal);
|
||||
Win64_SetupRemoteQNetPlayer(qnetPlayer, assignedSmallId, false, false);
|
||||
|
||||
EnterCriticalSection(&s_pendingJoinLock);
|
||||
s_pendingJoinSmallIds.push_back(assignedSmallId);
|
||||
LeaveCriticalSection(&s_pendingJoinLock);
|
||||
extern CPlatformNetworkManagerStub* g_pPlatformNetworkManager;
|
||||
g_pPlatformNetworkManager->NotifyPlayerJoined(qnetPlayer);
|
||||
|
||||
DWORD *threadParam = new DWORD;
|
||||
*threadParam = assignedSmallId;
|
||||
@@ -1142,20 +1226,6 @@ 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;
|
||||
|
||||
Reference in New Issue
Block a user