From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sat, 15 May 2021 20:30:45 -0700
Subject: [PATCH] Add PlayerKickEvent causes


diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 3f4a2a17790cad61218dcff351d70a2cc50cb56a..660e1b6955115d67cb37161ef556f90acc53bdfa 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -2101,7 +2101,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
                 ServerPlayer entityplayer = (ServerPlayer) iterator.next();
 
                 if (!whitelist.isWhiteListed(entityplayer.getGameProfile()) && !this.getPlayerList().isOp(entityplayer.getGameProfile())) { // Paper - Fix kicking ops when whitelist is reloaded (MC-171420)
-                    entityplayer.connection.disconnect(org.spigotmc.SpigotConfig.whitelistMessage); // Paper - use configurable message
+                    entityplayer.connection.disconnect(org.spigotmc.SpigotConfig.whitelistMessage, org.bukkit.event.player.PlayerKickEvent.Cause.WHITELIST); // Paper - use configurable message
                 }
             }
 
diff --git a/src/main/java/net/minecraft/server/commands/BanIpCommands.java b/src/main/java/net/minecraft/server/commands/BanIpCommands.java
index 37c6be64f618857abcbdbfe2a1fd58ed4f997a29..19023c0796f0b6d7b4acd4ff8984355ec5b3faa7 100644
--- a/src/main/java/net/minecraft/server/commands/BanIpCommands.java
+++ b/src/main/java/net/minecraft/server/commands/BanIpCommands.java
@@ -61,7 +61,7 @@ public class BanIpCommands {
             }
 
             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/src/main/java/net/minecraft/server/commands/BanPlayerCommands.java b/src/main/java/net/minecraft/server/commands/BanPlayerCommands.java
