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

---
 ...ntrol-player-s-insomnia-and-phantoms.patch |  2 +-
 .../server/Actually-optimise-explosions.patch |  7 ++--
 ...PlayerAttackEntityCooldownResetEvent.patch |  2 +-
 patches/server/Add-PlayerPickItemEvent.patch  |  2 +-
 ...ntom-creative-and-insomniac-controls.patch |  6 ++--
 .../Add-tick-times-API-and-mspt-command.patch | 12 +++----
 ...-titleOverride-to-InventoryOpenEvent.patch |  8 ++---
 .../server/Add-villager-reputation-API.patch  |  2 +-
 ...-should-not-bypass-cramming-gamerule.patch |  2 +-
 ...ktraces-in-log-messages-crash-report.patch |  4 +--
 ...low-bees-to-load-chunks-for-beehives.patch |  8 ++---
 ...layer-is-attempted-to-be-removed-fro.patch |  2 +-
 ...t-fire-BlockFade-on-worldgen-threads.patch |  3 +-
 ...move-existing-players-to-world-spawn.patch |  4 +--
 ...-entity-collision-code-if-not-needed.patch |  2 +-
 .../server/Ensure-safe-gateway-teleport.patch |  4 +--
 ...enceOrb-should-call-EntitySpawnEvent.patch |  6 ++--
 ...Event-when-Player-is-actually-ready.patch} | 26 +++++++--------
 ...tem-duplication-and-teleport-issues.patch} | 14 ++++----
 ...k-event-leave-message-not-being-sent.patch |  2 +-
 ...ston-physics-inconsistency-MC-188840.patch |  2 +-
 ...-pistons-and-BlockPistonRetractEvent.patch |  2 +-
 ...ix-villager-trading-demand-MC-163962.patch |  2 +-
 .../Force-close-world-loading-screen.patch    |  4 +--
 patches/server/Implement-Mob-Goal-API.patch   |  8 ++---
 .../Implement-Player-Client-Options-API.patch |  2 +-
 .../Improve-and-expand-AsyncCatcher.patch     |  2 +-
 ...pawn-point-if-spawn-in-unloaded-worl.patch |  6 ++--
 .../Optimise-chunk-tick-iteration.patch       |  2 +-
 patches/server/Optimize-Pathfinding.patch     | 12 +++----
 ...-maximum-exp-value-when-merging-orbs.patch |  4 +--
 ...spawn-settings-and-per-player-option.patch |  8 ++---
 ...-PlayerChunkMap-adds-crashing-server.patch |  4 +--
 ...vent-opening-inventories-when-frozen.patch |  8 ++---
 .../Prevent-teleporting-dead-entities.patch   |  4 +--
 patches/server/Properly-resend-entities.patch |  2 +-
 ...and-End-Portal-Frames-from-being-des.patch | 32 +++++++++----------
 .../Reduce-Either-Optional-allocation.patch   |  8 ++---
 ...uce-memory-footprint-of-CompoundTag.patch} | 10 +++---
 ...nnections-shouldn-t-hold-up-shutdown.patch |  4 +--
 ...-per-thread-native-byte-buffer-cache.patch |  2 +-
 ...ookup-for-Treasure-Maps-Fixes-lag-fr.patch |  2 +-
 ...le-player-info-update-packet-on-join.patch |  4 +--
 ...PickItem-Packet-and-kick-for-invalid.patch |  2 +-
 ...Wait-for-Async-Tasks-during-shutdown.patch |  6 ++--
 patches/server/misc-debugging-dumps.patch     | 16 +++++-----
 46 files changed, 136 insertions(+), 140 deletions(-)
 rename patches/server/{Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch => Fire-PlayerJoinEvent-when-Player-is-actually-ready.patch} (79%)
 rename patches/server/{Fix-numerous-item-duplication-issues-and-teleport-is.patch => Fix-item-duplication-and-teleport-issues.patch} (94%)
 rename patches/server/{Reduce-memory-footprint-of-NBTTagCompound.patch => Reduce-memory-footprint-of-CompoundTag.patch} (86%)

diff --git a/patches/server/Ability-to-control-player-s-insomnia-and-phantoms.patch b/patches/server/Ability-to-control-player-s-insomnia-and-phantoms.patch
index 2001683f37..489466a9fd 100644
--- a/patches/server/Ability-to-control-player-s-insomnia-and-phantoms.patch
+++ b/patches/server/Ability-to-control-player-s-insomnia-and-phantoms.patch
@@ -12,7 +12,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          return !entity.isSpectator();
      };
      public static final Predicate<Entity> CAN_BE_COLLIDED_WITH = EntitySelector.NO_SPECTATORS.and(Entity::canBeCollidedWith);
