diff --git a/patches/api/0156-Add-Material-Tags.patch b/patches/api/0156-Add-Material-Tags.patch
index 642ef12f82..e0cbc1d95c 100644
--- a/patches/api/0156-Add-Material-Tags.patch
+++ b/patches/api/0156-Add-Material-Tags.patch
@@ -114,7 +114,7 @@ index 0000000000000000000000000000000000000000..a02a02aa0c87e0f0ed9e509e4dcab015
 +}
 diff --git a/src/main/java/com/destroystokyo/paper/MaterialTags.java b/src/main/java/com/destroystokyo/paper/MaterialTags.java
 new file mode 100644
-index 0000000000000000000000000000000000000000..1a78872e26f8fadbddd9af15fff063d03690077f
+index 0000000000000000000000000000000000000000..0c3cd2856f7df3702ec0ff6b8dba1e052929863e
 --- /dev/null
 +++ b/src/main/java/com/destroystokyo/paper/MaterialTags.java
 @@ -0,0 +1,704 @@
@@ -223,7 +223,7 @@ index 0000000000000000000000000000000000000000..1a78872e26f8fadbddd9af15fff063d0
 +     */
 +    public static final MaterialSetTag DOORS = new MaterialSetTag(keyFor("doors"))
 +        .endsWith("_DOOR")
-+        .ensureSize("DOORS", 10).lock();
++        .ensureSize("DOORS", 11).lock();
 +
 +    /**
 +     * Covers all dyes.
@@ -237,14 +237,14 @@ index 0000000000000000000000000000000000000000..1a78872e26f8fadbddd9af15fff063d0
 +     */
 +    public static final MaterialSetTag FENCE_GATES = new MaterialSetTag(keyFor("fence_gates"))
 +        .endsWith("_GATE")
-+        .ensureSize("FENCE_GATES", 9).lock();
++        .ensureSize("FENCE_GATES", 10).lock();
 +
 +    /**
 +     * Covers all variants of fences.
 +     */
 +    public static final MaterialSetTag FENCES = new MaterialSetTag(keyFor("fences"))
 +        .endsWith("_FENCE")
-+        .ensureSize("FENCES", 10).lock();
++        .ensureSize("FENCES", 11).lock();
 +
 +    /**
 +     * Covers all variants of fish buckets.
@@ -358,7 +358,7 @@ index 0000000000000000000000000000000000000000..1a78872e26f8fadbddd9af15fff063d0
 +     */
 +    public static final MaterialSetTag PRESSURE_PLATES = new MaterialSetTag(keyFor("pressure_plates"))
 +        .endsWith("_PRESSURE_PLATE")
-+        .ensureSize("PRESSURE_PLATES", 13).lock();
++        .ensureSize("PRESSURE_PLATES", 14).lock();
 +
 +    /**
 +     * Covers the variants of prismarine blocks.
@@ -430,14 +430,14 @@ index 0000000000000000000000000000000000000000..1a78872e26f8fadbddd9af15fff063d0
 +        .endsWith("_HEAD")
 +        .endsWith("_SKULL")
 +        .not(Material.PISTON_HEAD)
-+        .ensureSize("SKULLS", 12).lock();
++        .ensureSize("SKULLS", 14).lock();
 +
 +    /**
 +     * Covers all spawn egg items.
 +     */
 +    public static final MaterialSetTag SPAWN_EGGS = new MaterialSetTag(keyFor("spawn_eggs"))
 +        .endsWith("_SPAWN_EGG")
-+        .ensureSize("SPAWN_EGGS", 71).lock();
++        .ensureSize("SPAWN_EGGS", 76).lock();
 +
 +    /**
 +     * Covers all colors of stained glass.
@@ -458,7 +458,7 @@ index 0000000000000000000000000000000000000000..1a78872e26f8fadbddd9af15fff063d0
 +     */
 +    public static final MaterialSetTag TRAPDOORS = new MaterialSetTag(keyFor("trapdoors"))
 +        .endsWith("_TRAPDOOR")
