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 9d7cebd703bd0171ca3e95d2985c1a52fdb59712..9e3b8c7478d97bf65a875807a268d1c98389c1f8 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -2041,7 +2041,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
                 ServerPlayer entityplayer = (ServerPlayer) iterator.next();
 
                 if (!whitelist.isWhiteListed(entityplayer.getGameProfile())) {
-                    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 6a16f2c88fe7b23f1ab92175763b768706674fd5..3f9c53627013f245627faa3a7bad841c56ca0726 100644
--- a/src/main/java/net/minecraft/server/commands/BanIpCommands.java
+++ b/src/main/java/net/minecraft/server/commands/BanIpCommands.java
@@ -74,7 +74,7 @@ public class BanIpCommands {
             while (iterator.hasNext()) {
                 ServerPlayer entityplayer = (ServerPlayer) iterator.next();
 
-                entityplayer.connection.disconnect(new TranslatableComponent("multiplayer.disconnect.ip_banned"));
+                entityplayer.connection.disconnect(new TranslatableComponent("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 cbe96b4af0ed34788149179eb4c521343d627440..0a7d6fa9af2ad8d1d63f1e5bf392b20a28311f67 100644
--- a/src/main/java/net/minecraft/server/commands/BanPlayerCommands.java
+++ b/src/main/java/net/minecraft/server/commands/BanPlayerCommands.java
@@ -52,7 +52,7 @@ public class BanPlayerCommands {
                 ServerPlayer entityplayer = source.getServer().getPlayerList().getPlayer(gameprofile.getId());
 
                 if (entityplayer != null) {
-                    entityplayer.connection.disconnect(new TranslatableComponent("multiplayer.disconnect.banned"));
+                    entityplayer.connection.disconnect(new TranslatableComponent("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 eeebebea3669d29c68a49c1b841afc96e1b92771..370055574a504dd497f18a4a5e7921bd48e73abe 100644
--- a/src/main/java/net/minecraft/server/commands/KickCommand.java
+++ b/src/main/java/net/minecraft/server/commands/KickCommand.java
@@ -31,7 +31,7 @@ public class KickCommand {
         while (iterator.hasNext()) {
             ServerPlayer entityplayer = (ServerPlayer) iterator.next();
 
-            entityplayer.connection.disconnect(reason);
+            entityplayer.connection.disconnect(reason, org.bukkit.event.player.PlayerKickEvent.Cause.KICK_COMMAND); // Paper - kick event cause
             source.sendSuccess(new TranslatableComponent("commands.kick.success", new Object[]{entityplayer.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 c454908f23a436f66f8e64fc346186f113b6eefb..3e257d2cf6bf5609247bd022ccc2f560a9566399 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -327,7 +327,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
         if (this.clientIsFloating && !this.player.isSleeping()) {
             if (++this.aboveGroundTickCount > 80) {
                 ServerGamePacketListenerImpl.LOGGER.warn("{} was kicked for floating too long!", this.player.getName().getString());
-                this.disconnect(com.destroystokyo.paper.PaperConfig.flyingKickPlayerMessage); // Paper - use configurable kick message
+                this.disconnect(com.destroystokyo.paper.PaperConfig.flyingKickPlayerMessage, org.bukkit.event.player.PlayerKickEvent.Cause.FLYING_PLAYER); // Paper - use configurable kick message & kick event cause
                 return;
             }
         } else {
@@ -346,7 +346,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
             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(com.destroystokyo.paper.PaperConfig.flyingKickVehicleMessage); // Paper - use configurable kick message
+                    this.disconnect(com.destroystokyo.paper.PaperConfig.flyingKickVehicleMessage, org.bukkit.event.player.PlayerKickEvent.Cause.FLYING_VEHICLE); // Paper - use configurable kick message & kick event cause
                     return;
                 }
             } else {
@@ -368,7 +368,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
         if (this.isPendingPing()) {
             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(new TranslatableComponent("disconnect.timeout", new Object[0]));
+                this.disconnect(new TranslatableComponent("disconnect.timeout", new Object[0]), org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause
             }
         } else {
             if (elapsedTime >= 15000L) { // 15 seconds
@@ -398,7 +398,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
 
         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(new TranslatableComponent("multiplayer.disconnect.idling"));
+            this.disconnect(new TranslatableComponent("multiplayer.disconnect.idling"), org.bukkit.event.player.PlayerKickEvent.Cause.IDLING); // Paper - kick event cause
         }
 
     }
@@ -423,14 +423,22 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
 
     public void disconnect(String s) {
         // Paper start
-        this.disconnect(PaperAdventure.LEGACY_SECTION_UXRC.deserialize(s));
+        this.disconnect(PaperAdventure.LEGACY_SECTION_UXRC.deserialize(s), org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN);
+    }
+
+    public void disconnect(String s, PlayerKickEvent.Cause cause) {
+        this.disconnect(PaperAdventure.LEGACY_SECTION_UXRC.deserialize(s), cause);
     }
 
     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) {
@@ -438,7 +446,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
         }
         net.kyori.adventure.text.Component leaveMessage = net.kyori.adventure.text.Component.translatable("multiplayer.player.left", net.kyori.adventure.text.format.NamedTextColor.YELLOW, this.player.getBukkitEntity().displayName()); // Paper - Adventure
 
-        PlayerKickEvent event = new PlayerKickEvent(this.craftServer.getPlayer(this.player), reason, leaveMessage); // Paper - Adventure
+        PlayerKickEvent event = new PlayerKickEvent(this.craftServer.getPlayer(this.player), reason, leaveMessage, cause); // Paper - Adventure & kick event reason
 
         if (this.craftServer.getServer().isRunning()) {
             this.craftServer.getPluginManager().callEvent(event);
@@ -516,7 +524,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
     public void handleMoveVehicle(ServerboundMoveVehiclePacket packet) {
         PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel());
         if (containsInvalidValues(packet)) {
-            this.disconnect(new TranslatableComponent("multiplayer.disconnect.invalid_vehicle_movement"));
+            this.disconnect(new TranslatableComponent("multiplayer.disconnect.invalid_vehicle_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_VEHICLE_MOVEMENT); // Paper - kick event cause
         } else {
             Entity entity = this.player.getRootVehicle();
 
@@ -750,13 +758,13 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
         // PlayerConnectionUtils.ensureMainThread(packetplayintabcomplete, this, this.player.getWorldServer()); // Paper - run this async
         // CraftBukkit start
         if (tabSpamLimiter.addAndGet(com.destroystokyo.paper.PaperConfig.tabSpamIncrement) > com.destroystokyo.paper.PaperConfig.tabSpamLimit && !this.server.getPlayerList().isOp(this.player.getGameProfile())) { // Paper start - split and make configurable
-            server.scheduleOnMain(() -> this.disconnect(new TranslatableComponent("disconnect.spam", new Object[0]))); // Paper
+            server.scheduleOnMain(() -> this.disconnect(new TranslatableComponent("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(new TranslatableComponent("disconnect.spam", new Object[0]))); // Paper
+            server.scheduleOnMain(() -> this.disconnect(new TranslatableComponent("disconnect.spam", new Object[0]), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM)); // Paper - kick event cause
             return;
         }
         // Paper end
@@ -904,7 +912,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
         // Paper start - validate pick item position
         if (!(packet.getSlot() >= 0 && packet.getSlot() < this.player.inventory.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.inventory.pickSlot(packet.getSlot()); // Paper - Diff above if changed
@@ -1058,7 +1066,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
             ListTag pageList = testStack.getTag().getList("pages", 8);
             if (pageList.size() > 100) {
                 ServerGamePacketListenerImpl.LOGGER.warn(this.player.getScoreboardName() + " tried to send a book with too many pages");
-                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;
             }
             long byteTotal = 0;
@@ -1070,7 +1078,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
                 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;
@@ -1093,14 +1101,14 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
 
             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;
@@ -1212,7 +1220,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
     public void handleMovePlayer(ServerboundMovePlayerPacket packet) {
         PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel());
         if (containsInvalidValues(packet)) {
-            this.disconnect(new TranslatableComponent("multiplayer.disconnect.invalid_player_movement"));
+            this.disconnect(new TranslatableComponent("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause
         } else {
             ServerLevel worldserver = this.player.getLevel();
 
@@ -1611,7 +1619,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
                         this.dropCount++;
                         if (this.dropCount >= 20) {
                             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;
                         }
                     }
@@ -1924,7 +1932,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
             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
         }
     }
 
@@ -1961,7 +1969,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
                         Waitable waitable = new Waitable() {
                             @Override
                             protected Object evaluate() {
-                                ServerGamePacketListenerImpl.this.disconnect(new TranslatableComponent("multiplayer.disconnect.illegal_characters"));
+                                ServerGamePacketListenerImpl.this.disconnect(new TranslatableComponent("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper - kick event cause
                                 return null;
                             }
                         };
@@ -1976,7 +1984,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
                             throw new RuntimeException(e);
                         }
                     } else {
-                        this.disconnect(new TranslatableComponent("multiplayer.disconnect.illegal_characters"));
+                        this.disconnect(new TranslatableComponent("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper - kick event cause
                     }
                     // CraftBukkit end
                     return;
@@ -2030,7 +2038,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
                     Waitable waitable = new Waitable() {
                         @Override
                         protected Object evaluate() {
-                            ServerGamePacketListenerImpl.this.disconnect(new TranslatableComponent("disconnect.spam"));
+                            ServerGamePacketListenerImpl.this.disconnect(new TranslatableComponent("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause
                             return null;
                         }
                     };
@@ -2045,7 +2053,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
                         throw new RuntimeException(e);
                     }
                 } else {
-                    this.disconnect(new TranslatableComponent("disconnect.spam"));
+                    this.disconnect(new TranslatableComponent("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause
                 }
                 // CraftBukkit end
             }
@@ -2318,7 +2326,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
         // Spigot Start
         if ( entity == player && !player.isSpectator() )
         {
-            disconnect( "Cannot interact with self!" );
+            disconnect( "Cannot interact with self!", org.bukkit.event.player.PlayerKickEvent.Cause.SELF_INTERACTION ); // Paper - kick event cause
             return;
         }
         // Spigot End
@@ -2395,7 +2403,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
                     // CraftBukkit end
                 } else if (packet.getAction() == ServerboundInteractPacket.Action.ATTACK) {
                     if (entity instanceof ItemEntity || entity instanceof ExperienceOrb || entity instanceof AbstractArrow || (entity == this.player && !player.isSpectator())) { // CraftBukkit
-                        this.disconnect(new TranslatableComponent("multiplayer.disconnect.invalid_entity_attacked"));
+                        this.disconnect(new TranslatableComponent("multiplayer.disconnect.invalid_entity_attacked"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_ENTITY_ATTACKED); // Paper - kick event cause
                         ServerGamePacketListenerImpl.LOGGER.warn("Player {} tried to attack an invalid entity", this.player.getName().getString());
                         return;
                     }
@@ -2800,7 +2808,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
         // Paper start
         if (!Bukkit.isPrimaryThread()) {
             if (recipeSpamPackets.addAndGet(PaperConfig.autoRecipeIncrement) > PaperConfig.autoRecipeLimit) {
-                server.scheduleOnMain(() -> this.disconnect(new TranslatableComponent("disconnect.spam", new Object[0]))); // Paper
+                server.scheduleOnMain(() -> this.disconnect(new TranslatableComponent("disconnect.spam", new Object[0]), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM)); // Paper - kick event cause
                 return;
             }
         }
@@ -2999,7 +3007,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
         } else if (!this.isSingleplayerOwner()) {
             // Paper start - This needs to be handled on the main thread for plugins
             server.scheduleOnMain(() -> {
-            this.disconnect(new TranslatableComponent("disconnect.timeout"));
+            this.disconnect(new TranslatableComponent("disconnect.timeout"), org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause
             });
             // Paper end
         }
@@ -3045,7 +3053,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
                 }
             } 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 {
@@ -3055,7 +3063,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
                 }
             } 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 {
@@ -3074,7 +3082,7 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
                 craftServer.getMessenger().dispatchIncomingMessage(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 6011b43ae8a858f88b8fcf6dc0bf147024a4742c..9e138bf9f9ee4efee462271d1a69a2126aa94946 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -716,7 +716,7 @@ public abstract class PlayerList {
         while (iterator.hasNext()) {
             entityplayer = (ServerPlayer) iterator.next();
             save(entityplayer); // CraftBukkit - Force the player's inventory to be saved
-            entityplayer.connection.disconnect(new TranslatableComponent("multiplayer.disconnect.duplicate_login", new Object[0]));
+            entityplayer.connection.disconnect(new TranslatableComponent("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
@@ -1385,8 +1385,8 @@ public abstract class PlayerList {
     public void shutdown(boolean isRestarting) {
         // 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 3dbe94d9b9647f5cc1e27335b36042e50c652cea..97aec6370a8a24a13ae04443d03f250f8938b2e0 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -495,16 +495,21 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         org.spigotmc.AsyncCatcher.catchOp("player kick"); // Spigot
         if (getHandle().connection == null) return;
 
-        getHandle().connection.disconnect(message == null ? "" : message);
+        getHandle().connection.disconnect(message == null ? "" : message, org.bukkit.event.player.PlayerKickEvent.Cause.PLUGIN); // Paper - kick event cause
     }
 
     // Paper start
     @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);
         }
     }
     // Paper end
diff --git a/src/main/java/org/spigotmc/RestartCommand.java b/src/main/java/org/spigotmc/RestartCommand.java
index 6498dc4c6630bfef1a52edf74d8574e5e4876720..ea1f088ac94616978af5e01a59c558cd2db4b619 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