--    public static Predicate<Player> IS_INSOMNIAC = (player) -> net.minecraft.util.Mth.clamp(((net.minecraft.server.level.ServerPlayer) player).getStats().getValue(net.minecraft.stats.Stats.CUSTOM.get(net.minecraft.stats.Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE) >= 72000; // Paper
+-    public static Predicate<Player> IS_INSOMNIAC = (player) -> net.minecraft.util.Mth.clamp(((net.minecraft.server.level.ServerPlayer) player).getStats().getValue(net.minecraft.stats.Stats.CUSTOM.get(net.minecraft.stats.Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE) >= 72000; // Paper - Add phantom creative and insomniac controls
 +    // Paper start - Ability to control player's insomnia and phantoms
 +    public static Predicate<Player> IS_INSOMNIAC = (player) -> {
 +        net.minecraft.server.level.ServerPlayer serverPlayer = (net.minecraft.server.level.ServerPlayer) player;
diff --git a/patches/server/Actually-optimise-explosions.patch b/patches/server/Actually-optimise-explosions.patch
index 4d3b290189..758a26ff5e 100644
--- a/patches/server/Actually-optimise-explosions.patch
+++ b/patches/server/Actually-optimise-explosions.patch
@@ -370,7 +370,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                          for (float f1 = 0.3F; f > 0.0F; f -= 0.22500001F) {
 -                            BlockPos blockposition = BlockPos.containing(d4, d5, d6);
 -                            BlockState iblockdata = this.level.getBlockState(blockposition);
--                            if (!iblockdata.isDestroyable()) continue; // Paper
+-                            if (!iblockdata.isDestroyable()) continue; // Paper - Protect Bedrock and End Portal/Frames from being destroyed
 -                            FluidState fluid = iblockdata.getFluidState(); // Paper
 +                            // Paper start - optimise explosions
 +                            final int blockX = Mth.floor(d4);
@@ -403,11 +403,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -                            if (optional.isPresent()) {
 -                                f -= ((Float) optional.get() + 0.3F) * 0.3F;
 -                            }
--
--                            if (f > 0.0F && this.damageCalculator.shouldBlockExplode(this, this.level, blockposition, iblockdata, f)) {
 +                            if (!iblockdata.isDestroyable()) continue; // Paper
 +                            // Paper - optimise explosions
-+
+ 
+-                            if (f > 0.0F && this.damageCalculator.shouldBlockExplode(this, this.level, blockposition, iblockdata, f)) {
 +                            f -= cachedBlock.resistance; // Paper - optimise explosions
 +
 +                            if (f > 0.0F && cachedBlock.shouldExplode == null) { // Paper - optimise explosions
diff --git a/patches/server/Add-PlayerAttackEntityCooldownResetEvent.patch b/patches/server/Add-PlayerAttackEntityCooldownResetEvent.patch
index 34aad73b10..0a98ed6f03 100644
--- a/patches/server/Add-PlayerAttackEntityCooldownResetEvent.patch
+++ b/patches/server/Add-PlayerAttackEntityCooldownResetEvent.patch
@@ -22,7 +22,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                } else {
 +                    ((net.minecraft.world.entity.player.Player) damagesource.getEntity()).resetAttackStrengthTicker();
 +                }
-+                // Paper end
++                // Paper end - PlayerAttackEntityCooldownResetEvent
              }
              if (event.isCancelled()) {
                  return false;
diff --git a/patches/server/Add-PlayerPickItemEvent.patch b/patches/server/Add-PlayerPickItemEvent.patch
index ee60f1e4d8..3d7090c4f8 100644
--- a/patches/server/Add-PlayerPickItemEvent.patch
+++ b/patches/server/Add-PlayerPickItemEvent.patch
@@ -13,7 +13,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              return;
          }
 -        this.player.getInventory().pickSlot(packet.getSlot()); // Paper - Diff above if changed
-         // Paper end
+         // Paper end - validate pick item position
 +        // Paper start - Add PlayerPickItemEvent
 +        Player bukkitPlayer = this.player.getBukkitEntity();
 +        int targetSlot = this.player.getInventory().getSuitableHotbarSlot();
diff --git a/patches/server/Add-phantom-creative-and-insomniac-controls.patch b/patches/server/Add-phantom-creative-and-insomniac-controls.patch
index d2e2b523c3..f8cdbb0aa9 100644
--- a/patches/server/Add-phantom-creative-and-insomniac-controls.patch
+++ b/patches/server/Add-phantom-creative-and-insomniac-controls.patch
@@ -12,7 +12,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          return !entity.isSpectator();
      };
      public static final Predicate<Entity> CAN_BE_COLLIDED_WITH = EntitySelector.NO_SPECTATORS.and(Entity::canBeCollidedWith);
-+    public static Predicate<Player> IS_INSOMNIAC = (player) -> net.minecraft.util.Mth.clamp(((net.minecraft.server.level.ServerPlayer) player).getStats().getValue(net.minecraft.stats.Stats.CUSTOM.get(net.minecraft.stats.Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE) >= 72000; // Paper
++    public static Predicate<Player> IS_INSOMNIAC = (player) -> net.minecraft.util.Mth.clamp(((net.minecraft.server.level.ServerPlayer) player).getStats().getValue(net.minecraft.stats.Stats.CUSTOM.get(net.minecraft.stats.Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE) >= 72000; // Paper - Add phantom creative and insomniac controls
  
      private EntitySelector() {}
      // Paper start
@@ -24,7 +24,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                          Player entityhuman = (Player) iterator.next();
  
                          if (Phantom.this.canAttack(entityhuman, TargetingConditions.DEFAULT)) {
-+                            if (!level().paperConfig().entities.behavior.phantomsOnlyAttackInsomniacs || EntitySelector.IS_INSOMNIAC.test(entityhuman)) // Paper
++                            if (!level().paperConfig().entities.behavior.phantomsOnlyAttackInsomniacs || EntitySelector.IS_INSOMNIAC.test(entityhuman)) // Paper - Add phantom creative and insomniac controls
                              Phantom.this.setTarget(entityhuman, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER, true); // CraftBukkit - reason
                              return true;
                          }
@@ -37,7 +37,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                          ServerPlayer entityplayer = (ServerPlayer) iterator.next();
  
 -                        if (!entityplayer.isSpectator()) {
-+                        if (!entityplayer.isSpectator() && (!world.paperConfig().entities.behavior.phantomsDoNotSpawnOnCreativePlayers || !entityplayer.isCreative())) { // Paper
++                        if (!entityplayer.isSpectator() && (!world.paperConfig().entities.behavior.phantomsDoNotSpawnOnCreativePlayers || !entityplayer.isCreative())) { // Paper - Add phantom creative and insomniac controls
                              BlockPos blockposition = entityplayer.blockPosition();
  
                              if (!world.dimensionType().hasSkyLight() || blockposition.getY() >= world.getSeaLevel() && world.canSeeSky(blockposition)) {
diff --git a/patches/server/Add-tick-times-API-and-mspt-command.patch b/patches/server/Add-tick-times-API-and-mspt-command.patch
index 11479ba47e..753a50cd37 100644
--- a/patches/server/Add-tick-times-API-and-mspt-command.patch
+++ b/patches/server/Add-tick-times-API-and-mspt-command.patch
@@ -132,11 +132,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      private int playerIdleTimeout;
      private final long[] tickTimesNanos;
      private long aggregatedTickTimesNanos;
-+    // Paper start
++    // Paper start - Add tick times API and /mspt command
 +    public final TickTimes tickTimes5s = new TickTimes(100);
 +    public final TickTimes tickTimes10s = new TickTimes(200);
 +    public final TickTimes tickTimes60s = new TickTimes(1200);
-+    // Paper end
++    // Paper end - Add tick times API and /mspt command
      @Nullable
      private KeyPair keyPair;
      @Nullable
@@ -144,11 +144,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          this.smoothedTickTimeMillis = this.smoothedTickTimeMillis * 0.8F + (float) j / (float) TimeUtil.NANOSECONDS_PER_MILLISECOND * 0.19999999F;
          long l = Util.getNanos();
  
-+        // Paper start
++        // Paper start - Add tick times API and /mspt command
 +        tickTimes5s.add(this.tickCount, j);
 +        tickTimes10s.add(this.tickCount, j);
 +        tickTimes60s.add(this.tickCount, j);
-+        // Paper end
++        // Paper end - Add tick times API and /mspt command
          this.logTickTime(l - i);
          this.profiler.pop();
          org.spigotmc.WatchdogThread.tick(); // Spigot
@@ -157,7 +157,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      }
 +
-+    // Paper start
++    // Paper start - Add tick times API and /mspt command
 +    public static class TickTimes {
 +        private final long[] times;
 +
@@ -181,7 +181,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            return ((double) total / (double) times.length) * 1.0E-6D;
 +        }
 +    }
-+    // Paper end
++    // Paper end - Add tick times API and /mspt command
  }
 diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
diff --git a/patches/server/Add-titleOverride-to-InventoryOpenEvent.patch b/patches/server/Add-titleOverride-to-InventoryOpenEvent.patch
index 440c669b0b..41209eb54b 100644
--- a/patches/server/Add-titleOverride-to-InventoryOpenEvent.patch
+++ b/patches/server/Add-titleOverride-to-InventoryOpenEvent.patch
@@ -31,7 +31,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              } else {
                  // CraftBukkit start
                  this.containerMenu = container;
--                if (!this.isImmobile()) this.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), container.getTitle())); // Paper
+-                if (!this.isImmobile()) this.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), container.getTitle())); // Paper - Prevent opening inventories when frozen
 +                if (!this.isImmobile()) this.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), Objects.requireNonNullElseGet(title, container::getTitle))); // Paper - Add titleOverride to InventoryOpenEvent
                  // CraftBukkit end
                  this.initMenu(container);
@@ -57,7 +57,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        if (result.getFirst() != null) adventure$title = result.getFirst(); // Paper - Add titleOverride to InventoryOpenEvent
  
          //player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, CraftChatMessage.fromString(title)[0])); // Paper - comment
-         if (!player.isImmobile()) player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper
+         if (!player.isImmobile()) player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper - Prevent opening inventories when frozen
 @@ -0,0 +0,0 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
          }
  
@@ -74,9 +74,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          //String title = inventory.getTitle(); // Paper - comment
          net.kyori.adventure.text.Component adventure$title = inventory.title(); // Paper
          if (adventure$title == null) adventure$title = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(inventory.getTitle()); // Paper
-+        if (result.getFirst() != null) adventure$title = result.getFirst(); // Paper - Add titleOverride to InventoryOpenEvent
++        if (result.getFirst() != null) adventure$title = result.getFirst(); // Paper - Add titleOverride to InventoryOpenEvent        //player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, CraftChatMessage.fromString(title)[0])); // Paper - comment
          //player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, CraftChatMessage.fromString(title)[0])); // Paper - comment
-         if (!player.isImmobile()) player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper
+         if (!player.isImmobile()) player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper - Prevent opening inventories when frozen
          player.containerMenu = container;
 diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
diff --git a/patches/server/Add-villager-reputation-API.patch b/patches/server/Add-villager-reputation-API.patch
index 554d3c9167..fdd3d8f901 100644
--- a/patches/server/Add-villager-reputation-API.patch
+++ b/patches/server/Add-villager-reputation-API.patch
@@ -52,7 +52,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                case TRADING -> com.destroystokyo.paper.entity.villager.ReputationType.TRADING;
 +            };
 +        }
