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:
2026-08-29 14:01:24 +03:00
parent a1541c590d
commit 4b962fcb21
146 changed files with 32837 additions and 272 deletions
+4
View File
@@ -303,6 +303,10 @@ void ClientConnection::handleLogin(shared_ptr<LoginPacket> packet)
level->isClientSide = true;
minecraft->setLevel(level);
}
else
{
level = (MultiPlayerLevel *)dimensionLevel;
}
minecraft->player->setPlayerIndex( packet->m_playerIndex );
minecraft->player->setCustomSkin( app.GetPlayerSkinId(m_userIndex) );
+13 -1
View File
@@ -1,15 +1,27 @@
#include "stdafx.h"
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
#include "../../Minecraft.Server/ServerLogManager.h"
#endif
//--------------------------------------------------------------------------------------
// Name: DebugSpewV()
// Desc: Internal helper function
//--------------------------------------------------------------------------------------
#ifndef _CONTENT_PACKAGE
static VOID DebugSpewV( const CHAR* strFormat, const va_list pArgList )
static VOID DebugSpewV( const CHAR* strFormat, va_list pArgList )
{
#if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__
assert(0);
#else
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
// Dedicated server routes legacy debug spew through ServerLogger to preserve CLI prompt handling.
if (ServerRuntime::ServerLogManager::ShouldForwardClientDebugLogs())
{
ServerRuntime::ServerLogManager::ForwardClientDebugSpewLogV(strFormat, pArgList);
return;
}
#endif
CHAR str[2048];
// Use the secure CRT to avoid buffer overruns. Specify a count of
// _TRUNCATE so that too long strings will be silently truncated
+34 -13
View File
@@ -30,6 +30,9 @@
#include "GameRules\ConsoleGameRules.h"
#include "GameRules\ConsoleSchematicFile.h"
#include "..\User.h"
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
#include "..\..\Minecraft.Server\ServerLogManager.h"
#endif
#include "..\\EntityRenderDispatcher.h"
#include "..\TexturePackRepository.h"
#include "..\DLCTexturePack.h"
@@ -230,12 +233,21 @@ void CMinecraftApp::DebugPrintf(const char *szFormat, ...)
{
#ifndef _FINAL_BUILD
char buf[1024];
va_list ap;
va_start(ap, szFormat);
vsnprintf(buf, sizeof(buf), szFormat, ap);
va_end(ap);
OutputDebugStringA(buf);
va_list ap;
va_start(ap, szFormat);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
// Dedicated server routes client debug spew through ServerLogger so CLI output stays prompt-safe.
if (ServerRuntime::ServerLogManager::ShouldForwardClientDebugLogs())
{
ServerRuntime::ServerLogManager::ForwardClientAppDebugLogV(szFormat, ap);
va_end(ap);
return;
}
#endif
char buf[1024];
vsnprintf(buf, sizeof(buf), szFormat, ap);
va_end(ap);
OutputDebugStringA(buf);
#endif
}
@@ -243,13 +255,22 @@ void CMinecraftApp::DebugPrintf(const char *szFormat, ...)
void CMinecraftApp::DebugPrintf(int user, const char *szFormat, ...)
{
#ifndef _FINAL_BUILD
if(user == USER_NONE)
return;
char buf[1024];
va_list ap;
va_start(ap, szFormat);
vsnprintf(buf, sizeof(buf), szFormat, ap);
va_end(ap);
if(user == USER_NONE)
return;
va_list ap;
va_start(ap, szFormat);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
// Dedicated server routes client debug spew through ServerLogger so CLI output stays prompt-safe.
if (ServerRuntime::ServerLogManager::ShouldForwardClientDebugLogs())
{
ServerRuntime::ServerLogManager::ForwardClientUserDebugLogV(user, szFormat, ap);
va_end(ap);
return;
}
#endif
char buf[1024];
vsnprintf(buf, sizeof(buf), szFormat, ap);
va_end(ap);
#ifdef __PS3__
unsigned int writelen;
sys_tty_write(SYS_TTYP_USER1 + ( user - 1 ), buf, strlen(buf), &writelen );
@@ -358,12 +358,24 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
// PRIMARY PLAYER
vector<ClientConnection *> createdConnections;
ClientConnection *connection;
ClientConnection *connection = NULL;
if( g_NetworkManager.IsHost() )
#ifdef MINECRAFT_SERVER_BUILD
{
app.DebugPrintf("Dedicated server mode: skipping local host client connection\n");
// Keep telemetry behavior consistent with the host path.
INT multiplayerInstanceId = TelemetryManager->GenerateMultiplayerInstanceId();
TelemetryManager->SetMultiplayerInstanceId(multiplayerInstanceId);
app.SetGameMode( eMode_Multiplayer );
}
#else
{
connection = new ClientConnection(minecraft, NULL);
}
#endif
else
{
INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(ProfileManager.GetLockedProfile());
@@ -389,155 +401,159 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
connection = new ClientConnection(minecraft, socket);
}
if( !connection->createdOk )
if (connection != NULL)
{
assert(false);
delete connection;
connection = NULL;
MinecraftServer::HaltServer();
return false;
}
connection->send( shared_ptr<PreLoginPacket>( new PreLoginPacket(minecraft->user->name) ) );
// Tick connection until we're ready to go. The stages involved in this are:
// (1) Creating the ClientConnection sends a prelogin packet to the server
// (2) the server sends a prelogin back, which is handled by the clientConnection, and returns a login packet
// (3) the server sends a login back, which is handled by the client connection to start the game
if( !g_NetworkManager.IsHost() )
{
Minecraft::GetInstance()->progressRenderer->progressStart(IDS_PROGRESS_CONNECTING);
}
else
{
// 4J Stu - Host needs to generate a unique multiplayer id for sentient telemetry reporting
INT multiplayerInstanceId = TelemetryManager->GenerateMultiplayerInstanceId();
TelemetryManager->SetMultiplayerInstanceId(multiplayerInstanceId);
}
TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected();
do
{
app.DebugPrintf("ticking connection A\n");
connection->tick();
// 4J Stu - We were ticking this way too fast which could cause the connection to time out
// The connections should tick at 20 per second
Sleep(50);
} while ( (IsInSession() && !connection->isStarted() && !connection->isClosed() && !g_NetworkManager.IsLeavingGame()) || tPack->isLoadingData() || (Minecraft::GetInstance()->skins->needsUIUpdate() || ui.IsReloadingSkin()) );
ui.CleanUpSkinReload();
// 4J Stu - Fix for #11279 - CRASH: TCR 001: BAS Game Stability: Signing out of game will cause title to crash
// We need to break out of the above loop if m_bLeavingGame is set, and close the connection
if( g_NetworkManager.IsLeavingGame() || !IsInSession() )
{
connection->close();
}
if( connection->isStarted() && !connection->isClosed() )
{
createdConnections.push_back( connection );
int primaryPad = ProfileManager.GetPrimaryPad();
app.SetRichPresenceContext(primaryPad,CONTEXT_GAME_STATE_BLANK);
if (GetPlayerCount() > 1) // Are we offline or online, and how many players are there
if( !connection->createdOk )
{
if (IsLocalGame()) ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYEROFFLINE,false);
else ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYER,false);
assert(false);
delete connection;
connection = NULL;
MinecraftServer::HaltServer();
return false;
}
connection->send( shared_ptr<PreLoginPacket>( new PreLoginPacket(minecraft->user->name) ) );
// Tick connection until we're ready to go. The stages involved in this are:
// (1) Creating the ClientConnection sends a prelogin packet to the server
// (2) the server sends a prelogin back, which is handled by the clientConnection, and returns a login packet
// (3) the server sends a login back, which is handled by the client connection to start the game
if( !g_NetworkManager.IsHost() )
{
Minecraft::GetInstance()->progressRenderer->progressStart(IDS_PROGRESS_CONNECTING);
}
else
{
if(IsLocalGame()) ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYER_1POFFLINE,false);
else ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYER_1P,false);
// 4J Stu - Host needs to generate a unique multiplayer id for sentient telemetry reporting
INT multiplayerInstanceId = TelemetryManager->GenerateMultiplayerInstanceId();
TelemetryManager->SetMultiplayerInstanceId(multiplayerInstanceId);
}
TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected();
do
{
app.DebugPrintf("ticking connection A\n");
connection->tick();
// 4J Stu - We were ticking this way too fast which could cause the connection to time out
// The connections should tick at 20 per second
Sleep(50);
} while ( (IsInSession() && !connection->isStarted() && !connection->isClosed() && !g_NetworkManager.IsLeavingGame()) || tPack->isLoadingData() || (Minecraft::GetInstance()->skins->needsUIUpdate() || ui.IsReloadingSkin()) );
ui.CleanUpSkinReload();
// 4J Stu - Fix for #11279 - CRASH: TCR 001: BAS Game Stability: Signing out of game will cause title to crash
// We need to break out of the above loop if m_bLeavingGame is set, and close the connection
if( g_NetworkManager.IsLeavingGame() || !IsInSession() )
{
connection->close();
}
// ALL OTHER LOCAL PLAYERS
for(int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
if( connection->isStarted() && !connection->isClosed() )
{
// Already have setup the primary pad
if(idx == ProfileManager.GetPrimaryPad() ) continue;
if( GetLocalPlayerByUserIndex(idx) != NULL && !ProfileManager.IsSignedIn(idx) )
{
INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(idx);
Socket *socket = pNetworkPlayer->GetSocket();
app.DebugPrintf("Closing socket due to player %d not being signed in any more\n");
if( !socket->close(false) ) socket->close(true);
continue;
}
// By default when we host we only have the local player, but currently allow multiple local players to join
// when joining any other way, so just because they are signed in doesn't mean they are in the session
// 4J Stu - If they are in the session, then we should add them to the game. Otherwise we won't be able to add them later
INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(idx);
if( pNetworkPlayer == NULL )
continue;
ClientConnection *connection;
Socket *socket = pNetworkPlayer->GetSocket();
connection = new ClientConnection(minecraft, socket, idx);
minecraft->addPendingLocalConnection(idx, connection);
//minecraft->createExtraLocalPlayer(idx, (convStringToWstring( ProfileManager.GetGamertag(idx) )).c_str(), idx, connection);
// Open the socket on the server end to accept incoming data
Socket::addIncomingSocket(socket);
connection->send( shared_ptr<PreLoginPacket>( new PreLoginPacket(convStringToWstring( ProfileManager.GetGamertag(idx) )) ) );
createdConnections.push_back( connection );
// Tick connection until we're ready to go. The stages involved in this are:
// (1) Creating the ClientConnection sends a prelogin packet to the server
// (2) the server sends a prelogin back, which is handled by the clientConnection, and returns a login packet
// (3) the server sends a login back, which is handled by the client connection to start the game
do
int primaryPad = ProfileManager.GetPrimaryPad();
app.SetRichPresenceContext(primaryPad,CONTEXT_GAME_STATE_BLANK);
if (GetPlayerCount() > 1) // Are we offline or online, and how many players are there
{
// We need to keep ticking the connections for players that already logged in
for(AUTO_VAR(it, createdConnections.begin()); it < createdConnections.end(); ++it)
{
(*it)->tick();
}
// 4J Stu - We were ticking this way too fast which could cause the connection to time out
// The connections should tick at 20 per second
Sleep(50);
app.DebugPrintf("<***> %d %d %d %d %d\n",IsInSession(), !connection->isStarted(),!connection->isClosed(),ProfileManager.IsSignedIn(idx),!g_NetworkManager.IsLeavingGame());
#if defined _XBOX || defined __PS3__ && !defined DISABLE_PSN
} while (IsInSession() && !connection->isStarted() && !connection->isClosed() && ProfileManager.IsSignedIn(idx) && !g_NetworkManager.IsLeavingGame() );
#else
// TODO - This SHOULD be something just like the code above but temporarily changing here so that we don't have to depend on the profilemanager behaviour
} while (IsInSession() && !connection->isStarted() && !connection->isClosed() && !g_NetworkManager.IsLeavingGame() );
#endif
// 4J Stu - Fix for #11279 - CRASH: TCR 001: BAS Game Stability: Signing out of game will cause title to crash
// We need to break out of the above loop if m_bLeavingGame is set, and stop creating new connections
// The connections in the createdConnections vector get closed at the end of the thread
if( g_NetworkManager.IsLeavingGame() || !IsInSession() ) break;
if( ProfileManager.IsSignedIn(idx) && !connection->isClosed() )
{
app.SetRichPresenceContext(idx,CONTEXT_GAME_STATE_BLANK);
if (IsLocalGame()) ProfileManager.SetCurrentGameActivity(idx,CONTEXT_PRESENCE_MULTIPLAYEROFFLINE,false);
else ProfileManager.SetCurrentGameActivity(idx,CONTEXT_PRESENCE_MULTIPLAYER,false);
if (IsLocalGame()) ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYEROFFLINE,false);
else ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYER,false);
}
else
{
connection->close();
AUTO_VAR(it, find( createdConnections.begin(), createdConnections.end(), connection ));
if(it != createdConnections.end() ) createdConnections.erase( it );
if(IsLocalGame()) ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYER_1POFFLINE,false);
else ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYER_1P,false);
}
}
app.SetGameMode( eMode_Multiplayer );
}
else if ( connection->isClosed() || !IsInSession())
{
// assert(false);
MinecraftServer::HaltServer();
return false;
// ALL OTHER LOCAL PLAYERS
for(int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
{
// Already have setup the primary pad
if(idx == ProfileManager.GetPrimaryPad() ) continue;
if( GetLocalPlayerByUserIndex(idx) != NULL && !ProfileManager.IsSignedIn(idx) )
{
INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(idx);
Socket *socket = pNetworkPlayer->GetSocket();
app.DebugPrintf("Closing socket due to player %d not being signed in any more\n");
if( !socket->close(false) ) socket->close(true);
continue;
}
// By default when we host we only have the local player, but currently allow multiple local players to join
// when joining any other way, so just because they are signed in doesn't mean they are in the session
// 4J Stu - If they are in the session, then we should add them to the game. Otherwise we won't be able to add them later
INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(idx);
if( pNetworkPlayer == NULL )
continue;
ClientConnection *connection;
Socket *socket = pNetworkPlayer->GetSocket();
connection = new ClientConnection(minecraft, socket, idx);
minecraft->addPendingLocalConnection(idx, connection);
//minecraft->createExtraLocalPlayer(idx, (convStringToWstring( ProfileManager.GetGamertag(idx) )).c_str(), idx, connection);
// Open the socket on the server end to accept incoming data
Socket::addIncomingSocket(socket);
connection->send( shared_ptr<PreLoginPacket>( new PreLoginPacket(convStringToWstring( ProfileManager.GetGamertag(idx) )) ) );
createdConnections.push_back( connection );
// Tick connection until we're ready to go. The stages involved in this are:
// (1) Creating the ClientConnection sends a prelogin packet to the server
// (2) the server sends a prelogin back, which is handled by the clientConnection, and returns a login packet
// (3) the server sends a login back, which is handled by the client connection to start the game
do
{
// We need to keep ticking the connections for players that already logged in
for(AUTO_VAR(it, createdConnections.begin()); it < createdConnections.end(); ++it)
{
(*it)->tick();
}
// 4J Stu - We were ticking this way too fast which could cause the connection to time out
// The connections should tick at 20 per second
Sleep(50);
app.DebugPrintf("<***> %d %d %d %d %d\n",IsInSession(), !connection->isStarted(),!connection->isClosed(),ProfileManager.IsSignedIn(idx),!g_NetworkManager.IsLeavingGame());
#if defined _XBOX || defined __PS3__ && !defined DISABLE_PSN
} while (IsInSession() && !connection->isStarted() && !connection->isClosed() && ProfileManager.IsSignedIn(idx) && !g_NetworkManager.IsLeavingGame() );
#else
// TODO - This SHOULD be something just like the code above but temporarily changing here so that we don't have to depend on the profilemanager behaviour
} while (IsInSession() && !connection->isStarted() && !connection->isClosed() && !g_NetworkManager.IsLeavingGame() );
#endif
// 4J Stu - Fix for #11279 - CRASH: TCR 001: BAS Game Stability: Signing out of game will cause title to crash
// We need to break out of the above loop if m_bLeavingGame is set, and stop creating new connections
// The connections in the createdConnections vector get closed at the end of the thread
if( g_NetworkManager.IsLeavingGame() || !IsInSession() ) break;
if( ProfileManager.IsSignedIn(idx) && !connection->isClosed() )
{
app.SetRichPresenceContext(idx,CONTEXT_GAME_STATE_BLANK);
if (IsLocalGame()) ProfileManager.SetCurrentGameActivity(idx,CONTEXT_PRESENCE_MULTIPLAYEROFFLINE,false);
else ProfileManager.SetCurrentGameActivity(idx,CONTEXT_PRESENCE_MULTIPLAYER,false);
}
else
{
connection->close();
AUTO_VAR(it, find( createdConnections.begin(), createdConnections.end(), connection ));
if(it != createdConnections.end() ) createdConnections.erase( it );
}
}
app.SetGameMode( eMode_Multiplayer );
}
else if ( connection->isClosed() || !IsInSession())
{
// assert(false);
MinecraftServer::HaltServer();
return false;
}
}
@@ -547,7 +563,7 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
{
(*it)->close();
}
// assert(false);
//assert(false);
MinecraftServer::HaltServer();
return false;
}
@@ -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;
@@ -34,6 +34,7 @@
#define NETWORK_LAN_DEFAULT_PORT 25565
#define NETWORK_LAN_MAX_CLIENTS 7
#define NETWORK_LAN_SMALLID_REJECT 0xFF
#define NETWORK_LAN_RECV_BUFFER_SIZE 65536
#define NETWORK_LAN_MAX_PACKET_SIZE (3 * 1024 * 1024)
#define NETWORK_LAN_DISCOVERY_PORT 25566
@@ -172,8 +173,6 @@ 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);
@@ -230,9 +229,6 @@ private:
static CRITICAL_SECTION s_disconnectLock;
static std::vector<BYTE> s_disconnectedSmallIds;
static CRITICAL_SECTION s_pendingJoinLock;
static std::vector<BYTE> s_pendingJoinSmallIds;
static CRITICAL_SECTION s_freeSmallIdLock;
static std::vector<BYTE> s_freeSmallIds;
};
@@ -241,3 +237,9 @@ extern bool g_MultiplayerHost;
extern bool g_MultiplayerJoin;
extern int g_MultiplayerPort;
extern char g_MultiplayerIP[256];
#ifdef MINECRAFT_SERVER_BUILD
extern int g_DedicatedServerPort;
extern char g_DedicatedServerBindIP[256];
extern bool g_DedicatedServerLanAdvertise;
#endif
@@ -171,6 +171,7 @@ bool CPlatformNetworkManagerStub::Initialise(CGameNetworkManager *pGameNetworkMa
m_bLeavingGame = false;
m_bLeaveGameOnTick = false;
m_bLeaveGameOnTick = false;
m_bHostChanged = false;
m_bSearchResultsReady = false;
@@ -233,53 +234,56 @@ 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)
IQNetPlayer* qnetPlayer = m_pIQNet->GetPlayerBySmallId(disconnectedSmallId);
if (qnetPlayer != nullptr && qnetPlayer->m_smallId == disconnectedSmallId)
{
NotifyPlayerLeaving(qnetPlayer);
qnetPlayer->m_smallId = 0;
qnetPlayer->m_isRemote = false;
qnetPlayer->m_isHostPlayer = false;
//qnetPlayer->m_resolvedXuid = INVALID_XUID; // TODO: implement
qnetPlayer->m_gamertag[0] = 0;
qnetPlayer->SetCustomDataValue(0);
NetworkSocketLayer::PushFreeSmallId(disconnectedSmallId);
if (IQNet::s_playerCount > 1)
// Recalculate s_playerCount as the highest active slot + 1.
// A blind decrement would hide players at higher-indexed slots when a
// lower-indexed player disconnects first: GetPlayerBySmallId scans
// [0, s_playerCount) so any slot at or above the decremented count
// becomes invisible, causing its disconnect to be missed (ghost player).
while (IQNet::s_playerCount > 1 &&
IQNet::m_player[IQNet::s_playerCount - 1].GetCustomDataValue() == 0)
IQNet::s_playerCount--;
}
}
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];
if (qp->GetCustomDataValue() == 0)
continue;
INetworkPlayer *np = (INetworkPlayer *)qp->GetCustomDataValue();
Socket *sock = np->GetSocket();
if (sock != NULL && sock->isClosing())
{
NetworkSocketLayer::CloseConnectionBySmallId((BYTE)i);
}
// NOTE: Do NOT call PushFreeSmallId here. The old PlayerConnection's
// write thread may still be alive (it dies in PlayerList::tick when
// m_smallIdsToClose is processed). If we recycle the smallId now,
// AcceptThread can reuse it for a new connection, and the old write
// thread's getPlayer() lookup will resolve to the NEW player, sending
// stale game packets to the new client's TCP socket — corrupting its
// login handshake (bad packet id crash). PushFreeSmallId and
// ClearSocketForSmallId are called from PlayerList::tick after the
// old Connection threads are dead.
//
// Clear chunk visibility flags for this system so rejoin gets fresh chunk state.
SystemFlagRemoveBySmallId(disconnectedSmallId);
}
}
if (_iQNetStubState == QNET_STATE_GAME_PLAY && !m_pIQNet->IsHost())
// Client-side host disconnect detection:
// if TCP is gone, propagate through normal network-disconnect flow so UI returns to menus.
// The processing from the Xbox version will be reused.
if (_iQNetStubState == QNET_STATE_GAME_PLAY && !m_pIQNet->IsHost() && !m_bLeavingGame)
{
if (!NetworkSocketLayer::IsConnected() && !g_NetworkManager.IsLeavingGame())
if (!NetworkSocketLayer::IsConnected())
{
if (app.GetDisconnectReason() == DisconnectPacket::eDisconnect_None)
app.SetDisconnectReason(DisconnectPacket::eDisconnect_Quitting);
app.SetAction(ProfileManager.GetPrimaryPad(), eAppAction_ExitWorld, (void *)TRUE);
if (!m_bLeaveGameOnTick)
{
m_bLeaveGameOnTick = true;
g_NetworkManager.HandleDisconnect(false);
}
}
else
{
m_bLeaveGameOnTick = false;
}
}
}
@@ -354,6 +358,7 @@ bool CPlatformNetworkManagerStub::LeaveGame(bool bMigrateHost)
if( m_bLeavingGame ) return true;
m_bLeavingGame = true;
m_bLeaveGameOnTick = false;
NetworkSocketLayer::StopAdvertising();
// If we are the host wait for the game server to end
@@ -397,6 +402,7 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame,
localUsersMask |= GetLocalPlayerMask( g_NetworkManager.GetPrimaryPad() );
m_bLeavingGame = false;
m_bLeaveGameOnTick = false;
m_pIQNet->HostGame();
@@ -418,9 +424,25 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame,
if (!NetworkSocketLayer::IsActive())
NetworkSocketLayer::HostGame(port);
const wchar_t *hostName = IQNet::m_player[0].m_gamertag;
unsigned int settings = app.GetGameHostOption(eGameHostOption_All);
NetworkSocketLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION);
if (NetworkSocketLayer::IsActive())
{
// For Dedicated Server, refer to `lan-advertise` in `server.properties`
bool enableLanAdvertising = true;
#ifdef MINECRAFT_SERVER_BUILD
enableLanAdvertising = g_DedicatedServerLanAdvertise;
#endif
if (enableLanAdvertising)
{
const wchar_t* hostName = IQNet::m_player[0].m_gamertag;
unsigned int settings = app.GetGameHostOption(eGameHostOption_All);
NetworkSocketLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION);
}
else
{
NetworkSocketLayer::StopAdvertising();
}
}
//#endif
}
@@ -626,6 +648,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()
{
@@ -663,6 +686,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++ )
@@ -99,12 +99,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);
@@ -0,0 +1,82 @@
param(
[string]$OutDir,
[string]$ProjectDir
)
Write-Host "Server Postbuild: script started. Output Directory: $OutDir, Project Directory: $ProjectDir"
$directories = @(
"Windows64\GameHDD",
"Common\Media",
"Common\res",
"redist64"
)
foreach ($dir in $directories) {
New-Item -ItemType Directory -Path (Join-Path $OutDir $dir) -Force | Out-Null
}
$folderCopies = @(
@{ Source = "Common\Media"; Dest = "Common\Media" },
@{ Source = "Common\res"; Dest = "Common\res" },
@{ Source = "Common\Postbuilds\Contents\Windows64\redist64"; Dest = "redist64" }
)
foreach ($copy in $folderCopies) {
$src = Join-Path "$ProjectDir\..\Minecraft.Client" $copy.Source
$dst = Join-Path $OutDir $copy.Dest
if (Test-Path $src) {
xcopy /q /y /i /s /e /d "$src" "$dst" 2>$null
}
}
$fileCopies = @(
@{ Source = "Common\Postbuilds\Contents\Windows64\Effects.msscmp"; Dest = "Effects.msscmp" },
@{ Source = "Common\Postbuilds\Contents\Windows64\iggy_w64.dll"; Dest = "iggy_w64.dll" },
@{ Source = "Common\Postbuilds\Contents\Windows64\mss64.dll"; Dest = "mss64.dll" }
)
foreach ($copy in $fileCopies) {
$src = Join-Path "$ProjectDir\..\Minecraft.Client" $copy.Source
$dst = Join-Path $OutDir $copy.Dest
if (Test-Path $src) {
Copy-Item -Path $src -Destination $dst -Force
}
}
$deleteDirs = @(
"Common\Media\Sound",
"Common\Media\Graphics",
"Common\Media\de-DE",
"Common\Media\es-ES",
"Common\Media\fr-FR",
"Common\Media\it-IT",
"Common\Media\ja-JP",
"Common\Media\ko-KR",
"Common\Media\pt-BR",
"Common\Media\pt-PT",
"Common\Media\zh-CHT"
)
foreach ($dir in $deleteDirs) {
$path = Join-Path $OutDir $dir
if (Test-Path $path) {
Remove-Item $path -Recurse -Force -ErrorAction SilentlyContinue
}
}
$delDirs = @(
"Common\Media",
"Common\Media\font"
)
foreach ($dir in $delDirs) {
if ($delDirs -contains $dir) {
$path = Join-Path $OutDir $dir
if (Test-Path $path) {
Get-ChildItem -Path $path -Recurse -Include *.swf, *.txt, *.resx, *.xml, *.loc, *.lang, *.col, *.h, *.xui, *.abc, "Mojang Font_7.ttf", "Mojang Font_11.ttf", MediaDurango.arc, MediaOrbis.arc, MediaPS3.arc, MediaPSVita.arc -File |
Remove-Item -Force -ErrorAction SilentlyContinue
}
}
}
File diff suppressed because it is too large Load Diff
+475 -1
View File
@@ -27,9 +27,19 @@
#include "..\Minecraft.World\Pos.h"
#include "..\Minecraft.World\System.h"
#include "..\Minecraft.World\StringHelpers.h"
#include "../Minecraft.World/net.minecraft.world.entity.item.h"
#include "../Minecraft.World/net.minecraft.world.item.h"
#include "../Minecraft.World/net.minecraft.world.item.enchantment.h"
#include "../Minecraft.World/net.minecraft.world.damagesource.h"
#ifdef SPLIT_SAVES
#include "..\Minecraft.World\ConsoleSaveFileSplit.h"
#endif
#if defined(_WINDOWS64) || defined(DISABLE_PSN) || defined(_DISABLE_XBLIVE)
#include "Common/Network/NetworkSocketLayer.h"
#endif
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
#include "../Minecraft.Server/ServerLogger.h"
#endif
#include "..\Minecraft.World\ConsoleSaveFileOriginal.h"
#include "..\Minecraft.World\Socket.h"
#include "..\Minecraft.World\net.minecraft.world.entity.h"
@@ -78,6 +88,462 @@ bool MinecraftServer::s_slowQueuePacketSent = false;
unordered_map<wstring, int> MinecraftServer::ironTimers;
static bool ShouldUseDedicatedServerProperties()
{
#if defined(MINECRAFT_SERVER_BUILD)
return true;
#else
return false;
#endif
}
static int GetDedicatedServerInt(Settings *settings, const wchar_t *key, int defaultValue)
{
return (ShouldUseDedicatedServerProperties() && settings != nullptr) ? settings->getInt(key, defaultValue) : defaultValue;
}
static bool GetDedicatedServerBool(Settings *settings, const wchar_t *key, bool defaultValue)
{
return (ShouldUseDedicatedServerProperties() && settings != nullptr) ? settings->getBoolean(key, defaultValue) : defaultValue;
}
static wstring GetDedicatedServerString(Settings *settings, const wchar_t *key, const wstring &defaultValue)
{
return (ShouldUseDedicatedServerProperties() && settings != nullptr) ? settings->getString(key, defaultValue) : defaultValue;
}
static void PrintConsoleLine(const wchar_t *prefix, const wstring &message)
{
wprintf(L"%ls%ls\n", prefix, message.c_str());
fflush(stdout);
}
static bool TryParseIntValue(const wstring &text, int &value)
{
std::wistringstream stream(text);
stream >> value;
return !stream.fail() && stream.eof();
}
static vector<wstring> SplitConsoleCommand(const wstring &command)
{
vector<wstring> tokens;
std::wistringstream stream(command);
wstring token;
while (stream >> token)
{
tokens.push_back(token);
}
return tokens;
}
static wstring JoinConsoleCommandTokens(const vector<wstring> &tokens, size_t startIndex)
{
wstring joined;
for (size_t i = startIndex; i < tokens.size(); ++i)
{
if (!joined.empty()) joined += L" ";
joined += tokens[i];
}
return joined;
}
static shared_ptr<ServerPlayer> FindPlayerByName(PlayerList *playerList, const wstring &name)
{
if (playerList == nullptr) return nullptr;
for (size_t i = 0; i < playerList->players.size(); ++i)
{
shared_ptr<ServerPlayer> player = playerList->players[i];
if (player != nullptr && equalsIgnoreCase(player->getName(), name))
{
return player;
}
}
return nullptr;
}
static void SetAllLevelTimes(MinecraftServer *server, int value)
{
for (unsigned int i = 0; i < server->levels.length; ++i)
{
if (server->levels[i] != nullptr)
{
server->levels[i]->setDayTime(value);
}
}
}
static bool ExecuteConsoleCommand(MinecraftServer *server, const wstring &rawCommand)
{
if (server == nullptr)
return false;
wstring command = trimString(rawCommand);
if (command.empty())
return true;
if (command[0] == L'/')
{
command = trimString(command.substr(1));
}
vector<wstring> tokens = SplitConsoleCommand(command);
if (tokens.empty())
return true;
const wstring action = toLower(tokens[0]);
PlayerList *playerList = server->getPlayers();
if (action == L"help" || action == L"?")
{
server->info(L"Commands: help, stop, list, say <message>, save-all, time <set day|night|ticks|add ticks>, weather <clear|rain|thunder> [seconds], tp <player> <target>, give <player> <itemId> [amount] [aux], enchant <player> <enchantId> [level], kill <player>");
return true;
}
if (action == L"stop")
{
server->info(L"Stopping server...");
MinecraftServer::HaltServer();
return true;
}
if (action == L"list")
{
wstring playerNames = (playerList != nullptr) ? playerList->getPlayerNames() : L"";
if (playerNames.empty()) playerNames = L"(none)";
server->info(L"Players (" + std::to_wstring((playerList != nullptr) ? playerList->getPlayerCount() : 0) + L"): " + playerNames);
return true;
}
if (action == L"say")
{
if (tokens.size() < 2)
{
server->warn(L"Usage: say <message>");
return false;
}
wstring message = L"[Server] " + JoinConsoleCommandTokens(tokens, 1);
if (playerList != nullptr)
{
playerList->broadcastAll(std::make_shared<ChatPacket>(message));
}
server->info(message);
return true;
}
if (action == L"save-all")
{
if (playerList != nullptr)
{
playerList->saveAll(nullptr, false);
}
server->info(L"World saved.");
return true;
}
if (action == L"time")
{
if (tokens.size() < 2)
{
server->warn(L"Usage: time set <day|night|ticks> | time add <ticks>");
return false;
}
if (toLower(tokens[1]) == L"add")
{
if (tokens.size() < 3)
{
server->warn(L"Usage: time add <ticks>");
return false;
}
int delta = 0;
if (!TryParseIntValue(tokens[2], delta))
{
server->warn(L"Invalid tick value: " + tokens[2]);
return false;
}
for (unsigned int i = 0; i < server->levels.length; ++i)
{
if (server->levels[i] != nullptr)
{
server->levels[i]->setDayTime(server->levels[i]->getDayTime() + delta);
}
}
server->info(L"Added " + std::to_wstring(delta) + L" ticks.");
return true;
}
wstring timeValue = toLower(tokens[1]);
if (timeValue == L"set")
{
if (tokens.size() < 3)
{
server->warn(L"Usage: time set <day|night|ticks>");
return false;
}
timeValue = toLower(tokens[2]);
}
int targetTime = 0;
if (timeValue == L"day")
{
targetTime = 0;
}
else if (timeValue == L"night")
{
targetTime = 12500;
}
else if (!TryParseIntValue(timeValue, targetTime))
{
server->warn(L"Invalid time value: " + timeValue);
return false;
}
SetAllLevelTimes(server, targetTime);
server->info(L"Time set to " + std::to_wstring(targetTime) + L".");
return true;
}
if (action == L"weather")
{
if (tokens.size() < 2)
{
server->warn(L"Usage: weather <clear|rain|thunder> [seconds]");
return false;
}
int durationSeconds = 600;
if (tokens.size() >= 3 && !TryParseIntValue(tokens[2], durationSeconds))
{
server->warn(L"Invalid duration: " + tokens[2]);
return false;
}
if (server->levels[0] == nullptr)
{
server->warn(L"The overworld is not loaded.");
return false;
}
LevelData *levelData = server->levels[0]->getLevelData();
int duration = durationSeconds * SharedConstants::TICKS_PER_SECOND;
levelData->setRainTime(duration);
levelData->setThunderTime(duration);
wstring weather = toLower(tokens[1]);
if (weather == L"clear")
{
levelData->setRaining(false);
levelData->setThundering(false);
}
else if (weather == L"rain")
{
levelData->setRaining(true);
levelData->setThundering(false);
}
else if (weather == L"thunder")
{
levelData->setRaining(true);
levelData->setThundering(true);
}
else
{
server->warn(L"Usage: weather <clear|rain|thunder> [seconds]");
return false;
}
server->info(L"Weather set to " + weather + L".");
return true;
}
if (action == L"tp" || action == L"teleport")
{
if (tokens.size() < 3)
{
server->warn(L"Usage: tp <player> <target>");
return false;
}
shared_ptr<ServerPlayer> subject = FindPlayerByName(playerList, tokens[1]);
shared_ptr<ServerPlayer> destination = FindPlayerByName(playerList, tokens[2]);
if (subject == nullptr)
{
server->warn(L"Unknown player: " + tokens[1]);
return false;
}
if (destination == nullptr)
{
server->warn(L"Unknown player: " + tokens[2]);
return false;
}
if (subject->level->dimension->id != destination->level->dimension->id || !subject->isAlive())
{
server->warn(L"Teleport failed because the players are not in the same dimension or the source player is dead.");
return false;
}
subject->ride(nullptr);
subject->connection->teleport(destination->x, destination->y, destination->z, destination->yRot, destination->xRot);
server->info(L"Teleported " + subject->getName() + L" to " + destination->getName() + L".");
return true;
}
if (action == L"give")
{
if (tokens.size() < 3)
{
server->warn(L"Usage: give <player> <itemId> [amount] [aux]");
return false;
}
shared_ptr<ServerPlayer> player = FindPlayerByName(playerList, tokens[1]);
if (player == nullptr)
{
server->warn(L"Unknown player: " + tokens[1]);
return false;
}
int itemId = 0;
int amount = 1;
int aux = 0;
if (!TryParseIntValue(tokens[2], itemId))
{
server->warn(L"Invalid item id: " + tokens[2]);
return false;
}
if (tokens.size() >= 4 && !TryParseIntValue(tokens[3], amount))
{
server->warn(L"Invalid amount: " + tokens[3]);
return false;
}
if (tokens.size() >= 5 && !TryParseIntValue(tokens[4], aux))
{
server->warn(L"Invalid aux value: " + tokens[4]);
return false;
}
if (itemId <= 0 || Item::items[itemId] == nullptr)
{
server->warn(L"Unknown item id: " + std::to_wstring(itemId));
return false;
}
if (amount <= 0)
{
server->warn(L"Amount must be positive.");
return false;
}
shared_ptr<ItemInstance> itemInstance(new ItemInstance(itemId, amount, aux));
shared_ptr<ItemEntity> drop = player->drop(itemInstance);
if (drop != nullptr)
{
drop->throwTime = 0;
}
server->info(L"Gave item " + std::to_wstring(itemId) + L" x" + std::to_wstring(amount) + L" to " + player->getName() + L".");
return true;
}
if (action == L"enchant")
{
if (tokens.size() < 3)
{
server->warn(L"Usage: enchant <player> <enchantId> [level]");
return false;
}
shared_ptr<ServerPlayer> player = FindPlayerByName(playerList, tokens[1]);
if (player == nullptr)
{
server->warn(L"Unknown player: " + tokens[1]);
return false;
}
int enchantmentId = 0;
int enchantmentLevel = 1;
if (!TryParseIntValue(tokens[2], enchantmentId))
{
server->warn(L"Invalid enchantment id: " + tokens[2]);
return false;
}
if (tokens.size() >= 4 && !TryParseIntValue(tokens[3], enchantmentLevel))
{
server->warn(L"Invalid enchantment level: " + tokens[3]);
return false;
}
shared_ptr<ItemInstance> selectedItem = player->getSelectedItem();
if (selectedItem == nullptr)
{
server->warn(L"The player is not holding an item.");
return false;
}
Enchantment *enchantment = Enchantment::enchantments[enchantmentId];
if (enchantment == nullptr)
{
server->warn(L"Unknown enchantment id: " + std::to_wstring(enchantmentId));
return false;
}
if (!enchantment->canEnchant(selectedItem))
{
server->warn(L"That enchantment cannot be applied to the selected item.");
return false;
}
if (enchantmentLevel < enchantment->getMinLevel()) enchantmentLevel = enchantment->getMinLevel();
if (enchantmentLevel > enchantment->getMaxLevel()) enchantmentLevel = enchantment->getMaxLevel();
if (selectedItem->hasTag())
{
ListTag<CompoundTag> *enchantmentTags = selectedItem->getEnchantmentTags();
if (enchantmentTags != nullptr)
{
for (int i = 0; i < enchantmentTags->size(); i++)
{
int type = enchantmentTags->get(i)->getShort((wchar_t *)ItemInstance::TAG_ENCH_ID);
if (Enchantment::enchantments[type] != nullptr && !Enchantment::enchantments[type]->isCompatibleWith(enchantment))
{
server->warn(L"That enchantment conflicts with an existing enchantment on the selected item.");
return false;
}
}
}
}
selectedItem->enchant(enchantment, enchantmentLevel);
server->info(L"Enchanted " + player->getName() + L"'s held item with " + std::to_wstring(enchantmentId) + L" " + std::to_wstring(enchantmentLevel) + L".");
return true;
}
if (action == L"kill")
{
if (tokens.size() < 2)
{
server->warn(L"Usage: kill <player>");
return false;
}
shared_ptr<ServerPlayer> player = FindPlayerByName(playerList, tokens[1]);
if (player == nullptr)
{
server->warn(L"Unknown player: " + tokens[1]);
return false;
}
player->hurt(DamageSource::outOfWorld, 3.4e38f);
server->info(L"Killed " + player->getName() + L".");
return true;
}
server->warn(L"Unknown command: " + command);
return false;
}
MinecraftServer::MinecraftServer()
{
// 4J - added initialisers
@@ -105,6 +571,7 @@ MinecraftServer::MinecraftServer()
playerIdleTimeout = 0;
m_postUpdateThread = NULL;
forceGameType = false;
m_spawnProtectionRadius = 0;
commandDispatcher = new ServerCommandDispatcher();
@@ -149,6 +616,10 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW
logger.info("Loading properties");
#endif
settings = new Settings(new File(L"server.properties"));
// Dedicated-only: spawn-protection radius in blocks; 0 disables protection.
m_spawnProtectionRadius = GetDedicatedServerInt(settings, L"spawn-protection", 0);
if (m_spawnProtectionRadius < 0) m_spawnProtectionRadius = 0;
if (m_spawnProtectionRadius > 256) m_spawnProtectionRadius = 256;
app.DebugPrintf("\n*** SERVER SETTINGS ***\n");
app.DebugPrintf("ServerSettings: host-friends-only is %s\n",(app.GetGameHostOption(eGameHostOption_FriendsOfFriends)>0)?"on":"off");
@@ -156,6 +627,7 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW
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("ServerSettings: spawn protection radius is %d\n", m_spawnProtectionRadius);
app.DebugPrintf("\n");
// TODO 4J Stu - Init a load of settings based on data passed as params
@@ -1172,7 +1644,9 @@ Level *MinecraftServer::getCommandSenderWorld()
int MinecraftServer::getSpawnProtectionRadius()
{
return 16;
// Client-host mode must never apply dedicated-server spawn protection settings.
if (!ShouldUseDedicatedServerProperties()) return 0;
return m_spawnProtectionRadius;
}
bool MinecraftServer::isUnderSpawnProtection(Level *level, int x, int y, int z, shared_ptr<Player> player)
+3
View File
@@ -40,6 +40,7 @@ typedef struct _NetworkGameInitData
LevelGenerationOptions *levelGen;
DWORD texturePackId;
bool findSeed;
bool dedicatedNoLocalHostPlayer;
unsigned int xzSize;
unsigned char hellScale;
ESavePlatform savePlatform;
@@ -52,6 +53,7 @@ typedef struct _NetworkGameInitData
levelGen = NULL;
texturePackId = 0;
findSeed = false;
dedicatedNoLocalHostPlayer = false;
xzSize = LEVEL_LEGACY_WIDTH;
hellScale = HELL_LEVEL_LEGACY_SCALE;
savePlatform = SAVE_FILE_PLATFORM_LOCAL;
@@ -113,6 +115,7 @@ public:
int maxBuildHeight;
int playerIdleTimeout;
bool forceGameType;
int m_spawnProtectionRadius;
private:
// 4J Added
+143 -1
View File
@@ -14,12 +14,37 @@
#include "..\Minecraft.World\net.minecraft.world.item.h"
#include "..\Minecraft.World\SharedConstants.h"
#include "Settings.h"
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
#include "../Minecraft.Server/ServerLogManager.h"
#include "../Minecraft.Server/Access/Access.h"
#include "../Minecraft.World/Socket.h"
#include <Common/Network/NetworkSocketLayer.h>
#endif
// #ifdef __PS3__
// #include "PS3\Network\NetworkPlayerSony.h"
// #endif
Random *PendingConnection::random = new Random();
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
namespace
{
static unsigned char GetPendingConnectionSmallId(Connection *connection)
{
if (connection != nullptr)
{
Socket *socket = connection->getSocket();
if (socket != nullptr)
{
return socket->getSmallId();
}
}
return 0;
}
}
#endif
PendingConnection::PendingConnection(MinecraftServer *server, Socket *socket, const wstring& id)
{
// 4J - added initialisers
@@ -159,12 +184,123 @@ void PendingConnection::handleLogin(shared_ptr<LoginPacket> packet)
bool sentDisconnect = false;
bool nameTaken = false;
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
{
std::string connectionIp = "";
int connectionPort = 0;
if (!connection || !connection->getSocket()) {
disconnect(DisconnectPacket::eDisconnect_EndOfStream);
return;
}
unsigned char smallId = connection->getSocket()->getSmallId();
if (smallId == 0) {
disconnect(DisconnectPacket::eDisconnect_EndOfStream);
return;
}
if (!ServerRuntime::ServerLogManager::TryGetConnectionRemoteIp(smallId, &connectionIp))
{
SOCKET sock = NetworkSocketLayer::GetSocketForSmallId(smallId);
if (sock != INVALID_SOCKET)
{
sockaddr_in addr;
int addrLen = sizeof(addr);
if (getpeername(sock, (sockaddr*)&addr, &addrLen) == 0)
{
char ipBuf[64] = {};
if (inet_ntop(AF_INET, &addr.sin_addr, ipBuf, sizeof(ipBuf)))
{
connectionIp = ipBuf;
connectionPort = (int)ntohs(addr.sin_port);
}
}
}
if (connectionIp.empty()) {
disconnect(DisconnectPacket::eDisconnect_EndOfStream);
return;
}
}
else {
SOCKET sock = NetworkSocketLayer::GetSocketForSmallId(smallId);
if (sock != INVALID_SOCKET)
{
sockaddr_in addr;
int addrLen = sizeof(addr);
if (getpeername(sock, (sockaddr*)&addr, &addrLen) == 0)
connectionPort = (int)ntohs(addr.sin_port);
}
}
}
#endif
// Use the same Xuid choice as handleAcceptedLogin (offline first, online fallback).
PlayerUID loginXuid = packet->m_offlineXuid;
if (loginXuid == INVALID_XUID) loginXuid = packet->m_onlineXuid;
bool duplicateXuid = false;
if (loginXuid != INVALID_XUID && server->getPlayers()->getPlayer(loginXuid) != nullptr)
{
duplicateXuid = true;
}
else if (packet->m_onlineXuid != INVALID_XUID &&
packet->m_onlineXuid != loginXuid &&
server->getPlayers()->getPlayer(packet->m_onlineXuid) != nullptr)
{
duplicateXuid = true;
}
bool bannedXuid = false;
if (packet->m_offlineXuid != INVALID_XUID)
{
bannedXuid = server->getPlayers()->isXuidBanned(packet->m_offlineXuid);
}
if (!bannedXuid && packet->m_onlineXuid != INVALID_XUID && packet->m_onlineXuid != packet->m_offlineXuid)
{
bannedXuid = server->getPlayers()->isXuidBanned(packet->m_onlineXuid);
}
bool whitelistSatisfied = true;
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (ServerRuntime::Access::IsWhitelistEnabled())
{
whitelistSatisfied = false;
if (loginXuid != INVALID_XUID)
{
whitelistSatisfied = ServerRuntime::Access::IsPlayerWhitelisted(loginXuid);
}
if (!whitelistSatisfied && packet->m_onlineXuid != INVALID_XUID && packet->m_onlineXuid != loginXuid)
{
whitelistSatisfied = ServerRuntime::Access::IsPlayerWhitelisted(packet->m_onlineXuid);
}
}
#endif
if( sentDisconnect )
{
// Do nothing
}
else if( server->getPlayers()->isXuidBanned( packet->m_onlineXuid ) )
else if (bannedXuid)
{
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_BannedXuid);
#endif
disconnect(DisconnectPacket::eDisconnect_Banned);
}
else if (!whitelistSatisfied)
{
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_NotWhitelisted);
#endif
disconnect(DisconnectPacket::eDisconnect_Banned);
}
else if (duplicateXuid)
{
// Reject the incoming connection — a player with this UID is already
// on the server. Allowing duplicates causes invisible players and
// other undefined behaviour.
app.DebugPrintf("LOGIN: Rejecting duplicate xuid for name: %ls\n", name.c_str());
disconnect(DisconnectPacket::eDisconnect_Banned);
}
else
@@ -182,6 +318,9 @@ void PendingConnection::handleLogin(shared_ptr<LoginPacket> packet)
if (nameTaken)
{
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_DuplicateName);
#endif
app.DebugPrintf("Rejecting duplicate name: %ls\n", name.c_str());
// to do: replace this with a dedicated duplicate name
@@ -237,6 +376,9 @@ void PendingConnection::handleAcceptedLogin(shared_ptr<LoginPacket> packet)
shared_ptr<ServerPlayer> playerEntity = server->getPlayers()->getPlayerForLogin(this, name, playerXuid,packet->m_onlineXuid);
if (playerEntity != NULL)
{
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
ServerRuntime::ServerLogManager::OnAcceptedPlayerLogin(GetPendingConnectionSmallId(connection), name);
#endif
server->getPlayers()->placeNewPlayer(connection, playerEntity, packet);
connection = NULL; // We've moved responsibility for this over to the new PlayerConnection, NULL so we don't delete our reference to it here in our dtor
}
+81 -13
View File
@@ -32,6 +32,22 @@
// 4J Added
#include "..\Minecraft.World\net.minecraft.world.item.crafting.h"
#include "Options.h"
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
#include "..\Minecraft.Server\ServerLogManager.h"
#endif
namespace
{
// Anti-cheat thresholds. Keep server-side checks authoritative even in host mode.
// Base max squared displacement allowed per move packet before speed flags trigger.
const double kMoveBaseAllowanceSq = 100.0;
// Extra squared displacement allowance derived from current server-side velocity.
const double kMoveVelocityAllowanceScale = 100.0;
// Max squared distance for interact/attack when the target is visible (normal reach).
const double kInteractReachSq = 6.0 * 6.0;
// Stricter max squared distance used when LOS is blocked to reduce wall-hit abuse.
const double kInteractBlockedReachSq = 3.0 * 3.0;
}
Random PlayerConnection::random;
@@ -64,6 +80,13 @@ PlayerConnection::PlayerConnection(MinecraftServer *server, Connection *connecti
m_offlineXUID = INVALID_XUID;
m_onlineXUID = INVALID_XUID;
m_bHasClientTickedOnce = false;
m_logSmallId = 0;
// Cache the first valid transport smallId because disconnect teardown can clear it before the server logger runs.
if (this->connection != NULL && this->connection->getSocket() != NULL)
{
m_logSmallId = this->connection->getSocket()->getSmallId();
}
setShowOnMaps(app.GetGameHostOption(eGameHostOption_Gamertags)!=0?true:false);
}
@@ -74,6 +97,17 @@ PlayerConnection::~PlayerConnection()
DeleteCriticalSection(&done_cs);
}
unsigned char PlayerConnection::getLogSmallId()
{
// Fall back to the live socket only while the cached value is still empty.
if (m_logSmallId == 0 && connection != NULL && connection->getSocket() != NULL)
{
m_logSmallId = connection->getSocket()->getSmallId();
}
return m_logSmallId;
}
void PlayerConnection::tick()
{
if( done ) return;
@@ -116,6 +150,14 @@ void PlayerConnection::disconnect(DisconnectPacket::eDisconnectReason reason)
return;
}
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
ServerRuntime::ServerLogManager::OnPlayerDisconnected(
getLogSmallId(),
(player != NULL) ? player->name : std::wstring(),
reason,
true);
#endif
app.DebugPrintf("PlayerConnection disconect reason: %d\n", reason );
player->disconnect();
@@ -280,6 +322,20 @@ void PlayerConnection::handleMovePlayer(shared_ptr<MovePlayerPacket> packet)
}
*/
// Anti-cheat: reject movement packets that exceed server-authoritative bounds.
double velocitySq = player->xd * player->xd + player->yd * player->yd + player->zd * player->zd;
double maxAllowedSq = kMoveBaseAllowanceSq + (velocitySq * kMoveVelocityAllowanceScale);
if (player->isAllowedToFly() || player->gameMode->isCreative())
{
// Creative / flight-allowed players can move farther legitimately per tick.
maxAllowedSq *= 1.5;
}
if (dist > maxAllowedSq)
{
disconnect(DisconnectPacket::eDisconnect_MovedTooQuickly);
return;
}
float r = 1 / 16.0f;
bool oldOk = level->getCubes(player, player->bb->copy()->shrink(r, r, r))->empty();
@@ -306,8 +362,8 @@ void PlayerConnection::handleMovePlayer(shared_ptr<MovePlayerPacket> packet)
xDist = xt - player->x;
yDist = yt - player->y;
// 4J-PB - line below will always be true!
if (yDist > -0.5 || yDist < 0.5)
// Clamp tiny Y drift noise to reduce false positives.
if (yDist > -0.5 && yDist < 0.5)
{
yDist = 0;
}
@@ -428,7 +484,8 @@ void PlayerConnection::handlePlayerAction(shared_ptr<PlayerActionPacket> packet)
if (packet->action == PlayerActionPacket::START_DESTROY_BLOCK)
{
if (true) player->gameMode->startDestroyBlock(x, y, z, packet->face); // 4J - condition was !server->isUnderSpawnProtection(level, x, y, z, player) (from Java 1.6.4) but putting back to old behaviour
// Anti-cheat: validate spawn protection on the server for mining start.
if (!server->isUnderSpawnProtection(level, x, y, z, player)) player->gameMode->startDestroyBlock(x, y, z, packet->face);
else player->connection->send( shared_ptr<TileUpdatePacket>( new TileUpdatePacket(x, y, z, level) ) );
}
@@ -456,8 +513,6 @@ void PlayerConnection::handleUseItem(shared_ptr<UseItemPacket> packet)
int face = packet->getFace();
player->resetLastActionTime();
// 4J Stu - We don't have ops, so just use the levels setting
bool canEditSpawn = level->canEditSpawn; // = level->dimension->id != 0 || server->players->isOp(player->name);
if (packet->getFace() == 255)
{
if (item == NULL) return;
@@ -467,7 +522,8 @@ void PlayerConnection::handleUseItem(shared_ptr<UseItemPacket> packet)
{
if (synched && player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) < 8 * 8)
{
if (true) // 4J - condition was !server->isUnderSpawnProtection(level, x, y, z, player) (from java 1.6.4) but putting back to old behaviour
// Anti-cheat: block placement/use must pass server-side spawn protection.
if (!server->isUnderSpawnProtection(level, x, y, z, player))
{
player->gameMode->useItemOn(player, level, item, x, y, z, face, packet->getClickX(), packet->getClickY(), packet->getClickZ());
}
@@ -536,7 +592,18 @@ void PlayerConnection::handleUseItem(shared_ptr<UseItemPacket> packet)
void PlayerConnection::onDisconnect(DisconnectPacket::eDisconnectReason reason, void *reasonObjects)
{
EnterCriticalSection(&done_cs);
if( done ) return;
if( done )
{
LeaveCriticalSection(&done_cs);
return;
}
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
ServerRuntime::ServerLogManager::OnPlayerDisconnected(
getLogSmallId(),
(player != NULL) ? player->name : std::wstring(),
reason,
false);
#endif
// logger.info(player.name + " lost connection: " + reason);
// 4J-PB - removed, since it needs to be localised in the language the client is in
//server->players->broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(L"§e" + player->name + L" left the game.") ) );
@@ -754,12 +821,13 @@ void PlayerConnection::handleInteract(shared_ptr<InteractPacket> packet)
// even though the ray is blocked.
if (target != NULL) // && player->canSee(target) && player->distanceToSqr(target) < 6 * 6)
{
//boole canSee = player->canSee(target);
//double maxDist = 6 * 6;
//if (!canSee)
//{
// maxDist = 3 * 3;
//}
// Anti-cheat: enforce reach and LOS on the server to reject forged hits.
bool canSeeTarget = player->canSee(target);
double maxDistSq = canSeeTarget ? kInteractReachSq : kInteractBlockedReachSq;
if (player->distanceToSqr(target) > maxDistSq)
{
return;
}
//if (player->distanceToSqr(target) < maxDist)
//{
+5
View File
@@ -37,6 +37,7 @@ private:
int dropSpamTickCount;
bool m_bHasClientTickedOnce;
unsigned char m_logSmallId;
public:
PlayerConnection(MinecraftServer *server, Connection *connection, shared_ptr<ServerPlayer> player);
@@ -45,6 +46,10 @@ public:
void disconnect(DisconnectPacket::eDisconnectReason reason);
private:
/**
* Returns the stable network smallId used by dedicated-server logging and refreshes it from the live socket when possible
*/
unsigned char getLogSmallId();
double xLastOk, yLastOk, zLastOk;
bool synched;
+11
View File
@@ -31,6 +31,10 @@
#include "Common\Network\Sony\NetworkPlayerSony.h"
#endif
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
#include "..\Minecraft.Server\Access\Access.h"
#endif
// 4J - this class is fairly substantially altered as there didn't seem any point in porting code for banning, whitelisting, ops etc.
PlayerList::PlayerList(MinecraftServer *server)
@@ -1626,6 +1630,13 @@ bool PlayerList::isXuidBanned(PlayerUID xuid)
}
}
#if defined(MINECRAFT_SERVER_BUILD)
if (!banned)
{
banned = ServerRuntime::Access::IsPlayerBanned(xuid);
}
#endif
return banned;
}
+14 -13
View File
@@ -183,24 +183,25 @@ void ServerPlayerGameMode::stopDestroyBlock(int x, int y, int z)
{
Tile *tile = Tile::tiles[t];
// MGH - removed checking for the destroy progress here, it has already been checked on the client before it sent the packet.
// fixes issues with this failing to destroy because of packets bunching up
// float destroyProgress = tile->getDestroyProgress(player, player->level, x, y, z) * (ticksSpentDestroying + 1);
// if (destroyProgress >= .7f || bIgnoreDestroyProgress)
// Anti-cheat: re-check destroy progress on the server for STOP_DESTROY.
int ticksSpentDestroying = gameTicks - destroyProgressStart;
float destroyProgress = tile->getDestroyProgress(player, player->level, x, y, z) * (ticksSpentDestroying + 1);
if (destroyProgress >= 1.0f)
{
isDestroyingBlock = false;
level->destroyTileProgress(player->entityId, x, y, z, -1);
destroyBlock(x, y, z);
}
// else if (!hasDelayedDestroy)
// {
// isDestroyingBlock = false;
// hasDelayedDestroy = true;
// delayedDestroyX = x;
// delayedDestroyY = y;
// delayedDestroyZ = z;
// delayedTickStart = destroyProgressStart;
// }
else if (!hasDelayedDestroy)
{
// Keep server-authoritative mining while allowing legit latency to finish via delayed tick progression.
isDestroyingBlock = false;
hasDelayedDestroy = true;
delayedDestroyX = x;
delayedDestroyY = y;
delayedDestroyZ = z;
delayedTickStart = destroyProgressStart;
}
}
}
}