index 991d9a2bfd8d577c9c25e559833af9ddf690f1dc..458b9a730a1f629bd605c26a4ec4440e8ee84b1d 100644
--- a/src/main/java/net/minecraft/server/commands/BanPlayerCommands.java
+++ b/src/main/java/net/minecraft/server/commands/BanPlayerCommands.java
@@ -42,7 +42,7 @@ public class BanPlayerCommands {
                 source.sendSuccess(Component.translatable("commands.ban.success", ComponentUtils.getDisplayName(gameProfile), userBanListEntry.getReason()), true);
                 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/src/main/java/net/minecraft/server/commands/KickCommand.java b/src/main/java/net/minecraft/server/commands/KickCommand.java
index 65637a33ba171a4b598f70cd943d24b0ee44a69f..57a9146bf2dee7a60aab16716e25348fad7ba127 100644
--- a/src/main/java/net/minecraft/server/commands/KickCommand.java
+++ b/src/main/java/net/minecraft/server/commands/KickCommand.java
@@ -22,7 +22,7 @@ public class KickCommand {
 
     private static int kickPlayers(CommandSourceStack source, Collection<ServerPlayer> targets, Component reason) {
         for(ServerPlayer serverPlayer : targets) {
-            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);
         }
 
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 7a7ce8bf83f02fc17e68fe4468d80f79490c9daa..c6d6973e15e3a7f81b908eef6d4de81b51958b14 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -372,7 +372,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
         if (this.clientIsFloating && !this.player.isSleeping() && !this.player.isPassenger()) {
             if (++this.aboveGroundTickCount > 80) {
                 ServerGamePacketListenerImpl.LOGGER.warn("{} was kicked for floating too long!", this.player.getName().getString());
-                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 {
@@ -391,7 +391,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
             if (this.clientVehicleIsFloating && this.player.getRootVehicle().getControllingPassenger() == this.player) {
                 if (++this.aboveGroundVehicleTickCount > 80) {
                     ServerGamePacketListenerImpl.LOGGER.warn("{} was kicked for floating a vehicle too long!", this.player.getName().getString());
-                    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 {
@@ -413,7 +413,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
         if (this.keepAlivePending) {
             if (!this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected
                 ServerGamePacketListenerImpl.LOGGER.warn("{} was kicked due to keepalive timeout!", this.player.getScoreboardName()); // more info
-                this.disconnect(Component.translatable("disconnect.timeout", new Object[0]));
+                this.disconnect(Component.translatable("disconnect.timeout", new Object[0]), org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause
             }
         } else {
             if (elapsedTime >= 15000L) { // 15 seconds
@@ -443,7 +443,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
 
         if (this.player.getLastActionTime() > 0L && this.server.getPlayerIdleTimeout() > 0 && Util.getMillis() - this.player.getLastActionTime() > (long) (this.server.getPlayerIdleTimeout() * 1000 * 60)) {
             this.player.resetLastActionTime(); // CraftBukkit - SPIGOT-854
-            this.disconnect(Component.translatable("multiplayer.disconnect.idling"));
+            this.disconnect(Component.translatable("multiplayer.disconnect.idling"), org.bukkit.event.player.PlayerKickEvent.Cause.IDLING); // Paper - kick event cause
         }
 
         this.chatPreviewThrottler.tick();
@@ -467,16 +467,26 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
         return this.server.isSingleplayerOwner(this.player.getGameProfile());
     }
 
+    @io.papermc.paper.annotation.DoNotUse // Paper
     public void disconnect(String s) {
         // Paper start
-        this.disconnect(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(s));
+        this.disconnect(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(s), org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN);
     }
 
+    public void disconnect(String s, PlayerKickEvent.Cause cause) {
+        this.disconnect(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(s), cause);
+    }
+
+    @io.papermc.paper.annotation.DoNotUse // Paper
     public void disconnect(final Component reason) {
-        this.disconnect(PaperAdventure.asAdventure(reason));
+        this.disconnect(PaperAdventure.asAdventure(reason), org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN);
+    }
+
+    public void disconnect(final Component reason, PlayerKickEvent.Cause cause) {
+        this.disconnect(PaperAdventure.asAdventure(reason), cause);
     }
 
-    public void disconnect(net.kyori.adventure.text.Component reason) {
+    public void disconnect(net.kyori.adventure.text.Component reason, org.bukkit.event.player.PlayerKickEvent.Cause cause) {
         // Paper end
         // CraftBukkit start - fire PlayerKickEvent
         if (this.processedDisconnect) {
@@ -505,7 +515,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
 
         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(), reason, leaveMessage); // Paper - Adventure
+        PlayerKickEvent event = new PlayerKickEvent(this.player.getBukkitEntity(), reason, leaveMessage, cause); // Paper - Adventure & kick event reason
 
         if (this.cserver.getServer().isRunning()) {
             this.cserver.getPluginManager().callEvent(event);
@@ -575,7 +585,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
     public void handleMoveVehicle(ServerboundMoveVehiclePacket packet) {
         PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel());
         if (ServerGamePacketListenerImpl.containsInvalidValues(packet.getX(), packet.getY(), packet.getZ(), packet.getYRot(), packet.getXRot())) {
-            this.disconnect(Component.translatable("multiplayer.disconnect.invalid_vehicle_movement"));
+            this.disconnect(Component.translatable("multiplayer.disconnect.invalid_vehicle_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_VEHICLE_MOVEMENT); // Paper - kick event cause
         } else {
             Entity entity = this.player.getRootVehicle();
 
@@ -773,7 +783,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
         PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel());
         if (packet.getId() == this.awaitingTeleport) {
             if (this.awaitingPositionFromClient == null) {
-                this.disconnect(Component.translatable("multiplayer.disconnect.invalid_player_movement"));
+                this.disconnect(Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause
                 return;
             }
 
@@ -830,13 +840,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
         // PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel()); // Paper - run this async
         // CraftBukkit start
         if (this.chatSpamTickCount.addAndGet(io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.tabSpamIncrement) > io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.tabSpamLimit && !this.server.getPlayerList().isOp(this.player.getGameProfile())) { // Paper start - split and make configurable
-            server.scheduleOnMain(() -> this.disconnect(Component.translatable("disconnect.spam", new Object[0]))); // Paper
+            server.scheduleOnMain(() -> this.disconnect(Component.translatable("disconnect.spam", new Object[0]), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM)); // Paper - kick event cause
             return;
         }
         // Paper start
         String str = packet.getCommand(); int index = -1;
         if (str.length() > 64 && ((index = str.indexOf(' ')) == -1 || index >= 64)) {
-            server.scheduleOnMain(() -> this.disconnect(Component.translatable("disconnect.spam", new Object[0]))); // Paper
+            server.scheduleOnMain(() -> this.disconnect(Component.translatable("disconnect.spam", new Object[0]), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM)); // Paper - kick event cause
             return;
         }
         // Paper end
@@ -989,7 +999,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
         // Paper start - validate pick item position
         if (!(packet.getSlot() >= 0 && packet.getSlot() < this.player.getInventory().items.size())) {
             ServerGamePacketListenerImpl.LOGGER.warn("{} tried to set an invalid carried item", this.player.getName().getString());
-            this.disconnect("Invalid hotbar selection (Hacking?)");
+            this.disconnect("Invalid hotbar selection (Hacking?)", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause
             return;
         }
         this.player.getInventory().pickSlot(packet.getSlot()); // Paper - Diff above if changed
@@ -1176,7 +1186,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
                 int byteLength = testString.getBytes(java.nio.charset.StandardCharsets.UTF_8).length;
                 if (byteLength > 256 * 4) {
                     ServerGamePacketListenerImpl.LOGGER.warn(this.player.getScoreboardName() + " tried to send a book with with a page too large!");
-                    server.scheduleOnMain(() -> this.disconnect("Book too large!"));
+                    server.scheduleOnMain(() -> this.disconnect("Book too large!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION)); // Paper - kick event cause
                     return;
                 }
                 byteTotal += byteLength;
@@ -1199,14 +1209,14 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
 
             if (byteTotal > byteAllowed) {
                 ServerGamePacketListenerImpl.LOGGER.warn(this.player.getScoreboardName() + " tried to send too large of a book. Book Size: " + byteTotal + " - Allowed:  "+ byteAllowed + " - Pages: " + pageList.size());
-                server.scheduleOnMain(() -> this.disconnect("Book too large!"));
+                server.scheduleOnMain(() -> this.disconnect("Book too large!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION)); // Paper - kick event cause
                 return;
             }
         }
         // Paper end
         // CraftBukkit start
         if (this.lastBookTick + 20 > MinecraftServer.currentTick) {
-            this.disconnect("Book edited too quickly!");
+            this.disconnect("Book edited too quickly!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause
             return;
         }
         this.lastBookTick = MinecraftServer.currentTick;
@@ -1330,7 +1340,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
     public void handleMovePlayer(ServerboundMovePlayerPacket packet) {
         PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel());
         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.translatable("multiplayer.disconnect.invalid_player_movement"));
+            this.disconnect(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.getLevel();
 
@@ -1757,7 +1767,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
                         this.dropCount++;
                         if (this.dropCount >= 20) {
                             ServerGamePacketListenerImpl.LOGGER.warn(this.player.getScoreboardName() + " dropped their items too quickly!");
-                            this.disconnect("You dropped your items too quickly (Hacking?)");
+                            this.disconnect("You dropped your items too quickly (Hacking?)", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause
                             return;
                         }
                     }
@@ -1965,7 +1975,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
         PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel());
         if (packet.getAction() == ServerboundResourcePackPacket.Action.DECLINED && this.server.isResourcePackRequired()) {
             ServerGamePacketListenerImpl.LOGGER.info("Disconnecting {} due to resource pack rejection", this.player.getName());
-            this.disconnect(Component.translatable("multiplayer.requiredTexturePrompt.disconnect"));
+            this.disconnect(Component.translatable("multiplayer.requiredTexturePrompt.disconnect"), org.bukkit.event.player.PlayerKickEvent.Cause.RESOURCE_PACK_REJECTION); // Paper - add cause
         }
         // Paper start
         PlayerResourcePackStatusEvent.Status packStatus = PlayerResourcePackStatusEvent.Status.values()[packet.action.ordinal()];
@@ -2078,7 +2088,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
             this.player.resetLastActionTime();
         } else {
             ServerGamePacketListenerImpl.LOGGER.warn("{} tried to set an invalid carried item", this.player.getName().getString());
-            this.disconnect("Invalid hotbar selection (Hacking?)"); // CraftBukkit
+            this.disconnect("Invalid hotbar selection (Hacking?)", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // CraftBukkit // Paper - kick event cause
         }
     }
 
@@ -2091,7 +2101,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
         }
         // CraftBukkit end
         if (ServerGamePacketListenerImpl.isChatMessageIllegal(packet.message())) {
-            this.disconnect(Component.translatable("multiplayer.disconnect.illegal_characters"));
+            this.disconnect(Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper - add cause
         } else {
             if (this.tryHandleChat(packet.message(), packet.timeStamp(), packet.lastSeenMessages())) {
                 // this.server.submit(() -> { // CraftBukkit - async chat
@@ -2119,7 +2129,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
     @Override
     public void handleChatCommand(ServerboundChatCommandPacket packet) {
         if (ServerGamePacketListenerImpl.isChatMessageIllegal(packet.command())) {
-            this.disconnect(Component.translatable("multiplayer.disconnect.illegal_characters"));
+            this.disconnect(Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper
         } else {
             if (this.tryHandleChat(packet.command(), packet.timeStamp(), packet.lastSeenMessages())) {
                 this.server.submit(() -> {
@@ -2205,7 +2215,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
     private boolean tryHandleChat(String message, Instant timestamp, LastSeenMessages.Update acknowledgment) {
         if (!this.updateChatOrder(timestamp)) {
             ServerGamePacketListenerImpl.LOGGER.warn("{} sent out-of-order chat: '{}'", this.player.getName().getString(), message);
-            this.disconnect(Component.translatable("multiplayer.disconnect.out_of_order_chat"));
+            this.disconnect(Component.translatable("multiplayer.disconnect.out_of_order_chat"), org.bukkit.event.player.PlayerKickEvent.Cause.OUT_OF_ORDER_CHAT); // Paper - kick event cause
             return false;
         } 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));
@@ -2457,7 +2467,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
         ChatSender chatsender = this.player.asChatSender();
 
         if (chatsender.profilePublicKey() != null && !message.verify(chatsender)) {
-            this.disconnect(Component.translatable("multiplayer.disconnect.unsigned_chat"));
+            this.disconnect(Component.translatable("multiplayer.disconnect.unsigned_chat"), org.bukkit.event.player.PlayerKickEvent.Cause.UNSIGNED_CHAT); // Paper - kick event cause
             return false;
         } else {
             if (message.hasExpiredServer(Instant.now())) {
@@ -2484,7 +2494,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
         // this.chatSpamTickCount += 20;
         if (this.chatSpamTickCount.addAndGet(20) > 200 && !this.server.getPlayerList().isOp(this.player.getGameProfile())) {
             // CraftBukkit end
-            this.disconnect(Component.translatable("disconnect.spam"));
+            this.disconnect(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause
         }
 
     }
@@ -2587,7 +2597,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
 
     private void handleValidationFailure(Set<LastSeenMessagesValidator.ErrorCondition> reasons) {
         ServerGamePacketListenerImpl.LOGGER.warn("Failed to validate message from {}, reasons: {}", this.player.getName().getString(), reasons.stream().map(LastSeenMessagesValidator.ErrorCondition::message).collect(Collectors.joining(",")));
-        this.disconnect(Component.translatable("multiplayer.disconnect.chat_validation_failed"));
+        this.disconnect(Component.translatable("multiplayer.disconnect.chat_validation_failed"), org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event cause
     }
 
     @Override
@@ -2734,7 +2744,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
             }
 
             if (i > 4096) {
-                this.disconnect(Component.translatable("multiplayer.disconnect.too_many_pending_chats"));
+                this.disconnect(Component.translatable("multiplayer.disconnect.too_many_pending_chats"), org.bukkit.event.player.PlayerKickEvent.Cause.TOO_MANY_PENDING_CHATS); // Paper - kick event cause
             }
 
         }
@@ -2749,7 +2759,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
         // Spigot Start
         if ( entity == this.player && !this.player.isSpectator() )
         {
-            this.disconnect( "Cannot interact with self!" );
+            this.disconnect( "Cannot interact with self!", org.bukkit.event.player.PlayerKickEvent.Cause.SELF_INTERACTION ); // Paper - add cause
             return;
         }
         // Spigot End
@@ -2842,7 +2852,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
                             }
                             // CraftBukkit end
                         } else {
-                            ServerGamePacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.invalid_entity_attacked"));
+                            ServerGamePacketListenerImpl.this.disconnect(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());
                         }
                     }
@@ -3250,7 +3260,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
         // Paper start
         if (!org.bukkit.Bukkit.isPrimaryThread()) {
             if (recipeSpamPackets.addAndGet(io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.recipeSpamIncrement) > io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.recipeSpamLimit) {
-                server.scheduleOnMain(() -> this.disconnect(net.minecraft.network.chat.Component.translatable("disconnect.spam", new Object[0]))); // Paper
+                server.scheduleOnMain(() -> this.disconnect(net.minecraft.network.chat.Component.translatable("disconnect.spam", new Object[0]), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM)); // Paper - kick event cause
                 return;
             }
         }
@@ -3453,7 +3463,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
         } else if (!this.isSingleplayerOwner()) {
             // Paper start - This needs to be handled on the main thread for plugins
             server.submit(() -> {
-            this.disconnect(Component.translatable("disconnect.timeout"));
+            this.disconnect(Component.translatable("disconnect.timeout"), org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause
             });
             // Paper end
         }
@@ -3499,7 +3509,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
                 }
             } catch (Exception ex) {
                 ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t register custom payload", ex);
-                this.disconnect("Invalid payload REGISTER!");
+                this.disconnect("Invalid payload REGISTER!", org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause
             }
         } else if (packet.identifier.equals(CUSTOM_UNREGISTER)) {
             try {
@@ -3509,7 +3519,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
                 }
             } catch (Exception ex) {
                 ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t unregister custom payload", ex);
-                this.disconnect("Invalid payload UNREGISTER!");
+                this.disconnect("Invalid payload UNREGISTER!", org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause
             }
         } else {
             try {
@@ -3527,7 +3537,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
                 this.cserver.getMessenger().dispatchIncomingMessage(this.player.getBukkitEntity(), packet.identifier.toString(), data);
             } catch (Exception ex) {
                 ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t dispatch custom payload", ex);
-                this.disconnect("Invalid custom payload!");
+                this.disconnect("Invalid custom payload!", org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause
             }
         }
 
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index 1cac9e3074551567960ae2e991414e62f7e089ee..8147396dc18fe199ffbb59437d0873c2f79b3ee7 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -720,7 +720,7 @@ public abstract class PlayerList {
         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", new Object[0]), org.bukkit.event.player.PlayerKickEvent.Cause.DUPLICATE_LOGIN); // Paper - kick event cause
         }
 
         // Instead of kicking then returning, we need to store the kick reason
@@ -1351,8 +1351,8 @@ public abstract class PlayerList {
         // Paper end
         // CraftBukkit start - disconnect safely
         for (ServerPlayer player : this.players) {
-            if (isRestarting) player.connection.disconnect(org.spigotmc.SpigotConfig.restartMessage); else // Paper
-            player.connection.disconnect(this.server.server.shutdownMessage()); // CraftBukkit - add custom shutdown message // Paper - Adventure
+            if (isRestarting) player.connection.disconnect(org.spigotmc.SpigotConfig.restartMessage, org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN); else // Paper - kick event cause (cause is never used here)
+            player.connection.disconnect(this.server.server.shutdownMessage(), org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN); // CraftBukkit - add custom shutdown message // Paper - Adventure & KickEventCause (cause is never used here)
         }
         // CraftBukkit end
 
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 7d030d7a8a58f4a031e09d09c9718af3d3c1e707..140730ff3e10066d19321c75e241a1e0d8d3ba17 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -516,7 +516,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         org.spigotmc.AsyncCatcher.catchOp("player kick"); // Spigot
         if (this.getHandle().connection == null) return;
 
-        this.getHandle().connection.disconnect(message == null ? "" : message);
+        this.getHandle().connection.disconnect(message == null ? "" : message, org.bukkit.event.player.PlayerKickEvent.Cause.PLUGIN); // Paper - kick event cause
     }
 
     // Paper start
@@ -528,10 +528,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);
         }
     }
 
diff --git a/src/main/java/org/spigotmc/RestartCommand.java b/src/main/java/org/spigotmc/RestartCommand.java
index 92d97a5810a379b427a99b4c63fb9844d823a84f..160115bf8a153ff981ba308599d22c4c08026fb6 100644
--- a/src/main/java/org/spigotmc/RestartCommand.java
+++ b/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(SpigotConfig.restartMessage);
+                p.connection.disconnect(SpigotConfig.restartMessage, 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