-+        .ensureSize("TRAPDOORS", 10).lock();
++        .ensureSize("TRAPDOORS", 11).lock();
 +
 +    /**
 +     * Covers all wood variants of doors.
@@ -466,7 +466,7 @@ index 0000000000000000000000000000000000000000..1a78872e26f8fadbddd9af15fff063d0
 +    public static final MaterialSetTag WOODEN_DOORS = new MaterialSetTag(keyFor("wooden_doors"))
 +        .endsWith("_DOOR")
 +        .not(Material.IRON_DOOR)
-+        .ensureSize("WOODEN_DOORS", 9).lock();
++        .ensureSize("WOODEN_DOORS", 10).lock();
 +
 +    /**
 +     * Covers all wood variants of fences.
@@ -474,7 +474,7 @@ index 0000000000000000000000000000000000000000..1a78872e26f8fadbddd9af15fff063d0
 +    public static final MaterialSetTag WOODEN_FENCES = new MaterialSetTag(keyFor("wooden_fences"))
 +        .endsWith("_FENCE")
 +        .not(Material.NETHER_BRICK_FENCE)
-+        .ensureSize("WOODEN_FENCES", 9).lock();
++        .ensureSize("WOODEN_FENCES", 10).lock();
 +
 +    /**
 +     * Covers all wood variants of trapdoors.
@@ -482,14 +482,14 @@ index 0000000000000000000000000000000000000000..1a78872e26f8fadbddd9af15fff063d0
 +    public static final MaterialSetTag WOODEN_TRAPDOORS = new MaterialSetTag(keyFor("wooden_trapdoors"))
 +        .endsWith("_TRAPDOOR")
 +        .not(Material.IRON_TRAPDOOR)
-+        .ensureSize("WOODEN_TRAPDOORS", 9).lock();
++        .ensureSize("WOODEN_TRAPDOORS", 10).lock();
 +
 +    /**
 +     * Covers the wood variants of gates.
 +     */
 +    public static final MaterialSetTag WOODEN_GATES = new MaterialSetTag(keyFor("wooden_gates"))
 +        .endsWith("_GATE")
-+        .ensureSize("WOODEN_GATES", 9).lock();
++        .ensureSize("WOODEN_GATES", 10).lock();
 +
 +    /**
 +     * Covers the variants of purpur.
@@ -503,7 +503,7 @@ index 0000000000000000000000000000000000000000..1a78872e26f8fadbddd9af15fff063d0
 +     */
 +    public static final MaterialSetTag SIGNS = new MaterialSetTag(keyFor("signs"))
 +        .endsWith("_SIGN")
