From bc99c40604001a5bd8e593bc023682b0b6c6ee8c Mon Sep 17 00:00:00 2001
From: Nassim Jahnke <nassim@njahnke.dev>
Date: Sun, 21 Jan 2024 17:39:05 +0100
Subject: [PATCH] [ci skip] Add more patch identifying comments, cleanup

---
 ...ock-entities-from-a-chunk-without-s.patch} |  2 +-
 ...nate-Current-redstone-implementation.patch |  8 +--
 ...ies-option-to-debug-dupe-uuid-issues.patch | 70 +++----------------
 patches/server/Add-EntityMoveEvent.patch      |  8 +--
 patches/server/Add-More-Creeper-API.patch     |  4 +-
 patches/server/Add-PhantomPreSpawnEvent.patch |  6 +-
 .../server/Add-PlayerKickEvent-causes.patch   |  2 +-
 patches/server/Add-TNTPrimeEvent.patch        | 18 ++---
 ...n-in-sunlight-API-for-Phantoms-and-S.patch |  1 -
 ...Allow-disabling-armor-stand-ticking.patch} | 22 +++---
 patches/server/Anti-Xray.patch                |  2 +-
 patches/server/AnvilDamageEvent.patch         |  4 +-
 ...nd-make-tab-spam-limits-configurable.patch |  4 +-
 patches/server/Collision-optimisations.patch  |  6 +-
 ...le-speed-for-water-flowing-over-lava.patch |  8 +--
 ...ktraces-in-log-messages-crash-report.patch | 24 -------
 ...n-t-fire-sync-events-during-worldgen.patch |  4 +-
 .../Duplicate-UUID-Resolve-Option.patch       | 29 +-------
 .../server/Entity-getEntitySpawnReason.patch  |  6 +-
 ...-API.patch => Expand-ArmorStand-API.patch} |  2 +-
 .../Expose-hand-in-BlockCanBuildEvent.patch   |  2 +-
 ...Folia-scheduler-and-owned-region-API.patch |  2 +-
 patches/server/Limit-recipe-packets.patch     |  2 +-
 ...ptimize-BlockPosition-helper-methods.patch | 29 ++++----
 .../Optimize-CraftBlockData-Creation.patch    |  4 +-
 patches/server/Optimize-MappedRegistry.patch  | 10 +--
 patches/server/Slime-Pathfinder-Events.patch  | 28 ++++----
 .../Use-ConcurrentHashMap-in-JsonList.patch   | 46 +++++-------
 .../Use-a-Queue-for-Queueing-Commands.patch   |  8 +--
 ...etChunkIfLoadedImmediately-in-places.patch |  2 +-
 .../Vanished-players-don-t-have-rights.patch  | 10 +--
 31 files changed, 129 insertions(+), 244 deletions(-)
 rename patches/server/{Ability-to-get-Tile-Entities-from-a-chunk-without-sn.patch => Ability-to-get-block-entities-from-a-chunk-without-s.patch} (96%)
 rename patches/server/{Allow-disabling-armour-stand-ticking.patch => Allow-disabling-armor-stand-ticking.patch} (88%)
 rename patches/server/{Implement-Expanded-ArmorStand-API.patch => Expand-ArmorStand-API.patch} (99%)

diff --git a/patches/server/Ability-to-get-Tile-Entities-from-a-chunk-without-sn.patch b/patches/server/Ability-to-get-block-entities-from-a-chunk-without-s.patch
similarity index 96%
rename from patches/server/Ability-to-get-Tile-Entities-from-a-chunk-without-sn.patch
rename to patches/server/Ability-to-get-block-entities-from-a-chunk-without-s.patch
index 179ea7e44b..d9f64842c0 100644
--- a/patches/server/Ability-to-get-Tile-Entities-from-a-chunk-without-sn.patch
+++ b/patches/server/Ability-to-get-block-entities-from-a-chunk-without-s.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Aikar <aikar@aikar.co>
 Date: Wed, 15 Aug 2018 01:16:34 -0400
-Subject: [PATCH] Ability to get Tile Entities from a chunk without snapshots
+Subject: [PATCH] Ability to get block entities from a chunk without snapshots
 
 
 diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
diff --git a/patches/server/Add-Alternate-Current-redstone-implementation.patch b/patches/server/Add-Alternate-Current-redstone-implementation.patch
index e43a3958aa..60be49ca07 100644
--- a/patches/server/Add-Alternate-Current-redstone-implementation.patch
+++ b/patches/server/Add-Alternate-Current-redstone-implementation.patch
@@ -2014,11 +2014,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 @@ -0,0 +0,0 @@ public class ServerLevel extends Level implements WorldGenLevel {
      public final UUID uuid;
      public boolean hasPhysicsEvent = true; // Paper
-     public boolean hasEntityMoveEvent = false; // Paper - Add EntityMoveEvent
+     public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent
 +    private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current)