-+        // Paper end
++        // Paper end - Add villager reputation API
      }
  
      static record GossipEntry(UUID target, GossipType type, int value) {
diff --git a/patches/server/Climbing-should-not-bypass-cramming-gamerule.patch b/patches/server/Climbing-should-not-bypass-cramming-gamerule.patch
index 9629bfd1aa..1f9a7b294d 100644
--- a/patches/server/Climbing-should-not-bypass-cramming-gamerule.patch
+++ b/patches/server/Climbing-should-not-bypass-cramming-gamerule.patch
@@ -55,7 +55,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            List<Entity> list = this.level().getEntities((Entity) this, this.getBoundingBox(), EntitySelector.pushable(this, this.level().paperConfig().collisions.fixClimbingBypassingCrammingRule)); // Paper - Climbing should not bypass cramming gamerule
  
              if (!list.isEmpty()) {
-                 // Paper - moved up
+                 // Paper - don't run getEntities if we're not going to use its result; moved up
 @@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable {
          return !this.isRemoved() && this.collides; // CraftBukkit
      }
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 ae50f7c08b..79ab1d74e5 100644
--- a/patches/server/Deobfuscate-stacktraces-in-log-messages-crash-report.patch
+++ b/patches/server/Deobfuscate-stacktraces-in-log-messages-crash-report.patch
@@ -586,12 +586,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +++ b/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
 @@ -0,0 +0,0 @@ public class ServerConfigurationPacketListenerImpl extends ServerCommonPacketLis
              ServerConfigurationPacketListenerImpl.LOGGER.error("Couldn't place player in world", exception);
-             // Paper start
+             // Paper start - Debugging
              if (MinecraftServer.getServer().isDebugging()) {
 -                exception.printStackTrace();
 +                io.papermc.paper.util.TraceUtil.printStackTrace(exception);
              }
-             // Paper end
+             // Paper end - Debugging
              this.connection.send(new ClientboundDisconnectPacket(ServerConfigurationPacketListenerImpl.DISCONNECT_REASON_INVALID_DATA));
 diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
diff --git a/patches/server/Do-not-allow-bees-to-load-chunks-for-beehives.patch b/patches/server/Do-not-allow-bees-to-load-chunks-for-beehives.patch
index d8c8d8a245..fe92df7e90 100644
--- a/patches/server/Do-not-allow-bees-to-load-chunks-for-beehives.patch
+++ b/patches/server/Do-not-allow-bees-to-load-chunks-for-beehives.patch
@@ -12,7 +12,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          if (this.hivePos == null) {
              return false;
          } else {
-+            if (!this.level().isLoadedAndInBounds(this.hivePos)) return false; // Paper
++            if (!this.level().isLoadedAndInBounds(this.hivePos)) return false; // Paper - Do not allow bees to load chunks for beehives
              BlockEntity tileentity = this.level().getBlockEntity(this.hivePos);
  
              return tileentity instanceof BeehiveBlockEntity && ((BeehiveBlockEntity) tileentity).isFireNearby();
@@ -20,7 +20,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      }
  
      private boolean doesHiveHaveSpace(BlockPos pos) {
-+        if (!this.level().isLoadedAndInBounds(pos)) return false; // Paper
++        if (!this.level().isLoadedAndInBounds(pos)) return false; // Paper - Do not allow bees to load chunks for beehives
          BlockEntity tileentity = this.level().getBlockEntity(pos);
  
          return tileentity instanceof BeehiveBlockEntity ? !((BeehiveBlockEntity) tileentity).isFull() : false;
@@ -28,7 +28,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          @Override
          public boolean canBeeUse() {
              if (Bee.this.hasHive() && Bee.this.wantsToEnterHive() && Bee.this.hivePos.closerToCenterThan(Bee.this.position(), 2.0D)) {
-+                if (!Bee.this.level().isLoadedAndInBounds(Bee.this.hivePos)) return false; // Paper
++                if (!Bee.this.level().isLoadedAndInBounds(Bee.this.hivePos)) return false; // Paper - Do not allow bees to load chunks for beehives
                  BlockEntity tileentity = Bee.this.level().getBlockEntity(Bee.this.hivePos);
  
                  if (tileentity instanceof BeehiveBlockEntity) {
@@ -36,7 +36,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
          @Override
          public void start() {
-+            if (!Bee.this.level().isLoadedAndInBounds(Bee.this.hivePos)) return; // Paper
++            if (!Bee.this.level().isLoadedAndInBounds(Bee.this.hivePos)) return; // Paper - Do not allow bees to load chunks for beehives
              BlockEntity tileentity = Bee.this.level().getBlockEntity(Bee.this.hivePos);
  
              if (tileentity instanceof BeehiveBlockEntity) {
diff --git a/patches/server/Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch b/patches/server/Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch
index ff04083261..4fac73d4f3 100644
--- a/patches/server/Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch
+++ b/patches/server/Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch
@@ -16,7 +16,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
 -        objectset.remove(player);
 -        if (objectset.isEmpty()) {
-+        if (objectset != null) objectset.remove(player); // Paper - some state corruption happens here, don't crash, clean up gracefully.
++        if (objectset != null) objectset.remove(player); // Paper - some state corruption happens here, don't crash, clean up gracefully
 +        if (objectset == null || objectset.isEmpty()) { // Paper
              this.playersPerChunk.remove(i);
              this.naturalSpawnChunkCounter.update(i, Integer.MAX_VALUE, false);
diff --git a/patches/server/Don-t-fire-BlockFade-on-worldgen-threads.patch b/patches/server/Don-t-fire-BlockFade-on-worldgen-threads.patch
index 15a2e801cf..40eec0ec1b 100644
--- a/patches/server/Don-t-fire-BlockFade-on-worldgen-threads.patch
+++ b/patches/server/Don-t-fire-BlockFade-on-worldgen-threads.patch
@@ -3,7 +3,6 @@ From: Aikar <aikar@aikar.co>
 Date: Thu, 23 Apr 2020 01:36:39 -0400
 Subject: [PATCH] Don't fire BlockFade on worldgen threads
 
-Caused a deadlock
 
 diff --git a/src/main/java/net/minecraft/world/level/block/FireBlock.java b/src/main/java/net/minecraft/world/level/block/FireBlock.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
@@ -22,7 +21,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              }
          }
 -        return this.getStateWithAge(world, pos, (Integer) state.getValue(FireBlock.AGE));
-+        return this.getStateWithAge(world, pos, (Integer) state.getValue(FireBlock.AGE)); // Paper - diff on change, see "don't fire events in world generation"
++        return this.getStateWithAge(world, pos, (Integer) state.getValue(FireBlock.AGE)); // Paper - don't fire events in world generation; diff on change, see "don't fire events in world generation"
          // CraftBukkit end
      }
  
diff --git a/patches/server/Don-t-move-existing-players-to-world-spawn.patch b/patches/server/Don-t-move-existing-players-to-world-spawn.patch
index 50a2b65ecc..7e3e8694db 100644
--- a/patches/server/Don-t-move-existing-players-to-world-spawn.patch
+++ b/patches/server/Don-t-move-existing-players-to-world-spawn.patch
@@ -21,7 +21,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          this.advancements = server.getPlayerList().getPlayerAdvancements(this);
          this.setMaxUpStep(1.0F);
 -        this.fudgeSpawnLocation(world);
-+        // this.fudgeSpawnLocation(world); // Paper - don't move to spawn on login, only first join
++        // this.fudgeSpawnLocation(world); // Paper - Don't move existing players to world spawn
          this.updateOptions(clientOptions);
  
          this.cachedSingleHashSet = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper
@@ -42,7 +42,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          // Paper start
          if (nbttagcompound == null) {
              player.spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT; // set Player SpawnReason to DEFAULT on first login
-+            player.fudgeSpawnLocation(worldserver1); // only move to spawn on first login, otherwise, stay where you are....
++            player.fudgeSpawnLocation(worldserver1); // Paper - Don't move existing players to world spawn
          }
          // Paper end
          player.setServerLevel(worldserver1);
diff --git a/patches/server/Don-t-run-entity-collision-code-if-not-needed.patch b/patches/server/Don-t-run-entity-collision-code-if-not-needed.patch
index 79256d1a2a..f65857cd30 100644
--- a/patches/server/Don-t-run-entity-collision-code-if-not-needed.patch
+++ b/patches/server/Don-t-run-entity-collision-code-if-not-needed.patch
@@ -37,7 +37,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
              if (!list.isEmpty()) {
 -                int i = this.level().getGameRules().getInt(GameRules.RULE_MAX_ENTITY_CRAMMING);
-+                // Paper - moved up
++                // Paper - don't run getEntities if we're not going to use its result; moved up
  
                  if (i > 0 && list.size() > i - 1 && this.random.nextInt(4) == 0) {
                      int j = 0;
diff --git a/patches/server/Ensure-safe-gateway-teleport.patch b/patches/server/Ensure-safe-gateway-teleport.patch
index ed45257899..51168bd3bd 100644
--- a/patches/server/Ensure-safe-gateway-teleport.patch
+++ b/patches/server/Ensure-safe-gateway-teleport.patch
@@ -13,14 +13,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
              if (!list.isEmpty()) {
 -                TheEndGatewayBlockEntity.teleportEntity(world, pos, state, (Entity) list.get(world.random.nextInt(list.size())), blockEntity);
-+                // Paper start
++                // Paper start - Ensure safe gateway teleport
 +                for (Entity entity : list) {
 +                    if (entity.canChangeDimensions()) {
 +                        TheEndGatewayBlockEntity.teleportEntity(world, pos, state, entity, blockEntity);
 +                        break;
 +                    }
 +                }
-+                // Paper end
++                // Paper end - Ensure safe gateway teleport
              }
  
              if (blockEntity.age % 2400L == 0L) {
diff --git a/patches/server/ExperienceOrb-should-call-EntitySpawnEvent.patch b/patches/server/ExperienceOrb-should-call-EntitySpawnEvent.patch
index e3a04d5d50..42e8d8b920 100644
--- a/patches/server/ExperienceOrb-should-call-EntitySpawnEvent.patch
+++ b/patches/server/ExperienceOrb-should-call-EntitySpawnEvent.patch
@@ -13,10 +13,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          if (entity instanceof net.minecraft.world.entity.ExperienceOrb xp) {
              double radius = world.spigotConfig.expMerge;
 -            if (radius > 0) {
-+            // Paper start - Call EntitySpawnEvent for ExperienceOrb entities
-+            event = CraftEventFactory.callEntitySpawnEvent(entity);
++            event = CraftEventFactory.callEntitySpawnEvent(entity); // Call spawn event for ExperienceOrb entities
 +            if (radius > 0 && !event.isCancelled() && !entity.isRemoved()) {
-+            // Paper end - Call EntitySpawnEvent for ExperienceOrb entities
-                 // Paper start - Maximum exp value when merging - Whole section has been tweaked, see comments for specifics
+                 // Paper start - Maximum exp value when merging; Whole section has been tweaked, see comments for specifics
                  final int maxValue = world.paperConfig().entities.behavior.experienceMergeMaxValue;
                  final boolean mergeUnconditionally = world.paperConfig().entities.behavior.experienceMergeMaxValue <= 0;
diff --git a/patches/server/Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch b/patches/server/Fire-PlayerJoinEvent-when-Player-is-actually-ready.patch
similarity index 79%
rename from patches/server/Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch
rename to patches/server/Fire-PlayerJoinEvent-when-Player-is-actually-ready.patch
index 717f3e3f48..3ce338b7ea 100644
--- a/patches/server/Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch
+++ b/patches/server/Fire-PlayerJoinEvent-when-Player-is-actually-ready.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Aikar <aikar@aikar.co>
 Date: Sun, 19 Apr 2020 00:05:46 -0400
-Subject: [PATCH] Fix Longstanding Broken behavior of PlayerJoinEvent
+Subject: [PATCH] Fire PlayerJoinEvent when Player is actually ready
 
 For years, plugin developers have had to delay many things they do
 inside of the PlayerJoinEvent by 1 tick to make it actually work.
@@ -35,13 +35,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java
 +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
 @@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
-                 + ": " + entity  + (this.entityMap.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : ""), new Throwable());
              return;
          }
-+        if (entity instanceof ServerPlayer && ((ServerPlayer) entity).supressTrackerForLogin) return; // Delay adding to tracker until after list packets
-         // Paper end
+         // Paper end - ignore and warn about illegal addEntity calls instead of crashing server
++        if (entity instanceof ServerPlayer && ((ServerPlayer) entity).supressTrackerForLogin) return; // Paper - Fire PlayerJoinEvent when Player is actually ready; Delay adding to tracker until after list packets
          if (!(entity instanceof EnderDragonPart)) {
              EntityType<?> entitytypes = entity.getType();
+             int i = entitytypes.clientTrackingRange() * 16;
 diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -50,7 +50,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      public double maxHealthCache;
      public boolean joining = true;
      public boolean sentListPacket = false;
-+    public boolean supressTrackerForLogin = false; // Paper
++    public boolean supressTrackerForLogin = false; // Paper - Fire PlayerJoinEvent when Player is actually ready
      public String kickLeaveMessage = null; // SPIGOT-3034: Forward leave message to PlayerQuitEvent
      // CraftBukkit end
      public boolean isRealPlayer; // Paper
@@ -62,12 +62,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          this.playersByUUID.put(player.getUUID(), player);
          // this.broadcastAll(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(entityplayer))); // CraftBukkit - replaced with loop below
  
-+        // Paper start - correctly register player BEFORE PlayerJoinEvent, so the entity is valid and doesn't require tick delay hacks
++        // Paper start - Fire PlayerJoinEvent when Player is actually ready; correctly register player BEFORE PlayerJoinEvent, so the entity is valid and doesn't require tick delay hacks
 +        player.supressTrackerForLogin = true;
 +        worldserver1.addNewPlayer(player);
 +        this.server.getCustomBossEvents().onPlayerConnect(player); // see commented out section below worldserver.addPlayerJoin(entityplayer);
 +        mountSavedVehicle(player, worldserver1, nbttagcompound);
-+        // Paper end
++        // Paper end - Fire PlayerJoinEvent when Player is actually ready
          // CraftBukkit start
          CraftPlayer bukkitPlayer = player.getBukkitEntity();
  
@@ -75,8 +75,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(entityplayer1)));
          }
          player.sentListPacket = true;
-+        player.supressTrackerForLogin = false; // Paper
-+        ((ServerLevel)player.level()).getChunkSource().chunkMap.addEntity(player); // Paper - track entity now
++        player.supressTrackerForLogin = false; // Paper - Fire PlayerJoinEvent when Player is actually ready
++        ((ServerLevel)player.level()).getChunkSource().chunkMap.addEntity(player); // Paper - Fire PlayerJoinEvent when Player is actually ready; track entity now
          // CraftBukkit end
  
          player.getEntityData().refresh(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn
@@ -84,11 +84,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              playerconnection.send(new ClientboundUpdateMobEffectPacket(player.getId(), mobeffect));
          }
  
-+        // Paper start - move vehicle into method so it can be called above - short circuit around that code
++        // Paper start - Fire PlayerJoinEvent when Player is actually ready; move vehicle into method so it can be called above - short circuit around that code
 +        onPlayerJoinFinish(player, worldserver1, s1);
 +    }
 +    private void mountSavedVehicle(ServerPlayer player, ServerLevel worldserver1, CompoundTag nbttagcompound) {
-+        // Paper end
++        // Paper end - Fire PlayerJoinEvent when Player is actually ready
          if (nbttagcompound != null && nbttagcompound.contains("RootVehicle", 10)) {
              CompoundTag nbttagcompound1 = nbttagcompound.getCompound("RootVehicle");
              // CraftBukkit start
@@ -96,10 +96,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              }
          }
  
-+        // Paper start
++        // Paper start - Fire PlayerJoinEvent when Player is actually ready
 +    }
 +    public void onPlayerJoinFinish(ServerPlayer player, ServerLevel worldserver1, String s1) {
-+        // Paper end
++        // Paper end - Fire PlayerJoinEvent when Player is actually ready
          player.initInventoryMenu();
          // CraftBukkit - Moved from above, added world
          // Paper start - Add to collideRule team if needed
diff --git a/patches/server/Fix-numerous-item-duplication-issues-and-teleport-is.patch b/patches/server/Fix-item-duplication-and-teleport-issues.patch
similarity index 94%
rename from patches/server/Fix-numerous-item-duplication-issues-and-teleport-is.patch
rename to patches/server/Fix-item-duplication-and-teleport-issues.patch
index 5e7474e221..8b90fcb3c6 100644
--- a/patches/server/Fix-numerous-item-duplication-issues-and-teleport-is.patch
+++ b/patches/server/Fix-item-duplication-and-teleport-issues.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Aikar <aikar@aikar.co>
 Date: Sat, 25 Apr 2020 06:46:35 -0400
-Subject: [PATCH] Fix numerous item duplication issues and teleport issues
+Subject: [PATCH] Fix item duplication and teleport issues
 
 This notably fixes the newest "Donkey Dupe", but also fixes a lot
 of dupe bugs in general around nether portals and entity world transfer
@@ -38,12 +38,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      @Nullable
      public Entity teleportTo(ServerLevel worldserver, Vec3 location) {
          // CraftBukkit end
-+        // Paper start - fix bad state entities causing dupes
++        // Paper start - Fix item duplication and teleport issues
 +        if (!this.isAlive() || !this.valid) {
 +            LOGGER.warn("Illegal Entity Teleport " + this + " to " + worldserver + ":" + location, new Throwable());
 +            return null;
 +        }
-+        // Paper end
++        // Paper end - Fix item duplication and teleport issues
          if (this.level() instanceof ServerLevel && !this.isRemoved()) {
              this.level().getProfiler().push("changeDimension");
              // CraftBukkit start
@@ -51,11 +51,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                  // CraftBukkit end
  
                  this.level().getProfiler().popPush("reloading");
-+                // Paper start - Change lead drop timing to prevent dupe
++                // Paper start - Fix item duplication and teleport issues
 +                if (this instanceof Mob) {
 +                    ((Mob) this).dropLeash(true, true); // Paper drop lead
 +                }
-+                // Paper end
++                // Paper end - Fix item duplication and teleport issues
                  Entity entity = this.getType().create(worldserver);
  
                  if (entity != null) {
@@ -75,7 +75,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      public boolean canChangeDimensions() {
 -        return !this.isPassenger() && !this.isVehicle();
-+        return !this.isPassenger() && !this.isVehicle() && isAlive() && valid; // Paper
++        return !this.isPassenger() && !this.isVehicle() && isAlive() && valid; // Paper - Fix item duplication and teleport issues
      }
  
      public float getBlockExplosionResistance(Explosion explosion, BlockGetter world, BlockPos pos, BlockState blockState, FluidState fluidState, float max) {
@@ -90,7 +90,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -                    if (this.deathScore >= 0 && entityliving != null) {
 -                        entityliving.awardKillScore(this, this.deathScore, damageSource);
 -                    }
-+                    // if (this.deathScore >= 0 && entityliving != null) { // Paper moved to be run earlier in #dropAllDeathLoot before destroying the drop items in CraftEventFactory#callEntityDeathEvent
++                    // if (this.deathScore >= 0 && entityliving != null) { // Paper - Fix item duplication and teleport issues; moved to be run earlier in #dropAllDeathLoot before destroying the drop items in CraftEventFactory#callEntityDeathEvent
 +                    //     entityliving.awardKillScore(this, this.deathScore, damageSource);
 +                    // }
                      // Paper start - clear equipment if event is not cancelled
diff --git a/patches/server/Fix-kick-event-leave-message-not-being-sent.patch b/patches/server/Fix-kick-event-leave-message-not-being-sent.patch
index 485b62ca0b..976512fe59 100644
--- a/patches/server/Fix-kick-event-leave-message-not-being-sent.patch
+++ b/patches/server/Fix-kick-event-leave-message-not-being-sent.patch
@@ -11,7 +11,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 @@ -0,0 +0,0 @@ public class ServerPlayer extends Player {
      public boolean joining = true;
      public boolean sentListPacket = false;
-     public boolean supressTrackerForLogin = false; // Paper
+     public boolean supressTrackerForLogin = false; // Paper - Fire PlayerJoinEvent when Player is actually ready
 -    public String kickLeaveMessage = null; // SPIGOT-3034: Forward leave message to PlayerQuitEvent
      // CraftBukkit end
      public boolean isRealPlayer; // Paper
diff --git a/patches/server/Fix-piston-physics-inconsistency-MC-188840.patch b/patches/server/Fix-piston-physics-inconsistency-MC-188840.patch
index e660772089..638a857c76 100644
--- a/patches/server/Fix-piston-physics-inconsistency-MC-188840.patch
+++ b/patches/server/Fix-piston-physics-inconsistency-MC-188840.patch
@@ -74,7 +74,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                      BlockState blockState = Block.updateFromNeighbourShapes(blockEntity.movedState, world, pos);
                      if (blockState.isAir()) {
 -                        world.setBlock(pos, blockEntity.movedState, 84);
-+                        world.setBlock(pos, blockEntity.movedState, io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPistonDuplication ? 84 : (84 | Block.UPDATE_CLIENTS)); // Paper - force notify (flag 2), it's possible the set type by the piston block (which doesn't notify) set this block to air
++                        world.setBlock(pos, blockEntity.movedState, io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPistonDuplication ? 84 : (84 | Block.UPDATE_CLIENTS)); // Paper - fix a variety of piston desync dupes; force notify (flag 2), it's possible the set type by the piston block (which doesn't notify) set this block to air
                          Block.updateOrDestroy(blockEntity.movedState, blockState, world, pos, 3);
                      } else {
                          if (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && blockState.getValue(BlockStateProperties.WATERLOGGED)) {
diff --git a/patches/server/Fix-sticky-pistons-and-BlockPistonRetractEvent.patch b/patches/server/Fix-sticky-pistons-and-BlockPistonRetractEvent.patch
index 76a0cb0b45..01fee3afd6 100644
--- a/patches/server/Fix-sticky-pistons-and-BlockPistonRetractEvent.patch
+++ b/patches/server/Fix-sticky-pistons-and-BlockPistonRetractEvent.patch
@@ -67,7 +67,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            }
 +            // Paper end - Fix sticky pistons and BlockPistonRetractEvent
              world.setBlock(pos, iblockdata2, 20);
-             world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata2, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true)); // Paper - diff on change
+             world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata2, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true)); // Paper - Protect Bedrock and End Portal/Frames from being destroyed; diff on change
              world.blockUpdated(pos, iblockdata2.getBlock());
 @@ -0,0 +0,0 @@ public class PistonBaseBlock extends DirectionalBlock {
                      if (type == 1 && !iblockdata3.isAir() && PistonBaseBlock.isPushable(iblockdata3, world, blockposition1, enumdirection.getOpposite(), false, enumdirection) && (iblockdata3.getPistonPushReaction() == PushReaction.NORMAL || iblockdata3.is(Blocks.PISTON) || iblockdata3.is(Blocks.STICKY_PISTON))) {
diff --git a/patches/server/Fix-villager-trading-demand-MC-163962.patch b/patches/server/Fix-villager-trading-demand-MC-163962.patch
index f1b2d790d5..bcb3116f73 100644
--- a/patches/server/Fix-villager-trading-demand-MC-163962.patch
+++ b/patches/server/Fix-villager-trading-demand-MC-163962.patch
@@ -14,7 +14,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      public void updateDemand() {
 -        this.demand = this.demand + this.uses - (this.maxUses - this.uses);
-+        this.demand = Math.max(0, this.demand + this.uses - (this.maxUses - this.uses)); // Paper
++        this.demand = Math.max(0, this.demand + this.uses - (this.maxUses - this.uses)); // Paper - Fix MC-163962
      }
  
      public ItemStack assemble() {
diff --git a/patches/server/Force-close-world-loading-screen.patch b/patches/server/Force-close-world-loading-screen.patch
index df9baa45d5..bd6a56f358 100644
--- a/patches/server/Force-close-world-loading-screen.patch
+++ b/patches/server/Force-close-world-loading-screen.patch
@@ -15,7 +15,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +++ b/src/main/java/net/minecraft/server/players/PlayerList.java
 @@ -0,0 +0,0 @@ public abstract class PlayerList {
  
-         // Paper start - move vehicle into method so it can be called above - short circuit around that code
+         // Paper start - Fire PlayerJoinEvent when Player is actually ready; move vehicle into method so it can be called above - short circuit around that code
          onPlayerJoinFinish(player, worldserver1, s1);
 +        // Paper start - Send empty chunk, so players aren't stuck in the world loading screen with our chunk system not sending chunks when dead
 +        if (player.isDeadOrDying()) {
@@ -29,4 +29,4 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        // Paper end - Send empty chunk
      }
      private void mountSavedVehicle(ServerPlayer player, ServerLevel worldserver1, CompoundTag nbttagcompound) {
-         // Paper end
+         // Paper end - Fire PlayerJoinEvent when Player is actually ready
diff --git a/patches/server/Implement-Mob-Goal-API.patch b/patches/server/Implement-Mob-Goal-API.patch
index 9a86d4fe20..b5d80da29b 100644
--- a/patches/server/Implement-Mob-Goal-API.patch
+++ b/patches/server/Implement-Mob-Goal-API.patch
@@ -751,13 +751,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      private final EnumSet<Goal.Flag> flags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be.
      private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector
  
-+    // Paper start make sure goaltypes is never empty
++    // Paper start - Mob goal API; make sure goaltypes is never empty
 +    public Goal() {
 +        if (this.goalTypes.size() == 0) {
 +            this.goalTypes.addUnchecked(Flag.UNKNOWN_BEHAVIOR);
 +        }
 +    }
-+    // Paper end
++    // Paper end - Mob goal API
 +
      public abstract boolean canUse();
  
@@ -777,7 +777,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          return Mth.positiveCeilDiv(serverTicks, 2);
      }
  
-+    // Paper start - mob goal api
++    // Paper start - Mob goal api
 +    private com.destroystokyo.paper.entity.ai.PaperVanillaGoal<?> vanillaGoal = null;
 +    public <T extends org.bukkit.entity.Mob> com.destroystokyo.paper.entity.ai.Goal<T> asPaperVanillaGoal() {
 +        if(this.vanillaGoal == null) {
@@ -786,7 +786,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        //noinspection unchecked
 +        return (com.destroystokyo.paper.entity.ai.Goal<T>) this.vanillaGoal;
 +    }
-+    // Paper end - mob goal api
++    // Paper end - Mob goal api
 +
      public static enum Flag {
 +        UNKNOWN_BEHAVIOR, // Paper - add UNKNOWN_BEHAVIOR
diff --git a/patches/server/Implement-Player-Client-Options-API.patch b/patches/server/Implement-Player-Client-Options-API.patch
index 4992c23751..584240c6f3 100644
--- a/patches/server/Implement-Player-Client-Options-API.patch
+++ b/patches/server/Implement-Player-Client-Options-API.patch
@@ -93,7 +93,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 @@ -0,0 +0,0 @@ public class ServerPlayer extends Player {
          this.advancements = server.getPlayerList().getPlayerAdvancements(this);
          this.setMaxUpStep(1.0F);
-         // this.fudgeSpawnLocation(world); // Paper - don't move to spawn on login, only first join
+         // this.fudgeSpawnLocation(world); // Paper - Don't move existing players to world spawn
 -        this.updateOptions(clientOptions);
 +        this.updateOptionsNoEvents(clientOptions); // Paper - don't call options events on login
  
diff --git a/patches/server/Improve-and-expand-AsyncCatcher.patch b/patches/server/Improve-and-expand-AsyncCatcher.patch
index e8c3112bdb..12851c6776 100644
--- a/patches/server/Improve-and-expand-AsyncCatcher.patch
+++ b/patches/server/Improve-and-expand-AsyncCatcher.patch
@@ -25,7 +25,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      public void internalTeleport(double d0, double d1, double d2, float f, float f1, Set<RelativeMovement> set) { // Paper
 +        org.spigotmc.AsyncCatcher.catchOp("teleport"); // Paper
-         // Paper start
+         // Paper start - Prevent teleporting dead entities
          if (player.isRemoved()) {
              LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName());
 diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
diff --git a/patches/server/Move-player-to-spawn-point-if-spawn-in-unloaded-worl.patch b/patches/server/Move-player-to-spawn-point-if-spawn-in-unloaded-worl.patch
index c443ce9196..2a4bd288ed 100644
--- a/patches/server/Move-player-to-spawn-point-if-spawn-in-unloaded-worl.patch
+++ b/patches/server/Move-player-to-spawn-point-if-spawn-in-unloaded-worl.patch
@@ -79,11 +79,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          // Paper start
          if (nbttagcompound == null) {
              player.spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT; // set Player SpawnReason to DEFAULT on first login
-+        // Paper start - reset to main world spawn if first spawn or invalid world
++            // Paper start - reset to main world spawn if first spawn or invalid world
 +        }
 +        if (nbttagcompound == null || invalidPlayerWorld) {
-+        // Paper end
-             player.fudgeSpawnLocation(worldserver1); // only move to spawn on first login, otherwise, stay where you are....
++            // Paper end - reset to main world spawn if first spawn or invalid world
+             player.fudgeSpawnLocation(worldserver1); // Paper - Don't move existing players to world spawn
          }
          // Paper end
 diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
diff --git a/patches/server/Optimise-chunk-tick-iteration.patch b/patches/server/Optimise-chunk-tick-iteration.patch
index e5a5615f81..e53d4070f2 100644
--- a/patches/server/Optimise-chunk-tick-iteration.patch
+++ b/patches/server/Optimise-chunk-tick-iteration.patch
@@ -162,7 +162,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          //this.tickingTicketsTracker.addTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair); // Paper - no longer used
      }
 @@ -0,0 +0,0 @@ public abstract class DistanceManager {
-         if (objectset != null) objectset.remove(player); // Paper - some state corruption happens here, don't crash, clean up gracefully.
+         if (objectset != null) objectset.remove(player); // Paper - some state corruption happens here, don't crash, clean up gracefully
          if (objectset == null || objectset.isEmpty()) { // Paper
              this.playersPerChunk.remove(i);
 -            this.naturalSpawnChunkCounter.update(i, Integer.MAX_VALUE, false);
diff --git a/patches/server/Optimize-Pathfinding.patch b/patches/server/Optimize-Pathfinding.patch
index f32745f2c0..327e70e688 100644
--- a/patches/server/Optimize-Pathfinding.patch
+++ b/patches/server/Optimize-Pathfinding.patch
@@ -14,20 +14,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          return this.moveTo(this.createPath(x, y, z, 1), speed);
      }
  
-+    // Paper start - optimise pathfinding
++    // Paper start - Perf: Optimise pathfinding
 +    private int lastFailure = 0;
 +    private int pathfindFailures = 0;
-+    // Paper end
++    // Paper end - Perf: Optimise pathfinding
 +
      public boolean moveTo(Entity entity, double speed) {
-+        // Paper start - Pathfinding optimizations
++        // Paper start - Perf: Optimise pathfinding
 +        if (this.pathfindFailures > 10 && this.path == null && net.minecraft.server.MinecraftServer.currentTick < this.lastFailure + 40) {
 +            return false;
 +        }
-+        // Paper end
++        // Paper end - Perf: Optimise pathfinding
          Path path = this.createPath(entity, 1);
 -        return path != null && this.moveTo(path, speed);
-+        // Paper start - Pathfinding optimizations
++        // Paper start - Perf: Optimise pathfinding
 +        if (path != null && this.moveTo(path, speed)) {
 +            this.lastFailure = 0;
 +            this.pathfindFailures = 0;
@@ -37,7 +37,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            this.lastFailure = net.minecraft.server.MinecraftServer.currentTick;
 +            return false;
 +        }
-+        // Paper end
++        // Paper end - Perf: Optimise pathfinding
      }
  
      public boolean moveTo(@Nullable Path path, double speed) {
diff --git a/patches/server/Option-for-maximum-exp-value-when-merging-orbs.patch b/patches/server/Option-for-maximum-exp-value-when-merging-orbs.patch
index 2e2cc71dd1..e47d59976b 100644
--- a/patches/server/Option-for-maximum-exp-value-when-merging-orbs.patch
+++ b/patches/server/Option-for-maximum-exp-value-when-merging-orbs.patch
@@ -12,7 +12,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          if (entity instanceof net.minecraft.world.entity.ExperienceOrb xp) {
              double radius = world.spigotConfig.expMerge;
              if (radius > 0) {
-+                // Paper start - Maximum exp value when merging - Whole section has been tweaked, see comments for specifics
++                // Paper start - Maximum exp value when merging; Whole section has been tweaked, see comments for specifics
 +                final int maxValue = world.paperConfig().entities.behavior.experienceMergeMaxValue;
 +                final boolean mergeUnconditionally = world.paperConfig().entities.behavior.experienceMergeMaxValue <= 0;
 +                if (mergeUnconditionally || xp.value < maxValue) { // Paper - Skip iteration if unnecessary
@@ -31,7 +31,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                            } else {
                              xp.value += loopItem.value;
                              loopItem.discard();
-+                            } // Paper end
++                            } // Paper end - Maximum exp value when merging
                          }
                      }
                  }
diff --git a/patches/server/Pillager-patrol-spawn-settings-and-per-player-option.patch b/patches/server/Pillager-patrol-spawn-settings-and-per-player-option.patch
index bc6257ed45..b63029fa43 100644
--- a/patches/server/Pillager-patrol-spawn-settings-and-per-player-option.patch
+++ b/patches/server/Pillager-patrol-spawn-settings-and-per-player-option.patch
@@ -17,7 +17,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      public boolean wonGame;
      private int containerUpdateDelay; // Paper
      public long loginTime; // Paper
-+    public int patrolSpawnDelay; // Paper - per player patrol spawns
++    public int patrolSpawnDelay; // Paper - Pillager patrol spawn settings and per player options
      // Paper start - cancellable death event
      public boolean queueHealthUpdatePacket = false;
      public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket;
@@ -30,7 +30,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      @Override
      public int tick(ServerLevel world, boolean spawnMonsters, boolean spawnAnimals) {
 -        if (world.paperConfig().entities.behavior.pillagerPatrols.disable) return 0; // Paper
-+        if (world.paperConfig().entities.behavior.pillagerPatrols.disable || world.paperConfig().entities.behavior.pillagerPatrols.spawnChance == 0) return 0; // Paper
++        if (world.paperConfig().entities.behavior.pillagerPatrols.disable || world.paperConfig().entities.behavior.pillagerPatrols.spawnChance == 0) return 0; // Paper - Pillager patrol spawn settings and per player options
          if (!spawnMonsters) {
              return 0;
          } else if (!world.getGameRules().getBoolean(GameRules.RULE_DO_PATROL_SPAWNING)) {
@@ -40,7 +40,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
 -            --this.nextTick;
 -            if (this.nextTick > 0) {
-+            // Paper start - Patrol settings
++            // Paper start - Pillager patrol spawn settings and per player options
 +            // Random player selection moved up for per player spawning and configuration
 +            int j = world.players().size();
 +            if (j < 1) {
@@ -82,7 +82,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -                    if (randomsource.nextInt(5) != 0) {
 +                if (days >= world.paperConfig().entities.behavior.pillagerPatrols.start.day && world.isDay()) {
 +                    if (randomsource.nextDouble() >= world.paperConfig().entities.behavior.pillagerPatrols.spawnChance) {
-+                        // Paper end
++                        // Paper end - Pillager patrol spawn settings and per player options
                          return 0;
                      } else {
 -                        int j = world.players().size();
diff --git a/patches/server/Prevent-Double-PlayerChunkMap-adds-crashing-server.patch b/patches/server/Prevent-Double-PlayerChunkMap-adds-crashing-server.patch
index 23272bbc9f..02d92dfad2 100644
--- a/patches/server/Prevent-Double-PlayerChunkMap-adds-crashing-server.patch
+++ b/patches/server/Prevent-Double-PlayerChunkMap-adds-crashing-server.patch
@@ -20,7 +20,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                + ": " + entity  + (this.entityMap.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : ""), new Throwable());
 +            return;
 +        }
-+        // Paper end
++        // Paper end - ignore and warn about illegal addEntity calls instead of crashing server
          if (!(entity instanceof EnderDragonPart)) {
              EntityType<?> entitytypes = entity.getType();
              int i = entitytypes.clientTrackingRange() * 16;
@@ -33,7 +33,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          public void onTrackingStart(Entity entity) {
              org.spigotmc.AsyncCatcher.catchOp("entity register"); // Spigot
 -            ServerLevel.this.getChunkSource().addEntity(entity);
-+            // ServerLevel.this.getChunkSource().addEntity(entity); // Paper - moved down below valid=true
++            // ServerLevel.this.getChunkSource().addEntity(entity); // Paper - ignore and warn about illegal addEntity calls instead of crashing server; moved down below valid=true
              if (entity instanceof ServerPlayer) {
                  ServerPlayer entityplayer = (ServerPlayer) entity;
  
diff --git a/patches/server/Prevent-opening-inventories-when-frozen.patch b/patches/server/Prevent-opening-inventories-when-frozen.patch
index da1f19a2e9..96567da3ef 100644
--- a/patches/server/Prevent-opening-inventories-when-frozen.patch
+++ b/patches/server/Prevent-opening-inventories-when-frozen.patch
@@ -13,7 +13,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          }
          // Paper end
 -        if (!this.level().isClientSide && !this.containerMenu.stillValid(this)) {
-+        if (!this.level().isClientSide && this.containerMenu != this.inventoryMenu && (this.isImmobile() || !this.containerMenu.stillValid(this))) { // Paper - auto close while frozen
++        if (!this.level().isClientSide && this.containerMenu != this.inventoryMenu && (this.isImmobile() || !this.containerMenu.stillValid(this))) { // Paper - Prevent opening inventories when frozen
              this.closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason.CANT_USE); // Paper
              this.containerMenu = this.inventoryMenu;
          }
@@ -22,7 +22,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                  // CraftBukkit start
                  this.containerMenu = container;
 -                this.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), container.getTitle()));
-+                if (!this.isImmobile()) this.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), container.getTitle())); // Paper
++                if (!this.isImmobile()) this.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), container.getTitle())); // Paper - Prevent opening inventories when frozen
                  // CraftBukkit end
                  this.initMenu(container);
                  return OptionalInt.of(this.containerCounter);
@@ -35,7 +35,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
          //player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, CraftChatMessage.fromString(title)[0])); // Paper - comment
 -        player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper
-+        if (!player.isImmobile()) player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper
++        if (!player.isImmobile()) player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper - Prevent opening inventories when frozen
          player.containerMenu = container;
          player.initMenu(container);
      }
@@ -44,7 +44,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          if (adventure$title == null) adventure$title = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(inventory.getTitle()); // Paper
          //player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, CraftChatMessage.fromString(title)[0])); // Paper - comment
 -        player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper
-+        if (!player.isImmobile()) player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper
++        if (!player.isImmobile()) player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper - Prevent opening inventories when frozen
          player.containerMenu = container;
          player.initMenu(container);
      }
diff --git a/patches/server/Prevent-teleporting-dead-entities.patch b/patches/server/Prevent-teleporting-dead-entities.patch
index d3a80aee8b..45b4ea4b30 100644
--- a/patches/server/Prevent-teleporting-dead-entities.patch
+++ b/patches/server/Prevent-teleporting-dead-entities.patch
@@ -12,13 +12,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      }
  
      public void internalTeleport(double d0, double d1, double d2, float f, float f1, Set<RelativeMovement> set) { // Paper
-+        // Paper start
++        // Paper start - Prevent teleporting dead entities
 +        if (player.isRemoved()) {
 +            LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName());
 +            if (server.isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Attempt to teleport removed player");
 +            return;
 +        }
-+        // Paper end
++        // Paper end - Prevent teleporting dead entities
          // CraftBukkit start
          if (Float.isNaN(f)) {
              f = 0;
diff --git a/patches/server/Properly-resend-entities.patch b/patches/server/Properly-resend-entities.patch
index 99ccc56552..67f7a67639 100644
--- a/patches/server/Properly-resend-entities.patch
+++ b/patches/server/Properly-resend-entities.patch
@@ -102,7 +102,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/net/minecraft/server/players/PlayerList.java
 +++ b/src/main/java/net/minecraft/server/players/PlayerList.java
 @@ -0,0 +0,0 @@ public abstract class PlayerList {
-         ((ServerLevel)player.level()).getChunkSource().chunkMap.addEntity(player); // Paper - track entity now
+         ((ServerLevel)player.level()).getChunkSource().chunkMap.addEntity(player); // Paper - Fire PlayerJoinEvent when Player is actually ready; track entity now
          // CraftBukkit end
  
 -        player.getEntityData().refresh(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn
diff --git a/patches/server/Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch b/patches/server/Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch
index 51a191a670..01cfcaa196 100644
--- a/patches/server/Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch
+++ b/patches/server/Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch
@@ -20,7 +20,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                          for (float f1 = 0.3F; f > 0.0F; f -= 0.22500001F) {
                              BlockPos blockposition = BlockPos.containing(d4, d5, d6);
                              BlockState iblockdata = this.level.getBlockState(blockposition);
-+                            if (!iblockdata.isDestroyable()) continue; // Paper
++                            if (!iblockdata.isDestroyable()) continue; // Paper - Protect Bedrock and End Portal/Frames from being destroyed
                              FluidState fluid = iblockdata.getFluidState(); // Paper
  
                              if (!this.level.isInWorldBounds(blockposition)) {
@@ -32,10 +32,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      public boolean setBlock(BlockPos pos, BlockState state, int flags, int maxUpdateDepth) {
          // CraftBukkit start - tree generation
          if (this.captureTreeGeneration) {
-+            // Paper start
++            // Paper start - Protect Bedrock and End Portal/Frames from being destroyed
 +            BlockState type = getBlockState(pos);
 +            if (!type.isDestroyable()) return false;
-+            // Paper end
++            // Paper end - Protect Bedrock and End Portal/Frames from being destroyed
              CraftBlockState blockstate = this.capturedBlockStates.get(pos);
              if (blockstate == null) {
                  blockstate = CapturedBlockState.getTreeBlockState(this, pos, flags);
@@ -71,12 +71,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      @Override
      public boolean triggerEvent(BlockState state, Level world, BlockPos pos, int type, int data) {
          Direction enumdirection = (Direction) state.getValue(PistonBaseBlock.FACING);
-+        // Paper start - prevent retracting when we're facing the wrong way (we were replaced before retraction could occur)
++        // Paper start - Protect Bedrock and End Portal/Frames from being destroyed; prevent retracting when we're facing the wrong way (we were replaced before retraction could occur)
 +        Direction directionQueuedAs = Direction.from3DDataValue(data & 7); // Paper - copied from below
 +        if (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits && enumdirection != directionQueuedAs) {
 +            return false;
 +        }
-+        // Paper end - prevent retracting when we're facing the wrong way
++        // Paper end - Protect Bedrock and End Portal/Frames from being destroyed
          BlockState iblockdata1 = (BlockState) state.setValue(PistonBaseBlock.EXTENDED, true);
  
          if (!world.isClientSide) {
@@ -85,7 +85,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
              world.setBlock(pos, iblockdata2, 20);
 -            world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata2, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true));
-+            world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata2, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true)); // Paper - diff on change
++            world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata2, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true)); // Paper - Protect Bedrock and End Portal/Frames from being destroyed; diff on change
              world.blockUpdated(pos, iblockdata2.getBlock());
              iblockdata2.updateNeighbourShapes(world, pos, 2);
              if (this.isSticky) {
@@ -94,14 +94,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                  }
              } else {
 -                world.removeBlock(pos.relative(enumdirection), false);
-+                // Paper start - fix headless pistons breaking blocks
++                // Paper start - Protect Bedrock and End Portal/Frames from being destroyed; fix headless pistons breaking blocks
 +                BlockPos headPos = pos.relative(enumdirection);
 +                if (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits || world.getBlockState(headPos) == Blocks.PISTON_HEAD.defaultBlockState().setValue(FACING, enumdirection)) { // double check to make sure we're not a headless piston.
 +                    world.removeBlock(headPos, false);
 +                } else {
-+                    ((ServerLevel)world).getChunkSource().blockChanged(headPos); // ... fix client desync
++                    ((ServerLevel) world).getChunkSource().blockChanged(headPos); // ... fix client desync
 +                }
-+                // Paper end - fix headless pistons breaking blocks
++                // Paper end - Protect Bedrock and End Portal/Frames from being destroyed
              }
  
              world.playSound((Player) null, pos, SoundEvents.PISTON_CONTRACT, SoundSource.BLOCKS, 0.5F, world.random.nextFloat() * 0.15F + 0.6F);
@@ -114,7 +114,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      @Deprecated
      public void onExplosionHit(BlockState state, Level world, BlockPos pos, Explosion explosion, BiConsumer<ItemStack, BlockPos> stackMerger) {
 -        if (!state.isAir() && explosion.getBlockInteraction() != Explosion.BlockInteraction.TRIGGER_BLOCK) {
-+        if (!state.isAir() && explosion.getBlockInteraction() != Explosion.BlockInteraction.TRIGGER_BLOCK && state.isDestroyable()) { // Paper
++        if (!state.isAir() && explosion.getBlockInteraction() != Explosion.BlockInteraction.TRIGGER_BLOCK && state.isDestroyable()) { // Paper - Protect Bedrock and End Portal/Frames from being destroyed
              Block block = state.getBlock();
              boolean flag = explosion.getIndirectSourceEntity() instanceof Player;
  
@@ -123,7 +123,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      @Deprecated
      public boolean canBeReplaced(BlockState state, BlockPlaceContext context) {
 -        return state.canBeReplaced() && (context.getItemInHand().isEmpty() || !context.getItemInHand().is(this.asItem()));
-+        return state.canBeReplaced() && (context.getItemInHand().isEmpty() || !context.getItemInHand().is(this.asItem())) && (state.isDestroyable() || (context.getPlayer() != null && context.getPlayer().getAbilities().instabuild)); // Paper;
++        return state.canBeReplaced() && (context.getItemInHand().isEmpty() || !context.getItemInHand().is(this.asItem())) && (state.isDestroyable() || (context.getPlayer() != null && context.getPlayer().getAbilities().instabuild)); // Paper - Protect Bedrock and End Portal/Frames from being destroyed
      }
  
      /** @deprecated */
@@ -131,11 +131,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              return this.legacySolid;
          }
  
-+        // Paper start
++        // Paper start - Protect Bedrock and End Portal/Frames from being destroyed
 +        public final boolean isDestroyable() {
 +            return getBlock().isDestroyable();
 +        }
-+        // Paper end
++        // Paper end - Protect Bedrock and End Portal/Frames from being destroyed
 +
          public boolean isValidSpawn(BlockGetter world, BlockPos pos, EntityType<?> type) {
              return this.getBlock().properties.isValidSpawn.test(this.asState(), world, pos, type);
@@ -145,7 +145,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
          public PushReaction getPistonPushReaction() {
 -            return this.pushReaction;
-+            return !this.isDestroyable() ? PushReaction.BLOCK : this.pushReaction; // Paper
++            return !this.isDestroyable() ? PushReaction.BLOCK : this.pushReaction; // Paper - Protect Bedrock and End Portal/Frames from being destroyed
          }
  
          public boolean isSolidRender(BlockGetter world, BlockPos pos) {
@@ -157,13 +157,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          for (int j = -1; j < 3; ++j) {
              for (int k = -1; k < 4; ++k) {
                  temp.setWithOffset(pos, portalDirection.getStepX() * j + enumdirection1.getStepX() * distanceOrthogonalToPortal, k, portalDirection.getStepZ() * j + enumdirection1.getStepZ() * distanceOrthogonalToPortal);
-+                // Paper start - prevent destroying unbreakable blocks
++                // Paper start - Protect Bedrock and End Portal/Frames from being destroyed
 +                if (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits) {
 +                    if (!this.level.getBlockState(temp).isDestroyable()) {
 +                        return false;
 +                    }
 +                }
-+                // Paper end - prevent destroying unbreakable blocks
++                // Paper end - Protect Bedrock and End Portal/Frames from being destroyed
                  if (k < 0 && !this.level.getBlockState(temp).isSolid()) {
                      return false;
                  }
diff --git a/patches/server/Reduce-Either-Optional-allocation.patch b/patches/server/Reduce-Either-Optional-allocation.patch
index ce6fe7068b..ea84670064 100644
--- a/patches/server/Reduce-Either-Optional-allocation.patch
+++ b/patches/server/Reduce-Either-Optional-allocation.patch
@@ -15,7 +15,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      private static final class Left<L, R> extends Either<L, R> {
 -        private final L value;
-+        private final L value; private Optional<L> valueOptional; // Paper - reduce the optional allocation...
++        private final L value; private Optional<L> valueOptional; // Paper - Perf: Reduce Either Optional allocation
  
          public Left(final L value) {
              this.value = value;
@@ -24,7 +24,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          @Override
          public Optional<L> left() {
 -            return Optional.of(value);
-+            return this.valueOptional == null ? this.valueOptional = Optional.of(this.value) : this.valueOptional; // Paper - reduce the optional allocation...
++            return this.valueOptional == null ? this.valueOptional = Optional.of(this.value) : this.valueOptional; // Paper - Perf: Reduce Either Optional allocation
          }
  
          @Override
@@ -33,7 +33,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      private static final class Right<L, R> extends Either<L, R> {
 -        private final R value;
-+        private final R value; private Optional<R> valueOptional; // Paper - reduce the optional allocation...
++        private final R value; private Optional<R> valueOptional; // Paper - Perf: Reduce Either Optional allocation
  
          public Right(final R value) {
              this.value = value;
@@ -42,7 +42,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          @Override
          public Optional<R> right() {
 -            return Optional.of(value);
-+            return this.valueOptional == null ? this.valueOptional = Optional.of(this.value) : this.valueOptional; // Paper - reduce the optional allocation...
++            return this.valueOptional == null ? this.valueOptional = Optional.of(this.value) : this.valueOptional; // Paper - Perf: Reduce Either Optional allocation
          }
  
          @Override
diff --git a/patches/server/Reduce-memory-footprint-of-NBTTagCompound.patch b/patches/server/Reduce-memory-footprint-of-CompoundTag.patch
similarity index 86%
rename from patches/server/Reduce-memory-footprint-of-NBTTagCompound.patch
rename to patches/server/Reduce-memory-footprint-of-CompoundTag.patch
index d96a736ac6..d7fbad64df 100644
--- a/patches/server/Reduce-memory-footprint-of-NBTTagCompound.patch
+++ b/patches/server/Reduce-memory-footprint-of-CompoundTag.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Spottedleaf <Spottedleaf@users.noreply.github.com>
 Date: Mon, 6 Apr 2020 17:39:25 -0700
-Subject: [PATCH] Reduce memory footprint of NBTTagCompound
+Subject: [PATCH] Reduce memory footprint of CompoundTag
 
 Fastutil maps are going to have a lower memory footprint - which
 is important because we clone chunk data after reading it for safety.
@@ -16,7 +16,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          private static CompoundTag loadCompound(DataInput input, NbtAccounter tracker) throws IOException {
              tracker.accountBytes(48L);
 -            Map<String, Tag> map = Maps.newHashMap();
-+            it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<String, Tag> map = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(8, 0.8f); // Paper - reduce memory footprint of NBTTagCompound
++            it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<String, Tag> map = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(8, 0.8f); // Paper - Reduce memory footprint of CompoundTag
  
              byte b;
              while((b = input.readByte()) != 0) {
@@ -25,7 +25,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      public CompoundTag() {
 -        this(Maps.newHashMap());
-+        this(new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(8, 0.8f)); // Paper - reduce memory footprint of NBTTagCompound
++        this(new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(8, 0.8f)); // Paper - Reduce memory footprint of CompoundTag
      }
  
      @Override
@@ -35,7 +35,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      public CompoundTag copy() {
 -        Map<String, Tag> map = Maps.newHashMap(Maps.transformValues(this.tags, Tag::copy));
 -        return new CompoundTag(map);
-+        // Paper start - reduce memory footprint of NBTTagCompound
++        // Paper start - Reduce memory footprint of CompoundTag
 +        it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<String, Tag> ret = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(this.tags.size(), 0.8f);
 +        java.util.Iterator<java.util.Map.Entry<String, Tag>> iterator = (this.tags instanceof it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap) ? ((it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap)this.tags).object2ObjectEntrySet().fastIterator() : this.tags.entrySet().iterator();
 +        while (iterator.hasNext()) {
@@ -44,7 +44,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        }
 +
 +        return new CompoundTag(ret);
-+        // Paper end - reduce memory footprint of NBTTagCompound
++        // Paper end - Reduce memory footprint of CompoundTag
      }
  
      @Override
diff --git a/patches/server/Remote-Connections-shouldn-t-hold-up-shutdown.patch b/patches/server/Remote-Connections-shouldn-t-hold-up-shutdown.patch
index ca87a1bbe9..2b5f319fe4 100644
--- a/patches/server/Remote-Connections-shouldn-t-hold-up-shutdown.patch
+++ b/patches/server/Remote-Connections-shouldn-t-hold-up-shutdown.patch
@@ -31,14 +31,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
          this.clients.clear();
      }
-+    // Paper start
++    // Paper start - don't wait for remote connections
 +    public void stopNonBlocking() {
 +        this.running = false;
 +        for (RconClient client : this.clients) {
 +            client.running = false;
 +        }
 +    }
-+    // Paper stop
++    // Paper stop - don't wait for remote connections
  
      private void closeSocket(ServerSocket socket) {
          LOGGER.debug("closeSocket: {}", (Object)socket);
diff --git a/patches/server/Set-cap-on-JDK-per-thread-native-byte-buffer-cache.patch b/patches/server/Set-cap-on-JDK-per-thread-native-byte-buffer-cache.patch
index 6267920ffd..ac1ec90499 100644
--- a/patches/server/Set-cap-on-JDK-per-thread-native-byte-buffer-cache.patch
+++ b/patches/server/Set-cap-on-JDK-per-thread-native-byte-buffer-cache.patch
@@ -24,7 +24,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          }
          // Paper end
          // Todo: Installation script
-+        if (System.getProperty("jdk.nio.maxCachedBufferSize") == null) System.setProperty("jdk.nio.maxCachedBufferSize", "262144"); // Paper - cap per-thread NIO cache size
++        if (System.getProperty("jdk.nio.maxCachedBufferSize") == null) System.setProperty("jdk.nio.maxCachedBufferSize", "262144"); // Paper - cap per-thread NIO cache size; https://www.evanjones.ca/java-bytebuffer-leak.html
          OptionParser parser = new OptionParser() {
              {
                  this.acceptsAll(Main.asList("?", "help"), "Show the help");
diff --git a/patches/server/Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch b/patches/server/Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch
index 81719fe157..643f0e86f4 100644
--- a/patches/server/Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch
+++ b/patches/server/Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch
@@ -21,7 +21,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                  for (j1 = 0; j1 < 128; ++j1) {
                      for (k1 = 0; k1 < 128; ++k1) {
 -                        Holder<Biome> holder = world.getBiome(blockposition_mutableblockposition.set((l + k1) * i, 0, (i1 + j1) * i));
-+                        Holder<Biome> holder = world.getUncachedNoiseBiome((l + k1) * i, 0, (i1 + j1) * i); // Paper
++                        Holder<Biome> holder = world.getUncachedNoiseBiome((l + k1) * i, 0, (i1 + j1) * i); // Paper - Perf: Use seed based lookup for treasure maps
  
                          aboolean[j1 * 128 + k1] = holder.is(BiomeTags.WATER_ON_MAP_OUTLINES);
                      }
diff --git a/patches/server/Use-single-player-info-update-packet-on-join.patch b/patches/server/Use-single-player-info-update-packet-on-join.patch
index d1e1688957..cd9466b9b2 100644
--- a/patches/server/Use-single-player-info-update-packet-on-join.patch
+++ b/patches/server/Use-single-player-info-update-packet-on-join.patch
@@ -47,5 +47,5 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        }
 +        // Paper end - Use single player info update packet on join
          player.sentListPacket = true;
-         player.supressTrackerForLogin = false; // Paper
-         ((ServerLevel)player.level()).getChunkSource().chunkMap.addEntity(player); // Paper - track entity now
+         player.supressTrackerForLogin = false; // Paper - Fire PlayerJoinEvent when Player is actually ready
+         ((ServerLevel)player.level()).getChunkSource().chunkMap.addEntity(player); // Paper - Fire PlayerJoinEvent when Player is actually ready; track entity now
diff --git a/patches/server/Validate-PickItem-Packet-and-kick-for-invalid.patch b/patches/server/Validate-PickItem-Packet-and-kick-for-invalid.patch
index edcd10c966..8cdf990217 100644
--- a/patches/server/Validate-PickItem-Packet-and-kick-for-invalid.patch
+++ b/patches/server/Validate-PickItem-Packet-and-kick-for-invalid.patch
@@ -20,7 +20,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            return;
 +        }
 +        this.player.getInventory().pickSlot(packet.getSlot()); // Paper - Diff above if changed
-+        // Paper end
++        // Paper end - validate pick item position
          this.player.connection.send(new ClientboundContainerSetSlotPacket(-2, 0, this.player.getInventory().selected, this.player.getInventory().getItem(this.player.getInventory().selected)));
          this.player.connection.send(new ClientboundContainerSetSlotPacket(-2, 0, packet.getSlot(), this.player.getInventory().getItem(packet.getSlot())));
          this.player.connection.send(new ClientboundSetCarriedItemPacket(this.player.getInventory().selected));
diff --git a/patches/server/Wait-for-Async-Tasks-during-shutdown.patch b/patches/server/Wait-for-Async-Tasks-during-shutdown.patch
index bb40d7de24..f199ceaa6f 100644
--- a/patches/server/Wait-for-Async-Tasks-during-shutdown.patch
+++ b/patches/server/Wait-for-Async-Tasks-during-shutdown.patch
@@ -17,7 +17,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          // CraftBukkit start
          if (this.server != null) {
              this.server.disablePlugins();
-+            this.server.waitForAsyncTasksShutdown(); // Paper
++            this.server.waitForAsyncTasksShutdown(); // Paper - Wait for Async Tasks during shutdown
          }
          // CraftBukkit end
          this.getConnection().stop();
@@ -29,7 +29,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          org.spigotmc.WatchdogThread.hasStarted = true; // Paper - Disable watchdog early timeout on reload
      }
  
-+    // Paper start
++    // Paper start - Wait for Async Tasks during shutdown
 +    public void waitForAsyncTasksShutdown() {
 +        int pollCount = 0;
 +
@@ -52,7 +52,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            ));
 +        }
 +    }
-+    // Paper end
++    // Paper end - Wait for Async Tasks during shutdown
 +
      @Override
      public void reloadData() {
diff --git a/patches/server/misc-debugging-dumps.patch b/patches/server/misc-debugging-dumps.patch
index 18f0e71375..08dfc42c83 100644
--- a/patches/server/misc-debugging-dumps.patch
+++ b/patches/server/misc-debugging-dumps.patch
@@ -37,7 +37,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              MutableComponent ichatmutablecomponent = Component.literal(exception.getMessage() == null ? exception.getClass().getName() : exception.getMessage());
  
 -            if (Commands.LOGGER.isDebugEnabled()) {
-+            if (commandlistenerwrapper.getServer().isDebugging() || Commands.LOGGER.isDebugEnabled()) { // Paper
++            if (commandlistenerwrapper.getServer().isDebugging() || Commands.LOGGER.isDebugEnabled()) { // Paper - Debugging
                  Commands.LOGGER.error("Command exception: /{}", s, exception);
                  StackTraceElement[] astacktraceelement = exception.getStackTrace();
  
@@ -49,7 +49,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      // CraftBukkit start
      private boolean hasStopped = false;
      public volatile boolean hasFullyShutdown = false; // Paper
-+    private boolean hasLoggedStop = false; // Paper
++    private boolean hasLoggedStop = false; // Paper - Debugging
      private final Object stopLock = new Object();
      public final boolean hasStopped() {
          synchronized (this.stopLock) {
@@ -57,7 +57,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              if (this.hasStopped) return;
              this.hasStopped = true;
          }
-+        if (!hasLoggedStop && isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper
++        if (!hasLoggedStop && isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper - Debugging
          // Paper start - kill main thread, and kill it hard
          shutdownThread = Thread.currentThread();
          org.spigotmc.WatchdogThread.doStop(); // Paper
@@ -65,8 +65,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      }
      public void safeShutdown(boolean waitForShutdown, boolean isRestarting) {
          this.isRestarting = isRestarting;
-+        this.hasLoggedStop = true; // Paper
-+        if (isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper
++        this.hasLoggedStop = true; // Paper - Debugging
++        if (isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper - Debugging
          // Paper end
          this.running = false;
          if (waitForShutdown) {
@@ -78,11 +78,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              this.connection.resumeInboundAfterProtocolChange();
          } catch (Exception exception) {
              ServerConfigurationPacketListenerImpl.LOGGER.error("Couldn't place player in world", exception);
-+            // Paper start
++            // Paper start - Debugging
 +            if (MinecraftServer.getServer().isDebugging()) {
 +                exception.printStackTrace();
 +            }
-+            // Paper end
++            // Paper end - Debugging
              this.connection.send(new ClientboundDisconnectPacket(ServerConfigurationPacketListenerImpl.DISCONNECT_REASON_INVALID_DATA));
              this.connection.disconnect(ServerConfigurationPacketListenerImpl.DISCONNECT_REASON_INVALID_DATA);
          }
@@ -94,7 +94,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                  plugin.getDescription().getFullName(),
                  "This plugin is not properly shutting down its async tasks when it is being reloaded.  This may cause conflicts with the newly loaded version of the plugin"
              ));
-+            if (console.isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread(worker.getThread(), "still running"); // Paper
++            if (console.isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread(worker.getThread(), "still running"); // Paper - Debugging
          }
          io.papermc.paper.plugin.PluginInitializerManager.reload(this.console); // Paper
          this.loadPlugins();