-+        .ensureSize("SIGNS", 18).lock();
++        .ensureSize("SIGNS", 40).lock();
 +
 +    /**
 +     * Covers the variants of a regular torch.
@@ -596,7 +596,7 @@ index 0000000000000000000000000000000000000000..1a78872e26f8fadbddd9af15fff063d0
 +        .endsWith("_HELMET")
 +        .add(SKULLS)
 +        .add(Material.CARVED_PUMPKIN)
-+        .ensureSize("HEAD_EQUIPPABLE", 20).lock();
++        .ensureSize("HEAD_EQUIPPABLE", 22).lock();
 +
 +    /**
 +     * Covers the variants of chestplate.
@@ -824,13 +824,14 @@ index 0000000000000000000000000000000000000000..1a78872e26f8fadbddd9af15fff063d0
 +}
 diff --git a/src/main/java/io/papermc/paper/tag/BaseTag.java b/src/main/java/io/papermc/paper/tag/BaseTag.java
 new file mode 100644
-index 0000000000000000000000000000000000000000..71484259de4783b861803c48850317eb2d60bda0
+index 0000000000000000000000000000000000000000..794787912325ae32b3cfc8217bc3fc2159ceabd5
 --- /dev/null
 +++ b/src/main/java/io/papermc/paper/tag/BaseTag.java
-@@ -0,0 +1,180 @@
+@@ -0,0 +1,181 @@
 +package io.papermc.paper.tag;
 +
 +import com.google.common.collect.Lists;
++import java.util.Collections;
 +import org.bukkit.Keyed;
 +import org.bukkit.NamespacedKey;
 +import org.bukkit.Tag;
@@ -900,7 +901,7 @@ index 0000000000000000000000000000000000000000..71484259de4783b861803c48850317eb
 +    @NotNull
 +    @Override
 +    public Set<T> getValues() {
-+        return tagged;
++        return Collections.unmodifiableSet(tagged);
 +    }
 +
 +    @Override
@@ -1117,7 +1118,7 @@ index 0000000000000000000000000000000000000000..d7eb49a05c3f0cacf285f8995433c5d5
 +        .ensureSize("WATER_BASED", 11).lock();
 +}
 diff --git a/src/main/java/org/bukkit/Tag.java b/src/main/java/org/bukkit/Tag.java
-index 6f6e359dfdba27f6550719479a2256a2b159d39e..60e98f0eb8028fc9dfb73320b2939a33435174c1 100644
+index bceaa1a97b2c5cc15c8f54ae8f6f18029708627a..15699ee58e06880a508689f761ecfdb77d44d182 100644
 --- a/src/main/java/org/bukkit/Tag.java
 +++ b/src/main/java/org/bukkit/Tag.java
 @@ -11,6 +11,10 @@ import org.jetbrains.annotations.NotNull;
diff --git a/patches/server/0009-Adventure.patch b/patches/server/0009-Adventure.patch
index 78967f9331..6e62c46196 100644
--- a/patches/server/0009-Adventure.patch
+++ b/patches/server/0009-Adventure.patch
@@ -1451,6 +1451,36 @@ index d476bf8435ee585d8c4faa431716524774527acc..36bbe7d0b2089361beda89097c15eca9
  
      public CommandSourceStack(CommandSource output, Vec3 pos, Vec2 rot, ServerLevel world, int level, String name, Component displayName, MinecraftServer server, @Nullable Entity entity) {
          this(output, pos, rot, world, level, name, displayName, server, entity, false, (commandcontext, flag, j) -> {
+diff --git a/src/main/java/net/minecraft/commands/arguments/MessageArgument.java b/src/main/java/net/minecraft/commands/arguments/MessageArgument.java
+index 42242735f8f85b1852381a9f77368527203172b4..4d0694c478d476717fd11f8975955c1741b47abf 100644
+--- a/src/main/java/net/minecraft/commands/arguments/MessageArgument.java
++++ b/src/main/java/net/minecraft/commands/arguments/MessageArgument.java
+@@ -50,10 +50,10 @@ public class MessageArgument implements SignedArgument<MessageArgument.Message>
+     private static void resolveSignedMessage(Consumer<PlayerChatMessage> callback, CommandSourceStack source, PlayerChatMessage message) {
+         MinecraftServer minecraftServer = source.getServer();
+         CompletableFuture<FilteredText> completableFuture = filterPlainText(source, message);
+-        CompletableFuture<Component> completableFuture2 = minecraftServer.getChatDecorator().decorate(source.getPlayer(), message.decoratedContent());
++        CompletableFuture<net.minecraft.network.chat.ChatDecorator.Result> completableFuture2 = minecraftServer.getChatDecorator().decorate(source.getPlayer(), source, message.decoratedContent()); // Paper
+         source.getChatMessageChainer().append((executor) -> {
+             return CompletableFuture.allOf(completableFuture, completableFuture2).thenAcceptAsync((void_) -> {
+-                PlayerChatMessage playerChatMessage2 = message.withUnsignedContent(completableFuture2.join()).filter(completableFuture.join().mask());
++                PlayerChatMessage playerChatMessage2 = message.withUnsignedContent(completableFuture2.join().component()).filter(completableFuture.join().mask()); // Paper
+                 callback.accept(playerChatMessage2);
+             }, executor);
+         });
+@@ -61,10 +61,10 @@ public class MessageArgument implements SignedArgument<MessageArgument.Message>
+ 
+     private static void resolveDisguisedMessage(Consumer<PlayerChatMessage> callback, CommandSourceStack source, PlayerChatMessage message) {
+         MinecraftServer minecraftServer = source.getServer();
+-        CompletableFuture<Component> completableFuture = minecraftServer.getChatDecorator().decorate(source.getPlayer(), message.decoratedContent());
++        CompletableFuture<net.minecraft.network.chat.ChatDecorator.Result> completableFuture = minecraftServer.getChatDecorator().decorate(source.getPlayer(), source, message.decoratedContent()); // Paper
+         source.getChatMessageChainer().append((executor) -> {
+             return completableFuture.thenAcceptAsync((content) -> {
+-                callback.accept(message.withUnsignedContent(content));
++                callback.accept(message.withUnsignedContent(content.component())); // Paper
+             }, executor);
+         });
+     }
 diff --git a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java
 index ca5d08eb4115817f846b312fe35f4ab28a099401..f25b9330e068c7d9e12cb57a7761cfef9ebaf7bc 100644
 --- a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java
@@ -1522,14 +1552,14 @@ index 021a26a6b1c258deffc26c035ab52a4ea027d9a1..00d432bd395e7f7fb6ee24e371818d13
                  try {
                      int i = friendlyByteBuf.writerIndex();
 diff --git a/src/main/java/net/minecraft/network/chat/ChatDecorator.java b/src/main/java/net/minecraft/network/chat/ChatDecorator.java
-index 825ab7534f1ad9787ae2a6c2bf9a300f52cbfc95..a12b50fdf62886a529a0dfad84fa44b066127455 100644
+index 825ab7534f1ad9787ae2a6c2bf9a300f52cbfc95..53be8a43d784db5e8450c242adeb06f3fab2717a 100644
 --- a/src/main/java/net/minecraft/network/chat/ChatDecorator.java
 +++ b/src/main/java/net/minecraft/network/chat/ChatDecorator.java
-@@ -10,5 +10,72 @@ public interface ChatDecorator {
+@@ -10,5 +10,64 @@ public interface ChatDecorator {
          return CompletableFuture.completedFuture(message);
      };
  
-+    @io.papermc.paper.annotation.DoNotUse // Paper
++    @io.papermc.paper.annotation.DoNotUse @Deprecated // Paper
      CompletableFuture<Component> decorate(@Nullable ServerPlayer sender, Component message);
 +
 +    // Paper start
@@ -1577,25 +1607,17 @@ index 825ab7534f1ad9787ae2a6c2bf9a300f52cbfc95..a12b50fdf62886a529a0dfad84fa44b0
 +        }
 +    }
 +
-+    record ModernResult(Component maybeAdventureComponent, boolean hasNoFormatting, boolean modernized) implements Result {
++    record ModernResult(Component component, boolean hasNoFormatting, boolean modernized) implements Result {
 +        public ModernResult(net.kyori.adventure.text.Component component, boolean hasNoFormatting, boolean modernized) {
 +            this(io.papermc.paper.adventure.PaperAdventure.asVanilla(component), hasNoFormatting, modernized);
 +        }
 +
 +        @Override
-+        public Component component() {
-+            return this.maybeAdventureComponent instanceof io.papermc.paper.adventure.AdventureComponent adventureComponent ? adventureComponent.deepConverted() : this.maybeAdventureComponent;
-+        }
-+
-+        @Override
 +        public MessagePair message() {
-+            final net.kyori.adventure.text.Component adventureComponent = io.papermc.paper.adventure.PaperAdventure.WRAPPER_AWARE_SERIALIZER.deserialize(this.maybeAdventureComponent);
++            final net.kyori.adventure.text.Component adventureComponent = io.papermc.paper.adventure.PaperAdventure.WRAPPER_AWARE_SERIALIZER.deserialize(this.component);
 +            return new MessagePair(adventureComponent, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(adventureComponent));
 +        }
 +    }
-+    default CompletableFuture<PlayerChatMessage> decorate(@Nullable ServerPlayer serverPlayer, @Nullable net.minecraft.commands.CommandSourceStack commandSourceStack, PlayerChatMessage playerChatMessage) {
-+        return this.decorate(serverPlayer, commandSourceStack, playerChatMessage.decoratedContent()).thenApply(result -> playerChatMessage.withUnsignedContent(result.component()));
-+    }
 +    // Paper end
  }
 diff --git a/src/main/java/net/minecraft/network/chat/Component.java b/src/main/java/net/minecraft/network/chat/Component.java
@@ -1688,7 +1710,7 @@ index f8773f2982e6cd40661d138a7c32f219cda9225c..74cf1c043beef03cfd5adf481414a5ee
                  sender.connection.sendPlayerChatMessage(playerChatMessage, params);
              }
 diff --git a/src/main/java/net/minecraft/network/chat/PlayerChatMessage.java b/src/main/java/net/minecraft/network/chat/PlayerChatMessage.java
-index b3a44ed8f365daf1031d46d879c84d2ea15cd951..acbcb582680c0e3215c4e083a2b01b81e1fcfa25 100644
+index b3a44ed8f365daf1031d46d879c84d2ea15cd951..3c7276598acb3711f8ceccaad95b30c72ea87d77 100644
 --- a/src/main/java/net/minecraft/network/chat/PlayerChatMessage.java
 +++ b/src/main/java/net/minecraft/network/chat/PlayerChatMessage.java
 @@ -15,7 +15,19 @@ import net.minecraft.util.ExtraCodecs;
@@ -1712,6 +1734,15 @@ index b3a44ed8f365daf1031d46d879c84d2ea15cd951..acbcb582680c0e3215c4e083a2b01b81
      public static final MapCodec<PlayerChatMessage> MAP_CODEC = RecordCodecBuilder.mapCodec((instance) -> {
          return instance.group(SignedMessageLink.CODEC.fieldOf("link").forGetter(PlayerChatMessage::link), MessageSignature.CODEC.optionalFieldOf("signature").forGetter((message) -> {
              return Optional.ofNullable(message.signature);
+@@ -40,7 +52,7 @@ public record PlayerChatMessage(SignedMessageLink link, @Nullable MessageSignatu
+     }
+ 
+     public PlayerChatMessage withUnsignedContent(Component unsignedContent) {
+-        Component component = !unsignedContent.equals(Component.literal(this.signedContent())) ? unsignedContent : null;
++        Component component = !(unsignedContent instanceof io.papermc.paper.adventure.AdventureComponent advComponent ? advComponent.deepConverted() : unsignedContent).equals(Component.literal(this.signedContent())) ? unsignedContent : null; // Paper
+         return new PlayerChatMessage(this.link, this.signature, this.signedBody, component, this.filterMask);
+     }
+ 
 diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetActionBarTextPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetActionBarTextPacket.java
 index 02183c810f9968621b9b20c1f7b54258b620c507..32ef3edebe94a2014168b7e438752a80b2687e5f 100644
 --- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetActionBarTextPacket.java
diff --git a/patches/server/0345-Anti-Xray.patch b/patches/server/0345-Anti-Xray.patch
index ca4b4e96a7..535262ed57 100644
--- a/patches/server/0345-Anti-Xray.patch
+++ b/patches/server/0345-Anti-Xray.patch
@@ -1045,7 +1045,7 @@ index 7825d6f0fdcfda6212cff8033ec55fb7db236154..000853110c7a89f2d0403a7a2737025a
  
      public ClientboundLevelChunkWithLightPacket(FriendlyByteBuf buf) {
 diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 2a988b6cd8ef3b3d9a0e857fcc0bb4459d815a47..750b1f0793efc2e45846d6f6f03852f2d11d949a 100644
+index 4b05139db6628808128337dbf817712e339c17d0..8371aac0302c8f7c327e0665d6c44d6482519522 100644
 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java
 +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
 @@ -630,7 +630,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1057,7 +1057,14 @@ index 2a988b6cd8ef3b3d9a0e857fcc0bb4459d815a47..750b1f0793efc2e45846d6f6f03852f2
          if (player.level == this.level) {
              if (newWithinViewDistance && !oldWithinViewDistance) {
                  ChunkHolder playerchunk = this.getVisibleChunkIfPresent(pos.toLong());
-@@ -1132,12 +1132,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1126,18 +1126,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+         for (Iterator iterator = this.getPlayers(chunkcoordintpair, false).iterator(); iterator.hasNext(); entityplayer.trackChunk(chunkcoordintpair, (Packet) mutableobject.getValue())) {
+             entityplayer = (ServerPlayer) iterator.next();
+             if (mutableobject.getValue() == null) {
+-                mutableobject.setValue(new ClientboundLevelChunkWithLightPacket(chunk1, this.lightEngine, (BitSet) null, (BitSet) null, true));
++                mutableobject.setValue(new ClientboundLevelChunkWithLightPacket(chunk1, this.lightEngine, (BitSet) null, (BitSet) null, true, true)); // Paper - Anti-Xray
+             }
+         }
  
      }
  
@@ -1080,7 +1087,7 @@ index 2a988b6cd8ef3b3d9a0e857fcc0bb4459d815a47..750b1f0793efc2e45846d6f6f03852f2
          List<Entity> list = Lists.newArrayList();
          List<Entity> list1 = Lists.newArrayList();
 diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 6a4759a5ec5802f7203febf157c7d6dcf6dce535..a94c43386b372cdff0b207bebbf8d5b29b5fd022 100644
+index 3b528a6adaa431ebdf11ce2ce8ea3c99f3b1dbe3..76c388347ebbff2d50a975b40dbe93cc2760f6bb 100644
 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java
 +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
 @@ -438,7 +438,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
@@ -1230,7 +1237,7 @@ index ae37e97e52557b48f129cc02eeea395378a48444..785fbcf9bafcdec1c5be213de3d85126
  
      public int getSerializedSize() {
 diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
-index e310b018004c8d7edbfec877f06671d17f7e47fd..510bbdc5b79736fca2f116a980e71f85b65cf9bc 100644
+index 4843bd864deba357e0a4b2fd844324218af9774f..02b7e3261f689b9d30b87661db23425f741b0fec 100644
 --- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
 +++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
 @@ -29,6 +29,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@@ -1525,7 +1532,7 @@ index 6772b7e6ebded67f656dfb1af99e4d516aca67c5..1ac0778fba70ba9f09487c76e086b5a8
  
      public CraftChunk(net.minecraft.world.level.chunk.LevelChunk chunk) {
 diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 9b5d3dbf302a44d6ed774c8397117e7a6231b2f9..b8e2b34973ea980e527a2064d10b21a42ad7f376 100644
+index 6fc57df47db575f4f3d56784ac0bc8b496d1eba1..9b187eba01689ae640117bd638ffe9f16499d051 100644
 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
 +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
 @@ -2259,7 +2259,7 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0634-Add-PlayerKickEvent-causes.patch b/patches/server/0634-Add-PlayerKickEvent-causes.patch
index a60c94d0e5..44512f85d6 100644
--- a/patches/server/0634-Add-PlayerKickEvent-causes.patch
+++ b/patches/server/0634-Add-PlayerKickEvent-causes.patch
@@ -4,6 +4,37 @@ Date: Sat, 15 May 2021 20:30:45 -0700
 Subject: [PATCH] Add PlayerKickEvent causes
 
 
+diff --git a/src/main/java/net/minecraft/network/chat/SignedMessageChain.java b/src/main/java/net/minecraft/network/chat/SignedMessageChain.java
+index b5f54ee82905e3e6ab5bfde35ab625f5feeb1393..c0a80824a0307ea673805015119cc834b268f0dc 100644
+--- a/src/main/java/net/minecraft/network/chat/SignedMessageChain.java
++++ b/src/main/java/net/minecraft/network/chat/SignedMessageChain.java
+@@ -39,7 +39,7 @@ public class SignedMessageChain {
+             } else {
+                 PlayerChatMessage playerChatMessage = new PlayerChatMessage(signedMessageLink, signature, body, (Component)null, FilterMask.PASS_THROUGH);
+                 if (!playerChatMessage.verify(signatureValidator)) {
+-                    throw new SignedMessageChain.DecodeException(Component.translatable("multiplayer.disconnect.unsigned_chat"), true);
++                    throw new SignedMessageChain.DecodeException(Component.translatable("multiplayer.disconnect.unsigned_chat"), true, org.bukkit.event.player.PlayerKickEvent.Cause.UNSIGNED_CHAT); // Paper - kick event causes
+                 } else {
+                     if (playerChatMessage.hasExpiredServer(Instant.now())) {
+                         LOGGER.warn("Received expired chat: '{}'. Is the client/server system time unsynchronized?", (Object)body.content());
+@@ -63,10 +63,17 @@ public class SignedMessageChain {
+ 
+     public static class DecodeException extends ThrowingComponent {
+         private final boolean shouldDisconnect;
++        public final org.bukkit.event.player.PlayerKickEvent.Cause kickCause; // Paper
+ 
+         public DecodeException(Component message, boolean shouldDisconnect) {
++            // Paper start
++            this(message, shouldDisconnect, org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN);
++        }
++        public DecodeException(Component message, boolean shouldDisconnect, org.bukkit.event.player.PlayerKickEvent.Cause kickCause) {
++            // Paper end
+             super(message);
+             this.shouldDisconnect = shouldDisconnect;
++            this.kickCause = kickCause; // Paper
+         }
+ 
+         public boolean shouldDisconnect() {
 diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
 index 13f8b4699c5b3a99715fada2d774469fb5c17363..213033a59be4239f1225286764be83dcc6fbf973 100644
 --- a/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -57,7 +88,7 @@ index 65637a33ba171a4b598f70cd943d24b0ee44a69f..57a9146bf2dee7a60aab16716e25348f
          }
  
 diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
-index 96f6eb2b152d1183ee45cb0b77c9c77ae8e03950..39d96d24c532c51c3ca8051b7fcf4b55c5971ef9 100644
+index 6680ef4b625e539de951a7ad59d68229fe49303a..297cc815a15096c1f723ed451d76fd55d9d5e629 100644
 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 @@ -362,7 +362,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
@@ -267,6 +298,15 @@ index 96f6eb2b152d1183ee45cb0b77c9c77ae8e03950..39d96d24c532c51c3ca8051b7fcf4b55
          } else {
              Optional<LastSeenMessages> optional = this.tryHandleChat(packet.command(), packet.timeStamp(), packet.lastSeenMessages());
  
+@@ -2169,7 +2179,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
+ 
+     private void handleMessageDecodeFailure(SignedMessageChain.DecodeException exception) {
+         if (exception.shouldDisconnect()) {
+-            this.disconnect(exception.getComponent());
++            this.disconnect(exception.getComponent(), exception.kickCause); // Paper - kick event causes
+         } else {
+             this.player.sendSystemMessage(exception.getComponent().copy().withStyle(ChatFormatting.RED));
+         }
 @@ -2200,7 +2210,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
      private Optional<LastSeenMessages> tryHandleChat(String message, Instant timestamp, LastSeenMessages.Update acknowledgment) {
          if (!this.updateChatOrder(timestamp)) {
@@ -276,6 +316,15 @@ index 96f6eb2b152d1183ee45cb0b77c9c77ae8e03950..39d96d24c532c51c3ca8051b7fcf4b55
              return Optional.empty();
          } 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));
+@@ -2221,7 +2231,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
+ 
+             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;
 @@ -2444,7 +2454,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
          // this.chatSpamTickCount += 20;
          if (this.chatSpamTickCount.addAndGet(20) > 200 && !this.server.getPlayerList().isOp(this.player.getGameProfile())) {
@@ -285,6 +334,15 @@ index 96f6eb2b152d1183ee45cb0b77c9c77ae8e03950..39d96d24c532c51c3ca8051b7fcf4b55
          }
  
      }
+@@ -2456,7 +2466,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
+         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
+             }
+ 
+         }
 @@ -2597,7 +2607,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
              }
  
@@ -357,6 +415,24 @@ index 96f6eb2b152d1183ee45cb0b77c9c77ae8e03950..39d96d24c532c51c3ca8051b7fcf4b55
              }
          }
  
+@@ -3450,7 +3460,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
+ 
+         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.getServiceSignatureValidator();
+@@ -3458,7 +3468,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
+                     this.resetPlayerChatState(remotechatsession_a.validate(this.player.getGameProfile(), signaturevalidator, Duration.ZERO));
+                 } 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
+                 }
+ 
+             }
 diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
 index 16210f30249ecc83c92e0374f7821693a9fd27c1..20598ef01293d8889145921cb3c5a9c7287b6b23 100644
 --- a/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -381,6 +457,39 @@ index 16210f30249ecc83c92e0374f7821693a9fd27c1..20598ef01293d8889145921cb3c5a9c7
          }
          // CraftBukkit end
  
+diff --git a/src/main/java/net/minecraft/world/entity/player/ProfilePublicKey.java b/src/main/java/net/minecraft/world/entity/player/ProfilePublicKey.java
+index a24e7a66d52eddbdad8db71cf5e45f1a458c389f..e1c13ac7b11fb0080435fc34502208c8fa3790e8 100644
+--- a/src/main/java/net/minecraft/world/entity/player/ProfilePublicKey.java
++++ b/src/main/java/net/minecraft/world/entity/player/ProfilePublicKey.java
+@@ -24,9 +24,9 @@ public record ProfilePublicKey(ProfilePublicKey.Data data) {
+ 
+     public static ProfilePublicKey createValidated(SignatureValidator servicesSignatureVerifier, UUID playerUuid, ProfilePublicKey.Data publicKeyData, Duration gracePeriod) throws ProfilePublicKey.ValidationException {
+         if (publicKeyData.hasExpired(gracePeriod)) {
+-            throw new ProfilePublicKey.ValidationException(EXPIRED_PROFILE_PUBLIC_KEY);
++            throw new ProfilePublicKey.ValidationException(EXPIRED_PROFILE_PUBLIC_KEY, org.bukkit.event.player.PlayerKickEvent.Cause.EXPIRED_PROFILE_PUBLIC_KEY); // Paper - kick event causes
+         } else 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);
+         }
+@@ -83,8 +83,16 @@ public record ProfilePublicKey(ProfilePublicKey.Data data) {
+     }
+ 
+     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/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
 index d404233e4982dc93fbca408003e52c6892253707..cc9db27d89c3b2a0ee7bc42f88bb68902188ba13 100644
 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
diff --git a/patches/server/0881-Remove-invalid-signature-login-stacktrace.patch b/patches/server/0881-Remove-invalid-signature-login-stacktrace.patch
index 0515815aa9..bdd4aae878 100644
--- a/patches/server/0881-Remove-invalid-signature-login-stacktrace.patch
+++ b/patches/server/0881-Remove-invalid-signature-login-stacktrace.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Remove invalid signature login stacktrace
 
 
 diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
-index ce9ed6aa7133239e3bbda1cb37c756164fee0159..b24676aaf4ea18cd3cd4e7eec8de544f20df37d6 100644
+index d37829b0a310abf212fbb516a27ae70abc265f64..230c3629f5e6bf80422812e7684a9bb643b1a46d 100644
 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 @@ -3593,7 +3593,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
@@ -13,7 +13,7 @@ index ce9ed6aa7133239e3bbda1cb37c756164fee0159..b24676aaf4ea18cd3cd4e7eec8de544f
                      this.resetPlayerChatState(remotechatsession_a.validate(this.player.getGameProfile(), signaturevalidator, Duration.ZERO));
                  } catch (ProfilePublicKey.ValidationException profilepublickey_b) {
 -                    ServerGamePacketListenerImpl.LOGGER.error("Failed to validate profile key: {}", profilepublickey_b.getMessage());
-+                    //ServerGamePacketListenerImpl.LOGGER.error("Failed to validate profile key: {}", profilepublickey_b.getMessage()); // Paper - unnecessary log
-                     this.disconnect(profilepublickey_b.getComponent());
++                    // ServerGamePacketListenerImpl.LOGGER.error("Failed to validate profile key: {}", profilepublickey_b.getMessage()); // Paper - unnecessary log
+                     this.disconnect(profilepublickey_b.getComponent(), profilepublickey_b.kickCause); // Paper - kick event causes
                  }