-     public static Throwable getAddToWorldStackTrace(Entity entity) {
-         final Throwable thr = new Throwable(entity + " Added to world at " + new java.util.Date());
-         io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(thr);
+ 
+     @Override public LevelChunk getChunkIfLoaded(int x, int z) { // Paper - this was added in world too but keeping here for NMS ABI
+         return this.chunkSource.getChunkAtIfLoadedImmediately(x, z); // Paper - Use getChunkIfLoadedImmediately
 @@ -0,0 +0,0 @@ public class ServerLevel extends Level implements WorldGenLevel {
          return crashreportsystemdetails;
      }
diff --git a/patches/server/Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch b/patches/server/Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch
index 02013805e5..9638d4a5d4 100644
--- a/patches/server/Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch
+++ b/patches/server/Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch
@@ -3,9 +3,6 @@ From: Aikar <aikar@aikar.co>
 Date: Sat, 21 Jul 2018 08:25:40 -0400
 Subject: [PATCH] Add Debug Entities option to debug dupe uuid issues
 
-Add -Ddebug.entities=true to your JVM flags to gain more information
-
-1.17: Needs to be reworked for new entity storage system
 
 diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
@@ -32,45 +29,19 @@ diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/mai
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java
 +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements WorldGenLevel {
-     public final LevelStorageSource.LevelStorageAccess convertable;
-     public final UUID uuid;
-     public boolean hasPhysicsEvent = true; // Paper
-+    public static Throwable getAddToWorldStackTrace(Entity entity) {
-+        return new Throwable(entity + " Added to world at " + new java.util.Date());
-+    }
- 
-     @Override public LevelChunk getChunkIfLoaded(int x, int z) { // Paper - this was added in world too but keeping here for NMS ABI
-         return this.chunkSource.getChunk(x, z, false);
 @@ -0,0 +0,0 @@ public class ServerLevel extends Level implements WorldGenLevel {
      // CraftBukkit start
      private boolean addEntity(Entity entity, CreatureSpawnEvent.SpawnReason spawnReason) {
          org.spigotmc.AsyncCatcher.catchOp("entity add"); // Spigot
-+        // Paper start
++        // Paper start - extra debug info
 +        if (entity.valid) {
-+            MinecraftServer.LOGGER.error("Attempted Double World add on " + entity, new Throwable());
-+
-+            if (DEBUG_ENTITIES) {
-+                Throwable thr = entity.addedToWorldStack;
-+                if (thr == null) {
-+                    MinecraftServer.LOGGER.error("Double add entity has no add stacktrace");
-+                } else {
-+                    MinecraftServer.LOGGER.error("Double add stacktrace: ", thr);
-+                }
-+            }
++            MinecraftServer.LOGGER.error("Attempted Double World add on {}", entity, new Throwable());
 +            return true;
 +        }
-+        // Paper end
++        // Paper end - extra debug info
          if (entity.isRemoved()) {
-+            // Paper start
-+            if (DEBUG_ENTITIES) {
-+                new Throwable("Tried to add entity " + entity + " but it was marked as removed already").printStackTrace(); // CraftBukkit
-+                getAddToWorldStackTrace(entity).printStackTrace();
-+            }
-+            // Paper end
              // WorldServer.LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityTypes.getKey(entity.getType())); // CraftBukkit
              return false;
-         } else {
 diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/world/entity/Entity.java
@@ -80,22 +51,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      private CraftEntity bukkitEntity;
  
 +    public @org.jetbrains.annotations.Nullable net.minecraft.server.level.ChunkMap.TrackedEntity tracker; // Paper
-+    public @Nullable Throwable addedToWorldStack; // Paper - entity debug
      public CraftEntity getBukkitEntity() {
          if (this.bukkitEntity == null) {
              this.bukkitEntity = CraftEntity.getEntity(this.level.getCraftServer(), this);
-diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/world/level/Level.java
-+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -0,0 +0,0 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
-     public boolean pvpMode;
-     public boolean keepSpawnInMemory = true;
-     public org.bukkit.generator.ChunkGenerator generator;
-+    public static final boolean DEBUG_ENTITIES = Boolean.getBoolean("debug.entities"); // Paper
- 
-     public boolean preventPoiUpdated = false; // CraftBukkit - SPIGOT-5710
-     public boolean captureBlockStates = false;
 diff --git a/src/main/java/net/minecraft/world/level/entity/EntityLookup.java b/src/main/java/net/minecraft/world/level/entity/EntityLookup.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/world/level/entity/EntityLookup.java
@@ -105,25 +63,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          if (this.byUuid.containsKey(uUID)) {
              LOGGER.warn("Duplicate entity UUID {}: {}", uUID, entity);
 +            // Paper start - extra debug info
-+            if (entity instanceof net.minecraft.world.entity.Entity entityCast) {
-+                if (net.minecraft.server.level.ServerLevel.DEBUG_ENTITIES) {
-+                    entityCast.addedToWorldStack = net.minecraft.server.level.ServerLevel.getAddToWorldStackTrace(entityCast);
-+                }
-+
-+                T old = this.byUuid.get(entity.getUUID());
-+                if (old instanceof net.minecraft.world.entity.Entity oldCast && old != null && oldCast.getId() != entity.getId() && oldCast.valid) {
-+                    LOGGER.error("Overwrote an existing entity " + oldCast + " with " + entity);
-+                    if (net.minecraft.server.level.ServerLevel.DEBUG_ENTITIES) {
-+                        if (oldCast.addedToWorldStack != null) {
-+                            oldCast.addedToWorldStack.printStackTrace();
-+                        } else {
-+                            LOGGER.error("Oddly, the old entity was not added to the world in the normal way. Plugins?");
-+                        }
-+                        entityCast.addedToWorldStack.printStackTrace();
-+                    }
++            if (entity instanceof net.minecraft.world.entity.Entity) {
++                final T old = this.byUuid.get(entity.getUUID());
++                if (old instanceof net.minecraft.world.entity.Entity oldCast && oldCast.getId() != entity.getId() && oldCast.valid) {
++                    LOGGER.error("Overwrote an existing entity {} with {}", oldCast, entity);
 +                }
 +            }
-+            // Paper end
++            // Paper end - extra debug info
          } else {
              this.byUuid.put(uUID, entity);
              this.byId.put(entity.getId(), entity);
diff --git a/patches/server/Add-EntityMoveEvent.patch b/patches/server/Add-EntityMoveEvent.patch
index 67baa75558..4d107692d7 100644
--- a/patches/server/Add-EntityMoveEvent.patch
+++ b/patches/server/Add-EntityMoveEvent.patch
@@ -24,10 +24,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      public final LevelStorageSource.LevelStorageAccess convertable;
      public final UUID uuid;
      public boolean hasPhysicsEvent = true; // Paper
-+    public boolean hasEntityMoveEvent = false; // Paper - Add EntityMoveEvent
-     public static Throwable getAddToWorldStackTrace(Entity entity) {
-         final Throwable thr = new Throwable(entity + " Added to world at " + new java.util.Date());
-         io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(thr);
++    public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent
+ 
+     @Override public LevelChunk getChunkIfLoaded(int x, int z) { // Paper - this was added in world too but keeping here for NMS ABI
+         return this.chunkSource.getChunk(x, z, false);
 diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
diff --git a/patches/server/Add-More-Creeper-API.patch b/patches/server/Add-More-Creeper-API.patch
index 8221e72099..7f6801b7c6 100644
--- a/patches/server/Add-More-Creeper-API.patch
+++ b/patches/server/Add-More-Creeper-API.patch
@@ -22,7 +22,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      public void ignite() {
 -        this.entityData.set(Creeper.DATA_IS_IGNITED, true);
-+        // Paper start
++        // Paper start - CreeperIgniteEvent
 +        setIgnited(true);
 +    }
 +
@@ -33,7 +33,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                this.entityData.set(Creeper.DATA_IS_IGNITED, event.isIgnited());
 +            }
 +        }
-+        // Paper end
++        // Paper end - CreeperIgniteEvent
      }
  
      public boolean canDropMobsSkull() {
diff --git a/patches/server/Add-PhantomPreSpawnEvent.patch b/patches/server/Add-PhantomPreSpawnEvent.patch
index a96d5ccf39..aae239d3a4 100644
--- a/patches/server/Add-PhantomPreSpawnEvent.patch
+++ b/patches/server/Add-PhantomPreSpawnEvent.patch
@@ -55,7 +55,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                                              int k = 1 + randomsource.nextInt(difficultydamagescaler.getDifficulty().getId() + 1);
  
                                              for (int l = 0; l < k; ++l) {
-+                                                // Paper start
++                                                // Paper start - PhantomPreSpawnEvent
 +                                                com.destroystokyo.paper.event.entity.PhantomPreSpawnEvent event = new com.destroystokyo.paper.event.entity.PhantomPreSpawnEvent(io.papermc.paper.util.MCUtil.toLocation(world, blockposition1), entityplayer.getBukkitEntity(), org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL);
 +                                                if (!event.callEvent()) {
 +                                                    if (event.shouldAbortSpawn()) {
@@ -63,11 +63,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                                                    }
 +                                                    continue;
 +                                                }
-+                                                // Paper end
++                                                // Paper end - PhantomPreSpawnEvent
                                                  Phantom entityphantom = (Phantom) EntityType.PHANTOM.create(world);
  
                                                  if (entityphantom != null) {
-+                                                    entityphantom.setSpawningEntity(entityplayer.getUUID()); // Paper
++                                                    entityphantom.setSpawningEntity(entityplayer.getUUID()); // Paper - PhantomPreSpawnEvent
                                                      entityphantom.moveTo(blockposition1, 0.0F, 0.0F);
                                                      groupdataentity = entityphantom.finalizeSpawn(world, difficultydamagescaler, MobSpawnType.NATURAL, groupdataentity, (CompoundTag) null);
                                                      world.addFreshEntityWithPassengers(entityphantom, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); // CraftBukkit
diff --git a/patches/server/Add-PlayerKickEvent-causes.patch b/patches/server/Add-PlayerKickEvent-causes.patch
index 8597fd367f..df77573c22 100644
--- a/patches/server/Add-PlayerKickEvent-causes.patch
+++ b/patches/server/Add-PlayerKickEvent-causes.patch
@@ -260,7 +260,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 @@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
          // PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); // 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
+         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 - configurable tab spam limits
 -            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;
diff --git a/patches/server/Add-TNTPrimeEvent.patch b/patches/server/Add-TNTPrimeEvent.patch
index dce5d82507..16262b0e2f 100644
--- a/patches/server/Add-TNTPrimeEvent.patch
+++ b/patches/server/Add-TNTPrimeEvent.patch
@@ -14,9 +14,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                  }
 +                // Paper start - TNTPrimeEvent
 +                org.bukkit.block.Block tntBlock = this.level().getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
-+                if(!new com.destroystokyo.paper.event.block.TNTPrimeEvent(tntBlock, com.destroystokyo.paper.event.block.TNTPrimeEvent.PrimeReason.EXPLOSION, explosionSource.getIndirectSourceEntity().getBukkitEntity()).callEvent())
++                if (!new com.destroystokyo.paper.event.block.TNTPrimeEvent(tntBlock, com.destroystokyo.paper.event.block.TNTPrimeEvent.PrimeReason.EXPLOSION, explosionSource.getIndirectSourceEntity().getBukkitEntity()).callEvent())
 +                    continue;
-+                // Paper end
++                // Paper end - TNTPrimeEvent
                  nmsBlock.wasExploded(this.level(), blockposition, this.explosionSource);
  
                  this.level().removeBlock(blockposition, false);
@@ -29,7 +29,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                  world.setBlock(blockposition, this.getStateWithAge(world, blockposition, l), 3);
              } else {
 -                world.removeBlock(blockposition, false);
-+                if(iblockdata.getBlock() != Blocks.TNT) world.removeBlock(blockposition, false); // Paper - TNTPrimeEvent - We might be cancelling it below, move the setAir down
++                if(iblockdata.getBlock() != Blocks.TNT) world.removeBlock(blockposition, false); // Paper - TNTPrimeEvent; We might be cancelling it below, move the setAir down
              }
  
              Block block = iblockdata.getBlock();
@@ -41,7 +41,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                    return;
 +                }
 +                world.removeBlock(blockposition, false);
-+                // Paper end
++                // Paper end - TNTPrimeEvent
                  TntBlock.explode(world, blockposition);
              }
          }
@@ -58,7 +58,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                if (!new com.destroystokyo.paper.event.block.TNTPrimeEvent(tntBlock, com.destroystokyo.paper.event.block.TNTPrimeEvent.PrimeReason.REDSTONE, null).callEvent()) {
 +                    return;
 +                }
-+                // Paper end
++                // Paper end - TNTPrimeEvent
                  TntBlock.explode(world, pos);
                  world.removeBlock(pos, false);
              }
@@ -71,7 +71,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            if (!new com.destroystokyo.paper.event.block.TNTPrimeEvent(tntBlock, com.destroystokyo.paper.event.block.TNTPrimeEvent.PrimeReason.REDSTONE, null).callEvent()) {
 +                return;
 +            }
-+            // Paper end
++            // Paper end - TNTPrimeEvent
              TntBlock.explode(world, pos);
              world.removeBlock(pos, false);
          }
@@ -85,7 +85,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            if (!new com.destroystokyo.paper.event.block.TNTPrimeEvent(tntBlock, com.destroystokyo.paper.event.block.TNTPrimeEvent.PrimeReason.EXPLOSION, source).callEvent()) {
 +                return;
 +            }
-+            // Paper end
++            // Paper end - TNTPrimeEvent
              PrimedTnt entitytntprimed = new PrimedTnt(world, (double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D, explosion.getIndirectSourceEntity());
              int i = entitytntprimed.getFuse();
  
@@ -98,7 +98,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            if (!new com.destroystokyo.paper.event.block.TNTPrimeEvent(tntBlock, com.destroystokyo.paper.event.block.TNTPrimeEvent.PrimeReason.ITEM, player.getBukkitEntity()).callEvent()) {
 +                return InteractionResult.FAIL;
 +            }
-+            // Paper end
++            // Paper end - TNTPrimeEvent
              TntBlock.explode(world, pos, player);
              world.setBlock(pos, Blocks.AIR.defaultBlockState(), 11);
              Item item = itemstack.getItem();
@@ -111,7 +111,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                if (!new com.destroystokyo.paper.event.block.TNTPrimeEvent(tntBlock, com.destroystokyo.paper.event.block.TNTPrimeEvent.PrimeReason.PROJECTILE, projectile.getBukkitEntity()).callEvent()) {
 +                    return;
 +                }
-+                // Paper end
++                // Paper end - TNTPrimeEvent
                  TntBlock.explode(world, blockposition, entity instanceof LivingEntity ? (LivingEntity) entity : null);
                  world.removeBlock(blockposition, false);
              }
diff --git a/patches/server/Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch b/patches/server/Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch
index f627bc15cf..135625530d 100644
--- a/patches/server/Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch
+++ b/patches/server/Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch
@@ -82,7 +82,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          return spawningEntity;
      }
      public void setSpawningEntity(java.util.UUID entity) { this.spawningEntity = entity; }
-+
 +    private boolean shouldBurnInDay = true;
 +    public boolean shouldBurnInDay() { return shouldBurnInDay; }
 +    public void setShouldBurnInDay(boolean shouldBurnInDay) { this.shouldBurnInDay = shouldBurnInDay; }
diff --git a/patches/server/Allow-disabling-armour-stand-ticking.patch b/patches/server/Allow-disabling-armor-stand-ticking.patch
similarity index 88%
rename from patches/server/Allow-disabling-armour-stand-ticking.patch
rename to patches/server/Allow-disabling-armor-stand-ticking.patch
index dac6199875..b384623ff9 100644
--- a/patches/server/Allow-disabling-armour-stand-ticking.patch
+++ b/patches/server/Allow-disabling-armor-stand-ticking.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Riley Park <rileysebastianpark@gmail.com>
 Date: Wed, 15 Aug 2018 01:26:09 -0700
-Subject: [PATCH] Allow disabling armour stand ticking
+Subject: [PATCH] Allow disabling armor stand ticking
 
 
 diff --git a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
@@ -17,11 +17,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    public boolean canTickSetByAPI = false;
 +    private boolean noTickPoseDirty = false;
 +    private boolean noTickEquipmentDirty = false;
-+    // Paper end
++    // Paper end - Allow ArmorStands not to tick
  
      public ArmorStand(EntityType<? extends ArmorStand> type, Level world) {
          super(type, world);
-+        if (world != null) this.canTick = world.paperConfig().entities.armorStands.tick; // Paper - armour stand ticking
++        if (world != null) this.canTick = world.paperConfig().entities.armorStands.tick; // Paper - Allow ArmorStands not to tick
          this.handItems = NonNullList.withSize(2, ItemStack.EMPTY);
          this.armorItems = NonNullList.withSize(4, ItemStack.EMPTY);
          this.headPose = ArmorStand.DEFAULT_HEAD_POSE;
@@ -29,7 +29,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                  this.onEquipItem(enumitemslot, (ItemStack) this.armorItems.set(enumitemslot.getIndex(), itemstack), itemstack, silent); // CraftBukkit
          }
  
-+        this.noTickEquipmentDirty = true; // Paper - Allow equipment to be updated even when tick disabled
++        this.noTickEquipmentDirty = true; // Paper - Allow ArmorStands not to tick; Still update equipment
      }
  
      @Override
@@ -37,7 +37,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          }
  
          nbt.put("Pose", this.writePose());
-+        if (this.canTickSetByAPI) nbt.putBoolean("Paper.CanTickOverride", this.canTick); // Paper - persist no tick setting
++        if (this.canTickSetByAPI) nbt.putBoolean("Paper.CanTickOverride", this.canTick); // Paper - Allow ArmorStands not to tick
      }
  
      @Override
@@ -45,12 +45,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          this.setNoBasePlate(nbt.getBoolean("NoBasePlate"));
          this.setMarker(nbt.getBoolean("Marker"));
          this.noPhysics = !this.hasPhysics();
-+        // Paper start - persist no tick
++        // Paper start - Allow ArmorStands not to tick
 +        if (nbt.contains("Paper.CanTickOverride")) {
 +            this.canTick = nbt.getBoolean("Paper.CanTickOverride");
 +            this.canTickSetByAPI = true;
 +        }
-+        // Paper end
++        // Paper end - Allow ArmorStands not to tick
          CompoundTag nbttagcompound1 = nbt.getCompound("Pose");
  
          this.readPose(nbttagcompound1);
@@ -58,7 +58,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      @Override
      public void tick() {
-+        // Paper start
++        // Paper start - Allow ArmorStands not to tick
 +        if (!this.canTick) {
 +            if (this.noTickPoseDirty) {
 +                this.noTickPoseDirty = false;
@@ -72,15 +72,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
 +            return;
 +        }
-+        // Paper end
++        // Paper end - Allow ArmorStands not to tick
 +
          super.tick();
-+        // Paper start - Split into separate method
++        // Paper start - Allow ArmorStands not to tick
 +        updatePose();
 +    }
 +
 +    public void updatePose() {
-+        // Paper end
++        // Paper end - Allow ArmorStands not to tick
          Rotations vector3f = (Rotations) this.entityData.get(ArmorStand.DATA_HEAD_POSE);
  
          if (!this.headPose.equals(vector3f)) {
diff --git a/patches/server/Anti-Xray.patch b/patches/server/Anti-Xray.patch
index 7970d1f5d5..57457923da 100644
--- a/patches/server/Anti-Xray.patch
+++ b/patches/server/Anti-Xray.patch
@@ -1182,7 +1182,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        this.chunkPacketBlockController = this.paperConfig().anticheat.antiXray.enabled ? new com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray(this, executor) : com.destroystokyo.paper.antixray.ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray
      }
  
-     // Paper start
+     // Paper start - Cancel hit for vanished players
 @@ -0,0 +0,0 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
              // CraftBukkit end
  
diff --git a/patches/server/AnvilDamageEvent.patch b/patches/server/AnvilDamageEvent.patch
index b540c15d39..d3748b8a9d 100644
--- a/patches/server/AnvilDamageEvent.patch
+++ b/patches/server/AnvilDamageEvent.patch
@@ -12,7 +12,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              if (!player.getAbilities().instabuild && iblockdata.is(BlockTags.ANVIL) && player.getRandom().nextFloat() < 0.12F) {
                  BlockState iblockdata1 = AnvilBlock.damage(iblockdata);
  
-+                // Paper start
++                // Paper start - AnvilDamageEvent
 +                com.destroystokyo.paper.event.block.AnvilDamagedEvent event = new com.destroystokyo.paper.event.block.AnvilDamagedEvent(getBukkitView(), iblockdata1 != null ? org.bukkit.craftbukkit.block.data.CraftBlockData.fromData(iblockdata1) : null);
 +                if (!event.callEvent()) {
 +                    return;
@@ -21,7 +21,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                } else {
 +                    iblockdata1 = ((org.bukkit.craftbukkit.block.data.CraftBlockData) event.getDamageState().getMaterial().createBlockData()).getState().setValue(AnvilBlock.FACING, iblockdata.getValue(AnvilBlock.FACING));
 +                }
-+                // Paper end
++                // Paper end - AnvilDamageEvent
                  if (iblockdata1 == null) {
                      world.removeBlock(blockposition, false);
                      world.levelEvent(1029, blockposition, 0);
diff --git a/patches/server/Break-up-and-make-tab-spam-limits-configurable.patch b/patches/server/Break-up-and-make-tab-spam-limits-configurable.patch
index 5fee0f2536..f0679286d5 100644
--- a/patches/server/Break-up-and-make-tab-spam-limits-configurable.patch
+++ b/patches/server/Break-up-and-make-tab-spam-limits-configurable.patch
@@ -37,7 +37,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          this.keepConnectionAlive();
          // CraftBukkit start
          for (int spam; (spam = this.chatSpamTickCount.get()) > 0 && !this.chatSpamTickCount.compareAndSet(spam, spam - 1); ) ;
-+        if (tabSpamLimiter.get() > 0) tabSpamLimiter.getAndDecrement(); // Paper - split to seperate variable
++        if (tabSpamLimiter.get() > 0) tabSpamLimiter.getAndDecrement(); // Paper - configurable tab spam limits
          /* Use thread-safe field access instead
          if (this.chatSpamTickCount > 0) {
              --this.chatSpamTickCount;
@@ -46,7 +46,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          // PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); // Paper - run this async
          // CraftBukkit start
 -        if (this.chatSpamTickCount.addAndGet(1) > 500 && !this.server.getPlayerList().isOp(this.player.getGameProfile())) {
-+        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
++        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 - configurable tab spam limits
              server.scheduleOnMain(() -> this.disconnect(Component.translatable("disconnect.spam", new Object[0]))); // Paper
              return;
          }
diff --git a/patches/server/Collision-optimisations.patch b/patches/server/Collision-optimisations.patch
index 089b38dd49..691a8d16dc 100644
--- a/patches/server/Collision-optimisations.patch
+++ b/patches/server/Collision-optimisations.patch
@@ -2613,11 +2613,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        // Paper end - optimise collisions
      }
  
-     // Paper start
+     // Paper start - Cancel hit for vanished players
 @@ -0,0 +0,0 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
          return true;
      }
-     // Paper end
+     // Paper end - Cancel hit for vanished players
 +    // Paper start - optimise collisions
 +    public final int minSection;
 +    public final int maxSection;
@@ -3030,7 +3030,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            this.id2 = it.unimi.dsi.fastutil.HashCommon.murmurHash3(it.unimi.dsi.fastutil.HashCommon.murmurHash3(ID_GENERATOR.getAndIncrement() + RANDOM_OFFSET) + RANDOM_OFFSET);
 +            // Paper end - optimise collisions
          }
-         // Paper start - impl cached craft block data, lazy load to fix issue with loading at the wrong time
+         // Paper start - Perf: impl cached craft block data, lazy load to fix issue with loading at the wrong time
          private org.bukkit.craftbukkit.block.data.CraftBlockData cachedCraftBlockData;
 @@ -0,0 +0,0 @@ public abstract class BlockBehaviour implements FeatureElement {
              return this.conditionallyFullOpaque;
diff --git a/patches/server/Configurable-speed-for-water-flowing-over-lava.patch b/patches/server/Configurable-speed-for-water-flowing-over-lava.patch
index 06207aafc5..1e4bf79493 100644
--- a/patches/server/Configurable-speed-for-water-flowing-over-lava.patch
+++ b/patches/server/Configurable-speed-for-water-flowing-over-lava.patch
@@ -13,12 +13,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      public void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean notify) {
          if (this.shouldSpreadLiquid(world, pos, state)) {
 -            world.scheduleTick(pos, state.getFluidState().getType(), this.fluid.getTickDelay(world));
-+            world.scheduleTick(pos, state.getFluidState().getType(), this.getFlowSpeed(world, pos)); // Paper
++            world.scheduleTick(pos, state.getFluidState().getType(), this.getFlowSpeed(world, pos)); // Paper - Configurable speed for water flowing over lava
          }
  
      }
  
-+    // Paper start - Get flow speed. Throttle if its water and flowing adjacent to lava
++    // Paper start - Configurable speed for water flowing over lava
 +    public int getFlowSpeed(Level world, BlockPos blockposition) {
 +        if (net.minecraft.core.registries.BuiltInRegistries.FLUID.wrapAsHolder(this.fluid).is(FluidTags.WATER)) {
 +            if (
@@ -36,7 +36,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        final FluidState fluidState = world.getFluidIfLoaded(blockPos);
 +        return fluidState != null && fluidState.is(FluidTags.LAVA);
 +    }
-+    // Paper end
++    // Paper end - Configurable speed for water flowing over lava
 +
      @Override
      public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) {
@@ -46,7 +46,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      public void neighborChanged(BlockState state, Level world, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) {
          if (this.shouldSpreadLiquid(world, pos, state)) {
 -            world.scheduleTick(pos, state.getFluidState().getType(), this.fluid.getTickDelay(world));
-+            world.scheduleTick(pos, state.getFluidState().getType(), this.getFlowSpeed(world, pos)); // Paper
++            world.scheduleTick(pos, state.getFluidState().getType(), this.getFlowSpeed(world, pos)); // Paper - Configurable speed for water flowing over lava
          }
  
      }
diff --git a/patches/server/Deobfuscate-stacktraces-in-log-messages-crash-report.patch b/patches/server/Deobfuscate-stacktraces-in-log-messages-crash-report.patch
index 79ab1d74e5..3cf0c1274b 100644
--- a/patches/server/Deobfuscate-stacktraces-in-log-messages-crash-report.patch
+++ b/patches/server/Deobfuscate-stacktraces-in-log-messages-crash-report.patch
@@ -556,30 +556,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          // Paper start - initialize global and world-defaults configuration
          this.paperConfigurations.initializeGlobalConfiguration(this.registryAccess());
          this.paperConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess());
-diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/level/ServerLevel.java
-+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements WorldGenLevel {
-     public final UUID uuid;
-     public boolean hasPhysicsEvent = true; // Paper
-     public static Throwable getAddToWorldStackTrace(Entity entity) {
--        return new Throwable(entity + " Added to world at " + new java.util.Date());
-+        final Throwable thr = new Throwable(entity + " Added to world at " + new java.util.Date());
-+        io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(thr);
-+        return thr;
-     }
- 
-     @Override public LevelChunk getChunkIfLoaded(int x, int z) { // Paper - this was added in world too but keeping here for NMS ABI
-@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements WorldGenLevel {
-         if (entity.isRemoved()) {
-             // Paper start
-             if (DEBUG_ENTITIES) {
--                new Throwable("Tried to add entity " + entity + " but it was marked as removed already").printStackTrace(); // CraftBukkit
-+                io.papermc.paper.util.TraceUtil.dumpTraceForThread("Tried to add entity " + entity + " but it was marked as removed already"); // CraftBukkit
-                 getAddToWorldStackTrace(entity).printStackTrace();
-             }
-             // Paper end
 diff --git a/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
diff --git a/patches/server/Don-t-fire-sync-events-during-worldgen.patch b/patches/server/Don-t-fire-sync-events-during-worldgen.patch
index 4120117fab..37749405df 100644
--- a/patches/server/Don-t-fire-sync-events-during-worldgen.patch
+++ b/patches/server/Don-t-fire-sync-events-during-worldgen.patch
@@ -27,9 +27,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      private boolean addEntity(Entity entity, CreatureSpawnEvent.SpawnReason spawnReason) {
          org.spigotmc.AsyncCatcher.catchOp("entity add"); // Spigot
 +        entity.generation = false; // Paper - Don't fire sync event during generation; Reset flag if it was added during a ServerLevel generation process
-         // Paper start
+         // Paper start - extra debug info
          if (entity.valid) {
-             MinecraftServer.LOGGER.error("Attempted Double World add on " + entity, new Throwable());
+             MinecraftServer.LOGGER.error("Attempted Double World add on {}", entity, new Throwable());
 diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/world/entity/Entity.java
diff --git a/patches/server/Duplicate-UUID-Resolve-Option.patch b/patches/server/Duplicate-UUID-Resolve-Option.patch
index 9cbd1429e1..97cf4268eb 100644
--- a/patches/server/Duplicate-UUID-Resolve-Option.patch
+++ b/patches/server/Duplicate-UUID-Resolve-Option.patch
@@ -32,29 +32,6 @@ But for those who are ok with leaving this inconsistent behavior, you may use WA
 
 It is recommended you regenerate the entities, as these were legit entities, and deserve your love.
 
-diff --git a/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java b/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java
-+++ b/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java
-@@ -0,0 +0,0 @@ public final class ChunkSystem {
-     }
- 
-     public static void onEntityPreAdd(final ServerLevel level, final Entity entity) {
--
-+        // Paper start - duplicate uuid resolving
-+        if (net.minecraft.server.level.ChunkMap.checkDupeUUID(level, entity)) {
-+            return;
-+        }
-+        if (net.minecraft.world.level.Level.DEBUG_ENTITIES && ((Entity) entity).level().paperConfig().entities.spawning.duplicateUuid.mode != io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.NOTHING) {
-+            if (((Entity) entity).addedToWorldStack != null) {
-+                ((Entity) entity).addedToWorldStack.printStackTrace();
-+            }
-+            net.minecraft.server.level.ServerLevel.getAddToWorldStackTrace((Entity) entity).printStackTrace();
-+        }
-+        // Paper end - duplicate uuid resolving
-     }
- 
-     public static void onChunkHolderCreate(final ServerLevel level, final ChunkHolder holder) {
 diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -90,24 +67,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            && Objects.equals(other.getEncodeId(), entity.getEncodeId())
 +            && entity.getBukkitEntity().getLocation().distance(other.getBukkitEntity().getLocation()) < level.paperConfig().entities.spawning.duplicateUuid.safeRegenDeleteRange
 +        ) {
-+            if (ServerLevel.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + " because it was near the duplicate and likely an actual duplicate. See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
 +            entity.discard();
 +            return true;
 +        }
-+        if (other != null && !other.isRemoved()) {
++        if (!other.isRemoved()) {
 +            switch (mode) {
 +                case SAFE_REGEN: {
 +                    entity.setUUID(java.util.UUID.randomUUID());
-+                    if (ServerLevel.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", regenerated UUID for " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
 +                    break;
 +                }
 +                case DELETE: {
-+                    if (ServerLevel.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
 +                    entity.discard();
 +                    return true;
 +                }
 +                default:
-+                    if (ServerLevel.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", doing nothing to " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
 +                    break;
 +            }
 +        }
diff --git a/patches/server/Entity-getEntitySpawnReason.patch b/patches/server/Entity-getEntitySpawnReason.patch
index 603e6dda39..deff36c67a 100644
--- a/patches/server/Entity-getEntitySpawnReason.patch
+++ b/patches/server/Entity-getEntitySpawnReason.patch
@@ -28,11 +28,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 @@ -0,0 +0,0 @@ public class ServerLevel extends Level implements WorldGenLevel {
              return true;
          }
-         // Paper end
+         // Paper end - extra debug info
 +        if (entity.spawnReason == null) entity.spawnReason = spawnReason; // Paper - Entity#getEntitySpawnReason
          if (entity.isRemoved()) {
-             // Paper start
-             if (DEBUG_ENTITIES) {
+             // WorldServer.LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityTypes.getKey(entity.getType())); // CraftBukkit
+             return false;
 diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/players/PlayerList.java
diff --git a/patches/server/Implement-Expanded-ArmorStand-API.patch b/patches/server/Expand-ArmorStand-API.patch
similarity index 99%
rename from patches/server/Implement-Expanded-ArmorStand-API.patch
rename to patches/server/Expand-ArmorStand-API.patch
index 8363e763dd..08a77ee21e 100644
--- a/patches/server/Implement-Expanded-ArmorStand-API.patch
+++ b/patches/server/Expand-ArmorStand-API.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: willies952002 <admin@domnian.com>
 Date: Thu, 26 Jul 2018 02:25:46 -0400
-Subject: [PATCH] Implement Expanded ArmorStand API
+Subject: [PATCH] Expand ArmorStand API
 
 Adds the following:
 - Add proper methods for getting and setting items in both hands. Deprecates old methods
diff --git a/patches/server/Expose-hand-in-BlockCanBuildEvent.patch b/patches/server/Expose-hand-in-BlockCanBuildEvent.patch
index f6f5563982..1ff8ab7178 100644
--- a/patches/server/Expose-hand-in-BlockCanBuildEvent.patch
+++ b/patches/server/Expose-hand-in-BlockCanBuildEvent.patch
@@ -9,7 +9,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/net/minecraft/world/item/BlockItem.java
 +++ b/src/main/java/net/minecraft/world/item/BlockItem.java
 @@ -0,0 +0,0 @@ public class BlockItem extends Item {
-         boolean defaultReturn = (!this.mustSurvive() || state.canSurvive(context.getLevel(), context.getClickedPos())) && world.checkEntityCollision(state, entityhuman, voxelshapecollision, context.getClickedPos(), true); // Paper
+         boolean defaultReturn = (!this.mustSurvive() || state.canSurvive(context.getLevel(), context.getClickedPos())) && world.checkEntityCollision(state, entityhuman, voxelshapecollision, context.getClickedPos(), true); // Paper - Cancel hit for vanished players
          org.bukkit.entity.Player player = (context.getPlayer() instanceof ServerPlayer) ? (org.bukkit.entity.Player) context.getPlayer().getBukkitEntity() : null;
  
 -        BlockCanBuildEvent event = new BlockCanBuildEvent(CraftBlock.at(context.getLevel(), context.getClickedPos()), player, CraftBlockData.fromData(state), defaultReturn);
diff --git a/patches/server/Folia-scheduler-and-owned-region-API.patch b/patches/server/Folia-scheduler-and-owned-region-API.patch
index 25d8526748..3cc5ec388b 100644
--- a/patches/server/Folia-scheduler-and-owned-region-API.patch
+++ b/patches/server/Folia-scheduler-and-owned-region-API.patch
@@ -1189,7 +1189,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/net/minecraft/world/entity/Entity.java
 +++ b/src/main/java/net/minecraft/world/entity/Entity.java
 @@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
-     public @Nullable Throwable addedToWorldStack; // Paper - entity debug
+     public @org.jetbrains.annotations.Nullable net.minecraft.server.level.ChunkMap.TrackedEntity tracker; // Paper
      public CraftEntity getBukkitEntity() {
          if (this.bukkitEntity == null) {
 -            this.bukkitEntity = CraftEntity.getEntity(this.level.getCraftServer(), this);
diff --git a/patches/server/Limit-recipe-packets.patch b/patches/server/Limit-recipe-packets.patch
index b8ef3582df..54362def34 100644
--- a/patches/server/Limit-recipe-packets.patch
+++ b/patches/server/Limit-recipe-packets.patch
@@ -19,7 +19,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 @@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
          // CraftBukkit start
          for (int spam; (spam = this.chatSpamTickCount.get()) > 0 && !this.chatSpamTickCount.compareAndSet(spam, spam - 1); ) ;
-         if (tabSpamLimiter.get() > 0) tabSpamLimiter.getAndDecrement(); // Paper - split to seperate variable
+         if (tabSpamLimiter.get() > 0) tabSpamLimiter.getAndDecrement(); // Paper - configurable tab spam limits
 +        if (recipeSpamPackets.get() > 0) recipeSpamPackets.getAndDecrement(); // Paper - auto recipe limit
          /* Use thread-safe field access instead
          if (this.chatSpamTickCount > 0) {
diff --git a/patches/server/Optimize-BlockPosition-helper-methods.patch b/patches/server/Optimize-BlockPosition-helper-methods.patch
index 5456be1da1..8bac9e96e7 100644
--- a/patches/server/Optimize-BlockPosition-helper-methods.patch
+++ b/patches/server/Optimize-BlockPosition-helper-methods.patch
@@ -3,7 +3,6 @@ From: Spottedleaf <Spottedleaf@users.noreply.github.com>
 Date: Wed, 15 Aug 2018 12:05:12 -0700
 Subject: [PATCH] Optimize BlockPosition helper methods
 
-Resolves #1338
 
 diff --git a/src/main/java/net/minecraft/core/BlockPos.java b/src/main/java/net/minecraft/core/BlockPos.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
@@ -14,78 +13,78 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      @Override
      public BlockPos above() {
 -        return this.relative(Direction.UP);
-+        return new BlockPos(this.getX(), this.getY() + 1, this.getZ()); // Paper - Optimize BlockPosition
++        return new BlockPos(this.getX(), this.getY() + 1, this.getZ()); // Paper - Perf: Optimize BlockPosition
      }
  
      @Override
      public BlockPos above(int distance) {
 -        return this.relative(Direction.UP, distance);
-+        return distance == 0 ? this : new BlockPos(this.getX(), this.getY() + distance, this.getZ()); // Paper - Optimize BlockPosition
++        return distance == 0 ? this : new BlockPos(this.getX(), this.getY() + distance, this.getZ()); // Paper - Perf: Optimize BlockPosition
      }
  
      @Override
      public BlockPos below() {
 -        return this.relative(Direction.DOWN);
-+        return new BlockPos(this.getX(), this.getY() - 1, this.getZ()); // Paper - Optimize BlockPosition
++        return new BlockPos(this.getX(), this.getY() - 1, this.getZ()); // Paper - Perf: Optimize BlockPosition
      }
  
      @Override
      public BlockPos below(int i) {
 -        return this.relative(Direction.DOWN, i);
-+        return i == 0 ? this : new BlockPos(this.getX(), this.getY() - i, this.getZ()); // Paper - Optimize BlockPosition
++        return i == 0 ? this : new BlockPos(this.getX(), this.getY() - i, this.getZ()); // Paper - Perf: Optimize BlockPosition
      }
  
      @Override
      public BlockPos north() {
 -        return this.relative(Direction.NORTH);
-+        return new BlockPos(this.getX(), this.getY(), this.getZ() - 1); // Paper - Optimize BlockPosition
++        return new BlockPos(this.getX(), this.getY(), this.getZ() - 1); // Paper - Perf: Optimize BlockPosition
      }
  
      @Override
      public BlockPos north(int distance) {
 -        return this.relative(Direction.NORTH, distance);
-+        return distance == 0 ? this : new BlockPos(this.getX(), this.getY(), this.getZ() - distance); // Paper - Optimize BlockPosition
++        return distance == 0 ? this : new BlockPos(this.getX(), this.getY(), this.getZ() - distance); // Paper - Perf: Optimize BlockPosition
      }
  
      @Override
      public BlockPos south() {
 -        return this.relative(Direction.SOUTH);
-+        return new BlockPos(this.getX(), this.getY(), this.getZ() + 1); // Paper - Optimize BlockPosition
++        return new BlockPos(this.getX(), this.getY(), this.getZ() + 1); // Paper - Perf: Optimize BlockPosition
      }
  
      @Override
      public BlockPos south(int distance) {
 -        return this.relative(Direction.SOUTH, distance);
-+        return distance == 0 ? this : new BlockPos(this.getX(), this.getY(), this.getZ() + distance); // Paper - Optimize BlockPosition
++        return distance == 0 ? this : new BlockPos(this.getX(), this.getY(), this.getZ() + distance); // Paper - Perf: Optimize BlockPosition
      }
  
      @Override
      public BlockPos west() {
 -        return this.relative(Direction.WEST);
-+        return new BlockPos(this.getX() - 1, this.getY(), this.getZ()); // Paper - Optimize BlockPosition
++        return new BlockPos(this.getX() - 1, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPosition
      }
  
      @Override
      public BlockPos west(int distance) {
 -        return this.relative(Direction.WEST, distance);
-+        return distance == 0 ? this : new BlockPos(this.getX() - distance, this.getY(), this.getZ()); // Paper - Optimize BlockPosition
++        return distance == 0 ? this : new BlockPos(this.getX() - distance, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPosition
      }
  
      @Override
      public BlockPos east() {
 -        return this.relative(Direction.EAST);
-+        return new BlockPos(this.getX() + 1, this.getY(), this.getZ()); // Paper - Optimize BlockPosition
++        return new BlockPos(this.getX() + 1, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPosition
      }
  
      @Override
      public BlockPos east(int distance) {
 -        return this.relative(Direction.EAST, distance);
-+        return distance == 0 ? this : new BlockPos(this.getX() + distance, this.getY(), this.getZ()); // Paper - Optimize BlockPosition
++        return distance == 0 ? this : new BlockPos(this.getX() + distance, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPosition
      }
  
      @Override
      public BlockPos relative(Direction direction) {
-+        // Paper start - Optimize BlockPosition
++        // Paper start - Perf: Optimize BlockPosition
 +        switch(direction) {
 +            case UP:
 +                return new BlockPos(this.getX(), this.getY() + 1, this.getZ());
@@ -102,7 +101,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            default:
          return new BlockPos(this.getX() + direction.getStepX(), this.getY() + direction.getStepY(), this.getZ() + direction.getStepZ());
 +        }
-+        // Paper end
++        // Paper end - Perf: Optimize BlockPosition
      }
  
      @Override
diff --git a/patches/server/Optimize-CraftBlockData-Creation.patch b/patches/server/Optimize-CraftBlockData-Creation.patch
index e5f1313956..8bd835b144 100644
--- a/patches/server/Optimize-CraftBlockData-Creation.patch
+++ b/patches/server/Optimize-CraftBlockData-Creation.patch
@@ -14,14 +14,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              this.replaceable = blockbase_info.replaceable;
              this.conditionallyFullOpaque = this.canOcclude & this.useShapeForLightOcclusion; // Paper
          }
-+        // Paper start - impl cached craft block data, lazy load to fix issue with loading at the wrong time
++        // Paper start - Perf: impl cached craft block data, lazy load to fix issue with loading at the wrong time
 +        private org.bukkit.craftbukkit.block.data.CraftBlockData cachedCraftBlockData;
 +
 +        public org.bukkit.craftbukkit.block.data.CraftBlockData createCraftBlockData() {
 +            if (cachedCraftBlockData == null) cachedCraftBlockData = org.bukkit.craftbukkit.block.data.CraftBlockData.createData(asState());
 +            return (org.bukkit.craftbukkit.block.data.CraftBlockData) cachedCraftBlockData.clone();
 +        }
-+        // Paper end
++        // Paper end - Perf: impl cached craft block data, lazy load to fix issue with loading at the wrong time
  
          private boolean calculateSolid() {
              if (((Block) this.owner).properties.forceSolidOn) {
diff --git a/patches/server/Optimize-MappedRegistry.patch b/patches/server/Optimize-MappedRegistry.patch
index fb7e639aba..bb71144bb4 100644
--- a/patches/server/Optimize-MappedRegistry.patch
+++ b/patches/server/Optimize-MappedRegistry.patch
@@ -16,17 +16,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      final ResourceKey<? extends Registry<T>> key;
      private final ObjectList<Holder.Reference<T>> byId = new ObjectArrayList<>(256);
 -    private final Reference2IntMap<T> toId = Util.make(new Reference2IntOpenHashMap<>(), (map) -> {
-+    private final Reference2IntMap<T> toId = Util.make(new Reference2IntOpenHashMap<>(2048), (map) -> { // Paper - use bigger expected size to reduce collisions
++    private final Reference2IntMap<T> toId = Util.make(new Reference2IntOpenHashMap<>(2048), (map) -> { // Paper - Perf: Use bigger expected size to reduce collisions
          map.defaultReturnValue(-1);
      });
 -    private final Map<ResourceLocation, Holder.Reference<T>> byLocation = new HashMap<>();
 -    private final Map<ResourceKey<T>, Holder.Reference<T>> byKey = new HashMap<>();
 -    private final Map<T, Holder.Reference<T>> byValue = new IdentityHashMap<>();
 -    private final Map<T, Lifecycle> lifecycles = new IdentityHashMap<>();
-+    private final Map<ResourceLocation, Holder.Reference<T>> byLocation = new HashMap<>(2048); // Paper - use bigger expected size to reduce collisions
-+    private final Map<ResourceKey<T>, Holder.Reference<T>> byKey = new HashMap<>(2048); // Paper - use bigger expected size to reduce collisions
-+    private final Map<T, Holder.Reference<T>> byValue = new IdentityHashMap<>(2048); // Paper - use bigger expected size to reduce collisions
-+    private final Map<T, Lifecycle> lifecycles = new IdentityHashMap<>(2048); // Paper - use bigger expected size to reduce collisions
++    private final Map<ResourceLocation, Holder.Reference<T>> byLocation = new HashMap<>(2048); // Paper - Perf: Use bigger expected size to reduce collisions
++    private final Map<ResourceKey<T>, Holder.Reference<T>> byKey = new HashMap<>(2048); // Paper - Perf: Use bigger expected size to reduce collisions
++    private final Map<T, Holder.Reference<T>> byValue = new IdentityHashMap<>(2048); // Paper - Perf: Use bigger expected size to reduce collisions
++    private final Map<T, Lifecycle> lifecycles = new IdentityHashMap<>(2048); // Paper - Perf: Use bigger expected size to reduce collisions
      private Lifecycle registryLifecycle;
      private volatile Map<TagKey<T>, HolderSet.Named<T>> tags = new IdentityHashMap<>();
      private boolean frozen;
diff --git a/patches/server/Slime-Pathfinder-Events.patch b/patches/server/Slime-Pathfinder-Events.patch
index 28b902961d..d4535e218f 100644
--- a/patches/server/Slime-Pathfinder-Events.patch
+++ b/patches/server/Slime-Pathfinder-Events.patch
@@ -20,7 +20,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      public void readAdditionalSaveData(CompoundTag nbt) {
          this.setSize(nbt.getInt("Size") + 1, false);
          super.readAdditionalSaveData(nbt);
-+        // Paper start - check exists before loading or this will be loaded as false
++        // Paper start
 +        if (nbt.contains("Paper.canWander")) {
 +            this.canWander = nbt.getBoolean("Paper.canWander");
 +        }
@@ -33,7 +33,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          @Override
          public boolean canUse() {
 -            return (this.slime.isInWater() || this.slime.isInLava()) && this.slime.getMoveControl() instanceof Slime.SlimeMoveControl;
-+            return (this.slime.isInWater() || this.slime.isInLava()) && this.slime.getMoveControl() instanceof Slime.SlimeMoveControl && this.slime.canWander && new com.destroystokyo.paper.event.entity.SlimeSwimEvent((org.bukkit.entity.Slime) this.slime.getBukkitEntity()).callEvent(); // Paper
++            return (this.slime.isInWater() || this.slime.isInLava()) && this.slime.getMoveControl() instanceof Slime.SlimeMoveControl && this.slime.canWander && new com.destroystokyo.paper.event.entity.SlimeSwimEvent((org.bukkit.entity.Slime) this.slime.getBukkitEntity()).callEvent(); // Paper - Slime pathfinder events
          }
  
          @Override
@@ -42,7 +42,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              LivingEntity entityliving = this.slime.getTarget();
  
 -            return entityliving == null ? false : (!this.slime.canAttack(entityliving) ? false : this.slime.getMoveControl() instanceof Slime.SlimeMoveControl);
-+            // Paper start
++            // Paper start - Slime pathfinder events
 +            if (entityliving == null || !entityliving.isAlive()) {
 +                return false;
 +            }
@@ -50,7 +50,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                return false;
 +            }
 +            return this.slime.getMoveControl() instanceof Slime.SlimeMoveControl && this.slime.canWander && new com.destroystokyo.paper.event.entity.SlimeTargetLivingEntityEvent((org.bukkit.entity.Slime) this.slime.getBukkitEntity(), (org.bukkit.entity.LivingEntity) entityliving.getBukkitEntity()).callEvent();
-+            // Paper end
++            // Paper end - Slime pathfinder events
          }
  
          @Override
@@ -59,7 +59,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              LivingEntity entityliving = this.slime.getTarget();
  
 -            return entityliving == null ? false : (!this.slime.canAttack(entityliving) ? false : --this.growTiredTimer > 0);
-+            // Paper start
++            // Paper start - Slime pathfinder events
 +            if (entityliving == null || !entityliving.isAlive()) {
 +                return false;
 +            }
@@ -67,7 +67,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                return false;
 +            }
 +            return --this.growTiredTimer > 0 && this.slime.canWander && new com.destroystokyo.paper.event.entity.SlimeTargetLivingEntityEvent((org.bukkit.entity.Slime) this.slime.getBukkitEntity(), (org.bukkit.entity.LivingEntity) entityliving.getBukkitEntity()).callEvent();
-+            // Paper end
++            // Paper end - Slime pathfinder events
          }
  
          @Override
@@ -76,12 +76,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
          }
 +
-+        // Paper start - clear timer and target when goal resets
++        // Paper start - Slime pathfinder events; clear timer and target when goal resets
 +        public void stop() {
 +            this.growTiredTimer = 0;
 +            this.slime.setTarget(null);
 +        }
-+        // Paper end
++        // Paper end - Slime pathfinder events
      }
  
      private static class SlimeRandomDirectionGoal extends Goal {
@@ -90,7 +90,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          @Override
          public boolean canUse() {
 -            return this.slime.getTarget() == null && (this.slime.onGround() || this.slime.isInWater() || this.slime.isInLava() || this.slime.hasEffect(MobEffects.LEVITATION)) && this.slime.getMoveControl() instanceof Slime.SlimeMoveControl;
-+            return this.slime.getTarget() == null && (this.slime.onGround() || this.slime.isInWater() || this.slime.isInLava() || this.slime.hasEffect(MobEffects.LEVITATION)) && this.slime.getMoveControl() instanceof Slime.SlimeMoveControl && this.slime.canWander; // Paper - add canWander
++            return this.slime.getTarget() == null && (this.slime.onGround() || this.slime.isInWater() || this.slime.isInLava() || this.slime.hasEffect(MobEffects.LEVITATION)) && this.slime.getMoveControl() instanceof Slime.SlimeMoveControl && this.slime.canWander; // Paper - Slime pathfinder events
          }
  
          @Override
@@ -98,11 +98,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              if (--this.nextRandomizeTime <= 0) {
                  this.nextRandomizeTime = this.adjustedTickDelay(40 + this.slime.getRandom().nextInt(60));
                  this.chosenDegrees = (float) this.slime.getRandom().nextInt(360);
-+                // Paper start
++                // Paper start - Slime pathfinder events
 +                com.destroystokyo.paper.event.entity.SlimeChangeDirectionEvent event = new com.destroystokyo.paper.event.entity.SlimeChangeDirectionEvent((org.bukkit.entity.Slime) this.slime.getBukkitEntity(), this.chosenDegrees);
 +                if (!this.slime.canWander || !event.callEvent()) return;
 +                this.chosenDegrees = event.getNewYaw();
-+                // Paper end
++                // Paper end - Slime pathfinder events
              }
  
              MoveControl controllermove = this.slime.getMoveControl();
@@ -111,7 +111,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          @Override
          public boolean canUse() {
 -            return !this.slime.isPassenger();
-+            return !this.slime.isPassenger() && this.slime.canWander && new com.destroystokyo.paper.event.entity.SlimeWanderEvent((org.bukkit.entity.Slime) this.slime.getBukkitEntity()).callEvent(); // Paper
++            return !this.slime.isPassenger() && this.slime.canWander && new com.destroystokyo.paper.event.entity.SlimeWanderEvent((org.bukkit.entity.Slime) this.slime.getBukkitEntity()).callEvent(); // Paper - Slime pathfinder events
          }
  
          @Override
@@ -120,7 +120,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          }
      }
 +
-+    // Paper start
++    // Paper start - Slime pathfinder events
 +    private boolean canWander = true;
 +    public boolean canWander() {
 +        return canWander;
@@ -129,7 +129,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    public void setWander(boolean canWander) {
 +        this.canWander = canWander;
 +    }
-+    // Paper end
++    // Paper end - Slime pathfinder events
  }
 diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSlime.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSlime.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
diff --git a/patches/server/Use-ConcurrentHashMap-in-JsonList.patch b/patches/server/Use-ConcurrentHashMap-in-JsonList.patch
index e95288f2ec..860b40a6e5 100644
--- a/patches/server/Use-ConcurrentHashMap-in-JsonList.patch
+++ b/patches/server/Use-ConcurrentHashMap-in-JsonList.patch
@@ -44,7 +44,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      private static final Gson GSON = (new GsonBuilder()).setPrettyPrinting().create();
      private final File file;
 -    private final Map<String, V> map = Maps.newHashMap();
-+    private final Map<String, V> map = Maps.newConcurrentMap(); // Paper - replace HashMap is ConcurrentHashMap
++    private final Map<String, V> map = Maps.newConcurrentMap(); // Paper - Use ConcurrentHashMap in JsonList
  
      public StoredUserList(File file) {
          this.file = file;
@@ -54,13 +54,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      public V get(K key) {
 -        this.removeExpired();
 -        return (V) this.map.get(this.getKeyForUser(key)); // CraftBukkit - fix decompile error
-+        // Paper start
-+        // this.g();
-+        // return (V) this.d.get(this.a(k0)); // CraftBukkit - fix decompile error
++        // Paper start - Use ConcurrentHashMap in JsonList
 +        return (V) this.map.computeIfPresent(this.getKeyForUser(key), (k, v) -> {
 +            return v.hasExpired() ? null : v;
 +        });
-+        // Paper end
++        // Paper end - Use ConcurrentHashMap in JsonList
      }
  
      public void remove(K key) {
@@ -69,8 +67,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      public boolean isEmpty() {
 -        return this.map.size() < 1;
-+        // return this.d.size() < 1; // Paper
-+        return this.map.isEmpty(); // Paper - readability is the goal. As an aside, isEmpty() uses only sumCount() and a comparison. size() uses sumCount(), casts, and boolean logic
++        return this.map.isEmpty(); // Paper - Use ConcurrentHashMap in JsonList
      }
  
      protected String getKeyForUser(K profile) {
@@ -80,29 +77,24 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      private void removeExpired() {
 -        List<K> list = Lists.newArrayList();
 -        Iterator iterator = this.map.values().iterator();
-+        /*List<K> list = Lists.newArrayList();
-+        Iterator iterator = this.d.values().iterator();
- 
-         while (iterator.hasNext()) {
-             V v0 = (V) iterator.next(); // CraftBukkit - decompile error
- 
-             if (v0.hasExpired()) {
+-
+-        while (iterator.hasNext()) {
+-            V v0 = (V) iterator.next(); // CraftBukkit - decompile error
+-
+-            if (v0.hasExpired()) {
 -                list.add(v0.getUser());
-+                list.add(v0.getKey());
-             }
-         }
- 
-@@ -0,0 +0,0 @@ public abstract class StoredUserList<K, V extends StoredUserEntry<K>> {
-         while (iterator.hasNext()) {
-             K k0 = (K) iterator.next(); // CraftBukkit - decompile error
- 
+-            }
+-        }
+-
+-        iterator = list.iterator();
+-
+-        while (iterator.hasNext()) {
+-            K k0 = (K) iterator.next(); // CraftBukkit - decompile error
+-
 -            this.map.remove(this.getKeyForUser(k0));
 -        }
-+            this.d.remove(this.a(k0));
-+        }*/
- 
-+        this.map.values().removeIf(StoredUserEntry::hasExpired);
-+        // Paper end
+-
++        this.map.values().removeIf(StoredUserEntry::hasExpired); // Paper - Use ConcurrentHashMap in JsonList
      }
  
      protected abstract StoredUserEntry<K> createEntry(JsonObject json);
diff --git a/patches/server/Use-a-Queue-for-Queueing-Commands.patch b/patches/server/Use-a-Queue-for-Queueing-Commands.patch
index 77d93c198c..6912b5b510 100644
--- a/patches/server/Use-a-Queue-for-Queueing-Commands.patch
+++ b/patches/server/Use-a-Queue-for-Queueing-Commands.patch
@@ -14,7 +14,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      private static final int CONVERSION_RETRY_DELAY_MS = 5000;
      private static final int CONVERSION_RETRIES = 2;
 -    private final List<ConsoleInput> consoleInput = Collections.synchronizedList(Lists.newArrayList());
-+    private final java.util.Queue<ConsoleInput> serverCommandQueue = new java.util.concurrent.ConcurrentLinkedQueue<>(); // Paper - use a proper queuemmands
++    private final java.util.Queue<ConsoleInput> serverCommandQueue = new java.util.concurrent.ConcurrentLinkedQueue<>(); // Paper - Perf: use a proper queue
      @Nullable
      private QueryThreadGs4 queryThreadGs4;
      // private final RemoteControlCommandListener rconConsoleSource; // CraftBukkit - remove field
@@ -23,17 +23,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          }
          // Paper end - rewrite chunk system
 -        this.consoleInput.add(new ConsoleInput(command, commandSource));
-+        this.serverCommandQueue.add(new ConsoleInput(command, commandSource)); // Paper - use proper queue
++        this.serverCommandQueue.add(new ConsoleInput(command, commandSource)); // Paper - Perf: use proper queue
      }
  
      public void handleConsoleInputs() {
          MinecraftTimings.serverCommandTimer.startTiming(); // Spigot
 -        while (!this.consoleInput.isEmpty()) {
 -            ConsoleInput servercommand = (ConsoleInput) this.consoleInput.remove(0);
-+        // Paper start - use proper queue
++        // Paper start - Perf: use proper queue
 +        ConsoleInput servercommand;
 +        while ((servercommand = this.serverCommandQueue.poll()) != null) {
-+            // Paper end
++            // Paper end - Perf: use proper queue
  
              // CraftBukkit start - ServerCommand for preprocessing
              ServerCommandEvent event = new ServerCommandEvent(this.console, servercommand.msg);
diff --git a/patches/server/Use-getChunkIfLoadedImmediately-in-places.patch b/patches/server/Use-getChunkIfLoadedImmediately-in-places.patch
index 60456ce5ba..271bef1f73 100644
--- a/patches/server/Use-getChunkIfLoadedImmediately-in-places.patch
+++ b/patches/server/Use-getChunkIfLoadedImmediately-in-places.patch
@@ -12,7 +12,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java
 +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
 @@ -0,0 +0,0 @@ public class ServerLevel extends Level implements WorldGenLevel {
-     }
+     public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent
  
      @Override public LevelChunk getChunkIfLoaded(int x, int z) { // Paper - this was added in world too but keeping here for NMS ABI
 -        return this.chunkSource.getChunk(x, z, false);
diff --git a/patches/server/Vanished-players-don-t-have-rights.patch b/patches/server/Vanished-players-don-t-have-rights.patch
index df927e6ce6..4a3475ecbd 100644
--- a/patches/server/Vanished-players-don-t-have-rights.patch
+++ b/patches/server/Vanished-players-don-t-have-rights.patch
@@ -20,7 +20,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                    return false;
 +                }
 +            }
-+            // Paper end
++            // Paper end - Cancel hit for vanished players
              return entity1 == null || this.leftOwner || !entity1.isPassengerOfSameVehicle(entity);
          }
      }
@@ -33,8 +33,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          CollisionContext voxelshapecollision = entityhuman == null ? CollisionContext.empty() : CollisionContext.of(entityhuman);
          // CraftBukkit start - store default return
 -        boolean defaultReturn = (!this.mustSurvive() || state.canSurvive(context.getLevel(), context.getClickedPos())) && context.getLevel().isUnobstructed(state, context.getClickedPos(), voxelshapecollision);
-+        Level world = context.getLevel(); // Paper
-+        boolean defaultReturn = (!this.mustSurvive() || state.canSurvive(context.getLevel(), context.getClickedPos())) && world.checkEntityCollision(state, entityhuman, voxelshapecollision, context.getClickedPos(), true); // Paper
++        Level world = context.getLevel(); // Paper - Cancel hit for vanished players
++        boolean defaultReturn = (!this.mustSurvive() || state.canSurvive(context.getLevel(), context.getClickedPos())) && world.checkEntityCollision(state, entityhuman, voxelshapecollision, context.getClickedPos(), true); // Paper - Cancel hit for vanished players
          org.bukkit.entity.Player player = (context.getPlayer() instanceof ServerPlayer) ? (org.bukkit.entity.Player) context.getPlayer().getBukkitEntity() : null;
  
          BlockCanBuildEvent event = new BlockCanBuildEvent(CraftBlock.at(context.getLevel(), context.getClickedPos()), player, CraftBlockData.fromData(state), defaultReturn);
@@ -46,7 +46,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          this.tileLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.tileMaxTickTime);
      }
  
-+    // Paper start
++    // Paper start - Cancel hit for vanished players
 +    // ret true if no collision
 +    public final boolean checkEntityCollision(BlockState data, Entity source, net.minecraft.world.phys.shapes.CollisionContext voxelshapedcollision,
 +                                              BlockPos position, boolean checkCanSee) {
@@ -84,7 +84,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
 +        return true;
 +    }
-+    // Paper end
++    // Paper end - Cancel hit for vanished players
      @Override
      public boolean isClientSide() {
          return this.isClientSide;