From 75225eb102e236d946dd63b7fa67660ccdf2ec30 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Sat, 15 May 2021 20:30:45 -0700 Subject: [PATCH] Add PlayerKickEvent causes --- .../chat/SignedMessageChain.java.patch | 37 +++++ .../server/MinecraftServer.java.patch | 2 +- .../server/commands/BanIpCommands.java.patch | 11 ++ .../commands/BanPlayerCommands.java.patch | 11 ++ .../server/commands/KickCommand.java.patch | 11 ++ .../ServerCommonPacketListenerImpl.java.patch | 64 +++++--- .../ServerGamePacketListenerImpl.java.patch | 147 +++++++++++++----- .../ServerLoginPacketListenerImpl.java.patch | 2 +- .../server/players/PlayerList.java.patch | 4 +- .../entity/player/ProfilePublicKey.java.patch | 28 ++++ .../craftbukkit/entity/CraftPlayer.java | 13 +- .../java/org/spigotmc/RestartCommand.java | 2 +- 12 files changed, 267 insertions(+), 65 deletions(-) create mode 100644 paper-server/patches/sources/net/minecraft/network/chat/SignedMessageChain.java.patch create mode 100644 paper-server/patches/sources/net/minecraft/server/commands/BanIpCommands.java.patch create mode 100644 paper-server/patches/sources/net/minecraft/server/commands/BanPlayerCommands.java.patch create mode 100644 paper-server/patches/sources/net/minecraft/server/commands/KickCommand.java.patch create mode 100644 paper-server/patches/sources/net/minecraft/world/entity/player/ProfilePublicKey.java.patch diff --git a/paper-server/patches/sources/net/minecraft/network/chat/SignedMessageChain.java.patch b/paper-server/patches/sources/net/minecraft/network/chat/SignedMessageChain.java.patch new file mode 100644 index 0000000000..9404843b29 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/network/chat/SignedMessageChain.java.patch @@ -0,0 +1,37 @@ +--- a/net/minecraft/network/chat/SignedMessageChain.java ++++ b/net/minecraft/network/chat/SignedMessageChain.java +@@ -40,14 +40,14 @@ + if (signature == null) { + throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.MISSING_PROFILE_KEY); + } else if (playerPublicKey.data().hasExpired()) { +- throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.EXPIRED_PROFILE_KEY); ++ throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.EXPIRED_PROFILE_KEY, org.bukkit.event.player.PlayerKickEvent.Cause.EXPIRED_PROFILE_PUBLIC_KEY); // Paper - kick event causes + } else { + SignedMessageLink signedMessageLink = SignedMessageChain.this.nextLink; + if (signedMessageLink == null) { + throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.CHAIN_BROKEN); + } else if (body.timeStamp().isBefore(SignedMessageChain.this.lastTimeStamp)) { + this.setChainBroken(); +- throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.OUT_OF_ORDER_CHAT); ++ throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.OUT_OF_ORDER_CHAT, org.bukkit.event.player.PlayerKickEvent.Cause.OUT_OF_ORDER_CHAT); // Paper - kick event causes + } else { + SignedMessageChain.this.lastTimeStamp = body.timeStamp(); + PlayerChatMessage playerChatMessage = new PlayerChatMessage(signedMessageLink, signature, body, null, FilterMask.PASS_THROUGH); +@@ -80,9 +80,16 @@ + static final Component INVALID_SIGNATURE = Component.translatable("chat.disabled.invalid_signature"); + static final Component OUT_OF_ORDER_CHAT = Component.translatable("chat.disabled.out_of_order_chat"); + +- public DecodeException(Component message) { ++ // Paper start ++ public final org.bukkit.event.player.PlayerKickEvent.Cause kickCause; ++ public DecodeException(Component message, org.bukkit.event.player.PlayerKickEvent.Cause event) { + super(message); ++ this.kickCause = event; + } ++ // Paper end ++ public DecodeException(Component message) { ++ this(message, org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN); // Paper ++ } + } + + @FunctionalInterface diff --git a/paper-server/patches/sources/net/minecraft/server/MinecraftServer.java.patch b/paper-server/patches/sources/net/minecraft/server/MinecraftServer.java.patch index ff16067d26..29cc806f6e 100644 --- a/paper-server/patches/sources/net/minecraft/server/MinecraftServer.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/MinecraftServer.java.patch @@ -1249,7 +1249,7 @@ - if (!whitelist.isWhiteListed(entityplayer.getGameProfile())) { - entityplayer.connection.disconnect((Component) Component.translatable("multiplayer.disconnect.not_whitelisted")); + if (!whitelist.isWhiteListed(entityplayer.getGameProfile()) && !this.getPlayerList().isOp(entityplayer.getGameProfile())) { // Paper - Fix kicking ops when whitelist is reloaded (MC-171420) -+ entityplayer.connection.disconnect(net.kyori.adventure.text.Component.text(org.spigotmc.SpigotConfig.whitelistMessage)); ++ entityplayer.connection.disconnect(net.kyori.adventure.text.Component.text(org.spigotmc.SpigotConfig.whitelistMessage), org.bukkit.event.player.PlayerKickEvent.Cause.WHITELIST); // Paper - use configurable message & kick event cause } } diff --git a/paper-server/patches/sources/net/minecraft/server/commands/BanIpCommands.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/BanIpCommands.java.patch new file mode 100644 index 0000000000..be620cd58a --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/server/commands/BanIpCommands.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/server/commands/BanIpCommands.java ++++ b/net/minecraft/server/commands/BanIpCommands.java +@@ -66,7 +66,7 @@ + } + + for (ServerPlayer serverPlayer : list) { +- serverPlayer.connection.disconnect(Component.translatable("multiplayer.disconnect.ip_banned")); ++ serverPlayer.connection.disconnect(Component.translatable("multiplayer.disconnect.ip_banned"), org.bukkit.event.player.PlayerKickEvent.Cause.IP_BANNED); // Paper - kick event cause + } + + return list.size(); diff --git a/paper-server/patches/sources/net/minecraft/server/commands/BanPlayerCommands.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/BanPlayerCommands.java.patch new file mode 100644 index 0000000000..a6e8c45f71 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/server/commands/BanPlayerCommands.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/server/commands/BanPlayerCommands.java ++++ b/net/minecraft/server/commands/BanPlayerCommands.java +@@ -55,7 +55,7 @@ + ); + ServerPlayer serverPlayer = source.getServer().getPlayerList().getPlayer(gameProfile.getId()); + if (serverPlayer != null) { +- serverPlayer.connection.disconnect(Component.translatable("multiplayer.disconnect.banned")); ++ serverPlayer.connection.disconnect(Component.translatable("multiplayer.disconnect.banned"), org.bukkit.event.player.PlayerKickEvent.Cause.BANNED); // Paper - kick event cause + } + } + } diff --git a/paper-server/patches/sources/net/minecraft/server/commands/KickCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/KickCommand.java.patch new file mode 100644 index 0000000000..8138965b1c --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/server/commands/KickCommand.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/server/commands/KickCommand.java ++++ b/net/minecraft/server/commands/KickCommand.java +@@ -48,7 +48,7 @@ + + for (ServerPlayer serverPlayer : targets) { + if (!source.getServer().isSingleplayerOwner(serverPlayer.getGameProfile())) { +- serverPlayer.connection.disconnect(reason); ++ serverPlayer.connection.disconnect(reason, org.bukkit.event.player.PlayerKickEvent.Cause.KICK_COMMAND); // Paper - kick event cause + source.sendSuccess(() -> Component.translatable("commands.kick.success", serverPlayer.getDisplayName(), reason), true); + i++; + } diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch index aacfd8a432..9f3963a9af 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch @@ -58,8 +58,8 @@ + } + + @Override -+ public void kickPlayer(Component reason) { -+ this.disconnect(reason); ++ public void kickPlayer(Component reason, org.bukkit.event.player.PlayerKickEvent.Cause cause) { // Paper - kick event causes ++ this.disconnect(reason, cause); // Paper - kick event causes + } + // CraftBukkit end private static final Logger LOGGER = LogUtils.getLogger(); @@ -125,13 +125,13 @@ - this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE); + // Paper start - This needs to be handled on the main thread for plugins + server.submit(() -> { -+ this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE); ++ this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause + }); + // Paper end - This needs to be handled on the main thread for plugins } } -@@ -94,9 +150,71 @@ +@@ -94,38 +150,127 @@ @Override public void handlePong(ServerboundPongPacket packet) {} @@ -162,7 +162,7 @@ + } + } catch (Exception ex) { + ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t register custom payload", ex); -+ this.disconnect(Component.literal("Invalid payload REGISTER!")); ++ this.disconnect(Component.literal("Invalid payload REGISTER!"), PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause + } + } else if (identifier.equals(ServerCommonPacketListenerImpl.CUSTOM_UNREGISTER)) { + try { @@ -172,7 +172,7 @@ + } + } catch (Exception ex) { + ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t unregister custom payload", ex); -+ this.disconnect(Component.literal("Invalid payload UNREGISTER!")); ++ this.disconnect(Component.literal("Invalid payload UNREGISTER!"), PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause + } + } else { + try { @@ -190,23 +190,24 @@ + this.cserver.getMessenger().dispatchIncomingMessage(this.player.getBukkitEntity(), identifier.toString(), data); + } catch (Exception ex) { + ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t dispatch custom payload", ex); -+ this.disconnect(Component.literal("Invalid custom payload!")); ++ this.disconnect(Component.literal("Invalid custom payload!"), PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause + } + } - ++ + } + + public final boolean isDisconnected() { + return (!this.player.joining && !this.connection.isConnected()) || this.processedDisconnect; // Paper - Fix duplication bugs + } + // CraftBukkit end -+ + @Override public void handleResourcePackResponse(ServerboundResourcePackPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, (BlockableEventLoop) this.server); -@@ -104,28 +222,55 @@ + if (packet.action() == ServerboundResourcePackPacket.Action.DECLINED && this.server.isResourcePackRequired()) { ServerCommonPacketListenerImpl.LOGGER.info("Disconnecting {} due to resource pack {} rejection", this.playerProfile().getName(), packet.id()); - this.disconnect((Component) Component.translatable("multiplayer.requiredTexturePrompt.disconnect")); +- this.disconnect((Component) Component.translatable("multiplayer.requiredTexturePrompt.disconnect")); ++ this.disconnect((Component) Component.translatable("multiplayer.requiredTexturePrompt.disconnect"), PlayerKickEvent.Cause.RESOURCE_PACK_REJECTION); // Paper - kick event cause } + // Paper start - adventure pack callbacks + // call the callbacks before the previously-existing event so the event has final say @@ -230,13 +231,14 @@ @Override public void handleCookieResponse(ServerboundCookieResponsePacket packet) { +- this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY); + // CraftBukkit start + PacketUtils.ensureRunningOnSameThread(packet, this, (BlockableEventLoop) this.server); + if (this.player.getBukkitEntity().handleCookieResponse(packet)) { + return; + } + // CraftBukkit end - this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY); ++ this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY, PlayerKickEvent.Cause.INVALID_COOKIE); // Paper - kick event cause } protected void keepConnectionAlive() { @@ -249,10 +251,11 @@ - if (!this.isSingleplayerOwner() && i - this.keepAliveTime >= 15000L) { - if (this.keepAlivePending) { +- this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE); +- } else if (this.checkIfClosed(i)) { + if (!this.isSingleplayerOwner() && elapsedTime >= 15000L) { // Paper - use vanilla's 15000L between keep alive packets + if (this.keepAlivePending && !this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // Paper - check keepalive limit, don't fire if already disconnected - this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE); -- } else if (this.checkIfClosed(i)) { ++ this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause + } else if (this.checkIfClosed(currentTime)) { // Paper this.keepAlivePending = true; - this.keepAliveTime = i; @@ -266,6 +269,15 @@ Profiler.get().pop(); } +@@ -133,7 +278,7 @@ + private boolean checkIfClosed(long time) { + if (this.closed) { + if (time - this.closedListenerTime >= 15000L) { +- this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE); ++ this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause + } + + return false; @@ -156,6 +301,14 @@ } @@ -281,23 +293,35 @@ if (packet.isTerminal()) { this.close(); } -@@ -175,20 +328,73 @@ +@@ -175,20 +328,83 @@ } } + // Paper start - adventure + public void disconnect(final net.kyori.adventure.text.Component reason) { -+ this.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(reason)); ++ this.disconnect(reason, PlayerKickEvent.Cause.UNKNOWN); ++ } ++ public void disconnect(final net.kyori.adventure.text.Component reason, PlayerKickEvent.Cause cause) { ++ this.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(reason), cause); ++ // Paper end - kick event causes + } + // Paper end - adventure + ++ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - kick event causes public void disconnect(Component reason) { - this.disconnect(new DisconnectionDetails(reason)); +- this.disconnect(new DisconnectionDetails(reason)); ++ // Paper start - kick event causes ++ this.disconnect(reason, PlayerKickEvent.Cause.UNKNOWN); } ++ public void disconnect(final Component reason, PlayerKickEvent.Cause cause) { ++ this.disconnect(new DisconnectionDetails(reason), cause); ++ // Paper end - kick event causes ++ } - public void disconnect(DisconnectionDetails disconnectionInfo) { +- public void disconnect(DisconnectionDetails disconnectionInfo) { - this.connection.send(new ClientboundDisconnectPacket(disconnectionInfo.reason()), PacketSendListener.thenRun(() -> { - this.connection.disconnect(disconnectionInfo); ++ public void disconnect(DisconnectionDetails disconnectionInfo, PlayerKickEvent.Cause cause) { // Paper - kick event cause + // CraftBukkit start - fire PlayerKickEvent + if (this.processedDisconnect) { + return; @@ -306,7 +330,7 @@ + Waitable waitable = new Waitable() { + @Override + protected Object evaluate() { -+ ServerCommonPacketListenerImpl.this.disconnect(disconnectionInfo); ++ ServerCommonPacketListenerImpl.this.disconnect(disconnectionInfo, cause); // Paper - kick event causes + return null; + } + }; @@ -325,7 +349,7 @@ + + net.kyori.adventure.text.Component leaveMessage = net.kyori.adventure.text.Component.translatable("multiplayer.player.left", net.kyori.adventure.text.format.NamedTextColor.YELLOW, io.papermc.paper.configuration.GlobalConfiguration.get().messages.useDisplayNameInQuitMessage ? this.player.getBukkitEntity().displayName() : net.kyori.adventure.text.Component.text(this.player.getScoreboardName())); // Paper - Adventure + -+ PlayerKickEvent event = new PlayerKickEvent(this.player.getBukkitEntity(), io.papermc.paper.adventure.PaperAdventure.asAdventure(disconnectionInfo.reason()), leaveMessage); // Paper - adventure ++ PlayerKickEvent event = new PlayerKickEvent(this.player.getBukkitEntity(), io.papermc.paper.adventure.PaperAdventure.asAdventure(disconnectionInfo.reason()), leaveMessage, cause); // Paper - adventure & kick event causes + + if (this.cserver.getServer().isRunning()) { + this.cserver.getPluginManager().callEvent(event); diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch index 2fa62f38ad..efffa7099c 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -48,7 +48,7 @@ import net.minecraft.world.level.GameRules; import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; -@@ -192,11 +196,72 @@ +@@ -192,12 +196,73 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; @@ -59,7 +59,7 @@ import net.minecraft.world.phys.shapes.VoxelShape; +import org.bukkit.NamespacedKey; import org.slf4j.Logger; -+ + +// CraftBukkit start +import io.papermc.paper.adventure.ChatProcessor; // Paper +import io.papermc.paper.adventure.PaperAdventure; // Paper @@ -118,9 +118,10 @@ +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.SmithingInventory; +// CraftBukkit end - ++ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl implements ServerGamePacketListener, ServerPlayerConnection, TickablePacketListener { + static final Logger LOGGER = LogUtils.getLogger(); @@ -212,7 +277,9 @@ private int tickCount; private int ackBlockChangesUpTo = -1; @@ -175,7 +176,7 @@ if (++this.aboveGroundTickCount > this.getMaximumFlyingTicks(this.player)) { ServerGamePacketListenerImpl.LOGGER.warn("{} was kicked for floating too long!", this.player.getName().getString()); - this.disconnect((Component) Component.translatable("multiplayer.disconnect.flying")); -+ this.disconnect(io.papermc.paper.configuration.GlobalConfiguration.get().messages.kick.flyingPlayer); // Paper - use configurable kick message ++ this.disconnect(io.papermc.paper.configuration.GlobalConfiguration.get().messages.kick.flyingPlayer, org.bukkit.event.player.PlayerKickEvent.Cause.FLYING_PLAYER); // Paper - use configurable kick message & kick event cause return; } } else { @@ -184,11 +185,11 @@ if (++this.aboveGroundVehicleTickCount > this.getMaximumFlyingTicks(this.lastVehicle)) { ServerGamePacketListenerImpl.LOGGER.warn("{} was kicked for floating a vehicle too long!", this.player.getName().getString()); - this.disconnect((Component) Component.translatable("multiplayer.disconnect.flying")); -+ this.disconnect(io.papermc.paper.configuration.GlobalConfiguration.get().messages.kick.flyingVehicle); // Paper - use configurable kick message ++ this.disconnect(io.papermc.paper.configuration.GlobalConfiguration.get().messages.kick.flyingVehicle, org.bukkit.event.player.PlayerKickEvent.Cause.FLYING_VEHICLE); // Paper - use configurable kick message & kick event cause return; } } else { -@@ -311,8 +395,11 @@ +@@ -311,9 +395,12 @@ this.keepConnectionAlive(); this.chatSpamThrottler.tick(); @@ -196,10 +197,12 @@ + this.recipeSpamPackets.tick(); // Paper - auto recipe limit this.dropSpamThrottler.tick(); if (this.player.getLastActionTime() > 0L && this.server.getPlayerIdleTimeout() > 0 && Util.getMillis() - this.player.getLastActionTime() > (long) this.server.getPlayerIdleTimeout() * 1000L * 60L) { +- this.disconnect((Component) Component.translatable("multiplayer.disconnect.idling")); + this.player.resetLastActionTime(); // CraftBukkit - SPIGOT-854 - this.disconnect((Component) Component.translatable("multiplayer.disconnect.idling")); ++ this.disconnect((Component) Component.translatable("multiplayer.disconnect.idling"), org.bukkit.event.player.PlayerKickEvent.Cause.IDLING); // Paper - kick event cause } + } @@ -376,6 +463,12 @@ @Override public void handlePlayerInput(ServerboundPlayerInputPacket packet) { @@ -213,7 +216,14 @@ this.player.setLastClientInput(packet.input()); } -@@ -401,21 +494,73 @@ +@@ -395,27 +488,79 @@ + public void handleMoveVehicle(ServerboundMoveVehiclePacket packet) { + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); + if (ServerGamePacketListenerImpl.containsInvalidValues(packet.position().x(), packet.position().y(), packet.position().z(), packet.yRot(), packet.xRot())) { +- this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_vehicle_movement")); ++ this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_vehicle_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_VEHICLE_MOVEMENT); // Paper - kick event cause + } else if (!this.updateAwaitingTeleport() && this.player.hasClientLoaded()) { + Entity entity = this.player.getRootVehicle(); if (entity != this.player && entity.getControllingPassenger() == this.player && entity == this.lastVehicle) { ServerLevel worldserver = this.player.serverLevel(); @@ -308,7 +318,7 @@ boolean flag1 = entity.verticalCollisionBelow; if (entity instanceof LivingEntity) { -@@ -449,19 +594,72 @@ +@@ -449,20 +594,73 @@ d10 = d6 * d6 + d7 * d7 + d8 * d8; boolean flag2 = false; @@ -327,8 +337,8 @@ + this.player.absMoveTo(d0, d1, d2, this.player.getYRot(), this.player.getXRot()); // CraftBukkit this.send(ClientboundMoveVehiclePacket.fromEntity(entity)); return; -+ } -+ + } + + // CraftBukkit start - fire PlayerMoveEvent + Player player = this.getCraftPlayer(); + if (!this.hasMoved) { @@ -377,12 +387,18 @@ + this.justTeleported = false; + return; + } - } ++ } + // CraftBukkit end - ++ this.player.serverLevel().getChunkSource().move(this.player); entity.recordMovementThroughBlocks(new Vec3(d0, d1, d2), entity.position()); -@@ -493,12 +691,13 @@ + Vec3 vec3d = new Vec3(entity.getX() - d0, entity.getY() - d1, entity.getZ() - d2); +@@ -489,16 +687,17 @@ + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); + if (packet.getId() == this.awaitingTeleport) { + if (this.awaitingPositionFromClient == null) { +- this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_player_movement")); ++ this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause return; } @@ -419,7 +435,7 @@ + // PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); // Paper - AsyncTabCompleteEvent; run this async + // CraftBukkit start + if (!this.tabSpamThrottler.isIncrementAndUnderThreshold() && !this.server.getPlayerList().isOp(this.player.getGameProfile()) && !this.server.isSingleplayerOwner(this.player.getGameProfile())) { // Paper - configurable tab spam limits -+ this.disconnect(Component.translatable("disconnect.spam")); ++ this.disconnect(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - Kick event cause + return; + } + // CraftBukkit end @@ -568,14 +584,14 @@ + + if (byteTotal > byteAllowed) { + ServerGamePacketListenerImpl.LOGGER.warn("{} tried to send a book too large. Book size: {} - Allowed: {} - Pages: {}", this.player.getScoreboardName(), byteTotal, byteAllowed, pageList.size()); -+ this.disconnect(Component.literal("Book too large!")); ++ this.disconnect(Component.literal("Book too large!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause + return; + } + } + // Paper end - Book size limits + // CraftBukkit start + if (this.lastBookTick + 20 > MinecraftServer.currentTick) { -+ this.disconnect(Component.literal("Book edited too quickly!")); ++ this.disconnect(Component.literal("Book edited too quickly!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause + return; + } + this.lastBookTick = MinecraftServer.currentTick; @@ -617,7 +633,12 @@ } } -@@ -982,22 +1298,30 @@ +@@ -978,26 +1294,34 @@ + public void handleMovePlayer(ServerboundMovePlayerPacket packet) { + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); + if (ServerGamePacketListenerImpl.containsInvalidValues(packet.getX(0.0D), packet.getY(0.0D), packet.getZ(0.0D), packet.getYRot(0.0F), packet.getXRot(0.0F))) { +- this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_player_movement")); ++ this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause } else { ServerLevel worldserver = this.player.serverLevel(); @@ -995,7 +1016,7 @@ + this.dropCount++; + if (this.dropCount >= 20) { + ServerGamePacketListenerImpl.LOGGER.warn(this.player.getScoreboardName() + " dropped their items too quickly!"); -+ this.disconnect(Component.literal("You dropped your items too quickly (Hacking?)")); ++ this.disconnect(Component.literal("You dropped your items too quickly (Hacking?)"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause + return; + } + } @@ -1088,10 +1109,12 @@ if (this.player.hasClientLoaded()) { this.ackBlockChangesUpTo(packet.getSequence()); ServerLevel worldserver = this.player.serverLevel(); -@@ -1296,6 +1888,47 @@ - this.player.absRotateTo(f, f1); - } +@@ -1294,8 +1886,49 @@ + if (f1 != this.player.getXRot() || f != this.player.getYRot()) { + this.player.absRotateTo(f, f1); ++ } ++ + // CraftBukkit start + // Raytrace to look for 'rogue armswings' + double d0 = this.player.getX(); @@ -1122,8 +1145,8 @@ + cancelled = event.useItemInHand() == Event.Result.DENY; + } + this.player.gameMode.firedInteract = false; -+ } -+ + } + + if (cancelled) { + this.player.getBukkitEntity().updateInventory(); // SPIGOT-2524 + return; @@ -1204,7 +1227,7 @@ this.player.resetLastActionTime(); } else { ServerGamePacketListenerImpl.LOGGER.warn("{} tried to set an invalid carried item", this.player.getName().getString()); -+ this.disconnect(Component.literal("Invalid hotbar selection (Hacking?)")); // CraftBukkit ++ this.disconnect(Component.literal("Invalid hotbar selection (Hacking?)"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // CraftBukkit // Paper - kick event cause } } @@ -1332,10 +1355,11 @@ - private void tryHandleChat(String message, Runnable callback) { - if (ServerGamePacketListenerImpl.isChatMessageIllegal(message)) { +- this.disconnect((Component) Component.translatable("multiplayer.disconnect.illegal_characters")); +- } else if (this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { + private void tryHandleChat(String s, Runnable runnable, boolean sync) { // CraftBukkit + if (ServerGamePacketListenerImpl.isChatMessageIllegal(s)) { - this.disconnect((Component) Component.translatable("multiplayer.disconnect.illegal_characters")); -- } else if (this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { ++ this.disconnect((Component) Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper + } else if (this.player.isRemoved() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { // CraftBukkit - dead men tell no tales this.send(new ClientboundSystemChatPacket(Component.translatable("chat.disabled.options").withStyle(ChatFormatting.RED), false)); } else { @@ -1351,6 +1375,15 @@ } } +@@ -1549,7 +2257,7 @@ + + if (optional.isEmpty()) { + ServerGamePacketListenerImpl.LOGGER.warn("Failed to validate message acknowledgements from {}", this.player.getName().getString()); +- this.disconnect(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED); ++ this.disconnect(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes + } + + return optional; @@ -1566,6 +2274,127 @@ return false; } @@ -1479,7 +1512,7 @@ private PlayerChatMessage getSignedMessage(ServerboundChatPacket packet, LastSeenMessages lastSeenMessages) throws SignedMessageChain.DecodeException { SignedMessageBody signedmessagebody = new SignedMessageBody(packet.message(), packet.timeStamp(), packet.salt(), lastSeenMessages); -@@ -1573,13 +2402,42 @@ +@@ -1573,15 +2402,44 @@ } private void broadcastChatMessage(PlayerChatMessage message) { @@ -1510,6 +1543,7 @@ - private void detectRateSpam() { - this.chatSpamThrottler.increment(); - if (!this.chatSpamThrottler.isUnderThreshold() && !this.server.getPlayerList().isOp(this.player.getGameProfile()) && !this.server.isSingleplayerOwner(this.player.getGameProfile())) { +- this.disconnect((Component) Component.translatable("disconnect.spam")); + // Spigot start - spam exclusions + private void detectRateSpam(String s) { + // CraftBukkit start - replaced with thread safe throttle @@ -1519,14 +1553,25 @@ + { + return; + } -+ } + } + // Spigot end + // this.chatSpamThrottler.increment(); + if (!this.chatSpamThrottler.isIncrementAndUnderThreshold() && !this.server.getPlayerList().isOp(this.player.getGameProfile()) && !this.server.isSingleplayerOwner(this.player.getGameProfile())) { + // CraftBukkit end - this.disconnect((Component) Component.translatable("disconnect.spam")); - } ++ this.disconnect((Component) Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause ++ } + } + +@@ -1592,7 +2450,7 @@ + synchronized (this.lastSeenMessages) { + if (!this.lastSeenMessages.applyOffset(packet.offset())) { + ServerGamePacketListenerImpl.LOGGER.warn("Failed to validate message acknowledgements from {}", this.player.getName().getString()); +- this.disconnect(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED); ++ this.disconnect(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes + } + + } @@ -1601,7 +2459,40 @@ @Override public void handleAnimate(ServerboundSwingPacket packet) { @@ -1610,7 +1655,15 @@ break; case RELEASE_SHIFT_KEY: this.player.setShiftKeyDown(false); -@@ -1691,6 +2610,12 @@ +@@ -1684,13 +2603,19 @@ + } + + if (i > 4096) { +- this.disconnect((Component) Component.translatable("multiplayer.disconnect.too_many_pending_chats")); ++ this.disconnect((Component) Component.translatable("multiplayer.disconnect.too_many_pending_chats"), org.bukkit.event.player.PlayerKickEvent.Cause.TOO_MANY_PENDING_CHATS); // Paper - kick event cause + } + + } } public void sendPlayerChatMessage(PlayerChatMessage message, ChatType.Bound params) { @@ -1653,7 +1706,7 @@ + // Spigot Start + if ( entity == this.player && !this.player.isSpectator() ) + { -+ this.disconnect( Component.literal( "Cannot interact with self!" ) ); ++ this.disconnect( Component.literal( "Cannot interact with self!" ), org.bukkit.event.player.PlayerKickEvent.Cause.SELF_INTERACTION ); // Paper - kick event cause + return; + } + // Spigot End @@ -1747,7 +1800,7 @@ label23: { if (entity instanceof AbstractArrow) { -@@ -1785,6 +2769,11 @@ +@@ -1785,17 +2769,41 @@ } ServerGamePacketListenerImpl.this.player.attack(entity); @@ -1759,7 +1812,11 @@ return; } } -@@ -1795,7 +2784,26 @@ + +- ServerGamePacketListenerImpl.this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_entity_attacked")); ++ ServerGamePacketListenerImpl.this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_entity_attacked"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_ENTITY_ATTACKED); // Paper - add cause + ServerGamePacketListenerImpl.LOGGER.warn("Player {} tried to attack an invalid entity", ServerGamePacketListenerImpl.this.player.getName().getString()); + } }); } } @@ -2133,7 +2190,7 @@ + // Paper start - auto recipe limit + if (!org.bukkit.Bukkit.isPrimaryThread()) { + if (!this.recipeSpamPackets.isIncrementAndUnderThreshold()) { -+ this.disconnect(net.minecraft.network.chat.Component.translatable("disconnect.spam")); ++ this.disconnect(net.minecraft.network.chat.Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause + return; + } + } @@ -2311,6 +2368,24 @@ } } +@@ -2033,7 +3440,7 @@ + + if (!Objects.equals(profilepublickey_a, profilepublickey_a1)) { + if (profilepublickey_a != null && profilepublickey_a1.expiresAt().isBefore(profilepublickey_a.expiresAt())) { +- this.disconnect(ProfilePublicKey.EXPIRED_PROFILE_PUBLIC_KEY); ++ this.disconnect(ProfilePublicKey.EXPIRED_PROFILE_PUBLIC_KEY, org.bukkit.event.player.PlayerKickEvent.Cause.EXPIRED_PROFILE_PUBLIC_KEY); // Paper - kick event causes + } else { + try { + SignatureValidator signaturevalidator = this.server.getProfileKeySignatureValidator(); +@@ -2046,7 +3453,7 @@ + this.resetPlayerChatState(remotechatsession_a.validate(this.player.getGameProfile(), signaturevalidator)); + } catch (ProfilePublicKey.ValidationException profilepublickey_b) { + ServerGamePacketListenerImpl.LOGGER.error("Failed to validate profile key: {}", profilepublickey_b.getMessage()); +- this.disconnect(profilepublickey_b.getComponent()); ++ this.disconnect(profilepublickey_b.getComponent(), profilepublickey_b.kickCause); // Paper - kick event causes + } + + } @@ -2058,7 +3465,7 @@ if (!this.waitingForSwitchToConfig) { throw new IllegalStateException("Client acknowledged config, but none was requested"); diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerLoginPacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerLoginPacketListenerImpl.java.patch index 544ef156a2..2c71b2620a 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerLoginPacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerLoginPacketListenerImpl.java.patch @@ -46,7 +46,7 @@ + } + + @Override -+ public void kickPlayer(Component reason) { ++ public void kickPlayer(Component reason, org.bukkit.event.player.PlayerKickEvent.Cause cause) { // Paper - kick event causes - during login, no event can be called. + this.disconnect(reason); + } + // CraftBukkit end diff --git a/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch b/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch index 9422549762..c039452d9c 100644 --- a/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch @@ -532,7 +532,7 @@ + while (iterator.hasNext()) { + entityplayer = (ServerPlayer) iterator.next(); + this.save(entityplayer); // CraftBukkit - Force the player's inventory to be saved -+ entityplayer.connection.disconnect(Component.translatable("multiplayer.disconnect.duplicate_login")); ++ entityplayer.connection.disconnect(Component.translatable("multiplayer.disconnect.duplicate_login"), org.bukkit.event.player.PlayerKickEvent.Cause.DUPLICATE_LOGIN); // Paper - kick event cause + } + + // Instead of kicking then returning, we need to store the kick reason @@ -1048,7 +1048,7 @@ + // Paper end + // CraftBukkit start - disconnect safely + for (ServerPlayer player : this.players) { -+ if (isRestarting) player.connection.disconnect(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.restartMessage)); else // Paper ++ if (isRestarting) player.connection.disconnect(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.restartMessage), org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN); else // Paper - kick event cause (cause is never used here) + player.connection.disconnect(java.util.Objects.requireNonNullElseGet(this.server.server.shutdownMessage(), net.kyori.adventure.text.Component::empty)); // CraftBukkit - add custom shutdown message // Paper - Adventure } + // CraftBukkit end diff --git a/paper-server/patches/sources/net/minecraft/world/entity/player/ProfilePublicKey.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/player/ProfilePublicKey.java.patch new file mode 100644 index 0000000000..a8a26d91a8 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/entity/player/ProfilePublicKey.java.patch @@ -0,0 +1,28 @@ +--- a/net/minecraft/world/entity/player/ProfilePublicKey.java ++++ b/net/minecraft/world/entity/player/ProfilePublicKey.java +@@ -24,7 +24,7 @@ + + public static ProfilePublicKey createValidated(SignatureValidator servicesSignatureVerifier, UUID playerUuid, ProfilePublicKey.Data publicKeyData) throws ProfilePublicKey.ValidationException { + if (!publicKeyData.validateSignature(servicesSignatureVerifier, playerUuid)) { +- throw new ProfilePublicKey.ValidationException(INVALID_SIGNATURE); ++ throw new ProfilePublicKey.ValidationException(INVALID_SIGNATURE, org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PUBLIC_KEY_SIGNATURE); // Paper - kick event causes + } else { + return new ProfilePublicKey(publicKeyData); + } +@@ -88,8 +88,16 @@ + } + + public static class ValidationException extends ThrowingComponent { ++ public final org.bukkit.event.player.PlayerKickEvent.Cause kickCause; // Paper ++ @io.papermc.paper.annotation.DoNotUse @Deprecated // Paper + public ValidationException(Component messageText) { ++ // Paper start ++ this(messageText, org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN); ++ } ++ public ValidationException(Component messageText, org.bukkit.event.player.PlayerKickEvent.Cause kickCause) { ++ // Paper end + super(messageText); ++ this.kickCause = kickCause; // Paper + } + } + } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 0b151a66d7..ff8d6a0ceb 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -280,7 +280,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { void sendPacket(Packet packet); - void kickPlayer(Component reason); + void kickPlayer(Component reason, org.bukkit.event.player.PlayerKickEvent.Cause cause); // Paper - kick event causes } public record CookieFuture(ResourceLocation key, CompletableFuture future) { @@ -640,7 +640,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { @Override public void kickPlayer(String message) { org.spigotmc.AsyncCatcher.catchOp("player kick"); // Spigot - this.getHandle().transferCookieConnection.kickPlayer(CraftChatMessage.fromStringOrEmpty(message, true)); + this.getHandle().transferCookieConnection.kickPlayer(CraftChatMessage.fromStringOrEmpty(message, true), org.bukkit.event.player.PlayerKickEvent.Cause.PLUGIN); // Paper - kick event cause } // Paper start @@ -652,10 +652,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player { @Override public void kick(final net.kyori.adventure.text.Component message) { + kick(message, org.bukkit.event.player.PlayerKickEvent.Cause.PLUGIN); + } + + @Override + public void kick(net.kyori.adventure.text.Component message, org.bukkit.event.player.PlayerKickEvent.Cause cause) { org.spigotmc.AsyncCatcher.catchOp("player kick"); final ServerGamePacketListenerImpl connection = this.getHandle().connection; if (connection != null) { - connection.disconnect(message == null ? net.kyori.adventure.text.Component.empty() : message); + connection.disconnect(message == null ? net.kyori.adventure.text.Component.empty() : message, cause); } } @@ -716,7 +721,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { // Paper start - Improve chat handling if (ServerGamePacketListenerImpl.isChatMessageIllegal(msg)) { - this.getHandle().connection.disconnect(Component.translatable("multiplayer.disconnect.illegal_characters")); + this.getHandle().connection.disconnect(Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper - kick event causes } else { if (msg.startsWith("/")) { this.getHandle().connection.handleCommand(msg); diff --git a/paper-server/src/main/java/org/spigotmc/RestartCommand.java b/paper-server/src/main/java/org/spigotmc/RestartCommand.java index 824c4ad135..39e56b95aa 100644 --- a/paper-server/src/main/java/org/spigotmc/RestartCommand.java +++ b/paper-server/src/main/java/org/spigotmc/RestartCommand.java @@ -74,7 +74,7 @@ public class RestartCommand extends Command // Kick all players for ( ServerPlayer p : com.google.common.collect.ImmutableList.copyOf( MinecraftServer.getServer().getPlayerList().players ) ) { - p.connection.disconnect( CraftChatMessage.fromStringOrEmpty( SpigotConfig.restartMessage, true ) ); + p.connection.disconnect( CraftChatMessage.fromStringOrEmpty( SpigotConfig.restartMessage, true ), org.bukkit.event.player.PlayerKickEvent.Cause.RESTART_COMMAND); // Paper - kick event reason (cause is never used)) } // Give the socket a chance to send the packets try