mirror of
https://github.com/BluTac10/Xbox-Neo.git
synced 2026-07-18 23:18:18 +00:00
Merge remote-tracking branch 'itsRevela/main'
# Conflicts: # .github/workflows/nightly.yml # .gitignore # Minecraft.Client/ChatScreen.cpp # Minecraft.Client/ClientConnection.cpp # Minecraft.Client/Common/Audio/SoundEngine.cpp # Minecraft.Client/Common/Audio/SoundEngine.h # Minecraft.Client/Common/Media/MediaWindows64.arc # Minecraft.Client/Common/UI/IUIScene_HUD.cpp # Minecraft.Client/Common/UI/UIControl_Base.cpp # Minecraft.Client/Common/UI/UIScene_DeathMenu.cpp # Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp # Minecraft.Client/Common/XUI/XUI_Chat.cpp # Minecraft.Client/Common/XUI/XUI_Death.cpp # Minecraft.Client/Font.cpp # Minecraft.Client/Gui.cpp # Minecraft.Client/PendingConnection.cpp # Minecraft.Client/PlayerConnection.cpp # Minecraft.Client/PlayerConnection.h # Minecraft.Client/PlayerList.cpp # Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp # Minecraft.Client/Windows64/Network/WinsockNetLayer.h # Minecraft.Client/Windows64Media/strings.h # Minecraft.Client/cmake/sources/Common.cmake # Minecraft.Server/Console/ServerCliEngine.cpp # Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.cpp # Minecraft.Server/Windows64/ServerMain.cpp # Minecraft.World/WitherBoss.h # Minecraft.World/cmake/sources/Common.cmake # README.md
This commit is contained in:
@@ -16,27 +16,28 @@
|
||||
#include "../Minecraft.World/net.minecraft.world.level.tile.entity.h"
|
||||
#include "../Minecraft.World/net.minecraft.world.level.saveddata.h"
|
||||
#include "../Minecraft.World/net.minecraft.world.entity.animal.h"
|
||||
#include "../Minecraft.World/net.minecraft.network.h"
|
||||
#include "../Minecraft.World/net.minecraft.world.food.h"
|
||||
#include "../Minecraft.World/AABB.h"
|
||||
#include "../Minecraft.World/Pos.h"
|
||||
#include "../Minecraft.World/SharedConstants.h"
|
||||
#include "../Minecraft.World/ChatPacket.h"
|
||||
#include "../Minecraft.World/StringHelpers.h"
|
||||
#include "../Minecraft.World/Socket.h"
|
||||
#include "../Minecraft.World/Achievements.h"
|
||||
#include "../Minecraft.World/net.minecraft.h"
|
||||
#include "EntityTracker.h"
|
||||
#include "../Minecraft.World/LevelData.h"
|
||||
#include "ServerConnection.h"
|
||||
#include "../Minecraft.World/GenericStats.h"
|
||||
#include "../Minecraft.World/JavaMath.h"
|
||||
|
||||
#include "..\Minecraft.World\ListTag.h"
|
||||
// 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"
|
||||
#include "../Minecraft.Server/Access/Access.h"
|
||||
#include "../Minecraft.Server/Security/IdentityTokenManager.h"
|
||||
#include "../Minecraft.Server/Security/SecurityConfig.h"
|
||||
#include "../Minecraft.Server/Security/ConnectionCipher.h"
|
||||
extern bool g_Win64DedicatedServer;
|
||||
#endif
|
||||
|
||||
namespace
|
||||
@@ -85,6 +86,9 @@ PlayerConnection::PlayerConnection(MinecraftServer *server, Connection *connecti
|
||||
m_onlineXUID = INVALID_XUID;
|
||||
m_bHasClientTickedOnce = false;
|
||||
m_logSmallId = 0;
|
||||
m_identityVerified = false;
|
||||
m_identityChallengeTick = -1;
|
||||
m_securityGateOpen = true; // default open; closed when cipher is required
|
||||
|
||||
// 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)
|
||||
@@ -143,6 +147,14 @@ void PlayerConnection::tick()
|
||||
{
|
||||
dropSpamTickCount--;
|
||||
}
|
||||
|
||||
// Ensure server-side player tick runs even when no move packet was received this tick.
|
||||
// Without this, environmental damage (drowning, fire, lava) is never applied to clients
|
||||
// that don't send frequent move packets.
|
||||
if (!didTick && player != nullptr)
|
||||
{
|
||||
player->doTick(false);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerConnection::disconnect(DisconnectPacket::eDisconnectReason reason)
|
||||
@@ -483,9 +495,9 @@ void PlayerConnection::handlePlayerAction(shared_ptr<PlayerActionPacket> packet)
|
||||
}
|
||||
else if (packet->action == PlayerActionPacket::STOP_DESTROY_BLOCK)
|
||||
{
|
||||
player->gameMode->stopDestroyBlock(x, y, z);
|
||||
bool destroyed = player->gameMode->stopDestroyBlock(x, y, z);
|
||||
server->getPlayers()->prioritiseTileChanges(x, y, z, level->dimension->id); // 4J added - make sure that the update packets for this get prioritised over other general world updates
|
||||
if (level->getTile(x, y, z) != 0) player->connection->send(std::make_shared<TileUpdatePacket>(x, y, z, level));
|
||||
if (!destroyed && level->getTile(x, y, z) != 0) player->connection->send(std::make_shared<TileUpdatePacket>(x, y, z, level));
|
||||
}
|
||||
else if (packet->action == PlayerActionPacket::ABORT_DESTROY_BLOCK)
|
||||
{
|
||||
@@ -612,6 +624,22 @@ void PlayerConnection::onDisconnect(DisconnectPacket::eDisconnectReason reason,
|
||||
LeaveCriticalSection(&done_cs);
|
||||
}
|
||||
|
||||
void PlayerConnection::openSecurityGate()
|
||||
{
|
||||
if (m_securityGateOpen)
|
||||
return;
|
||||
|
||||
m_securityGateOpen = true;
|
||||
|
||||
// Flush all buffered packets now that the cipher is active
|
||||
for (auto &buffered : m_securityBuffer)
|
||||
{
|
||||
send(buffered);
|
||||
}
|
||||
m_securityBuffer.clear();
|
||||
m_securityBuffer.shrink_to_fit();
|
||||
}
|
||||
|
||||
void PlayerConnection::onUnhandledPacket(shared_ptr<Packet> packet)
|
||||
{
|
||||
// logger.warning(getClass() + " wasn't prepared to deal with a " + packet.getClass());
|
||||
@@ -622,6 +650,39 @@ void PlayerConnection::send(shared_ptr<Packet> packet)
|
||||
{
|
||||
if( connection->getSocket() != nullptr )
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
// Security gate: when require-secure-client is enabled, buffer ALL outgoing
|
||||
// packets until the cipher handshake completes. Only the cipher handshake
|
||||
// CustomPayloadPacket (MC|CKey) is sent immediately. Once the cipher activates,
|
||||
// openSecurityGate() flushes the buffer. This prevents unsecured/old clients
|
||||
// from receiving any game data (PlayerInfoPackets, XUIDs, etc.) before being kicked.
|
||||
if (!m_securityGateOpen)
|
||||
{
|
||||
// Allow cipher handshake packets through immediately
|
||||
if (packet->getId() == 250)
|
||||
{
|
||||
auto cpp = dynamic_pointer_cast<CustomPayloadPacket>(packet);
|
||||
if (cpp != nullptr &&
|
||||
(cpp->identifier == CustomPayloadPacket::CIPHER_KEY_CHANNEL ||
|
||||
cpp->identifier == CustomPayloadPacket::CIPHER_ACK_CHANNEL ||
|
||||
cpp->identifier == CustomPayloadPacket::CIPHER_ON_CHANNEL))
|
||||
{
|
||||
// Fall through to send
|
||||
}
|
||||
else
|
||||
{
|
||||
m_securityBuffer.push_back(packet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_securityBuffer.push_back(packet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if( !server->getPlayers()->canReceiveAllPackets( player ) )
|
||||
{
|
||||
// Check if we are allowed to send this packet type
|
||||
@@ -817,9 +878,7 @@ void PlayerConnection::handleInteract(shared_ptr<InteractPacket> packet)
|
||||
{
|
||||
if ((target->GetType() == eTYPE_ITEMENTITY) || (target->GetType() == eTYPE_EXPERIENCEORB) || (target->GetType() == eTYPE_ARROW) || target == player)
|
||||
{
|
||||
//disconnect("Attempting to attack an invalid entity");
|
||||
//server.warn("Player " + player.getName() + " tried to attack an invalid entity");
|
||||
return;
|
||||
return;
|
||||
}
|
||||
player->attack(target);
|
||||
}
|
||||
@@ -1064,10 +1123,19 @@ void PlayerConnection::handleServerSettingsChanged(shared_ptr<ServerSettingsChan
|
||||
{
|
||||
if(packet->action==ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS)
|
||||
{
|
||||
// Need to check that this player has permission to change each individual setting?
|
||||
|
||||
INetworkPlayer *networkPlayer = getNetworkPlayer();
|
||||
if( (networkPlayer != nullptr && networkPlayer->IsHost()) || player->isModerator())
|
||||
bool isHost = (networkPlayer != nullptr && networkPlayer->IsHost());
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
// On dedicated servers, only the host can change server settings.
|
||||
// Moderators (OPs) should not be able to alter game rules.
|
||||
if (!isHost)
|
||||
{
|
||||
app.DebugPrintf("SECURITY: Non-host player %ls attempted to change server settings\n",
|
||||
player->getName().c_str());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if( isHost || player->isModerator())
|
||||
{
|
||||
app.SetGameHostOption(eGameHostOption_FireSpreads, app.GetGameHostOption(packet->data,eGameHostOption_FireSpreads));
|
||||
app.SetGameHostOption(eGameHostOption_TNT, app.GetGameHostOption(packet->data,eGameHostOption_TNT));
|
||||
@@ -1090,14 +1158,81 @@ void PlayerConnection::handleServerSettingsChanged(shared_ptr<ServerSettingsChan
|
||||
void PlayerConnection::handleKickPlayer(shared_ptr<KickPlayerPacket> packet)
|
||||
{
|
||||
INetworkPlayer *networkPlayer = getNetworkPlayer();
|
||||
if( (networkPlayer != nullptr && networkPlayer->IsHost()) || player->isModerator())
|
||||
bool isHost = (networkPlayer != nullptr && networkPlayer->IsHost());
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
// Live ops.json check for non-host players
|
||||
if (!isHost)
|
||||
{
|
||||
PlayerUID kickerXuid = m_offlineXUID;
|
||||
if (kickerXuid == INVALID_XUID) kickerXuid = m_onlineXUID;
|
||||
if (!ServerRuntime::Access::IsPlayerOp(kickerXuid))
|
||||
{
|
||||
app.DebugPrintf("SECURITY: Non-OP player %ls attempted to kick\n", player->getName().c_str());
|
||||
{
|
||||
INetworkPlayer *npLog = getNetworkPlayer();
|
||||
if (npLog != nullptr)
|
||||
ServerRuntime::ServerLogManager::OnUnauthorizedCommand(npLog->GetSmallId(), player->getName(), "kick");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if( isHost || player->isModerator())
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
// On dedicated servers, non-host moderators cannot kick other moderators or the host.
|
||||
if (!isHost)
|
||||
{
|
||||
for (auto &checkingPlayer : server->getPlayers()->players)
|
||||
{
|
||||
if (checkingPlayer != nullptr &&
|
||||
checkingPlayer->connection->getNetworkPlayer() != nullptr &&
|
||||
checkingPlayer->connection->getNetworkPlayer()->GetSmallId() == packet->m_networkSmallId)
|
||||
{
|
||||
if (checkingPlayer->isModerator() ||
|
||||
checkingPlayer->connection->getNetworkPlayer()->IsHost())
|
||||
{
|
||||
app.DebugPrintf("SECURITY: Moderator %ls tried to kick host/moderator %ls\n",
|
||||
player->getName().c_str(), checkingPlayer->getName().c_str());
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
app.DebugPrintf("CMD: Player %ls kicked player with smallId=%d\n",
|
||||
player->getName().c_str(), packet->m_networkSmallId);
|
||||
#endif
|
||||
server->getPlayers()->kickPlayerByShortId(packet->m_networkSmallId);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerConnection::handleGameCommand(shared_ptr<GameCommandPacket> packet)
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
INetworkPlayer *networkPlayer = getNetworkPlayer();
|
||||
bool isHost = (networkPlayer != nullptr && networkPlayer->IsHost());
|
||||
if (!isHost)
|
||||
{
|
||||
// Live ops.json check - in-memory isModerator() can be stale if ops.json was edited mid-session
|
||||
PlayerUID cmdXuid = m_offlineXUID;
|
||||
if (cmdXuid == INVALID_XUID) cmdXuid = m_onlineXUID;
|
||||
if (!ServerRuntime::Access::IsPlayerOp(cmdXuid))
|
||||
{
|
||||
app.DebugPrintf("SECURITY: Non-OP player %ls attempted server command id=%d\n",
|
||||
player->getName().c_str(), static_cast<int>(packet->command));
|
||||
{
|
||||
INetworkPlayer *npLog = getNetworkPlayer();
|
||||
if (npLog != nullptr)
|
||||
ServerRuntime::ServerLogManager::OnUnauthorizedCommand(npLog->GetSmallId(), player->getName(), "game-command");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
app.DebugPrintf("CMD: Player %ls (OP=%d, Host=%d) executed command id=%d\n",
|
||||
player->getName().c_str(), player->isModerator() ? 1 : 0, isHost ? 1 : 0,
|
||||
static_cast<int>(packet->command));
|
||||
#endif
|
||||
MinecraftServer::getInstance()->getCommandDispatcher()->performCommand(player, packet->command, packet->data);
|
||||
}
|
||||
|
||||
@@ -1110,22 +1245,12 @@ void PlayerConnection::handleClientCommand(shared_ptr<ClientCommandPacket> packe
|
||||
{
|
||||
player = server->getPlayers()->respawn(player, player->m_enteredEndExitPortal?0:player->dimension, true);
|
||||
}
|
||||
//else if (player.getLevel().getLevelData().isHardcore())
|
||||
//{
|
||||
// if (server.isSingleplayer() && player.name.equals(server.getSingleplayerName()))
|
||||
// {
|
||||
// player.connection.disconnect("You have died. Game over, man, it's game over!");
|
||||
// server.selfDestruct();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// BanEntry ban = new BanEntry(player.name);
|
||||
// ban.setReason("Death in Hardcore");
|
||||
|
||||
// server.getPlayers().getBans().add(ban);
|
||||
// player.connection.disconnect("You have died. Game over, man, it's game over!");
|
||||
// }
|
||||
//}
|
||||
else if (player->level->getLevelData()->isHardcore())
|
||||
{
|
||||
// Hardcore mode — server rejects respawn. Ban and disconnect are already
|
||||
// handled in ServerPlayer::die() via banPlayerForHardcoreDeath().
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (player->getHealth() > 0) return;
|
||||
@@ -1377,10 +1502,21 @@ void PlayerConnection::handleKeepAlive(shared_ptr<KeepAlivePacket> packet)
|
||||
|
||||
void PlayerConnection::handlePlayerInfo(shared_ptr<PlayerInfoPacket> packet)
|
||||
{
|
||||
// Need to check that this player has permission to change each individual setting?
|
||||
|
||||
INetworkPlayer *networkPlayer = getNetworkPlayer();
|
||||
if( (networkPlayer != nullptr && networkPlayer->IsHost()) || player->isModerator() )
|
||||
bool isHost = (networkPlayer != nullptr && networkPlayer->IsHost());
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
// Live ops.json check for non-host players
|
||||
if (!isHost)
|
||||
{
|
||||
PlayerUID infoXuid = m_offlineXUID;
|
||||
if (infoXuid == INVALID_XUID) infoXuid = m_onlineXUID;
|
||||
if (!ServerRuntime::Access::IsPlayerOp(infoXuid))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if( isHost || player->isModerator() )
|
||||
{
|
||||
shared_ptr<ServerPlayer> serverPlayer;
|
||||
// Find the player being edited
|
||||
@@ -1458,7 +1594,24 @@ void PlayerConnection::handlePlayerInfo(shared_ptr<PlayerInfoPacket> packet)
|
||||
serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanToggleClassicHunger,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleClassicHunger) );
|
||||
serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanTeleport,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanTeleport) );
|
||||
}
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
// On dedicated servers, OP can only be granted/revoked if the target is in ops.json.
|
||||
// This prevents runtime OP escalation via crafted PlayerInfoPackets.
|
||||
bool wantsOp = Player::getPlayerGamePrivilege(packet->m_playerPrivileges, Player::ePlayerGamePrivilege_Op) != 0;
|
||||
PlayerUID targetXuid = serverPlayer->connection->m_offlineXUID;
|
||||
if (targetXuid == INVALID_XUID) targetXuid = serverPlayer->connection->m_onlineXUID;
|
||||
if (wantsOp && !ServerRuntime::Access::IsPlayerOp(targetXuid))
|
||||
{
|
||||
app.DebugPrintf("SECURITY: Host tried to OP player %ls who is not in ops.json\n",
|
||||
serverPlayer->getName().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_Op, wantsOp ? 1u : 0u);
|
||||
}
|
||||
#else
|
||||
serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_Op,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_Op) );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1496,6 +1649,46 @@ void PlayerConnection::handlePlayerAbilities(shared_ptr<PlayerAbilitiesPacket> p
|
||||
|
||||
void PlayerConnection::handleCustomPayload(shared_ptr<CustomPayloadPacket> customPayloadPacket)
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
// Identity token response from client
|
||||
if (CustomPayloadPacket::IDENTITY_TOKEN_RESPONSE.compare(customPayloadPacket->identifier) == 0)
|
||||
{
|
||||
PlayerUID xuid = m_offlineXUID;
|
||||
if (xuid == INVALID_XUID) xuid = m_onlineXUID;
|
||||
|
||||
bool tokenValid = false;
|
||||
if (customPayloadPacket->length == ServerRuntime::Security::IdentityTokenManager::TOKEN_SIZE &&
|
||||
customPayloadPacket->data.length == ServerRuntime::Security::IdentityTokenManager::TOKEN_SIZE &&
|
||||
customPayloadPacket->data.data != nullptr)
|
||||
{
|
||||
tokenValid = ServerRuntime::Security::GetIdentityTokenManager().VerifyToken(xuid, customPayloadPacket->data.data);
|
||||
}
|
||||
|
||||
if (tokenValid)
|
||||
{
|
||||
m_identityVerified = true;
|
||||
app.DebugPrintf("SECURITY: Identity token verified for player %ls\n", player->getName().c_str());
|
||||
INetworkPlayer *npLog = getNetworkPlayer();
|
||||
if (npLog != nullptr)
|
||||
ServerRuntime::ServerLogManager::OnIdentityTokenVerified(npLog->GetSmallId());
|
||||
}
|
||||
else
|
||||
{
|
||||
app.DebugPrintf("SECURITY: Identity token MISMATCH for player %ls - will disconnect\n", player->getName().c_str());
|
||||
app.DebugPrintf("SECURITY: If this player lost their token, use: revoketoken %ls\n", player->getName().c_str());
|
||||
INetworkPlayer *npLog = getNetworkPlayer();
|
||||
if (npLog != nullptr)
|
||||
ServerRuntime::ServerLogManager::OnIdentityTokenMismatch(npLog->GetSmallId(), player->getName());
|
||||
// Defer disconnect to avoid re-entrancy issues during packet dispatch
|
||||
setWasKicked();
|
||||
closeOnTick();
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
if (CustomPayloadPacket.CUSTOM_BOOK_PACKET.equals(customPayloadPacket.identifier))
|
||||
if (CustomPayloadPacket::CUSTOM_BOOK_PACKET.compare(customPayloadPacket->identifier) == 0)
|
||||
{
|
||||
ByteArrayInputStream bais(customPayloadPacket->data);
|
||||
@@ -1535,7 +1728,9 @@ void PlayerConnection::handleCustomPayload(shared_ptr<CustomPayloadPacket> custo
|
||||
player->inventory->setItem(player->inventory->selected, sentItem);
|
||||
}
|
||||
}
|
||||
else if (CustomPayloadPacket::TRADER_SELECTION_PACKET.compare(customPayloadPacket->identifier) == 0)
|
||||
else
|
||||
#endif
|
||||
if (CustomPayloadPacket::TRADER_SELECTION_PACKET.compare(customPayloadPacket->identifier) == 0)
|
||||
{
|
||||
ByteArrayInputStream bais(customPayloadPacket->data);
|
||||
DataInputStream input(&bais);
|
||||
|
||||
Reference in New Issue
Block a user