aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.Client
diff options
context:
space:
mode:
Diffstat (limited to 'Minecraft.Client')
-rw-r--r--Minecraft.Client/Common/Console_Utils.cpp36
-rw-r--r--Minecraft.Client/Common/Consoles_App.cpp117
-rw-r--r--Minecraft.Client/Common/Network/GameNetworkManager.cpp53
-rw-r--r--Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp50
-rw-r--r--Minecraft.Client/MinecraftServer.cpp10
-rw-r--r--Minecraft.Client/MinecraftServer.h3
-rw-r--r--Minecraft.Client/PendingConnection.cpp72
-rw-r--r--Minecraft.Client/PlayerConnection.cpp104
-rw-r--r--Minecraft.Client/PlayerConnection.h5
-rw-r--r--Minecraft.Client/PlayerList.cpp12
-rw-r--r--Minecraft.Client/ServerPlayerGameMode.cpp32
-rw-r--r--Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp103
-rw-r--r--Minecraft.Client/Windows64/Network/WinsockNetLayer.h1
13 files changed, 463 insertions, 135 deletions
diff --git a/Minecraft.Client/Common/Console_Utils.cpp b/Minecraft.Client/Common/Console_Utils.cpp
index cb0f1b58..9a64dbea 100644
--- a/Minecraft.Client/Common/Console_Utils.cpp
+++ b/Minecraft.Client/Common/Console_Utils.cpp
@@ -1,21 +1,32 @@
#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);
+ assert(0);
#else
- 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
- // rather than triggering an error.
- _vsnprintf_s( str, _TRUNCATE, strFormat, pArgList );
- OutputDebugStringA( str );
+#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
+ // rather than triggering an error.
+ _vsnprintf_s( str, _TRUNCATE, strFormat, pArgList );
+ OutputDebugStringA( str );
#endif
}
#endif
@@ -31,10 +42,9 @@ VOID CDECL DebugPrintf( const CHAR* strFormat, ... )
#endif
{
#ifndef _CONTENT_PACKAGE
- va_list pArgList;
- va_start( pArgList, strFormat );
- DebugSpewV( strFormat, pArgList );
- va_end( pArgList );
+ va_list pArgList;
+ va_start( pArgList, strFormat );
+ DebugSpewV( strFormat, pArgList );
+ va_end( pArgList );
#endif
}
-
diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp
index c3a623d5..0a2fd159 100644
--- a/Minecraft.Client/Common/Consoles_App.cpp
+++ b/Minecraft.Client/Common/Consoles_App.cpp
@@ -38,6 +38,9 @@
#include "GameRules\ConsoleSchematicFile.h"
#include "..\User.h"
#include "..\..\Minecraft.World\LevelData.h"
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+#include "..\..\Minecraft.Server\ServerLogManager.h"
+#endif
#include "..\..\Minecraft.World\net.minecraft.world.entity.player.h"
#include "..\EntityRenderDispatcher.h"
#include "..\..\Minecraft.World\compression.h"
@@ -240,12 +243,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
}
@@ -253,53 +265,62 @@ 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 );
+ unsigned int writelen;
+ sys_tty_write(SYS_TTYP_USER1 + ( user - 1 ), buf, strlen(buf), &writelen );
#elif defined __PSVITA__
- switch(user)
- {
- case 0:
- {
- SceUID tty2 = sceIoOpen("tty2:", SCE_O_WRONLY, 0);
- if(tty2>=0)
- {
- std::string string1(buf);
- sceIoWrite(tty2, string1.c_str(), string1.length());
- sceIoClose(tty2);
- }
- }
- break;
- case 1:
- {
- SceUID tty3 = sceIoOpen("tty3:", SCE_O_WRONLY, 0);
- if(tty3>=0)
- {
- std::string string1(buf);
- sceIoWrite(tty3, string1.c_str(), string1.length());
- sceIoClose(tty3);
- }
- }
- break;
- default:
- OutputDebugStringA(buf);
- break;
- }
+ switch(user)
+ {
+ case 0:
+ {
+ SceUID tty2 = sceIoOpen("tty2:", SCE_O_WRONLY, 0);
+ if(tty2>=0)
+ {
+ std::string string1(buf);
+ sceIoWrite(tty2, string1.c_str(), string1.length());
+ sceIoClose(tty2);
+ }
+ }
+ break;
+ case 1:
+ {
+ SceUID tty3 = sceIoOpen("tty3:", SCE_O_WRONLY, 0);
+ if(tty3>=0)
+ {
+ std::string string1(buf);
+ sceIoWrite(tty3, string1.c_str(), string1.length());
+ sceIoClose(tty3);
+ }
+ }
+ break;
+ default:
+ OutputDebugStringA(buf);
+ break;
+ }
#else
- OutputDebugStringA(buf);
+ OutputDebugStringA(buf);
#endif
#ifndef _XBOX
- if(user == USER_UI)
- {
- ui.logDebugString(buf);
- }
+ if(user == USER_UI)
+ {
+ ui.logDebugString(buf);
+ }
#endif
#endif
}
diff --git a/Minecraft.Client/Common/Network/GameNetworkManager.cpp b/Minecraft.Client/Common/Network/GameNetworkManager.cpp
index a502dbfb..50aeae68 100644
--- a/Minecraft.Client/Common/Network/GameNetworkManager.cpp
+++ b/Minecraft.Client/Common/Network/GameNetworkManager.cpp
@@ -200,10 +200,12 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
#endif
int64_t seed = 0;
+ bool dedicatedNoLocalHostPlayer = false;
if (lpParameter != nullptr)
{
NetworkGameInitData *param = static_cast<NetworkGameInitData *>(lpParameter);
seed = param->seed;
+ dedicatedNoLocalHostPlayer = param->dedicatedNoLocalHostPlayer;
app.setLevelGenerationOptions(param->levelGen);
if(param->levelGen != nullptr)
@@ -359,9 +361,19 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
// PRIMARY PLAYER
vector<ClientConnection *> createdConnections;
- ClientConnection *connection;
+ ClientConnection *connection = nullptr;
- if( g_NetworkManager.IsHost() )
+ if( g_NetworkManager.IsHost() && dedicatedNoLocalHostPlayer )
+ {
+ 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 if( g_NetworkManager.IsHost() )
{
connection = new ClientConnection(minecraft, nullptr);
}
@@ -390,16 +402,18 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
connection = new ClientConnection(minecraft, socket);
}
- if( !connection->createdOk )
+ if (connection != nullptr)
{
- assert(false);
- delete connection;
- connection = nullptr;
- MinecraftServer::HaltServer();
- return false;
- }
+ if( !connection->createdOk )
+ {
+ assert(false);
+ delete connection;
+ connection = nullptr;
+ MinecraftServer::HaltServer();
+ return false;
+ }
- connection->send(std::make_shared<PreLoginPacket>(minecraft->user->name));
+ connection->send(std::make_shared<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
@@ -434,9 +448,9 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
connection->close();
}
- if( connection->isStarted() && !connection->isClosed() )
- {
- createdConnections.push_back( connection );
+ if( connection->isStarted() && !connection->isClosed() )
+ {
+ createdConnections.push_back( connection );
int primaryPad = ProfileManager.GetPrimaryPad();
app.SetRichPresenceContext(primaryPad,CONTEXT_GAME_STATE_BLANK);
@@ -533,13 +547,14 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
}
}
- app.SetGameMode( eMode_Multiplayer );
- }
- else if ( connection->isClosed() || !IsInSession())
- {
+ app.SetGameMode( eMode_Multiplayer );
+ }
+ else if ( connection->isClosed() || !IsInSession())
+ {
// assert(false);
- MinecraftServer::HaltServer();
- return false;
+ MinecraftServer::HaltServer();
+ return false;
+ }
}
diff --git a/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp b/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp
index 7340a7e0..1e625098 100644
--- a/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp
+++ b/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp
@@ -240,7 +240,13 @@ void CPlatformNetworkManagerStub::DoWork()
qnetPlayer->m_resolvedXuid = INVALID_XUID;
qnetPlayer->m_gamertag[0] = 0;
qnetPlayer->SetCustomDataValue(0);
- while (IQNet::s_playerCount > 1 && IQNet::m_player[IQNet::s_playerCount - 1].GetCustomDataValue() == 0)
+ // 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--;
}
// NOTE: Do NOT call PushFreeSmallId here. The old PlayerConnection's
@@ -257,6 +263,25 @@ void CPlatformNetworkManagerStub::DoWork()
SystemFlagRemoveBySmallId(disconnectedSmallId);
}
}
+
+ // 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 (!WinsockNetLayer::IsConnected())
+ {
+ if (!m_bLeaveGameOnTick)
+ {
+ m_bLeaveGameOnTick = true;
+ g_NetworkManager.HandleDisconnect(false);
+ }
+ }
+ else
+ {
+ m_bLeaveGameOnTick = false;
+ }
+ }
#endif
}
@@ -356,6 +381,7 @@ bool CPlatformNetworkManagerStub::LeaveGame(bool bMigrateHost)
if( m_bLeavingGame ) return true;
m_bLeavingGame = true;
+ m_bLeaveGameOnTick = false;
#ifdef _WINDOWS64
WinsockNetLayer::StopAdvertising();
@@ -404,6 +430,7 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame,
localUsersMask |= GetLocalPlayerMask( g_NetworkManager.GetPrimaryPad() );
m_bLeavingGame = false;
+ m_bLeaveGameOnTick = false;
m_pIQNet->HostGame();
@@ -433,9 +460,23 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame,
if (WinsockNetLayer::IsActive())
{
- const wchar_t* hostName = IQNet::m_player[0].m_gamertag;
- unsigned int settings = app.GetGameHostOption(eGameHostOption_All);
- WinsockNetLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION);
+ // For Dedicated Server, refer to `lan-advertise` in `server.properties`
+ bool enableLanAdvertising = true;
+ if (g_Win64DedicatedServer)
+ {
+ enableLanAdvertising = g_Win64DedicatedServerLanAdvertise;
+ }
+
+ if (enableLanAdvertising)
+ {
+ const wchar_t* hostName = IQNet::m_player[0].m_gamertag;
+ unsigned int settings = app.GetGameHostOption(eGameHostOption_All);
+ WinsockNetLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION);
+ }
+ else
+ {
+ WinsockNetLayer::StopAdvertising();
+ }
}
#endif
//#endif
@@ -463,6 +504,7 @@ int CPlatformNetworkManagerStub::JoinGame(FriendSessionInfo* searchResult, int l
return CGameNetworkManager::JOINGAME_FAIL_GENERAL;
m_bLeavingGame = false;
+ m_bLeaveGameOnTick = false;
IQNet::s_isHosting = false;
m_pIQNet->ClientJoinGame();
diff --git a/Minecraft.Client/MinecraftServer.cpp b/Minecraft.Client/MinecraftServer.cpp
index 2cf6930a..a545eff8 100644
--- a/Minecraft.Client/MinecraftServer.cpp
+++ b/Minecraft.Client/MinecraftServer.cpp
@@ -569,6 +569,7 @@ MinecraftServer::MinecraftServer()
playerIdleTimeout = 0;
m_postUpdateThread = nullptr;
forceGameType = false;
+ m_spawnProtectionRadius = 0;
commandDispatcher = new ServerCommandDispatcher();
InitializeCriticalSection(&m_consoleInputCS);
@@ -615,6 +616,10 @@ bool MinecraftServer::initServer(int64_t 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.SetGameHostOption(eGameHostOption_Difficulty, GetDedicatedServerInt(settings, L"difficulty", app.GetGameHostOption(eGameHostOption_Difficulty)));
app.SetGameHostOption(eGameHostOption_GameType, GetDedicatedServerInt(settings, L"gamemode", app.GetGameHostOption(eGameHostOption_GameType)));
@@ -631,6 +636,7 @@ bool MinecraftServer::initServer(int64_t 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
@@ -1661,7 +1667,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)
diff --git a/Minecraft.Client/MinecraftServer.h b/Minecraft.Client/MinecraftServer.h
index a33888bc..1ed5db9d 100644
--- a/Minecraft.Client/MinecraftServer.h
+++ b/Minecraft.Client/MinecraftServer.h
@@ -44,6 +44,7 @@ typedef struct _NetworkGameInitData
LevelGenerationOptions *levelGen;
DWORD texturePackId;
bool findSeed;
+ bool dedicatedNoLocalHostPlayer;
unsigned int xzSize;
unsigned char hellScale;
ESavePlatform savePlatform;
@@ -57,6 +58,7 @@ typedef struct _NetworkGameInitData
levelGen = nullptr;
texturePackId = 0;
findSeed = false;
+ dedicatedNoLocalHostPlayer = false;
xzSize = LEVEL_LEGACY_WIDTH;
hellScale = HELL_LEVEL_LEGACY_SCALE;
savePlatform = SAVE_FILE_PLATFORM_LOCAL;
@@ -119,6 +121,7 @@ public:
int maxBuildHeight;
int playerIdleTimeout;
bool forceGameType;
+ int m_spawnProtectionRadius;
private:
// 4J Added
diff --git a/Minecraft.Client/PendingConnection.cpp b/Minecraft.Client/PendingConnection.cpp
index 6d5497f0..f24086c1 100644
--- a/Minecraft.Client/PendingConnection.cpp
+++ b/Minecraft.Client/PendingConnection.cpp
@@ -14,6 +14,11 @@
#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"
+#endif
// #ifdef __PS3__
// #include "PS3\Network\NetworkPlayerSony.h"
// #endif
@@ -24,6 +29,24 @@ Random *PendingConnection::random = new Random();
bool g_bRejectDuplicateNames = true;
#endif
+#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
@@ -180,16 +203,55 @@ void PendingConnection::handleLogin(shared_ptr<LoginPacket> packet)
duplicateXuid = true;
}
+ bool bannedXuid = false;
+ if (loginXuid != INVALID_XUID)
+ {
+ bannedXuid = server->getPlayers()->isXuidBanned(loginXuid);
+ }
+ if (!bannedXuid && packet->m_onlineXuid != INVALID_XUID && packet->m_onlineXuid != loginXuid)
+ {
+ 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)
{
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+ ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_DuplicateXuid);
+#endif
// Reject the incoming connection — a player with this UID is already
// on the server. Allowing duplicates causes invisible players and
// other undefined behaviour.
@@ -211,6 +273,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());
disconnect(DisconnectPacket::eDisconnect_Banned);
}
@@ -268,6 +333,9 @@ void PendingConnection::handleAcceptedLogin(shared_ptr<LoginPacket> packet)
shared_ptr<ServerPlayer> playerEntity = server->getPlayers()->getPlayerForLogin(this, name, playerXuid,packet->m_onlineXuid);
if (playerEntity != nullptr)
{
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+ ServerRuntime::ServerLogManager::OnAcceptedPlayerLogin(GetPendingConnectionSmallId(connection), name);
+#endif
server->getPlayers()->placeNewPlayer(connection, playerEntity, packet);
connection = nullptr; // We've moved responsibility for this over to the new PlayerConnection, nullptr so we don't delete our reference to it here in our dtor
}
@@ -325,4 +393,4 @@ bool PendingConnection::isServerPacketListener()
bool PendingConnection::isDisconnected()
{
return done;
-} \ No newline at end of file
+}
diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp
index d9915cf6..6319b660 100644
--- a/Minecraft.Client/PlayerConnection.cpp
+++ b/Minecraft.Client/PlayerConnection.cpp
@@ -34,9 +34,26 @@
// 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;
+
PlayerConnection::PlayerConnection(MinecraftServer *server, Connection *connection, shared_ptr<ServerPlayer> player)
{
// 4J - added initialisers
@@ -66,6 +83,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);
}
@@ -76,6 +100,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;
@@ -118,6 +153,13 @@ 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();
@@ -271,16 +313,19 @@ void PlayerConnection::handleMovePlayer(shared_ptr<MovePlayerPacket> packet)
double dist = xDist * xDist + yDist * yDist + zDist * zDist;
- // 4J-PB - removing this one for now
- /*if (dist > 100.0f)
+ // 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())
{
- // logger.warning(player->name + " moved too quickly!");
- disconnect(DisconnectPacket::eDisconnect_MovedTooQuickly);
- // System.out.println("Moved too quickly at " + xt + ", " + yt + ", " + zt);
- // teleport(player->x, player->y, player->z, player->yRot, player->xRot);
- return;
+ // 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();
@@ -308,8 +353,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;
}
@@ -430,7 +475,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(std::make_shared<TileUpdatePacket>(x, y, z, level));
}
@@ -458,8 +504,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 == nullptr) return;
@@ -469,7 +513,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());
}
@@ -538,7 +583,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.") ) );
@@ -742,17 +798,16 @@ void PlayerConnection::handleInteract(shared_ptr<InteractPacket> packet)
// 4J Stu - If the client says that we hit something, then agree with it. The canSee can fail here as it checks
// a ray from head->head, but we may actually be looking at a different part of the entity that can be seen
// even though the ray is blocked.
- if (target != nullptr) // && player->canSee(target) && player->distanceToSqr(target) < 6 * 6)
+ if (target != nullptr)
{
- //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)
- //{
if (packet->action == InteractPacket::INTERACT)
{
player->interact(target);
@@ -767,7 +822,6 @@ void PlayerConnection::handleInteract(shared_ptr<InteractPacket> packet)
}
player->attack(target);
}
- //}
}
}
diff --git a/Minecraft.Client/PlayerConnection.h b/Minecraft.Client/PlayerConnection.h
index ff6093a3..0284bc6a 100644
--- a/Minecraft.Client/PlayerConnection.h
+++ b/Minecraft.Client/PlayerConnection.h
@@ -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;
diff --git a/Minecraft.Client/PlayerList.cpp b/Minecraft.Client/PlayerList.cpp
index 645e3fb5..ba82ec6a 100644
--- a/Minecraft.Client/PlayerList.cpp
+++ b/Minecraft.Client/PlayerList.cpp
@@ -37,6 +37,11 @@
#include "Common\Network\Sony\NetworkPlayerSony.h"
#endif
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+#include "..\Minecraft.Server\Access\Access.h"
+extern bool g_Win64DedicatedServer;
+#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)
@@ -1674,6 +1679,13 @@ bool PlayerList::isXuidBanned(PlayerUID xuid)
}
}
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+ if (!banned && g_Win64DedicatedServer)
+ {
+ banned = ServerRuntime::Access::IsPlayerBanned(xuid);
+ }
+#endif
+
return banned;
}
diff --git a/Minecraft.Client/ServerPlayerGameMode.cpp b/Minecraft.Client/ServerPlayerGameMode.cpp
index d2dcaaf6..041487f5 100644
--- a/Minecraft.Client/ServerPlayerGameMode.cpp
+++ b/Minecraft.Client/ServerPlayerGameMode.cpp
@@ -176,31 +176,29 @@ void ServerPlayerGameMode::stopDestroyBlock(int x, int y, int z)
{
if (x == xDestroyBlock && y == yDestroyBlock && z == zDestroyBlock)
{
- // int ticksSpentDestroying = gameTicks - destroyProgressStart;
-
int t = level->getTile(x, y, z);
if (t != 0)
{
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;
+ }
}
}
}
@@ -393,4 +391,4 @@ void ServerPlayerGameMode::setGameRules(GameRulesInstance *rules)
{
if(m_gameRules != nullptr) delete m_gameRules;
m_gameRules = rules;
-} \ No newline at end of file
+}
diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp
index 9d73eda8..981ab3ab 100644
--- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp
+++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp
@@ -8,11 +8,20 @@
#include "WinsockNetLayer.h"
#include "..\..\Common\Network\PlatformNetworkManagerStub.h"
#include "..\..\..\Minecraft.World\Socket.h"
+#if defined(MINECRAFT_SERVER_BUILD)
+#include "..\..\..\Minecraft.Server\Access\Access.h"
+#include "..\..\..\Minecraft.Server\ServerLogManager.h"
+#endif
#include "..\..\..\Minecraft.World\DisconnectPacket.h"
#include "..\..\Minecraft.h"
#include "..\4JLibs\inc\4J_Profile.h"
+#include <string>
+
static bool RecvExact(SOCKET sock, BYTE* buf, int len);
+#if defined(MINECRAFT_SERVER_BUILD)
+static bool TryGetNumericRemoteIp(const sockaddr_in &remoteAddress, std::string *outIp);
+#endif
SOCKET WinsockNetLayer::s_listenSocket = INVALID_SOCKET;
SOCKET WinsockNetLayer::s_hostConnectionSocket = INVALID_SOCKET;
@@ -65,6 +74,7 @@ char g_Win64MultiplayerIP[256] = "127.0.0.1";
bool g_Win64DedicatedServer = false;
int g_Win64DedicatedServerPort = WIN64_NET_DEFAULT_PORT;
char g_Win64DedicatedServerBindIP[256] = "";
+bool g_Win64DedicatedServerLanAdvertise = true;
bool WinsockNetLayer::Initialize()
{
@@ -90,7 +100,11 @@ bool WinsockNetLayer::Initialize()
s_initialized = true;
- StartDiscovery();
+ // Dedicated Server does not use LAN session discovery and therefore does not initiate discovery.
+ if (!g_Win64DedicatedServer)
+ {
+ StartDiscovery();
+ }
return true;
}
@@ -512,6 +526,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 WinsockNetLayer::HandleDataReceived(BYTE fromSmallId, BYTE toSmallId, unsigned char* data, unsigned int dataSize)
{
INetworkPlayer* pPlayerFrom = g_NetworkManager.GetPlayerBySmallId(fromSmallId);
@@ -546,7 +581,10 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
{
while (s_active)
{
- SOCKET clientSocket = accept(s_listenSocket, nullptr, nullptr);
+ sockaddr_in remoteAddress;
+ ZeroMemory(&remoteAddress, sizeof(remoteAddress));
+ int remoteAddressLength = sizeof(remoteAddress);
+ SOCKET clientSocket = accept(s_listenSocket, (sockaddr*)&remoteAddress, &remoteAddressLength);
if (clientSocket == INVALID_SOCKET)
{
if (s_active)
@@ -557,10 +595,36 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
int noDelay = 1;
setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&noDelay, sizeof(noDelay));
+#if defined(MINECRAFT_SERVER_BUILD)
+ std::string remoteIp;
+ const bool hasRemoteIp = TryGetNumericRemoteIp(remoteAddress, &remoteIp);
+ const char *remoteIpForLog = hasRemoteIp ? remoteIp.c_str() : "unknown";
+ if (g_Win64DedicatedServer)
+ {
+ 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)
{
- app.DebugPrintf("Win64 LAN: Rejecting connection, game not ready\n");
+#if defined(MINECRAFT_SERVER_BUILD)
+ if (g_Win64DedicatedServer)
+ {
+ ServerRuntime::ServerLogManager::OnRejectedTcpConnection(remoteIpForLog, ServerRuntime::ServerLogManager::eTcpRejectReason_GameNotReady);
+ }
+ else
+#endif
+ {
+ app.DebugPrintf("Win64 LAN: Rejecting connection, game not ready\n");
+ }
closesocket(clientSocket);
continue;
}
@@ -568,7 +632,16 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
extern CPlatformNetworkManagerStub* g_pPlatformNetworkManager;
if (g_pPlatformNetworkManager != nullptr && !g_pPlatformNetworkManager->CanAcceptMoreConnections())
{
- app.DebugPrintf("Win64 LAN: Rejecting connection, server at max players\n");
+#if defined(MINECRAFT_SERVER_BUILD)
+ if (g_Win64DedicatedServer)
+ {
+ ServerRuntime::ServerLogManager::OnRejectedTcpConnection(remoteIpForLog, ServerRuntime::ServerLogManager::eTcpRejectReason_ServerFull);
+ }
+ else
+#endif
+ {
+ app.DebugPrintf("Win64 LAN: Rejecting connection, server at max players\n");
+ }
SendRejectWithReason(clientSocket, DisconnectPacket::eDisconnect_ServerFull);
closesocket(clientSocket);
continue;
@@ -588,7 +661,16 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
else
{
LeaveCriticalSection(&s_freeSmallIdLock);
- app.DebugPrintf("Win64 LAN: Server full, rejecting connection\n");
+#if defined(MINECRAFT_SERVER_BUILD)
+ if (g_Win64DedicatedServer)
+ {
+ ServerRuntime::ServerLogManager::OnRejectedTcpConnection(remoteIpForLog, ServerRuntime::ServerLogManager::eTcpRejectReason_ServerFull);
+ }
+ else
+#endif
+ {
+ app.DebugPrintf("Win64 LAN: Server full, rejecting connection\n");
+ }
SendRejectWithReason(clientSocket, DisconnectPacket::eDisconnect_ServerFull);
closesocket(clientSocket);
continue;
@@ -616,7 +698,16 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
int connIdx = static_cast<int>(s_connections.size()) - 1;
LeaveCriticalSection(&s_connectionsLock);
- app.DebugPrintf("Win64 LAN: Client connected, assigned smallId=%d\n", assignedSmallId);
+#if defined(MINECRAFT_SERVER_BUILD)
+ if (g_Win64DedicatedServer)
+ {
+ ServerRuntime::ServerLogManager::OnAcceptedTcpConnection(assignedSmallId, remoteIpForLog);
+ }
+ else
+#endif
+ {
+ app.DebugPrintf("Win64 LAN: Client connected, assigned smallId=%d\n", assignedSmallId);
+ }
EnterCriticalSection(&s_smallIdToSocketLock);
s_smallIdToSocket[assignedSmallId] = clientSocket;
diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.h b/Minecraft.Client/Windows64/Network/WinsockNetLayer.h
index c5d68975..afccbd66 100644
--- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.h
+++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.h
@@ -170,5 +170,6 @@ extern char g_Win64MultiplayerIP[256];
extern bool g_Win64DedicatedServer;
extern int g_Win64DedicatedServerPort;
extern char g_Win64DedicatedServerBindIP[256];
+extern bool g_Win64DedicatedServerLanAdvertise;
#endif