From bc5acdddad21d004e590510d1da20d2dee58ed76 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 25 Jun 2020 21:58:00 -0400
Subject: [PATCH] Current non compilable status of all patches - THIS IS NOT
 READY

THERE IS STILL NO ETA. GOBLINS WILL EAT YOU.
---
 Paper-MojangAPI/pom.xml                       |   2 +-
 .../Add-ProjectileCollideEvent.patch          |   5 -
 ...dd-configurable-portal-search-radius.patch |   4 +-
 .../Add-permission-for-command-blocks.patch   |  79 -----
 .../Add-villager-reputation-API.patch         |   8 +-
 ...unk-Unloads-based-on-Player-Movement.patch |   6 -
 ...sure-Entity-AABB-s-are-never-invalid.patch |  55 ----
 ...der-respects-game-and-entity-rules-f.patch |   5 +-
 .../Ensure-safe-gateway-teleport.patch        |  17 +-
 ...ent-consumeArrow-and-getArrowItem-AP.patch |   8 -
 .../Expose-Arrow-getItemStack.patch           |   2 +-
 .../Fix-CraftServer.unloadWorld-Leak.patch    | 124 --------
 Spigot-Server-Patches/Fix-Light-Command.patch |   2 +-
 .../Fix-enderdragon-exp-dupe.patch            |  21 +-
 ...x-items-vanishing-through-end-portal.patch |   2 +-
 ...ston-physics-inconsistency-MC-188840.patch |   9 +-
 .../Fix-this-stupid-bullshit.patch            |   2 +-
 ...k-Priority-Urgency-System-for-Chunks.patch |  89 ++++--
 ...quid-s-Entity-Collision-optimisation.patch | 101 -------
 .../Implement-PlayerLocaleChangeEvent.patch   |   1 +
 ...mprove-Chunk-Status-Transition-Speed.patch |  26 +-
 ...generate-Carving-Masks-BitSet-unless.patch |  43 ---
 .../Optimize-Bit-Operations-by-inlining.patch |  97 ++-----
 .../Optimize-Light-Engine.patch               | 268 +++++++----------
 ...ze-NibbleArray-to-use-pooled-buffers.patch |  12 +-
 ...der-Remove-Streams-Optimized-collect.patch | 162 -----------
 .../Optimize-Villagers.patch                  | 273 ------------------
 ...WorldBorder-collision-checks-and-air.patch |  61 ----
 ...-packets-to-nearby-locations-sounds-.patch |  66 ++---
 Spigot-Server-Patches/POM-Changes.patch       |   2 +-
 Spigot-Server-Patches/Potential-bed-API.patch |  17 +-
 ...vent-opening-inventories-when-frozen.patch |   8 +-
 ...-desync-in-playerconnection-causing-.patch |   2 +-
 ...and-End-Portal-Frames-from-being-des.patch |  69 ++---
 ...llocation-of-Vec3D-by-entity-tracker.patch |   2 +-
 ...me-Streams-usage-in-Entity-Collision.patch | 178 ------------
 ...ient-crashes-server-lists-and-Mojang.patch |   2 +-
 ...ookup-for-Treasure-Maps-Fixes-lag-fr.patch |   5 +-
 ...Wait-for-Async-Tasks-during-shutdown.patch |   2 +-
 ...ound-for-Client-Lag-Spikes-MC-162253.patch |   6 +-
 work/Bukkit                                   |   2 +-
 work/CraftBukkit                              |   2 +-
 work/Spigot                                   |   2 +-
 43 files changed, 316 insertions(+), 1533 deletions(-)
 delete mode 100644 Spigot-Server-Patches/Add-permission-for-command-blocks.patch
 delete mode 100644 Spigot-Server-Patches/Ensure-Entity-AABB-s-are-never-invalid.patch
 delete mode 100644 Spigot-Server-Patches/Fix-CraftServer.unloadWorld-Leak.patch
 delete mode 100644 Spigot-Server-Patches/Implement-JellySquid-s-Entity-Collision-optimisation.patch
 delete mode 100644 Spigot-Server-Patches/MC-183249-Don-t-generate-Carving-Masks-BitSet-unless.patch
 delete mode 100644 Spigot-Server-Patches/Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch
 delete mode 100644 Spigot-Server-Patches/Optimize-Villagers.patch
 delete mode 100644 Spigot-Server-Patches/Optimize-WorldBorder-collision-checks-and-air.patch
 delete mode 100644 Spigot-Server-Patches/Remove-some-Streams-usage-in-Entity-Collision.patch

diff --git a/Paper-MojangAPI/pom.xml b/Paper-MojangAPI/pom.xml
index d0da7daf4e..4c7c62c787 100644
--- a/Paper-MojangAPI/pom.xml
+++ b/Paper-MojangAPI/pom.xml
@@ -10,7 +10,7 @@
 
     <groupId>com.destroystokyo.paper</groupId>
     <artifactId>paper-mojangapi</artifactId>
-    <version>1.15.2-R0.1-SNAPSHOT</version>
+    <version>1.16.1-R0.1-SNAPSHOT</version>
     <packaging>jar</packaging>
 
     <name>Paper-MojangAPI</name>
diff --git a/Spigot-Server-Patches/Add-ProjectileCollideEvent.patch b/Spigot-Server-Patches/Add-ProjectileCollideEvent.patch
index bec046d55f..46d33f3603 100644
--- a/Spigot-Server-Patches/Add-ProjectileCollideEvent.patch
+++ b/Spigot-Server-Patches/Add-ProjectileCollideEvent.patch
@@ -66,11 +66,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            if (movingobjectposition != null) {
 +            // Paper end
              this.a(movingobjectposition);
-             // CraftBukkit start
-             if (this.dead) {
-                 org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this, movingobjectposition);
-             }
-             // CraftBukkit end
 +            } // Paper
          }
  
diff --git a/Spigot-Server-Patches/Add-configurable-portal-search-radius.patch b/Spigot-Server-Patches/Add-configurable-portal-search-radius.patch
index f5aaed2731..ed9e673a98 100644
--- a/Spigot-Server-Patches/Add-configurable-portal-search-radius.patch
+++ b/Spigot-Server-Patches/Add-configurable-portal-search-radius.patch
@@ -41,8 +41,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              // CraftBukkit start
              Location enter = this.getBukkitEntity().getLocation();
              Location exit = (worldserver == null) ? null : new Location(worldserver.getWorld(), d0, d1, d2, f1, f);
--            PlayerPortalEvent event = new PlayerPortalEvent(this.getBukkitEntity(), enter, exit, cause, 128, true, resourcekey == World.THE_END ? 0 : 16);
-+            PlayerPortalEvent event = new PlayerPortalEvent(this.getBukkitEntity(), enter, exit, cause, worldserver.paperConfig.portalSearchRadius, true, resourcekey == World.THE_END ? 0 : worldserver.paperConfig.portalCreateRadius);
+-            PlayerPortalEvent event = new PlayerPortalEvent(this.getBukkitEntity(), enter, exit, cause, 128, true, resourcekey == DimensionManager.THE_END ? 0 : 16);
++            PlayerPortalEvent event = new PlayerPortalEvent(this.getBukkitEntity(), enter, exit, cause, worldserver.paperConfig.portalSearchRadius, true, resourcekey == DimensionManager.THE_END ? 0 : worldserver.paperConfig.portalCreateRadius);
              Bukkit.getServer().getPluginManager().callEvent(event);
              if (event.isCancelled() || event.getTo() == null) {
                  return null;
diff --git a/Spigot-Server-Patches/Add-permission-for-command-blocks.patch b/Spigot-Server-Patches/Add-permission-for-command-blocks.patch
deleted file mode 100644
index 5dd95643b7..0000000000
--- a/Spigot-Server-Patches/Add-permission-for-command-blocks.patch
+++ /dev/null
@@ -1,79 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Mariell Hoversholm <proximyst@proximyst.com>
-Date: Sat, 16 May 2020 10:05:30 +0200
-Subject: [PATCH] Add permission for command blocks
-
-
-diff --git a/src/main/java/net/minecraft/server/BlockCommand.java b/src/main/java/net/minecraft/server/BlockCommand.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/BlockCommand.java
-+++ b/src/main/java/net/minecraft/server/BlockCommand.java
-@@ -0,0 +0,0 @@ public class BlockCommand extends BlockTileEntity {
-     public EnumInteractionResult interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, MovingObjectPositionBlock movingobjectpositionblock) {
-         TileEntity tileentity = world.getTileEntity(blockposition);
- 
--        if (tileentity instanceof TileEntityCommand && entityhuman.isCreativeAndOp()) {
-+        if (tileentity instanceof TileEntityCommand && (entityhuman.isCreativeAndOp() || (entityhuman.isCreative() && entityhuman.getBukkitEntity().hasPermission("minecraft.commandblock")))) { // Paper - command block permission
-             entityhuman.a((TileEntityCommand) tileentity);
-             return EnumInteractionResult.SUCCESS;
-         } else {
-diff --git a/src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java b/src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java
-+++ b/src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java
-@@ -0,0 +0,0 @@ public abstract class CommandBlockListenerAbstract implements ICommandListener {
-     }
- 
-     public boolean a(EntityHuman entityhuman) {
--        if (!entityhuman.isCreativeAndOp()) {
-+        if (!entityhuman.isCreativeAndOp() && !entityhuman.isCreative() && !entityhuman.getBukkitEntity().hasPermission("minecraft.commandblock")) { // Paper - command block permission
-             return false;
-         } else {
-             if (entityhuman.getWorld().isClientSide) {
-diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/PlayerConnection.java
-+++ b/src/main/java/net/minecraft/server/PlayerConnection.java
-@@ -0,0 +0,0 @@ public class PlayerConnection implements PacketListenerPlayIn {
-         PlayerConnectionUtils.ensureMainThread(packetplayinsetcommandblock, this, this.player.getWorldServer());
-         if (!this.minecraftServer.getEnableCommandBlock()) {
-             this.player.sendMessage(new ChatMessage("advMode.notEnabled", new Object[0]));
--        } else if (!this.player.isCreativeAndOp()) {
-+        } else if (!this.player.isCreativeAndOp() && !this.player.isCreative() && !this.player.getBukkitEntity().hasPermission("minecraft.commandblock")) { // Paper - command block permission
-             this.player.sendMessage(new ChatMessage("advMode.notAllowed", new Object[0]));
-         } else {
-             CommandBlockListenerAbstract commandblocklistenerabstract = null;
-@@ -0,0 +0,0 @@ public class PlayerConnection implements PacketListenerPlayIn {
-         PlayerConnectionUtils.ensureMainThread(packetplayinsetcommandminecart, this, this.player.getWorldServer());
-         if (!this.minecraftServer.getEnableCommandBlock()) {
-             this.player.sendMessage(new ChatMessage("advMode.notEnabled", new Object[0]));
--        } else if (!this.player.isCreativeAndOp()) {
-+        } else if (!this.player.isCreativeAndOp() && !this.player.isCreative() && !this.player.getBukkitEntity().hasPermission("minecraft.commandblock")) { // Paper - command block permission
-             this.player.sendMessage(new ChatMessage("advMode.notAllowed", new Object[0]));
-         } else {
-             CommandBlockListenerAbstract commandblocklistenerabstract = packetplayinsetcommandminecart.a(this.player.world);
-diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/PlayerInteractManager.java
-+++ b/src/main/java/net/minecraft/server/PlayerInteractManager.java
-@@ -0,0 +0,0 @@ public class PlayerInteractManager {
-             TileEntity tileentity = this.world.getTileEntity(blockposition);
-             Block block = iblockdata.getBlock();
- 
--            if ((block instanceof BlockCommand || block instanceof BlockStructure || block instanceof BlockJigsaw) && !this.player.isCreativeAndOp()) {
-+            if ((block instanceof BlockCommand || block instanceof BlockStructure || block instanceof BlockJigsaw) && !this.player.isCreativeAndOp() && !(block instanceof BlockCommand && (this.player.isCreative() && this.player.getBukkitEntity().hasPermission("minecraft.commandblock")))) { // Paper - command block permission
-                 this.world.notify(blockposition, iblockdata, iblockdata, 3);
-                 return false;
-             } else if (this.player.a((World) this.world, blockposition, this.gamemode)) {
-diff --git a/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java b/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java
-+++ b/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java
-@@ -0,0 +0,0 @@ public final class CraftDefaultPermissions {
-         DefaultPermissions.registerPermission(ROOT + ".nbt.copy", "Gives the user the ability to copy NBT in creative", org.bukkit.permissions.PermissionDefault.TRUE, parent);
-         DefaultPermissions.registerPermission(ROOT + ".debugstick", "Gives the user the ability to use the debug stick in creative", org.bukkit.permissions.PermissionDefault.OP, parent);
-         DefaultPermissions.registerPermission(ROOT + ".debugstick.always", "Gives the user the ability to use the debug stick in all game modes", org.bukkit.permissions.PermissionDefault.FALSE, parent);
-+        DefaultPermissions.registerPermission(ROOT + ".commandblock", "Gives the user the ability to use command blocks.", org.bukkit.permissions.PermissionDefault.OP, parent); // Paper
-         // Spigot end
-         parent.recalculatePermissibles();
-     }
diff --git a/Spigot-Server-Patches/Add-villager-reputation-API.patch b/Spigot-Server-Patches/Add-villager-reputation-API.patch
index d37d07d188..46409b3cdb 100644
--- a/Spigot-Server-Patches/Add-villager-reputation-API.patch
+++ b/Spigot-Server-Patches/Add-villager-reputation-API.patch
@@ -24,12 +24,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/net/minecraft/server/EntityVillager.java
 +++ b/src/main/java/net/minecraft/server/EntityVillager.java
 @@ -0,0 +0,0 @@ public class EntityVillager extends EntityVillagerAbstract implements Reputation
-         this.bL = 0;
+         this.bK = 0;
      }
  
-+    public Reputation getReputation() { return this.eN(); } // Paper - OBFHELPER
-     public Reputation eN() {
-         return this.bG;
++    public Reputation getReputation() { return this.fj(); } // Paper - OBFHELPER
+     public Reputation fj() {
+         return this.bF;
      }
 diff --git a/src/main/java/net/minecraft/server/Reputation.java b/src/main/java/net/minecraft/server/Reputation.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
diff --git a/Spigot-Server-Patches/Delay-Chunk-Unloads-based-on-Player-Movement.patch b/Spigot-Server-Patches/Delay-Chunk-Unloads-based-on-Player-Movement.patch
index 9f68c91343..11429dfed7 100644
--- a/Spigot-Server-Patches/Delay-Chunk-Unloads-based-on-Player-Movement.patch
+++ b/Spigot-Server-Patches/Delay-Chunk-Unloads-based-on-Player-Movement.patch
@@ -85,12 +85,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      public int compareTo(Ticket<?> ticket) {
 @@ -0,0 +0,0 @@ public final class Ticket<T> implements Comparable<Ticket<?>> {
-         return this.b;
-     }
- 
-+    protected void setCurrentTick(long i) { a(i); } // Paper - OBFHELPER
-     protected void a(long i) {
-         this.d = i;
      }
  
      protected boolean b(long i) {
diff --git a/Spigot-Server-Patches/Ensure-Entity-AABB-s-are-never-invalid.patch b/Spigot-Server-Patches/Ensure-Entity-AABB-s-are-never-invalid.patch
deleted file mode 100644
index df47863da2..0000000000
--- a/Spigot-Server-Patches/Ensure-Entity-AABB-s-are-never-invalid.patch
+++ /dev/null
@@ -1,55 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Aikar <aikar@aikar.co>
-Date: Sun, 10 May 2020 22:12:46 -0400
-Subject: [PATCH] Ensure Entity AABB's are never invalid
-
-If anything used setPositionRaw, it left potential for an AABB
-to be left stale at their old location, which could cause massive
-AABB boxes if movement ever then got called on the new position.
-
-This guarantees any time we set the entities position, we also
-update their AABB.
-
-diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/Entity.java
-+++ b/src/main/java/net/minecraft/server/Entity.java
-@@ -0,0 +0,0 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
- 
-     public void setPosition(double d0, double d1, double d2) {
-         this.setPositionRaw(d0, d1, d2);
--        float f = this.size.width / 2.0F;
--        float f1 = this.size.height;
--
--        this.a(new AxisAlignedBB(d0 - (double) f, d1, d2 - (double) f, d0 + (double) f, d1 + (double) f1, d2 + (double) f));
-+        // Paper start - move into setPositionRaw
-+        //float f = this.size.width / 2.0F;
-+        //float f1 = this.size.height;
-+        //this.a(new AxisAlignedBB(d0 - (double) f, d1, d2 - (double) f, d0 + (double) f, d1 + (double) f1, d2 + (double) f));
-+        // Paper end
-         if (valid) ((WorldServer) world).chunkCheck(this); // CraftBukkit
-     }
- 
-@@ -0,0 +0,0 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
-         return new AxisAlignedBB(vec3d, vec3d1);
-     }
- 
-+    public final void setBoundingBox(AxisAlignedBB axisalignedbb) { a(axisalignedbb); } // Paper - OBFHELPER
-     public void a(AxisAlignedBB axisalignedbb) {
-         // CraftBukkit start - block invalid bounding boxes
-         double minX = axisalignedbb.minX,
-@@ -0,0 +0,0 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
-     }
- 
-     public void setPositionRaw(double d0, double d1, double d2) {
-+        // Paper start - never allow AABB to become desynced from position
-+        // hanging has its own special logic
-+        if (!(this instanceof EntityHanging) && (locX != d0 || locY != d1 || locZ != d2)) {
-+            float f = this.size.width / 2.0F;
-+            float f1 = this.size.height;
-+            this.setBoundingBox(new AxisAlignedBB(d0 - (double) f, d1, d2 - (double) f, d0 + (double) f, d1 + (double) f1, d2 + (double) f));
-+        }
-+        // Paper end
-         this.locX = d0;
-         this.locY = d1;
-         this.locZ = d2;
diff --git a/Spigot-Server-Patches/Ensure-EntityRaider-respects-game-and-entity-rules-f.patch b/Spigot-Server-Patches/Ensure-EntityRaider-respects-game-and-entity-rules-f.patch
index a28cbb2327..18a1927e0b 100644
--- a/Spigot-Server-Patches/Ensure-EntityRaider-respects-game-and-entity-rules-f.patch
+++ b/Spigot-Server-Patches/Ensure-EntityRaider-respects-game-and-entity-rules-f.patch
@@ -23,7 +23,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          @Override
          public boolean a() {
 +            if (!getRaider().world.getGameRules().getBoolean(GameRules.MOB_GRIEFING) || !getRaider().canPickupLoot()) return false; // Paper - respect game and entity rules for picking up items
-+
-             Raid raid = this.b.eE();
+             Raid raid = this.b.fb();
  
-             if (this.b.eF() && !this.b.eE().a() && this.b.es() && !ItemStack.matches(this.b.getEquipment(EnumItemSlot.HEAD), Raid.s())) {
+             if (this.b.fc() && !this.b.fb().a() && this.b.eO() && !ItemStack.matches(this.b.getEquipment(EnumItemSlot.HEAD), Raid.s())) {
diff --git a/Spigot-Server-Patches/Ensure-safe-gateway-teleport.patch b/Spigot-Server-Patches/Ensure-safe-gateway-teleport.patch
index f350339dd9..1d08fe4d31 100644
--- a/Spigot-Server-Patches/Ensure-safe-gateway-teleport.patch
+++ b/Spigot-Server-Patches/Ensure-safe-gateway-teleport.patch
@@ -14,16 +14,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              List<Entity> list = this.world.a(Entity.class, new AxisAlignedBB(this.getPosition()));
 -
 -            if (!list.isEmpty()) {
--                this.a(((Entity) list.get(0)).getRootVehicle());
--            }
-+                // Paper start
-+                for (Entity entity : list) {
-+                    if (!entity.isPassenger() && !entity.isVehicle() && entity.canPortal()) {
-+                        this.a(entity);
-+                        break;
-+                    }
+-                this.a((Entity) list.get(this.world.random.nextInt(list.size())));
++            // Paper start
++            for (Entity entity : list) {
++                if (!entity.isPassenger() && !entity.isVehicle() && entity.canPortal()) {
++                    this.a(entity);
++                    break;
 +                }
-+                // Paper end
+             }
++            // Paper end
  
              if (this.age % 2400L == 0L) {
                  this.h();
diff --git a/Spigot-Server-Patches/EntityShootBowEvent-consumeArrow-and-getArrowItem-AP.patch b/Spigot-Server-Patches/EntityShootBowEvent-consumeArrow-and-getArrowItem-AP.patch
index 89ac9db8d9..2daa038477 100644
--- a/Spigot-Server-Patches/EntityShootBowEvent-consumeArrow-and-getArrowItem-AP.patch
+++ b/Spigot-Server-Patches/EntityShootBowEvent-consumeArrow-and-getArrowItem-AP.patch
@@ -75,14 +75,6 @@ diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
 +++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
-@@ -0,0 +0,0 @@ import net.minecraft.server.GeneratorAccess;
- import net.minecraft.server.IBlockData;
- import net.minecraft.server.IChatBaseComponent;
- import net.minecraft.server.IInventory;
-+import net.minecraft.server.IProjectile;
- import net.minecraft.server.ItemActionContext;
- import net.minecraft.server.ItemStack;
- import net.minecraft.server.Items;
 @@ -0,0 +0,0 @@ public class CraftEventFactory {
      /**
       * EntityShootBowEvent
diff --git a/Spigot-Server-Patches/Expose-Arrow-getItemStack.patch b/Spigot-Server-Patches/Expose-Arrow-getItemStack.patch
index d08a9ac7a2..374b7dc20c 100644
--- a/Spigot-Server-Patches/Expose-Arrow-getItemStack.patch
+++ b/Spigot-Server-Patches/Expose-Arrow-getItemStack.patch
@@ -8,7 +8,7 @@ diff --git a/src/main/java/net/minecraft/server/EntityArrow.java b/src/main/java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/EntityArrow.java
 +++ b/src/main/java/net/minecraft/server/EntityArrow.java
-@@ -0,0 +0,0 @@ public abstract class EntityArrow extends Entity implements IProjectile {
+@@ -0,0 +0,0 @@ public abstract class EntityArrow extends IProjectile {
          }
      }
  
diff --git a/Spigot-Server-Patches/Fix-CraftServer.unloadWorld-Leak.patch b/Spigot-Server-Patches/Fix-CraftServer.unloadWorld-Leak.patch
deleted file mode 100644
index 6226dbf047..0000000000
--- a/Spigot-Server-Patches/Fix-CraftServer.unloadWorld-Leak.patch
+++ /dev/null
@@ -1,124 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Aikar <aikar@aikar.co>
-Date: Fri, 8 May 2020 20:30:58 -0400
-Subject: [PATCH] Fix CraftServer.unloadWorld Leak
-
-The dimension manager was still registered which leaked the entire World
-
-diff --git a/src/main/java/net/minecraft/server/DimensionManager.java b/src/main/java/net/minecraft/server/DimensionManager.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/DimensionManager.java
-+++ b/src/main/java/net/minecraft/server/DimensionManager.java
-@@ -0,0 +0,0 @@ public class DimensionManager implements MinecraftSerializable {
-     private final boolean hasSkyLight;
-     private final GenLayerZoomer genLayerZoomer;
- 
-+    // Paper start
-+    public static void unregister(String s, DimensionManager dimensionmanager) {
-+        if (dimensionmanager == OVERWORLD || dimensionmanager == NETHER || dimensionmanager == THE_END) { return; } // do not unregister the default worlds
-+        MCUtil.MAIN_EXECUTOR.execute(() -> {
-+            RegistryMaterials<DimensionManager> registry = (RegistryMaterials<DimensionManager>) IRegistry.DIMENSION_TYPE;
-+            registry.deleteValue(new MinecraftKey(s), dimensionmanager);
-+        });
-+    }
-+    // Paper end
-     public static DimensionManager register(String s, DimensionManager dimensionmanager) {
-         return (DimensionManager) IRegistry.a(IRegistry.DIMENSION_TYPE, dimensionmanager.id, s, dimensionmanager);
-     }
-diff --git a/src/main/java/net/minecraft/server/RegistryID.java b/src/main/java/net/minecraft/server/RegistryID.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/RegistryID.java
-+++ b/src/main/java/net/minecraft/server/RegistryID.java
-@@ -0,0 +0,0 @@ import javax.annotation.Nullable;
- public class RegistryID<K> implements Registry<K> {
- 
-     private static final Object a = null;
--    private K[] b;
-+    private K[] b; // Paper - diff below
-     private int[] c;
--    private K[] d;
-+    private K[] d; // Paper - diff below
-     private int e;
-     private int f;
-     private java.util.BitSet usedIds; // Paper
-+    // Paper start
-+    public void removeValue(K value) {
-+        removeValue(value, this.b);
-+        removeValue(value, this.d);
-+        rehash(this.b.length);
-+    }
-+    public void removeValue(K value, K[] arr) {
-+        for (int i = 0; i < arr.length; i++) {
-+            K k = arr[i];
-+            if (k == value) {
-+                arr[i] = null;
-+            }
-+        }
-+    }
- 
-     public RegistryID(int i) {
-         i = (int) ((float) i / 0.8F);
-@@ -0,0 +0,0 @@ public class RegistryID<K> implements Registry<K> {
-         return this.e;
-     }
- 
-+    private void rehash(int i) { d(i); } // Paper - OBFHELPER
-     private void d(int i) {
-         K[] ak = this.b;
-         int[] aint = this.c;
-diff --git a/src/main/java/net/minecraft/server/RegistryMaterials.java b/src/main/java/net/minecraft/server/RegistryMaterials.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/RegistryMaterials.java
-+++ b/src/main/java/net/minecraft/server/RegistryMaterials.java
-@@ -0,0 +0,0 @@ public class RegistryMaterials<T> extends IRegistryWritable<T> {
-     private int V;
- 
-     public RegistryMaterials() {}
-+    public T deleteValue(MinecraftKey minecraftkey, T value) {
-+        this.b.removeValue(value); // Diff 1
-+        this.d = null; // Diff 2
-+        return this.c.remove(minecraftkey); // Diff 3
-+    }
- 
-     @Override
-     public <V extends T> V a(int i, MinecraftKey minecraftkey, V v0) {
--        this.b.a(v0, i);
-+        this.b.a(v0, i); // Paper - diff above 1
-         Validate.notNull(minecraftkey);
-         Validate.notNull(v0);
--        this.d = null;
-+        this.d = null; // Diff 2
-         if (this.c.containsKey(minecraftkey)) {
-             RegistryMaterials.LOGGER.debug("Adding duplicate key '{}' to registry", minecraftkey);
-         }
- 
--        this.c.put(minecraftkey, v0);
-+        this.c.put(minecraftkey, v0); // Paper - diff3
-         if (this.V <= i) {
-             this.V = i + 1;
-         }
-diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-@@ -0,0 +0,0 @@ import net.minecraft.server.EntityPlayer;
- import net.minecraft.server.EnumDifficulty;
- import net.minecraft.server.EnumGamemode;
- import net.minecraft.server.IRecipe;
-+import net.minecraft.server.IRegistry;
- import net.minecraft.server.Item;
- import net.minecraft.server.ItemWorldMap;
- import net.minecraft.server.Items;
-@@ -0,0 +0,0 @@ public final class CraftServer implements Server {
-         }
- 
-         worlds.remove(world.getName().toLowerCase(java.util.Locale.ENGLISH));
--        console.worldServer.remove(handle.getWorldProvider().getDimensionManager());
-+        // Paper start
-+        DimensionManager dimensionManager = handle.getWorldProvider().getDimensionManager();
-+        DimensionManager.unregister(world.getName().toLowerCase(java.util.Locale.ENGLISH), dimensionManager);
-+        console.worldServer.remove(dimensionManager);
-+        // Paper end
-         return true;
-     }
- 
diff --git a/Spigot-Server-Patches/Fix-Light-Command.patch b/Spigot-Server-Patches/Fix-Light-Command.patch
index 098b71e7c8..2fbdef8430 100644
--- a/Spigot-Server-Patches/Fix-Light-Command.patch
+++ b/Spigot-Server-Patches/Fix-Light-Command.patch
@@ -161,6 +161,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          this.mailboxWorldGen = this.p.a(threadedmailbox, false);
          this.mailboxMain = this.p.a(mailbox, false);
 +        this.mailboxLight = this.p.a(lightthreaded, false);// Paper
-         this.lightEngine = new LightEngineThreaded(ilightaccess, this, this.world.getWorldProvider().f(), threadedmailbox1, this.p.a(threadedmailbox1, false));
+         this.lightEngine = new LightEngineThreaded(ilightaccess, this, this.world.getDimensionManager().hasSkyLight(), threadedmailbox1, this.p.a(threadedmailbox1, false));
          this.chunkDistanceManager = new PlayerChunkMap.a(executor, iasynctaskhandler); this.chunkDistanceManager.chunkMap = this; // Paper
          this.l = supplier;
diff --git a/Spigot-Server-Patches/Fix-enderdragon-exp-dupe.patch b/Spigot-Server-Patches/Fix-enderdragon-exp-dupe.patch
index e854d69013..f986ea7e3c 100644
--- a/Spigot-Server-Patches/Fix-enderdragon-exp-dupe.patch
+++ b/Spigot-Server-Patches/Fix-enderdragon-exp-dupe.patch
@@ -11,27 +11,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/net/minecraft/server/EntityEnderDragon.java
 +++ b/src/main/java/net/minecraft/server/EntityEnderDragon.java
 @@ -0,0 +0,0 @@ public class EntityEnderDragon extends EntityInsentient implements IMonster {
-     public float bx;
-     public float by;
-     public boolean bz;
--    public int bA;
-+    public int bA; public final int getDeathTicks() { return this.bA; } public final void setDeathTicks(final int value) { this.bA = value; } // Paper
-     public float bB;
-     @Nullable
-     public EntityEnderCrystal currentEnderCrystal;
-@@ -0,0 +0,0 @@ public class EntityEnderDragon extends EntityInsentient implements IMonster {
-     public void b(NBTTagCompound nbttagcompound) {
-         super.b(nbttagcompound);
-         nbttagcompound.setInt("DragonPhase", this.bO.a().getControllerPhase().b());
-+        nbttagcompound.setInt("Paper.DeathTick", this.getDeathTicks()); // Paper
+     public void saveData(NBTTagCompound nbttagcompound) {
+         super.saveData(nbttagcompound);
+         nbttagcompound.setInt("DragonPhase", this.bN.a().getControllerPhase().b());
++        nbttagcompound.setInt("Paper.DeathTick", this.deathAnimationTicks); // Paper
      }
  
      @Override
 @@ -0,0 +0,0 @@ public class EntityEnderDragon extends EntityInsentient implements IMonster {
          if (nbttagcompound.hasKey("DragonPhase")) {
-             this.bO.setControllerPhase(DragonControllerPhase.getById(nbttagcompound.getInt("DragonPhase")));
+             this.bN.setControllerPhase(DragonControllerPhase.getById(nbttagcompound.getInt("DragonPhase")));
          }
-+        this.setDeathTicks(nbttagcompound.getInt("Paper.DeathTick")); // Paper
++        this.deathAnimationTicks = nbttagcompound.getInt("Paper.DeathTick"); // Paper
  
      }
  
diff --git a/Spigot-Server-Patches/Fix-items-vanishing-through-end-portal.patch b/Spigot-Server-Patches/Fix-items-vanishing-through-end-portal.patch
index 2892b633b8..6001012c5c 100644
--- a/Spigot-Server-Patches/Fix-items-vanishing-through-end-portal.patch
+++ b/Spigot-Server-Patches/Fix-items-vanishing-through-end-portal.patch
@@ -19,7 +19,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 @@ -0,0 +0,0 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
  
          if (blockposition == null) { // CraftBukkit
-             if (this.world.getDimensionKey() == World.THE_END && worldserver.getDimensionKey() == World.OVERWORLD) {
+             if (this.world.getTypeKey() == DimensionManager.THE_END && worldserver.getTypeKey() == DimensionManager.OVERWORLD) { // CraftBukkit
 +                // Paper start - Ensure spawn chunk is always loaded before calculating Y coordinate
 +                this.world.getChunkAtWorldCoords(this.world.getSpawn());
 +                // Paper end
diff --git a/Spigot-Server-Patches/Fix-piston-physics-inconsistency-MC-188840.patch b/Spigot-Server-Patches/Fix-piston-physics-inconsistency-MC-188840.patch
index 4e5bd971a2..665a490241 100644
--- a/Spigot-Server-Patches/Fix-piston-physics-inconsistency-MC-188840.patch
+++ b/Spigot-Server-Patches/Fix-piston-physics-inconsistency-MC-188840.patch
@@ -63,20 +63,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                  blockposition3 = blockposition3.shift(enumdirection1);
                  map.remove(blockposition3);
                  world.setTypeAndData(blockposition3, (IBlockData) Blocks.MOVING_PISTON.getBlockData().set(BlockPiston.FACING, enumdirection), 68);
--                world.setTileEntity(blockposition3, BlockPistonMoving.a((IBlockData) list1.get(k), enumdirection, flag, false));
 +                // Paper start - fix a variety of piston desync dupes
 +                if (!allowDesync) {
 +                    iblockdata1 = world.getType(oldPos);
 +                    map.replace(oldPos, iblockdata1);
 +                }
-+                world.setTileEntity(blockposition3, BlockPistonMoving.a(allowDesync ? list1.get(k) : iblockdata1, enumdirection, flag, false));
+                 world.setTileEntity(blockposition3, BlockPistonMoving.a((IBlockData) list1.get(k), enumdirection, flag, false));
 +                if (!allowDesync) {
 +                    world.setTypeAndData(oldPos, Blocks.AIR.getBlockData(), 2 | 4 | 16 | 1024); // set air to prevent later physics updates from seeing this block
 +                }
 +                // Paper end - fix a variety of piston desync dupes
-                 --j;
-                 aiblockdata[j] = iblockdata1;
+                 aiblockdata[j++] = iblockdata1;
              }
+ 
 diff --git a/src/main/java/net/minecraft/server/TileEntityPiston.java b/src/main/java/net/minecraft/server/TileEntityPiston.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/TileEntityPiston.java
@@ -89,4 +88,4 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                    this.world.setTypeAndData(this.position, this.a, com.destroystokyo.paper.PaperConfig.allowPistonDuplication ? 84 : (84 | 2)); // Paper - force notify (flag 2), it's possible the set type by the piston block (which doesn't notify) set this block to air
                      Block.a(this.a, iblockdata, this.world, this.position, 3);
                  } else {
-                     if (iblockdata.b((IBlockState) BlockProperties.C) && (Boolean) iblockdata.get(BlockProperties.C)) {
+                     if (iblockdata.b(BlockProperties.C) && (Boolean) iblockdata.get(BlockProperties.C)) {
diff --git a/Spigot-Server-Patches/Fix-this-stupid-bullshit.patch b/Spigot-Server-Patches/Fix-this-stupid-bullshit.patch
index df33fd65b8..d3df7489ae 100644
--- a/Spigot-Server-Patches/Fix-this-stupid-bullshit.patch
+++ b/Spigot-Server-Patches/Fix-this-stupid-bullshit.patch
@@ -14,7 +14,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +++ b/src/main/java/org/bukkit/craftbukkit/Main.java
 @@ -0,0 +0,0 @@ public class Main {
                      Calendar deadline = Calendar.getInstance();
-                     deadline.add(Calendar.DAY_OF_YEAR, -3);
+                     deadline.add(Calendar.DAY_OF_YEAR, -1);
                      if (buildDate.before(deadline.getTime())) {
 -                        System.err.println("*** Error, this build is outdated ***");
 +                        // Paper start - This is some stupid bullshit
diff --git a/Spigot-Server-Patches/Implement-Chunk-Priority-Urgency-System-for-Chunks.patch b/Spigot-Server-Patches/Implement-Chunk-Priority-Urgency-System-for-Chunks.patch
index 582ad169ef..ee4cbb798c 100644
--- a/Spigot-Server-Patches/Implement-Chunk-Priority-Urgency-System-for-Chunks.patch
+++ b/Spigot-Server-Patches/Implement-Chunk-Priority-Urgency-System-for-Chunks.patch
@@ -87,7 +87,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
 +    public final BlockPosition asPosition() { return l(); } // Paper - OBFHELPER
      public BlockPosition l() {
-         return new BlockPosition(this.x << 4, 0, this.z << 4);
+         return new BlockPosition(this.d(), 0, this.e());
      }
 diff --git a/src/main/java/net/minecraft/server/ChunkMapDistance.java b/src/main/java/net/minecraft/server/ChunkMapDistance.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
@@ -113,8 +113,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 @@ -0,0 +0,0 @@ public abstract class ChunkMapDistance {
      }
  
-     private static int a(ArraySetSorted<Ticket<?>> arraysetsorted) {
-+        AsyncCatcher.catchOp("ChunkMapDistance::getHighestTicketLevel"); // Paper
+     private static int getLowestTicketLevel(ArraySetSorted<Ticket<?>> arraysetsorted) {
++        AsyncCatcher.catchOp("ChunkMapDistance::getLowestTicketLevel"); // Paper
          return !arraysetsorted.isEmpty() ? ((Ticket) arraysetsorted.b()).b() : PlayerChunkMap.GOLDEN_TICKET + 1;
      }
  
@@ -122,9 +122,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      public boolean a(PlayerChunkMap playerchunkmap) {
          //this.f.a(); // Paper - no longer used
-+        AsyncCatcher.catchOp("DistanceManagerTick");
++        AsyncCatcher.catchOp("DistanceManagerTick"); // Paper
          this.g.a();
-         int i = Integer.MAX_VALUE - this.e.a(Integer.MAX_VALUE);
+         int i = Integer.MAX_VALUE - this.ticketLevelTracker.a(Integer.MAX_VALUE);
          boolean flag = i != 0;
 @@ -0,0 +0,0 @@ public abstract class ChunkMapDistance {
  
@@ -149,7 +149,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      private boolean addTicket(long i, Ticket<?> ticket) { // CraftBukkit - void -> boolean
 +        AsyncCatcher.catchOp("ChunkMapDistance::addTicket"); // Paper
          ArraySetSorted<Ticket<?>> arraysetsorted = this.e(i);
-         int j = a(arraysetsorted);
+         int j = getLowestTicketLevel(arraysetsorted);
          Ticket<?> ticket1 = (Ticket) arraysetsorted.a(ticket); // CraftBukkit - decompile error
 @@ -0,0 +0,0 @@ public abstract class ChunkMapDistance {
      }
@@ -157,18 +157,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      private boolean removeTicket(long i, Ticket<?> ticket) { // CraftBukkit - void -> boolean
 +        AsyncCatcher.catchOp("ChunkMapDistance::removeTicket"); // Paper
          ArraySetSorted<Ticket<?>> arraysetsorted = this.e(i);
-+        int oldLevel = a(arraysetsorted); // Paper
++        int oldLevel = getLowestTicketLevel(arraysetsorted); // Paper
  
          boolean removed = false; // CraftBukkit
          if (arraysetsorted.remove(ticket)) {
 @@ -0,0 +0,0 @@ public abstract class ChunkMapDistance {
-         if (arraysetsorted.isEmpty()) {
              this.tickets.remove(i);
          }
--
--        this.e.b(i, a(arraysetsorted), false);
-+        int newLevel = a(arraysetsorted); // Paper
-+        if (newLevel > oldLevel) this.e.b(i, newLevel, false); // Paper
+ 
+-        this.ticketLevelTracker.update(i, getLowestTicketLevel(arraysetsorted), false);
++        int newLevel = getLowestTicketLevel(arraysetsorted); // Paper
++        if (newLevel > oldLevel) this.ticketLevelTracker.update(i, newLevel, false); // Paper
          return removed; // CraftBukkit
      }
  
@@ -317,6 +316,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                Ticket<?> ticket = new Ticket<>(TicketType.PLAYER, 33, coords); // Paper - no-tick view distance
  
                  if (flag1) {
+-                    ChunkMapDistance.this.j.a(ChunkTaskQueueSorter.a(() -> {
 +                    scheduleChunkLoad(i, MinecraftServer.currentTick, j, (priority) -> { // Paper - smarter ticket delay based on frustum and distance
 +                    // Paper start - recheck its still valid if not cancel
 +                    if (!isChunkInRange(i)) {
@@ -337,16 +337,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                        return;
 +                    }
 +                    // Paper end
-                     ChunkMapDistance.this.j.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error
++                    ChunkMapDistance.this.j.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error
                          ChunkMapDistance.this.m.execute(() -> {
 -                            if (this.c(this.c(i))) {
 +                            if (isChunkInRange(i)) { if (!hasPlayerTicket(coords, 33)) { // Paper - high priority might of already added it
                                  ChunkMapDistance.this.addTicket(i, ticket);
                                  ChunkMapDistance.this.l.add(i);
 -                            } else {
-+                            }
-+                            } else { // Paper
-                                 ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error
+-                                ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> {
++                            }} else { // Paper
++                                ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error
                                  }, i, false));
                              }
  
@@ -357,7 +357,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                      }));
 +                    }); // Paper
                  } else {
-                     ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error
+                     ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> {
                          ChunkMapDistance.this.m.execute(() -> {
                              ChunkMapDistance.this.removeTicket(i, ticket);
 +                            ChunkMapDistance.this.clearPriorityTickets(coords); // Paper
@@ -562,11 +562,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      private boolean a(@Nullable PlayerChunk playerchunk, int i) {
 @@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider {
-         return this.serverThreadQueue.executeNext();
      }
  
--    private boolean tickDistanceManager() {
-+    public boolean tickDistanceManager() { // Paper - public
+     public boolean tickDistanceManager() { // Paper - private -> public
 +        if (chunkMapDistance.delayDistanceManagerTick) return false; // Paper
          boolean flag = this.chunkMapDistance.a(this.playerChunkMap);
          boolean flag1 = this.playerChunkMap.b();
@@ -611,7 +609,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      // Yes, this doesn't match Vanilla, but it's the best we can do for now.
      // If this is an issue, PRs are welcome
 @@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
-             if (valid && (!this.isSpectator() || this.world.isLoaded(new BlockPosition(this)))) { // Paper - don't tick dead players that are not in the world currently (pending respawn)
+             if (valid && !this.isSpectator() || this.world.isLoaded(this.getChunkCoordinates())) { // Paper - don't tick dead players that are not in the world currently (pending respawn)
                  super.tick();
              }
 +            if (valid && isAlive() && playerConnection != null) ((WorldServer)world).getChunkProvider().playerChunkMap.checkHighPriorityChunks(this); // Paper
@@ -844,11 +842,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                      // note: Here is a very good place to add callbacks to logic waiting on this.
                      Chunk entityTickingChunk = either.left().get();
 @@ -0,0 +0,0 @@ public class PlayerChunk {
-             this.entityTickingFuture.complete(PlayerChunk.UNLOADED_CHUNK); this.isEntityTickingReady = false; // Paper - cache chunk ticking stage
              this.entityTickingFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE;
          }
--
--        this.w.a(this.location, this::k, this.ticketLevel, this::d);
+ 
+-        this.v.a(this.location, this::k, this.ticketLevel, this::d);
 +        // Paper start - raise IO/load priority if priority changes, use our preferred priority
 +        priorityBoost = chunkMap.chunkDistanceManager.getChunkPriority(location);
 +        int priority = getDemandedPriority();
@@ -862,7 +859,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            chunkMap.world.asyncChunkTaskManager.raisePriority(location.x, location.z, ioPriority);
 +        }
 +        if (getCurrentPriority() != priority) {
-+            this.w.a(this.location, this::getCurrentPriority, priority, this::setPriority); // use preferred priority
++            this.v.a(this.location, this::getCurrentPriority, priority, this::setPriority); // use preferred priority
 +            int neighborsPriority = getNeighborsPriority();
 +            this.neighbors.forEach((neighbor, neighborDesired) -> neighbor.setNeighborPriority(this, neighborsPriority));
 +        }
@@ -896,6 +893,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
  public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
  
+@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
+     public final WorldServer world;
+     private final LightEngineThreaded lightEngine;
+     private final IAsyncTaskHandler<Runnable> executor;
++    final java.util.concurrent.Executor mainInvokingExecutor; // Paper
+     public final ChunkGenerator chunkGenerator;
+     private final Supplier<WorldPersistentData> l; public final Supplier<WorldPersistentData> getWorldPersistentDataSupplier() { return this.l; } // Paper - OBFHELPER
+     private final VillagePlace m;
 @@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
  
          @Override
@@ -912,6 +917,22 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              if (queued == null) {
                  return;
              }
+@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
+         this.world = worldserver;
+         this.chunkGenerator = chunkgenerator;
+         this.executor = iasynctaskhandler;
++        // Paper start
++        this.mainInvokingExecutor = (run) -> {
++            if (MCUtil.isMainThread()) {
++                run.run();
++            } else {
++                iasynctaskhandler.execute(run);
++            }
++        };
++        // Paper end
+         ThreadedMailbox<Runnable> threadedmailbox = ThreadedMailbox.a(executor, "worldgen");
+ 
+         iasynctaskhandler.getClass();
 @@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
          this.playerViewDistanceTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
              (EntityPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
@@ -1105,8 +1126,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              long i = playerchunk.i().pair();
  
              playerchunk.getClass();
--            mailbox.a(ChunkTaskQueueSorter.a(runnable, i, playerchunk::getTicketLevel)); // CraftBukkit - decompile error
-+            mailbox.a(ChunkTaskQueueSorter.a(runnable, i, () -> 1)); // CraftBukkit - decompile error // Paper - final loads are always urgent!
+-            mailbox.a(ChunkTaskQueueSorter.a(runnable, i, playerchunk::getTicketLevel));
++            mailbox.a(ChunkTaskQueueSorter.a(runnable, i, () -> 1)); // Paper - final loads are always urgent!
          });
      }
  
@@ -1138,7 +1159,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              if (updatingChunk != null) {
                  return updatingChunk.getEntityTickingFuture();
 @@ -0,0 +0,0 @@ public abstract class PlayerList {
-                     entityplayer, finalWorldserver, networkmanager, playerconnection,
+                     entityplayer, finalWorldserver, finalWorldserver1, networkmanager, playerconnection,
                      nbttagcompound, networkmanager.getSocketAddress().toString(), lastKnownName
                  );
 -                //playerChunkMap.chunkDistanceManager.removeTicketAtLevel(TicketType.LOGIN, pos, 31, pos.pair());
@@ -1148,7 +1169,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 @@ -0,0 +0,0 @@ public abstract class PlayerList {
          SocketAddress socketaddress = loginlistener.networkManager.getSocketAddress();
  
-         EntityPlayer entity = new EntityPlayer(this.server, this.server.getWorldServer(DimensionManager.OVERWORLD), gameprofile, new PlayerInteractManager(this.server.getWorldServer(DimensionManager.OVERWORLD)));
+         EntityPlayer entity = new EntityPlayer(this.server, this.server.getWorldServer(World.OVERWORLD), gameprofile, new PlayerInteractManager(this.server.getWorldServer(World.OVERWORLD)));
 +        entity.isRealPlayer = true; // Paper
          Player player = entity.getBukkitEntity();
          PlayerLoginEvent event = new PlayerLoginEvent(player, hostname, ((java.net.InetSocketAddress) socketaddress).getAddress(), ((java.net.InetSocketAddress) loginlistener.networkManager.getRawAddress()).getAddress());
@@ -1158,7 +1179,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
          worldserver.getChunkProvider().addTicket(TicketType.POST_TELEPORT, new ChunkCoordIntPair(location.getBlockX() >> 4, location.getBlockZ() >> 4), 1, entityplayer.getId()); // Paper
 +        entityplayer1.forceCheckHighPriority(); // Player
-         while (avoidSuffocation && !worldserver.getCubes(entityplayer1) && entityplayer1.locY() < 256.0D) {
+         while (avoidSuffocation && !worldserver1.getCubes(entityplayer1) && entityplayer1.locY() < 256.0D) {
              entityplayer1.setPosition(entityplayer1.locX(), entityplayer1.locY() + 1.0D, entityplayer1.locZ());
          }
 diff --git a/src/main/java/net/minecraft/server/Ticket.java b/src/main/java/net/minecraft/server/Ticket.java
@@ -1173,6 +1194,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      protected Ticket(TicketType<T> tickettype, int i, T t0) {
          this.a = tickettype;
+@@ -0,0 +0,0 @@ public final class Ticket<T> implements Comparable<Ticket<?>> {
+         return this.b;
+     }
+ 
++    public final void setCurrentTick(long i) { this.a(i); } // Paper - OBFHELPER
+     protected void a(long i) {
+         this.d = i;
+     }
 diff --git a/src/main/java/net/minecraft/server/TicketType.java b/src/main/java/net/minecraft/server/TicketType.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/TicketType.java
diff --git a/Spigot-Server-Patches/Implement-JellySquid-s-Entity-Collision-optimisation.patch b/Spigot-Server-Patches/Implement-JellySquid-s-Entity-Collision-optimisation.patch
deleted file mode 100644
index afa783e784..0000000000
--- a/Spigot-Server-Patches/Implement-JellySquid-s-Entity-Collision-optimisation.patch
+++ /dev/null
@@ -1,101 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: JellySquid <jellysquid+atwork@protonmail.com>
-Date: Sat, 9 May 2020 16:25:21 -0400
-Subject: [PATCH] Implement JellySquid's Entity Collision optimisations patch
-
-Optimizes Full Block voxel collisions, and removes streams from Entity collisions
-
-Original code by JellySquid, licensed under GNU Lesser General Public License v3.0
-you can find the original code on https://github.com/jellysquid3/lithium-fabric/tree/1.15.x/fabric (Yarn mappings)
-
-diff --git a/src/main/java/net/minecraft/server/ICollisionAccess.java b/src/main/java/net/minecraft/server/ICollisionAccess.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/ICollisionAccess.java
-+++ b/src/main/java/net/minecraft/server/ICollisionAccess.java
-@@ -0,0 +0,0 @@ public interface ICollisionAccess extends IBlockAccess {
- 
-                             if ((j2 != 1 || iblockdata.f()) && (j2 != 2 || iblockdata.getBlock() == Blocks.MOVING_PISTON)) {
-                                 VoxelShape voxelshape2 = iblockdata.b((IBlockAccess) ICollisionAccess.this, blockposition_mutableblockposition, voxelshapecollision);
--                                VoxelShape voxelshape3 = voxelshape2.a((double) k1, (double) l1, (double) i2);
- 
--                                if (VoxelShapes.c(voxelshape, voxelshape3, OperatorBoolean.AND)) {
--                                    consumer.accept(voxelshape3);
--                                    return true;
-+                                // Paper start - Lithium Collision Optimizations
-+                                if (voxelshape2 == VoxelShapes.empty()) {
-+                                    continue;
-+                                }
-+
-+                                if (voxelshape2 == VoxelShapes.fullCube()) {
-+                                    if (axisalignedbb.intersects(x, y, z, x + 1.0D, y + 1.0D, z + 1.0D)) {
-+                                        consumer.accept(voxelshape2.offset(x, y, z));
-+                                        return true;
-+                                    }
-+                                } else {
-+                                    VoxelShape shape = voxelshape2.offset(x, y, z);
-+                                    if (VoxelShapes.applyOperation(shape, voxelshape, OperatorBoolean.AND)) {
-+                                        consumer.accept(shape);
-+                                        return true;
-+                                    }
-+                                    // Paper end
-                                 }
-                             }
-                         }
-diff --git a/src/main/java/net/minecraft/server/IEntityAccess.java b/src/main/java/net/minecraft/server/IEntityAccess.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/IEntityAccess.java
-+++ b/src/main/java/net/minecraft/server/IEntityAccess.java
-@@ -0,0 +0,0 @@ public interface IEntityAccess {
-     // Paper end - optimise hard collision
- 
-     default Stream<VoxelShape> b(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set<Entity> set) {
--        if (axisalignedbb.a() < 1.0E-7D) {
-+        // Paper start - remove streams from entity collision
-+        if (axisalignedbb.getAverageSideLength() < 1.0E-7D) {
-             return Stream.empty();
--        } else {
--            AxisAlignedBB axisalignedbb1 = axisalignedbb.g(1.0E-7D);
--            Stream<AxisAlignedBB> stream = ((entity != null && entity.hardCollides()) ? this.getEntities(entity, axisalignedbb) : this.getHardCollidingEntities(entity, axisalignedbb1)).stream().filter((entity1) -> { // Paper - decompile fix // Paper - optimise hard collision
--                return !set.contains(entity1);
--            }).filter((entity1) -> {
--                return entity == null || !entity.isSameVehicle(entity1);
--            }).flatMap((entity1) -> {
--                return Stream.of(entity1.au(), entity == null ? null : entity.j(entity1)); // Paper - optimise hard collision - diff on change, these are the methods that only hard colliding entities override
--            }).filter(Objects::nonNull);
--
--            return stream.filter(axisalignedbb1::c).map(VoxelShapes::a);
-+
-         }
-+        AxisAlignedBB selection = axisalignedbb.grow(1.0E-7D);
-+        List<Entity> entities = entity != null && entity.hardCollides() ? getEntities(entity, selection) : getHardCollidingEntities(entity, selection);
-+        List<VoxelShape> shapes = new java.util.ArrayList<>();
-+
-+        for (Entity otherEntity : entities) {
-+            if (!set.isEmpty() && set.contains(otherEntity)) {
-+                continue;
-+            }
-+
-+            if (entity != null && entity.isSameVehicle(otherEntity)) {
-+                continue;
-+            }
-+
-+            AxisAlignedBB otherEntityBox = otherEntity.getCollisionBox();
-+
-+            if (otherEntityBox != null && selection.intersects(otherEntityBox)) {
-+                shapes.add(VoxelShapes.of(otherEntityBox));
-+            }
-+
-+            if (entity != null) {
-+                AxisAlignedBB otherEntityHardBox = entity.getHardCollisionBox(otherEntity);
-+
-+                if (otherEntityHardBox != null && selection.intersects(otherEntityHardBox)) {
-+                    shapes.add(VoxelShapes.of(otherEntityHardBox));
-+                }
-+            }
-+        }
-+
-+        return shapes.stream();
-+        // Paper end
-     }
- 
-     @Nullable
diff --git a/Spigot-Server-Patches/Implement-PlayerLocaleChangeEvent.patch b/Spigot-Server-Patches/Implement-PlayerLocaleChangeEvent.patch
index 3d44288fc9..731e33773e 100644
--- a/Spigot-Server-Patches/Implement-PlayerLocaleChangeEvent.patch
+++ b/Spigot-Server-Patches/Implement-PlayerLocaleChangeEvent.patch
@@ -25,6 +25,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              PlayerLocaleChangeEvent event = new PlayerLocaleChangeEvent(getBukkitEntity(), packetplayinsettings.locale);
              this.server.server.getPluginManager().callEvent(event);
          }
+         this.locale = packetplayinsettings.locale;
          this.clientViewDistance = packetplayinsettings.viewDistance;
          // CraftBukkit end
 +        // Paper start - add PlayerLocaleChangeEvent
diff --git a/Spigot-Server-Patches/Improve-Chunk-Status-Transition-Speed.patch b/Spigot-Server-Patches/Improve-Chunk-Status-Transition-Speed.patch
index 2fa7eecbdf..b18c0d5013 100644
--- a/Spigot-Server-Patches/Improve-Chunk-Status-Transition-Speed.patch
+++ b/Spigot-Server-Patches/Improve-Chunk-Status-Transition-Speed.patch
@@ -57,30 +57,6 @@ diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/j
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
 +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
-@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
-     public final WorldServer world;
-     private final LightEngineThreaded lightEngine;
-     private final IAsyncTaskHandler<Runnable> executor;
-+    final java.util.concurrent.Executor mainInvokingExecutor; // Paper
-     public final ChunkGenerator<?> chunkGenerator;
-     private final Supplier<WorldPersistentData> l; public final Supplier<WorldPersistentData> getWorldPersistentDataSupplier() { return this.l; } // Paper - OBFHELPER
-     private final VillagePlace m;
-@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
-         this.world = worldserver;
-         this.chunkGenerator = chunkgenerator;
-         this.executor = iasynctaskhandler;
-+        // Paper start - optimize chunk status progression without jumping through thread pool
-+        this.mainInvokingExecutor = (run) -> {
-+            if (MCUtil.isMainThread()) {
-+                run.run();
-+            } else {
-+                iasynctaskhandler.execute(run);
-+            }
-+        };
-+        // Paper end
-         ThreadedMailbox<Runnable> threadedmailbox = ThreadedMailbox.a(executor, "worldgen");
- 
-         iasynctaskhandler.getClass();
 @@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
              return either.mapLeft((list) -> {
                  return (Chunk) list.get(list.size() / 2);
@@ -118,6 +94,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                return;
 +            }
 +            // Paper end
-             this.mailboxWorldGen.a(ChunkTaskQueueSorter.a(playerchunk, runnable)); // CraftBukkit - decompile error
+             this.mailboxWorldGen.a(ChunkTaskQueueSorter.a(playerchunk, runnable));
          });
      }
diff --git a/Spigot-Server-Patches/MC-183249-Don-t-generate-Carving-Masks-BitSet-unless.patch b/Spigot-Server-Patches/MC-183249-Don-t-generate-Carving-Masks-BitSet-unless.patch
deleted file mode 100644
index 8b6545fd60..0000000000
--- a/Spigot-Server-Patches/MC-183249-Don-t-generate-Carving-Masks-BitSet-unless.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Aikar <aikar@aikar.co>
-Date: Sat, 9 May 2020 12:11:47 -0400
-Subject: [PATCH] MC-183249: Don't generate Carving Masks BitSet unless needed
-
-This was using SIGNIFICANT amounts of memory allocating many
-long[]'s for BitSets for every ProtoChunk in the cache that had
-been unloaded and reloaded.
-
-This will result in a nice memory reduction.
-
-diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java
-+++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
-@@ -0,0 +0,0 @@ public class ChunkRegionLoader {
-             for (int l = 0; l < k; ++l) {
-                 WorldGenStage.Features worldgenstage_features = aworldgenstage_features[l];
- 
--                nbttagcompound3.setByteArray(worldgenstage_features.toString(), ichunkaccess.a(worldgenstage_features).toByteArray());
-+                // Paper start - don't create carving mask bitsets if not even at that chunk status yet
-+                BitSet mask = protochunk.getCarvingMaskIfSet(worldgenstage_features);
-+                if (mask != null) {
-+                    nbttagcompound3.setByteArray(worldgenstage_features.toString(), mask.toByteArray());
-+                }
-+                // Paper end
-             }
- 
-             nbttagcompound1.set("CarvingMasks", nbttagcompound3);
-diff --git a/src/main/java/net/minecraft/server/ProtoChunk.java b/src/main/java/net/minecraft/server/ProtoChunk.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/ProtoChunk.java
-+++ b/src/main/java/net/minecraft/server/ProtoChunk.java
-@@ -0,0 +0,0 @@ public class ProtoChunk implements IChunkAccess {
-     private final ProtoChunkTickList<Block> q;
-     private final ProtoChunkTickList<FluidType> r;
-     private long s;
--    private final Map<WorldGenStage.Features, BitSet> t;
-+    private final Map<WorldGenStage.Features, BitSet> t;public BitSet getCarvingMaskIfSet(WorldGenStage.Features worldgenstage_features) { return this.t.get(worldgenstage_features); } // Paper
-+    // Paper end
-     private volatile boolean u;
-     private final World world; // Paper - Anti-Xray - Add world
- 
diff --git a/Spigot-Server-Patches/Optimize-Bit-Operations-by-inlining.patch b/Spigot-Server-Patches/Optimize-Bit-Operations-by-inlining.patch
index 5dfc5efb24..5a84de14ae 100644
--- a/Spigot-Server-Patches/Optimize-Bit-Operations-by-inlining.patch
+++ b/Spigot-Server-Patches/Optimize-Bit-Operations-by-inlining.patch
@@ -10,8 +10,8 @@ diff --git a/src/main/java/net/minecraft/server/BlockPosition.java b/src/main/ja
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/BlockPosition.java
 +++ b/src/main/java/net/minecraft/server/BlockPosition.java
-@@ -0,0 +0,0 @@ public class BlockPosition extends BaseBlockPosition implements MinecraftSeriali
-         return dynamicops.createIntList(IntStream.of(new int[]{this.getX(), this.getY(), this.getZ()}));
+@@ -0,0 +0,0 @@ public class BlockPosition extends BaseBlockPosition {
+         this(baseblockposition.getX(), baseblockposition.getY(), baseblockposition.getZ());
      }
  
 +    public static long getAdjacent(int baseX, int baseY, int baseZ, EnumDirection enumdirection) { return asLong(baseX + enumdirection.getAdjacentX(), baseY + enumdirection.getAdjacentY(), baseZ + enumdirection.getAdjacentZ()); } // Paper
@@ -25,17 +25,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      }
  
      public static int b(long i) {
--        return (int) (i << 64 - BlockPosition.k - BlockPosition.c >> 64 - BlockPosition.c);
+-        return (int) (i << 64 - BlockPosition.m - BlockPosition.f >> 64 - BlockPosition.f);
 +        return (int) (i >> 38); // Paper - simplify/inline
      }
  
      public static int c(long i) {
--        return (int) (i << 64 - BlockPosition.f >> 64 - BlockPosition.f);
-+        return (int) ((i << 52) >> 52); // Paper - simplify/inline
+-        return (int) (i << 64 - BlockPosition.h >> 64 - BlockPosition.h);
++        return (int) ((i << 26) >> 38);  // Paper - simplify/inline
      }
  
      public static int d(long i) {
--        return (int) (i << 64 - BlockPosition.j - BlockPosition.d >> 64 - BlockPosition.d);
+-        return (int) (i << 64 - BlockPosition.l - BlockPosition.g >> 64 - BlockPosition.g);
 +        return (int) ((i << 26) >> 38);  // Paper - simplify/inline
      }
  
@@ -44,13 +44,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        return new BlockPosition((int) (i >> 38), (int) ((i << 52) >> 52), (int) ((i << 26) >> 38)); // Paper - simplify/inline
      }
  
+     public long asLong() {
+@@ -0,0 +0,0 @@ public class BlockPosition extends BaseBlockPosition {
+ 
      public static long asLong(int x, int y, int z) { return a(x, y, z); } // Paper - OBFHELPER
      public static long a(int i, int j, int k) {
 -        long l = 0L;
 -
--        l |= ((long) i & BlockPosition.g) << BlockPosition.k;
--        l |= ((long) j & BlockPosition.h) << 0;
--        l |= ((long) k & BlockPosition.i) << BlockPosition.j;
+-        l |= ((long) i & BlockPosition.i) << BlockPosition.m;
+-        l |= ((long) j & BlockPosition.j) << 0;
+-        l |= ((long) k & BlockPosition.k) << BlockPosition.l;
 -        return l;
 +        return (((long) i & (long) 67108863) << 38) | (((long) j & (long) 4095)) | (((long) k & (long) 67108863) << 12); // Paper - inline constants and simplify
      }
@@ -104,75 +107,35 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        int k = b(blockposition.getZ());
 -
 -        return (short) (i << 8 | k << 4 | j);
-+        return (short) ((blockposition.x & 15) << 8 | (blockposition.z & 15) << 4 | blockposition.y & 15); // Paper - simplify/inline
++        return (short) ((blockposition.getX() & 15) << 8 | (blockposition.getZ() & 15) << 4 | blockposition.getY() & 15); // Paper - simplify/inline
      }
  
      public static int c(int i) {
 @@ -0,0 +0,0 @@ public class SectionPosition extends BaseBlockPosition {
-     }
- 
-     public static int b(long i) {
--        return (int) (i << 0 >> 42);
-+        return (int) (i >> 42); // Paper
-     }
- 
-     public static int c(long i) {
-@@ -0,0 +0,0 @@ public class SectionPosition extends BaseBlockPosition {
-         return (int) (i << 22 >> 42);
-     }
- 
--    public int a() {
--        return this.getX();
-+    public final int a() { // Paper
-+        return x; // Paper
-     }
- 
--    public int b() {
--        return this.getY();
-+    public final int b() { // Paper
-+        return y; // Paper
-     }
- 
--    public int c() {
--        return this.getZ();
-+    public final int c() { // Paper
-+        return z; // Paper
+         return this.getZ();
      }
  
 -    public int d() {
 -        return this.a() << 4;
 +    public final int d() { // Paper
-+        return x << 4; // Paper
++        return this.getX() << 4; // Paper
      }
  
 -    public int e() {
 -        return this.b() << 4;
 +    public final int e() { // Paper
-+        return y << 4; // Paper
++        return this.getY() << 4; // Paper
      }
  
 -    public int f() {
 -        return this.c() << 4;
 +    public final int f() { // Paper
-+        return z << 4; // Paper
++        return this.getZ() << 4; // Paper
      }
  
--    public int g() {
--        return (this.a() << 4) + 15;
-+    public final int g() { // Paper
-+        return (x << 4) + 15; // Paper
-     }
- 
--    public int h() {
--        return (this.b() << 4) + 15;
-+    public final int h() { // Paper
-+        return (y << 4) + 15; // Paper
-     }
- 
--    public int r() {
--        return (this.c() << 4) + 15;
-+    public final int r() { // Paper
-+        return (z << 4) + 15; // Paper
+     public int g() {
+@@ -0,0 +0,0 @@ public class SectionPosition extends BaseBlockPosition {
+         return (this.c() << 4) + 15;
      }
  
 +    public static long blockToSection(long i) { return e(i); } // Paper - OBFHELPER
@@ -183,15 +146,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      }
  
      public static long f(long i) {
-@@ -0,0 +0,0 @@ public class SectionPosition extends BaseBlockPosition {
-     }
- 
-     public BlockPosition s() {
--        return new BlockPosition(c(this.a()), c(this.b()), c(this.c()));
-+        return new BlockPosition(x << 4, y << 4, z << 4); // Paper
-     }
- 
-     public BlockPosition t() {
 @@ -0,0 +0,0 @@ public class SectionPosition extends BaseBlockPosition {
          return new ChunkCoordIntPair(this.a(), this.c());
      }
@@ -212,14 +166,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        return (((long) i & 4194303L) << 42) | (((long) j & 1048575L)) | (((long) k & 4194303L) << 20); // Paper - Simplify to reduce instruction count
      }
  
-     public long v() {
+     public long s() {
 -        return b(this.a(), this.b(), this.c());
-+        return (((long) x & 4194303L) << 42) | (((long) y & 1048575L)) | (((long) z & 4194303L) << 20); // Paper - Simplify to reduce instruction count
++        return (((long) getX() & 4194303L) << 42) | (((long) getY() & 1048575L)) | (((long) getZ() & 4194303L) << 20); // Paper - Simplify to reduce instruction count
      }
  
-     public Stream<BlockPosition> w() {
--        return BlockPosition.a(this.d(), this.e(), this.f(), this.g(), this.h(), this.r());
-+        return BlockPosition.a(x << 4, y << 4, z << 4, (x << 4) + 15, (y << 4) + 15, (z << 4) + 15); // Paper - simplify/inline
+     public Stream<BlockPosition> t() {
+@@ -0,0 +0,0 @@ public class SectionPosition extends BaseBlockPosition {
      }
  
      public static Stream<SectionPosition> a(SectionPosition sectionposition, int i) {
@@ -228,7 +181,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        int l = sectionposition.c();
 -
 -        return a(j - i, k - i, l - i, j + i, k + i, l + i);
-+        return a(sectionposition.x - i, sectionposition.y - i, sectionposition.z - i, sectionposition.x + i, sectionposition.y + i, sectionposition.z + i); // Paper - simplify/inline
++        return a(sectionposition.getX() - i, sectionposition.getY() - i, sectionposition.getZ() - i, sectionposition.getX() + i, sectionposition.getY() + i, sectionposition.getZ() + i); // Paper - simplify/inline
      }
  
      public static Stream<SectionPosition> b(ChunkCoordIntPair chunkcoordintpair, int i) {
diff --git a/Spigot-Server-Patches/Optimize-Light-Engine.patch b/Spigot-Server-Patches/Optimize-Light-Engine.patch
index 6cf38f1f3d..fb85cb52b1 100644
--- a/Spigot-Server-Patches/Optimize-Light-Engine.patch
+++ b/Spigot-Server-Patches/Optimize-Light-Engine.patch
@@ -24,19 +24,6 @@ Massive update to light to improve performance and chunk loading/generation.
 7) Buffer non urgent tasks even if queueUpdate is called multiple times to improve efficiency.
 8) Fix NPE risk that crashes server in getting nibble data
 
-diff --git a/src/main/java/net/minecraft/server/Block.java b/src/main/java/net/minecraft/server/Block.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/Block.java
-+++ b/src/main/java/net/minecraft/server/Block.java
-@@ -0,0 +0,0 @@ public class Block implements IMaterial {
-         return false;
-     }
- 
--    @Deprecated
-+    public final boolean canOcclude(IBlockData blockData) { return n(blockData); } @Deprecated // Paper - OBFHELPER
-     public final boolean n(IBlockData iblockdata) {
-         return this.j;
-     }
 diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
@@ -50,46 +37,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                  return super.executeNext() || execChunkTask; // Paper
              }
          } finally {
-diff --git a/src/main/java/net/minecraft/server/IBlockData.java b/src/main/java/net/minecraft/server/IBlockData.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/IBlockData.java
-+++ b/src/main/java/net/minecraft/server/IBlockData.java
-@@ -0,0 +0,0 @@ public class IBlockData extends BlockDataAbstract<Block, IBlockData> implements
-     private final boolean e;
-     private final boolean isAir; // Paper
-     private final boolean isTicking; // Paper
-+    private final boolean canOcclude; // Paper
- 
-     public IBlockData(Block block, ImmutableMap<IBlockState<?>, Comparable<?>> immutablemap) {
-         super(block, immutablemap);
-@@ -0,0 +0,0 @@ public class IBlockData extends BlockDataAbstract<Block, IBlockData> implements
-         this.e = block.o(this);
-         this.isAir = this.getBlock().isAir(this); // Paper
-         this.isTicking = this.getBlock().isTicking(this); // Paper
-+        this.canOcclude = this.getBlock().canOcclude(this); // Paper
-     }
- 
-     public void c() {
-@@ -0,0 +0,0 @@ public class IBlockData extends BlockDataAbstract<Block, IBlockData> implements
-         return this.c == null || this.c.h;
-     }
- 
--    public boolean g() {
-+    public final boolean g() { // Paper
-         return this.e;
-     }
- 
-@@ -0,0 +0,0 @@ public class IBlockData extends BlockDataAbstract<Block, IBlockData> implements
-         return this.c != null ? this.c.c : this.getBlock().k(this, iblockaccess, blockposition);
-     }
- 
--    public boolean o() {
--        return this.c != null ? this.c.b : this.getBlock().n(this);
-+    public final boolean o() { // Paper
-+        return canOcclude; // Paper
-     }
- 
-     public VoxelShape getShape(IBlockAccess iblockaccess, BlockPosition blockposition) {
 diff --git a/src/main/java/net/minecraft/server/LightEngineBlock.java b/src/main/java/net/minecraft/server/LightEngineBlock.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/LightEngineBlock.java
@@ -277,8 +224,36 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -    private final IBlockAccess[] h = new IBlockAccess[2];
 +    private final IChunkAccess[] h = new IChunkAccess[2]; // Paper
  
++    // Paper start - see fully commented out method below (look for Bedrock)
++    // optimized method with less branching for when scenarios arent needed.
++    // avoid using mutable version if can
++    protected final IBlockData getBlockOptimized(int x, int y, int z, MutableInt mutableint) {
++        IChunkAccess iblockaccess = this.a(x >> 4, z >> 4);
++
++        if (iblockaccess == null) {
++            mutableint.setValue(16);
++            return Blocks.BEDROCK.getBlockData();
++        } else {
++            this.pos.setValues(x, y, z);
++            IBlockData iblockdata = iblockaccess.getType(x, y, z);
++            mutableint.setValue(iblockdata.b(this.a.getWorld(), this.pos));
++            return iblockdata.l() && iblockdata.e() ? iblockdata : Blocks.AIR.getBlockData();
++        }
++    }
++    protected final IBlockData getBlockOptimized(int x, int y, int z) {
++        IChunkAccess iblockaccess = this.a(x >> 4, z >> 4);
++
++        if (iblockaccess == null) {
++            return Blocks.BEDROCK.getBlockData();
++        } else {
++            IBlockData iblockdata = iblockaccess.getType(x, y, z);
++            return iblockdata.l() && iblockdata.e() ? iblockdata : Blocks.AIR.getBlockData();
++        }
++    }
++    // Paper end
      public LightEngineLayer(ILightAccess ilightaccess, EnumSkyBlock enumskyblock, S s0) {
          super(16, 256, 8192);
+         this.a = ilightaccess;
 @@ -0,0 +0,0 @@ public abstract class LightEngineLayer<M extends LightEngineStorageArray<M>, S e
      }
  
@@ -308,7 +283,31 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -            }
 -
 -            return Blocks.AIR.getBlockData();
-+    // Paper start - unused, optimized versions below, comment out to detect changes
+-        } else {
+-            int j = SectionPosition.a(BlockPosition.b(i));
+-            int k = SectionPosition.a(BlockPosition.d(i));
+-            IBlockAccess iblockaccess = this.a(j, k);
+-
+-            if (iblockaccess == null) {
+-                if (mutableint != null) {
+-                    mutableint.setValue(16);
+-                }
+-
+-                return Blocks.BEDROCK.getBlockData();
+-            } else {
+-                this.d.g(i);
+-                IBlockData iblockdata = iblockaccess.getType(this.d);
+-                boolean flag = iblockdata.l() && iblockdata.e();
+-
+-                if (mutableint != null) {
+-                    mutableint.setValue(iblockdata.b(this.a.getWorld(), (BlockPosition) this.d));
+-                }
+-
+-                return flag ? iblockdata : Blocks.AIR.getBlockData();
+-            }
+-        }
+-    }
++    // Paper start - comment out, see getBlockOptimized
 +//    protected IBlockData a(long i, @Nullable MutableInt mutableint) {
 +//        if (i == Long.MAX_VALUE) {
 +//            if (mutableint != null) {
@@ -330,7 +329,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +//            } else {
 +//                this.d.g(i);
 +//                IBlockData iblockdata = iblockaccess.getType(this.d);
-+//                boolean flag = iblockdata.o() && iblockdata.g();
++//                boolean flag = iblockdata.l() && iblockdata.e();
 +//
 +//                if (mutableint != null) {
 +//                    mutableint.setValue(iblockdata.b(this.a.getWorld(), (BlockPosition) this.d));
@@ -340,55 +339,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +//            }
 +//        }
 +//    }
-+    // optimized method with less branching for when scenarios arent needed.
-+    // avoid using mutable version if can
-+    protected final IBlockData getBlockOptimized(int x, int y, int z, MutableInt mutableint) {
-+        IChunkAccess iblockaccess = this.a(x >> 4, z >> 4);
-+
-+        if (iblockaccess == null) {
-+            mutableint.setValue(16);
-+            return Blocks.BEDROCK.getBlockData();
-         } else {
--            int j = SectionPosition.a(BlockPosition.b(i));
--            int k = SectionPosition.a(BlockPosition.d(i));
--            IBlockAccess iblockaccess = this.a(j, k);
--
--            if (iblockaccess == null) {
--                if (mutableint != null) {
--                    mutableint.setValue(16);
--                }
--
--                return Blocks.BEDROCK.getBlockData();
--            } else {
--                this.d.g(i);
--                IBlockData iblockdata = iblockaccess.getType(this.d);
--                boolean flag = iblockdata.o() && iblockdata.g();
--
--                if (mutableint != null) {
--                    mutableint.setValue(iblockdata.b(this.a.getWorld(), (BlockPosition) this.d));
--                }
-+            this.pos.setValues(x, y, z);
-+            IBlockData iblockdata = iblockaccess.getType(x, y, z);
-+            mutableint.setValue(iblockdata.b(this.a.getWorld(), this.pos));
-+            return iblockdata.o() && iblockdata.g() ? iblockdata : Blocks.AIR.getBlockData();
-+        }
-+    }
-+    protected final IBlockData getBlockOptimized(int x, int y, int z) {
-+        IChunkAccess iblockaccess = this.a(x >> 4, z >> 4);
- 
--                return flag ? iblockdata : Blocks.AIR.getBlockData();
--            }
-+        if (iblockaccess == null) {
-+            return Blocks.BEDROCK.getBlockData();
-+        } else {
-+            IBlockData iblockdata = iblockaccess.getType(x, y, z);
-+            return iblockdata.o() && iblockdata.g() ? iblockdata : Blocks.AIR.getBlockData();
-         }
-     }
 +    // Paper end
  
      protected VoxelShape a(IBlockData iblockdata, long i, EnumDirection enumdirection) {
-         return iblockdata.o() ? iblockdata.a(this.a.getWorld(), this.d.g(i), enumdirection) : VoxelShapes.a();
+         return iblockdata.l() ? iblockdata.a(this.a.getWorld(), this.d.g(i), enumdirection) : VoxelShapes.a();
 @@ -0,0 +0,0 @@ public abstract class LightEngineLayer<M extends LightEngineStorageArray<M>, S e
          return i == Long.MAX_VALUE ? 0 : 15 - this.c.i(i);
      }
@@ -583,6 +537,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      protected final Long2ObjectMap<NibbleArray> i = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap());
      private final LongSet n = new LongOpenHashSet();
      private final LongSet o = new LongOpenHashSet();
+@@ -0,0 +0,0 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e
      protected volatile boolean j;
  
      protected LightEngineStorage(EnumSkyBlock enumskyblock, ILightAccess ilightaccess, M m0) {
@@ -696,12 +651,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          }
  
          if (k >= 2 && j != 2) {
--            if (this.o.contains(i)) {
--                this.o.remove(i);
+-            if (this.p.contains(i)) {
+-                this.p.remove(i);
 -            } else {
-+            if (!this.o.remove(i)) { // Paper - remove useless contains - credit to JellySquid
-+                //this.o.remove(i); // Paper
-+            //} else { // Pape
++            if (!this.p.remove(i)) { // Paper - remove useless contains - credit to JellySquid
++                //this.p.remove(i); // Paper
++            //} else { // Paper
                  this.f.a(i, this.j(i));
                  this.g.add(i);
                  this.k(i);
@@ -746,7 +701,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                  NibbleArray nibblearray1 = (NibbleArray) this.i.remove(i);
  
 @@ -0,0 +0,0 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e
-             longiterator = this.o.iterator();
+             longiterator = this.p.iterator();
  
              while (longiterator.hasNext()) {
 -                i = (Long) longiterator.next();
@@ -754,16 +709,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                  this.l(i);
              }
  
-             this.o.clear();
-             this.j = false;
--            ObjectIterator objectiterator = this.i.long2ObjectEntrySet().iterator();
-+            ObjectIterator<Long2ObjectMap.Entry<NibbleArray>> objectiterator = Long2ObjectMaps.fastIterator(this.i); // Paper
- 
+@@ -0,0 +0,0 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e
              Entry entry;
              long j;
  
 +            NibbleArray test = null; // Paper
-+            LongSet propagating = new LongOpenHashSet(); // Paper - credit JellySquid for idea to move this up
              while (objectiterator.hasNext()) {
                  entry = (Entry) objectiterator.next();
                  j = entry.getLongKey();
@@ -775,66 +725,48 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                          this.a(lightenginelayer, j);
                          this.f.a(j, nibblearray);
                          this.g.add(j);
-                     }
-+                    if (!flag1) propagating.add(j); // Paper
-+                    objectiterator.remove(); // Paper
-                 }
-             }
- 
-             this.f.c();
--            if (!flag1) {
--                longiterator = this.i.keySet().iterator();
-+            if (!flag1) {// Paper - diff on change, change propagating add a few lines up
-+                longiterator = propagating.iterator(); // Paper
+@@ -0,0 +0,0 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e
+                 longiterator = this.i.keySet().iterator();
  
                  while (longiterator.hasNext()) {
 -                    i = (Long) longiterator.next();
--                    if (this.g(i)) {
--                        int k = SectionPosition.c(SectionPosition.b(i));
--                        int l = SectionPosition.c(SectionPosition.c(i));
--                        int i1 = SectionPosition.c(SectionPosition.d(i));
-+                    // Paper start
-+                    i = longiterator.nextLong();
-+                    if (true) { // don't check hasLight, this iterator is filtered already
-+                        int secX = (int) (i >> 42);
-+                        int secY = (int) (i << 44 >> 44);
-+                        int secZ = (int) (i << 22 >> 42);
-+                        int k = secX << 4; // baseX
-+                        int l = secY << 4; // baseY
-+                        int i1 = secZ << 4; // baseZ
-+                        // Paper end
-                         EnumDirection[] aenumdirection = LightEngineStorage.k;
-                         int j1 = aenumdirection.length;
++                    i = longiterator.nextLong(); // Paper
+                     this.b(lightenginelayer, i);
+                 }
+             } else {
+                 longiterator = this.n.iterator();
  
-                         for (int k1 = 0; k1 < j1; ++k1) {
-                             EnumDirection enumdirection = aenumdirection[k1];
--                            long l1 = SectionPosition.a(i, enumdirection);
--
--                            if (!this.i.containsKey(l1) && this.g(l1)) {
-+                            long l1 = SectionPosition.getAdjacentFromSectionPos(secX, secY, secZ, enumdirection); // Paper - avoid extra unpacking
-+                            if (!propagating.contains(l1) && this.g(l1)) { // Paper - use propagating
-                                 for (int i2 = 0; i2 < 16; ++i2) {
-                                     for (int j2 = 0; j2 < 16; ++j2) {
-                                         long k2;
-@@ -0,0 +0,0 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e
+                 while (longiterator.hasNext()) {
+-                    i = (Long) longiterator.next();
++                    i = longiterator.nextLong(); // Paper
+                     this.b(lightenginelayer, i);
                  }
              }
- 
-+            // Paper start - moved above - Credit JellySquid for idea
-+            /*
-             objectiterator = this.i.long2ObjectEntrySet().iterator();
- 
-             while (objectiterator.hasNext()) {
 @@ -0,0 +0,0 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e
-                 if (this.g(j)) {
-                     objectiterator.remove();
-                 }
--            }
-+            }*/
-+            // Paper end
  
-         }
-     }
+     private void b(LightEngineLayer<M, ?> lightenginelayer, long i) {
+         if (this.g(i)) {
+-            int j = SectionPosition.c(SectionPosition.b(i));
+-            int k = SectionPosition.c(SectionPosition.c(i));
+-            int l = SectionPosition.c(SectionPosition.d(i));
++            // Paper start
++            int secX = (int) (i >> 42);
++            int secY = (int) (i << 44 >> 44);
++            int secZ = (int) (i << 22 >> 42);
++            int j = secX << 4; // baseX
++            int k = secY << 4; // baseY
++            int l = secZ << 4; // baseZ
++            // Paper end
+             EnumDirection[] aenumdirection = LightEngineStorage.k;
+             int i1 = aenumdirection.length;
+ 
+             for (int j1 = 0; j1 < i1; ++j1) {
+                 EnumDirection enumdirection = aenumdirection[j1];
+-                long k1 = SectionPosition.a(i, enumdirection);
++                long k1 = SectionPosition.getAdjacentFromSectionPos(secX, secY, secZ, enumdirection); // Paper - avoid extra unpacking
+ 
+                 if (!this.i.containsKey(k1) && this.g(k1)) {
+                     for (int l1 = 0; l1 < 16; ++l1) {
 diff --git a/src/main/java/net/minecraft/server/LightEngineStorageArray.java b/src/main/java/net/minecraft/server/LightEngineStorageArray.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/LightEngineStorageArray.java
@@ -1211,7 +1143,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 @@ -0,0 +0,0 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable {
  
      private void a(int i, int j, IntSupplier intsupplier, LightEngineThreaded.Update lightenginethreaded_update, Runnable runnable) {
-         this.e.a(ChunkTaskQueueSorter.a(() -> { // Paper - decompile error
+         this.e.a(ChunkTaskQueueSorter.a(() -> {
 -            this.c.add(Pair.of(lightenginethreaded_update, runnable));
 -            if (this.c.size() >= this.f) {
 +            // Paper start
@@ -1347,7 +1279,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            chunkMap.world.getChunkProvider().getLightEngine().changePriority(location.pair(), getCurrentPriority(), priority);
          }
          if (getCurrentPriority() != priority) {
-             this.w.a(this.location, this::getCurrentPriority, priority, this::setPriority); // use preferred priority
+             this.v.a(this.location, this::getCurrentPriority, priority, this::setPriority); // use preferred priority
 diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
@@ -1378,7 +1310,7 @@ diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/WorldServer.java
 +++ b/src/main/java/net/minecraft/server/WorldServer.java
-@@ -0,0 +0,0 @@ public class WorldServer extends World {
+@@ -0,0 +0,0 @@ public class WorldServer extends World implements GeneratorAccessSeed {
              }
              gameprofilerfiller.exit();
              timings.chunkTicksBlocks.stopTiming(); // Paper
diff --git a/Spigot-Server-Patches/Optimize-NibbleArray-to-use-pooled-buffers.patch b/Spigot-Server-Patches/Optimize-NibbleArray-to-use-pooled-buffers.patch
index 807f67ee00..b3b2dca4eb 100644
--- a/Spigot-Server-Patches/Optimize-NibbleArray-to-use-pooled-buffers.patch
+++ b/Spigot-Server-Patches/Optimize-NibbleArray-to-use-pooled-buffers.patch
@@ -41,10 +41,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      protected void a(LightEngineLayer<?, ?> lightenginelayer, long i) {
 @@ -0,0 +0,0 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e
  
-     protected void a(long i, @Nullable NibbleArray nibblearray) {
+     protected void a(long i, @Nullable NibbleArray nibblearray, boolean flag) {
          if (nibblearray != null) {
 -            this.i.put(i, nibblearray);
 +            NibbleArray remove = this.i.put(i, nibblearray); if (remove != null && remove.cleaner != null) remove.cleaner.run(); // Paper - clean up when removed
+             if (!flag) {
+                 this.n.add(i);
+             }
          } else {
 -            this.i.remove(i);
 +            NibbleArray remove = this.i.remove(i); if (remove != null && remove.cleaner != null) remove.cleaner.run(); // Paper - clean up when removed
@@ -248,8 +251,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/net/minecraft/server/PacketPlayOutLightUpdate.java
 +++ b/src/main/java/net/minecraft/server/PacketPlayOutLightUpdate.java
 @@ -0,0 +0,0 @@ public class PacketPlayOutLightUpdate implements Packet<PacketListenerPlayOut> {
-     private List<byte[]> g;
      private List<byte[]> h;
+     private boolean i;
  
 +    // Paper start
 +    java.lang.Runnable cleaner1;
@@ -282,9 +285,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    // Paper end
      public PacketPlayOutLightUpdate() {}
  
-     public PacketPlayOutLightUpdate(ChunkCoordIntPair chunkcoordintpair, LightEngine lightengine) {
+     public PacketPlayOutLightUpdate(ChunkCoordIntPair chunkcoordintpair, LightEngine lightengine, boolean flag) {
          this.a = chunkcoordintpair.x;
          this.b = chunkcoordintpair.z;
+         this.i = flag;
 -        this.g = Lists.newArrayList();
 -        this.h = Lists.newArrayList();
 +        this.g = Lists.newArrayList();cleaner1 = MCUtil.registerListCleaner(this, this.g, NibbleArray::releaseBytes); // Paper
@@ -311,7 +315,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              }
          }
 @@ -0,0 +0,0 @@ public class PacketPlayOutLightUpdate implements Packet<PacketListenerPlayOut> {
-         this.b = chunkcoordintpair.z;
+         this.i = flag;
          this.c = i;
          this.d = j;
 -        this.g = Lists.newArrayList();
diff --git a/Spigot-Server-Patches/Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch b/Spigot-Server-Patches/Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch
deleted file mode 100644
index a4f5fc72c8..0000000000
--- a/Spigot-Server-Patches/Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch
+++ /dev/null
@@ -1,162 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Aikar <aikar@aikar.co>
-Date: Mon, 11 May 2020 04:18:54 -0400
-Subject: [PATCH] Optimize Pathfinder - Remove Streams / Optimized collections
-
-I utilized the IDE to convert streams to non streams code, so shouldn't
-be any risk of behavior change. Only did minor optimization of the
-generated code set to remove unnecessary things.
-
-I expect us to just drop this patch on next major update and re-apply
-it with the IDE again and re-apply the collections optimization.
-
-Optimize collection by creating a list instead of a set of the key and value.
-
-This lets us get faster foreach iteration, as well as avoids map lookups on
-the values when needed.
-
-diff --git a/src/main/java/net/minecraft/server/Pathfinder.java b/src/main/java/net/minecraft/server/Pathfinder.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/Pathfinder.java
-+++ b/src/main/java/net/minecraft/server/Pathfinder.java
-@@ -0,0 +0,0 @@ public class Pathfinder {
-         this.a.a();
-         this.e.a(chunkcache, entityinsentient);
-         PathPoint pathpoint = this.e.b();
--        Map<PathDestination, BlockPosition> map = (Map) set.stream().collect(Collectors.toMap((blockposition) -> {
--            return this.e.a((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ());
--        }, Function.identity()));
-+        // Paper start - remove streams - and optimize collection
-+        List<Map.Entry<PathDestination, BlockPosition>> map = new java.util.ArrayList<>();
-+        for (BlockPosition blockposition : set) {
-+            // cast is important
-+            //noinspection RedundantCast
-+            PathDestination path = this.e.a((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ());
-+            map.add(new java.util.AbstractMap.SimpleEntry<>(path, blockposition));
-+        }
-+        // Paper end
-         PathEntity pathentity = this.a(pathpoint, map, f, i, f1);
- 
-         this.e.a();
-@@ -0,0 +0,0 @@ public class Pathfinder {
-     }
- 
-     @Nullable
--    private PathEntity a(PathPoint pathpoint, Map<PathDestination, BlockPosition> map, float f, int i, float f1) {
--        Set<PathDestination> set = map.keySet();
-+    private PathEntity a(PathPoint pathpoint, java.util.List<java.util.Map.Entry<PathDestination, BlockPosition>> list, float f, int i, float f1) { // Paper - list instead of set
-+        //Set<PathDestination> set = map.keySet(); // Paper
- 
-         pathpoint.e = 0.0F;
--        pathpoint.f = this.a(pathpoint, set);
-+        pathpoint.f = this.a(pathpoint, list); // Paper - list instead of map
-         pathpoint.g = pathpoint.f;
-         this.a.a();
-         this.b.clear();
-@@ -0,0 +0,0 @@ public class Pathfinder {
-             PathPoint pathpoint1 = this.a.c();
- 
-             pathpoint1.i = true;
--            set.stream().filter((pathdestination) -> {
--                return pathpoint1.c((PathPoint) pathdestination) <= (float) i;
--            }).forEach(PathDestination::e);
--            if (set.stream().anyMatch(PathDestination::f)) {
-+            // Paper start - remove streams
-+            for (int i1 = 0, listSize = list.size(); i1 < listSize; i1++) {
-+                PathDestination pathdestination = list.get(i1).getKey();
-+                if (pathpoint1.c(pathdestination) <= (float) i) {
-+                    pathdestination.e();
-+                }
-+            }
-+            boolean result = false;
-+            for (int i1 = 0, listSize = list.size(); i1 < listSize; i1++) {
-+                PathDestination pathdestination = list.get(i1).getKey();
-+                if (pathdestination.f()) {
-+                    result = true;
-+                    break;
-+                }
-+            }
-+            if (result) {
-+                // Paper end
-                 break;
-             }
- 
-@@ -0,0 +0,0 @@ public class Pathfinder {
-                     if (pathpoint2.j < f && (!pathpoint2.c() || f3 < pathpoint2.e)) {
-                         pathpoint2.h = pathpoint1;
-                         pathpoint2.e = f3;
--                        pathpoint2.f = this.a(pathpoint2, set) * 1.5F;
-+                        pathpoint2.f = this.a(pathpoint2, list) * 1.5F; // Paper - use list instead of map
-                         if (pathpoint2.c()) {
-                             this.a.a(pathpoint2, pathpoint2.e + pathpoint2.f);
-                         } else {
-@@ -0,0 +0,0 @@ public class Pathfinder {
-             }
-         }
- 
--        Stream stream;
- 
--        if (set.stream().anyMatch(PathDestination::f)) {
--            stream = set.stream().filter(PathDestination::f).map((pathdestination) -> {
--                return this.a(pathdestination.d(), (BlockPosition) map.get(pathdestination), true);
--            }).sorted(Comparator.comparingInt(PathEntity::e));
--        } else {
--            stream = set.stream().map((pathdestination) -> {
--                return this.a(pathdestination.d(), (BlockPosition) map.get(pathdestination), false);
--            }).sorted(Comparator.comparingDouble(PathEntity::l).thenComparingInt(PathEntity::e));
-+        // Paper start - remove streams
-+        boolean result = false;
-+        for (int i1 = 0, listSize = list.size(); i1 < listSize; i1++) {
-+            PathDestination pathDestination = list.get(i1).getKey(); // Paper
-+            if (pathDestination.f()) {
-+                result = true;
-+                break;
-+            }
-         }
--
--        Optional<PathEntity> optional = stream.findFirst();
--
--        if (!optional.isPresent()) {
--            return null;
-+        List<PathEntity> candidates = new java.util.ArrayList<>();
-+        if (result) {
-+            for (int i1 = 0, listSize = list.size(); i1 < listSize; i1++) {
-+                Map.Entry<PathDestination, BlockPosition> entry = list.get(i1);
-+                PathDestination pathdestination = entry.getKey();
-+                if (pathdestination.f()) {
-+                    PathEntity pathEntity = this.a(pathdestination.d(), entry.getValue(), true);
-+                    candidates.add(pathEntity);
-+                }
-+            }
-+            if (candidates.isEmpty()) return null;
-+            candidates.sort(Comparator.comparingInt(PathEntity::e));
-         } else {
--            PathEntity pathentity = (PathEntity) optional.get();
--
--            return pathentity;
-+            for (int i1 = 0, listSize = list.size(); i1 < listSize; i1++) {
-+                Map.Entry<PathDestination, BlockPosition> entry = list.get(i1);
-+                PathDestination pathdestination = entry.getKey();
-+                PathEntity pathEntity = this.a(pathdestination.d(), entry.getValue(), false);
-+                candidates.add(pathEntity);
-+            }
-+            if (candidates.isEmpty()) return null;
-+            candidates.sort(Comparator.comparingDouble(PathEntity::l).thenComparingInt(PathEntity::e));
-         }
-+        return candidates.get(0);
-+        // Paper end
-     }
- 
--    private float a(PathPoint pathpoint, Set<PathDestination> set) {
-+    private float a(PathPoint pathpoint, java.util.List<java.util.Map.Entry<PathDestination, BlockPosition>> list) {
-         float f = Float.MAX_VALUE;
- 
-         float f1;
- 
--        for (Iterator iterator = set.iterator(); iterator.hasNext(); f = Math.min(f1, f)) {
--            PathDestination pathdestination = (PathDestination) iterator.next();
-+        for (int i = 0, listSize = list.size(); i < listSize; f = Math.min(f1, f), i++) { // Paper
-+            PathDestination pathdestination = list.get(i).getKey(); // Paper
- 
-             f1 = pathpoint.a(pathdestination);
-             pathdestination.a(f1, pathpoint);
diff --git a/Spigot-Server-Patches/Optimize-Villagers.patch b/Spigot-Server-Patches/Optimize-Villagers.patch
deleted file mode 100644
index 28c23761a5..0000000000
--- a/Spigot-Server-Patches/Optimize-Villagers.patch
+++ /dev/null
@@ -1,273 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Aikar <aikar@aikar.co>
-Date: Tue, 26 May 2020 21:32:05 -0400
-Subject: [PATCH] Optimize Villagers
-
-This change reimplements the entire BehaviorFindPosition method to
-get rid of all of the streams, and implement the logic in a more sane way.
-
-We keep vanilla behavior 100% the same with this change, just wrote more
-optimal, as we can abort iterating POI's as soon as we find a match....
-
-One slight change is that Minecraft adds a random delay before a POI is
-attempted again. I've increased the amount of that delay based on the distance
-to said POI, so farther POI's will not be attempted as often.
-
-Additionally, we spiral out, so we favor local POI's before we ever favor farther POI's.
-
-We also try to pathfind 1 POI at a time instead of collecting multiple POI's then tossing them
-all to the pathfinder, so that once we get a match we can return before even looking at other
-POI's.
-
-This benefits us in that ideally, a villager will constantly find the near POI's and
-not even try to pathfind to the farther POI. Trying to pathfind to distant POI's is
-what causes significant lag.
-
-Other improvements here is to stop spamming the POI manager with empty nullables.
-Vanilla used them to represent if they needed to load POI data off disk or not.
-
-Well, we load POI data async on chunk load, so we have it, and we surely do not ever
-want to load POI data sync either for unloaded chunks!
-
-So this massively reduces object count in the POI hashmaps, resulting in less hash collions,
-and also less memory use.
-
-Additionally, unemployed villagers were using significant time due to major ineffeciency in
-the code rebuilding data that is static every single invocation for every POI type...
-
-So we cache that and only rebuild it if professions change, which should be never unless
-a plugin manipulates and adds custom professions, which it will handle by rebuilding.
-
-diff --git a/src/main/java/net/minecraft/server/BehaviorFindPosition.java b/src/main/java/net/minecraft/server/BehaviorFindPosition.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/BehaviorFindPosition.java
-+++ b/src/main/java/net/minecraft/server/BehaviorFindPosition.java
-@@ -0,0 +0,0 @@ public class BehaviorFindPosition extends Behavior<EntityCreature> {
- 
-     protected void a(WorldServer worldserver, EntityCreature entitycreature, long i) {
-         this.f = 0;
--        this.d = worldserver.getTime() + (long) worldserver.getRandom().nextInt(20);
-+        this.d = worldserver.getTime() + (long) java.util.concurrent.ThreadLocalRandom.current().nextInt(20); // Paper
-         VillagePlace villageplace = worldserver.B();
-+
-+        // Paper start - replace implementation completely
-+        BlockPosition blockposition2 = new BlockPosition(entitycreature);
-+        int dist = 48;
-+        int requiredDist = dist * dist;
-+        int cdist = Math.floorDiv(dist, 16);
-+        Predicate<VillagePlaceType> predicate = this.a.c();
-+        int maxPoiAttempts = 4;
-+        int poiAttempts = 0;
-+        OUT:
-+        for (ChunkCoordIntPair chunkcoordintpair : MCUtil.getSpiralOutChunks(blockposition2, cdist)) {
-+            for (int i1 = 0; i1 < 16; i1++) {
-+                java.util.Optional<VillagePlaceSection> section = villageplace.getSection(SectionPosition.a(chunkcoordintpair, i1).v());
-+                if (section == null || !section.isPresent()) continue;
-+                for (java.util.Map.Entry<VillagePlaceType, java.util.Set<VillagePlaceRecord>> e : section.get().getRecords().entrySet()) {
-+                    if (!predicate.test(e.getKey())) continue;
-+                    for (VillagePlaceRecord record : e.getValue()) {
-+                        if (!record.hasVacancy()) continue;
-+
-+                        BlockPosition pos = record.getPosition();
-+                        long key = pos.asLong();
-+                        if (this.e.containsKey(key)) {
-+                            continue;
-+                        }
-+                        double poiDist = pos.distanceSquared(blockposition2);
-+                        if (poiDist <= (double) requiredDist) {
-+                            this.e.put(key, (long) (this.d + Math.sqrt(poiDist) * 4)); // use dist instead of 40 to blacklist longer if farther distance
-+                            ++poiAttempts;
-+                            PathEntity pathentity = entitycreature.getNavigation().a(com.google.common.collect.ImmutableSet.of(pos), 8, false, this.a.d());
-+
-+                            if (pathentity != null && pathentity.h()) {
-+                                record.decreaseVacancy();
-+                                GlobalPos globalPos = GlobalPos.create(worldserver.getWorldProvider().getDimensionManager(), pos);
-+                                entitycreature.getBehaviorController().setMemory(this.b, globalPos);
-+                                break OUT;
-+                            }
-+                            if (poiAttempts >= maxPoiAttempts) {
-+                                break OUT;
-+                            }
-+                        }
-+                    }
-+                }
-+            }
-+        }
-+        // Clean up - vanilla does this only when it runs out, but that would push it to try farther POI's...
-+        this.e.long2LongEntrySet().removeIf((entry) -> entry.getLongValue() < this.d);
-+        /*
-         Predicate<BlockPosition> predicate = (blockposition) -> {
-             long j = blockposition.asLong();
- 
-@@ -0,0 +0,0 @@ public class BehaviorFindPosition extends Behavior<EntityCreature> {
-                 return entry.getLongValue() < this.d;
-             });
-         }
--
-+        */ // Paper end
-     }
- }
-diff --git a/src/main/java/net/minecraft/server/RegionFileSection.java b/src/main/java/net/minecraft/server/RegionFileSection.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/RegionFileSection.java
-+++ b/src/main/java/net/minecraft/server/RegionFileSection.java
-@@ -0,0 +0,0 @@ public class RegionFileSection<R extends MinecraftSerializable> extends RegionFi
- 
-     @Nullable
-     protected Optional<R> c(long i) {
--        return (Optional) this.c.get(i);
-+        return this.c.getOrDefault(i, Optional.empty()); // Paper
-     }
- 
-+    protected final Optional<R> getSection(long i) { return d(i); } // Paper - OBFHELPER
-     protected Optional<R> d(long i) {
--        SectionPosition sectionposition = SectionPosition.a(i);
--
--        if (this.b(sectionposition)) {
--            return Optional.empty();
--        } else {
--            Optional<R> optional = this.c(i);
--
--            if (optional != null) {
--                return optional;
--            } else {
--                this.b(sectionposition.u());
--                optional = this.c(i);
--                if (optional == null) {
--                    throw (IllegalStateException) SystemUtils.c(new IllegalStateException());
--                } else {
--                    return optional;
--                }
--            }
--        }
-+        // Paper start - replace method - never load POI data sync, we load this in chunk load already, reduce ops
-+        // If it's an unloaded chunk, well too bad.
-+        return this.c(i);
-+        // Paper end
-     }
- 
-     protected boolean b(SectionPosition sectionposition) {
-@@ -0,0 +0,0 @@ public class RegionFileSection<R extends MinecraftSerializable> extends RegionFi
-     private <T> void a(ChunkCoordIntPair chunkcoordintpair, DynamicOps<T> dynamicops, @Nullable T t0) {
-         if (t0 == null) {
-             for (int i = 0; i < 16; ++i) {
--                this.c.put(SectionPosition.a(chunkcoordintpair, i).v(), Optional.empty());
-+                //this.c.put(SectionPosition.a(chunkcoordintpair, i).v(), Optional.empty()); // Paper - NO!!!
-             }
-         } else {
-             Dynamic<T> dynamic = new Dynamic(dynamicops, t0);
-@@ -0,0 +0,0 @@ public class RegionFileSection<R extends MinecraftSerializable> extends RegionFi
-                     }, dynamic2);
-                 });
- 
--                this.c.put(i1, optional);
-+                if (optional.isPresent()) this.c.put(i1, optional); // Paper - NO!!!
-                 optional.ifPresent((minecraftserializable) -> {
-                     this.b(i1);
-                     if (flag) {
-@@ -0,0 +0,0 @@ public class RegionFileSection<R extends MinecraftSerializable> extends RegionFi
-         if (optional != null && optional.isPresent()) {
-             this.d.add(i);
-         } else {
--            RegionFileSection.LOGGER.warn("No data for position: {}", SectionPosition.a(i));
-+            //RegionFileSection.LOGGER.warn("No data for position: {}", SectionPosition.a(i)); // Paper - hush
-         }
-     }
- 
-diff --git a/src/main/java/net/minecraft/server/VillagePlaceRecord.java b/src/main/java/net/minecraft/server/VillagePlaceRecord.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/VillagePlaceRecord.java
-+++ b/src/main/java/net/minecraft/server/VillagePlaceRecord.java
-@@ -0,0 +0,0 @@ public class VillagePlaceRecord implements MinecraftSerializable {
-         return dynamicops.createMap(ImmutableMap.of(dynamicops.createString("pos"), this.a.a(dynamicops), dynamicops.createString("type"), dynamicops.createString(IRegistry.POINT_OF_INTEREST_TYPE.getKey(this.b).toString()), dynamicops.createString("free_tickets"), dynamicops.createInt(this.c)));
-     }
- 
-+    protected final boolean decreaseVacancy() { return b(); } // Paper - OBFHELPER
-     protected boolean b() {
-         if (this.c <= 0) {
-             return false;
-@@ -0,0 +0,0 @@ public class VillagePlaceRecord implements MinecraftSerializable {
-         }
-     }
- 
-+    protected final boolean increaseVacancy() { return c(); } // Paper - OBFHELPER
-     protected boolean c() {
-         if (this.c >= this.b.b()) {
-             return false;
-@@ -0,0 +0,0 @@ public class VillagePlaceRecord implements MinecraftSerializable {
-         }
-     }
- 
-+    public final boolean hasVacancy() { return d(); } // Paper - OBFHELPER
-     public boolean d() {
-         return this.c > 0;
-     }
- 
-+    public final boolean isOccupied() { return e(); } // Paper - OBFHELPER
-     public boolean e() {
-         return this.c != this.b.b();
-     }
- 
-+    public final BlockPosition getPosition() { return f(); } // Paper
-     public BlockPosition f() {
-         return this.a;
-     }
-diff --git a/src/main/java/net/minecraft/server/VillagePlaceSection.java b/src/main/java/net/minecraft/server/VillagePlaceSection.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/VillagePlaceSection.java
-+++ b/src/main/java/net/minecraft/server/VillagePlaceSection.java
-@@ -0,0 +0,0 @@ public class VillagePlaceSection implements MinecraftSerializable {
- 
-     private static final Logger LOGGER = LogManager.getLogger();
-     private final Short2ObjectMap<VillagePlaceRecord> b = new Short2ObjectOpenHashMap();
--    private final Map<VillagePlaceType, Set<VillagePlaceRecord>> c = Maps.newHashMap();
-+    private final Map<VillagePlaceType, Set<VillagePlaceRecord>> c = Maps.newHashMap(); public final Map<VillagePlaceType, Set<VillagePlaceRecord>> getRecords() { return c; } // Paper - OBFHELPER
-     private final Runnable d;
-     private boolean e;
- 
-diff --git a/src/main/java/net/minecraft/server/VillagePlaceType.java b/src/main/java/net/minecraft/server/VillagePlaceType.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/VillagePlaceType.java
-+++ b/src/main/java/net/minecraft/server/VillagePlaceType.java
-@@ -0,0 +0,0 @@ import java.util.stream.Stream;
- 
- public class VillagePlaceType {
- 
-+    static Set<VillagePlaceType> professionCache; // Paper
-     private static final Predicate<VillagePlaceType> v = (villageplacetype) -> {
--        return ((Set) IRegistry.VILLAGER_PROFESSION.d().map(VillagerProfession::b).collect(Collectors.toSet())).contains(villageplacetype);
-+        // Paper start
-+        if (professionCache == null) {
-+            professionCache = IRegistry.VILLAGER_PROFESSION.d().map(VillagerProfession::b).collect(Collectors.toSet());
-+        }
-+        return professionCache.contains(villageplacetype);
-+        // Paper end
-     };
-     public static final Predicate<VillagePlaceType> a = (villageplacetype) -> {
-         return true;
-@@ -0,0 +0,0 @@ public class VillagePlaceType {
-     }
- 
-     private static VillagePlaceType a(String s, Set<IBlockData> set, int i, int j) {
--        return a((VillagePlaceType) IRegistry.POINT_OF_INTEREST_TYPE.a(new MinecraftKey(s), (Object) (new VillagePlaceType(s, set, i, j))));
-+        return a((VillagePlaceType) IRegistry.POINT_OF_INTEREST_TYPE.a(new MinecraftKey(s), (new VillagePlaceType(s, set, i, j)))); // Paper - decompile error
-     }
- 
-     private static VillagePlaceType a(String s, Set<IBlockData> set, int i, Predicate<VillagePlaceType> predicate, int j) {
--        return a((VillagePlaceType) IRegistry.POINT_OF_INTEREST_TYPE.a(new MinecraftKey(s), (Object) (new VillagePlaceType(s, set, i, predicate, j))));
-+        return a((VillagePlaceType) IRegistry.POINT_OF_INTEREST_TYPE.a(new MinecraftKey(s), (new VillagePlaceType(s, set, i, predicate, j)))); // Paper - decompile error
-     }
- 
-     private static VillagePlaceType a(VillagePlaceType villageplacetype) {
-diff --git a/src/main/java/net/minecraft/server/VillagerProfession.java b/src/main/java/net/minecraft/server/VillagerProfession.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/VillagerProfession.java
-+++ b/src/main/java/net/minecraft/server/VillagerProfession.java
-@@ -0,0 +0,0 @@ public class VillagerProfession {
-     }
- 
-     static VillagerProfession a(String s, VillagePlaceType villageplacetype, ImmutableSet<Item> immutableset, ImmutableSet<Block> immutableset1, @Nullable SoundEffect soundeffect) {
-+        VillagePlaceType.professionCache = null; // Paper
-         return (VillagerProfession) IRegistry.a((IRegistry) IRegistry.VILLAGER_PROFESSION, new MinecraftKey(s), (Object) (new VillagerProfession(s, villageplacetype, immutableset, immutableset1, soundeffect)));
-     }
- }
diff --git a/Spigot-Server-Patches/Optimize-WorldBorder-collision-checks-and-air.patch b/Spigot-Server-Patches/Optimize-WorldBorder-collision-checks-and-air.patch
deleted file mode 100644
index a56e8b94c7..0000000000
--- a/Spigot-Server-Patches/Optimize-WorldBorder-collision-checks-and-air.patch
+++ /dev/null
@@ -1,61 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <Spottedleaf@users.noreply.github.com>
-Date: Sun, 10 May 2020 22:49:05 -0400
-Subject: [PATCH] Optimize WorldBorder collision checks and air
-
-
-diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/Entity.java
-+++ b/src/main/java/net/minecraft/server/Entity.java
-@@ -0,0 +0,0 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
-         AxisAlignedBB axisalignedbb = this.getBoundingBox();
-         VoxelShapeCollision voxelshapecollision = VoxelShapeCollision.a(this);
-         VoxelShape voxelshape = this.world.getWorldBorder().a();
--        Stream<VoxelShape> stream = VoxelShapes.c(voxelshape, VoxelShapes.a(axisalignedbb.shrink(1.0E-7D)), OperatorBoolean.AND) ? Stream.empty() : Stream.of(voxelshape);
-+        Stream<VoxelShape> stream = !this.world.getWorldBorder().isInBounds(axisalignedbb) ? Stream.empty() : Stream.of(this.world.getWorldBorder().a()); // Paper
-         Stream<VoxelShape> stream1 = this.world.b(this, axisalignedbb.a(vec3d), (Set) ImmutableSet.of());
-         StreamAccumulator<VoxelShape> streamaccumulator = new StreamAccumulator<>(Stream.concat(stream1, stream));
-         Vec3D vec3d1 = vec3d.g() == 0.0D ? vec3d : a(this, vec3d, axisalignedbb, this.world, voxelshapecollision, streamaccumulator);
-diff --git a/src/main/java/net/minecraft/server/ICollisionAccess.java b/src/main/java/net/minecraft/server/ICollisionAccess.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/ICollisionAccess.java
-+++ b/src/main/java/net/minecraft/server/ICollisionAccess.java
-@@ -0,0 +0,0 @@ public interface ICollisionAccess extends IBlockAccess {
-             if (true) { //public boolean tryAdvance(Consumer<? super VoxelShape> consumer) {*/ // Paper
-                 if (entity != null) {
-                     // Paper end
--                    VoxelShape voxelshape1 = ICollisionAccess.this.getWorldBorder().a();
--                    boolean flag = VoxelShapes.c(voxelshape1, VoxelShapes.a(entity.getBoundingBox().shrink(1.0E-7D)), OperatorBoolean.AND);
--                    boolean flag1 = VoxelShapes.c(voxelshape1, VoxelShapes.a(entity.getBoundingBox().g(1.0E-7D)), OperatorBoolean.AND);
-+                    //VoxelShape voxelshape1 = ICollisionAccess.this.getWorldBorder().a(); // Paper - only make if collides
-+                    boolean flag = !ICollisionAccess.this.getWorldBorder().isInBounds(entity.getBoundingBox().shrink(1.0E-7D)); // Paper
-+                    boolean flag1 = !ICollisionAccess.this.getWorldBorder().isInBounds(entity.getBoundingBox().g(1.0E-7D)); // Paper
- 
-                     if (!flag && flag1) {
--                        collisions.add(voxelshape1);// Paper
-+                        collisions.add(ICollisionAccess.this.getWorldBorder().a());// Paper
-                         if (returnFast) return collisions;
-                     }
-                 }
-@@ -0,0 +0,0 @@ public interface ICollisionAccess extends IBlockAccess {
-                             //IBlockData iblockdata = iblockaccess.getType(blockposition_mutableblockposition); // moved up
-                             // Paper end
- 
--                            if ((j2 != 1 || iblockdata.f()) && (j2 != 2 || iblockdata.getBlock() == Blocks.MOVING_PISTON)) {
-+                            if (!iblockdata.isAir() && (j2 != 1 || iblockdata.f()) && (j2 != 2 || iblockdata.getBlock() == Blocks.MOVING_PISTON)) { // Paper - fast track air
-                                 VoxelShape voxelshape2 = iblockdata.b((IBlockAccess) ICollisionAccess.this, blockposition_mutableblockposition, voxelshapecollision);
- 
-                                 // Paper start - Lithium Collision Optimizations
-diff --git a/src/main/java/net/minecraft/server/WorldBorder.java b/src/main/java/net/minecraft/server/WorldBorder.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/WorldBorder.java
-+++ b/src/main/java/net/minecraft/server/WorldBorder.java
-@@ -0,0 +0,0 @@ public class WorldBorder {
-         return (double) chunkcoordintpair.f() > this.c() && (double) chunkcoordintpair.d() < this.e() && (double) chunkcoordintpair.g() > this.d() && (double) chunkcoordintpair.e() < this.f();
-     }
- 
-+    public final boolean isInBounds(AxisAlignedBB aabb) { return this.a(aabb); } // Paper - OBFHELPER
-     public boolean a(AxisAlignedBB axisalignedbb) {
-         return axisalignedbb.maxX > this.c() && axisalignedbb.minX < this.e() && axisalignedbb.maxZ > this.d() && axisalignedbb.minZ < this.f();
-     }
diff --git a/Spigot-Server-Patches/Optimize-sending-packets-to-nearby-locations-sounds-.patch b/Spigot-Server-Patches/Optimize-sending-packets-to-nearby-locations-sounds-.patch
index 7ea3613afe..134174dfab 100644
--- a/Spigot-Server-Patches/Optimize-sending-packets-to-nearby-locations-sounds-.patch
+++ b/Spigot-Server-Patches/Optimize-sending-packets-to-nearby-locations-sounds-.patch
@@ -15,59 +15,49 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/net/minecraft/server/PlayerList.java
 +++ b/src/main/java/net/minecraft/server/PlayerList.java
 @@ -0,0 +0,0 @@ public abstract class PlayerList {
-             world = (WorldServer) entityhuman.world;
-         }
+     }
  
--        List<? extends EntityHuman> players1 = world == null ? players : world.players;
--        for (int j = 0; j < players1.size(); ++j) {
--            EntityHuman entity = players1.get(j);
--            if (!(entity instanceof EntityPlayer)) continue;
--            EntityPlayer entityplayer = (EntityPlayer) entity;
+     public void sendPacketNearby(@Nullable EntityHuman entityhuman, double d0, double d1, double d2, double d3, ResourceKey<World> resourcekey, Packet<?> packet) {
+-        for (int i = 0; i < this.players.size(); ++i) {
+-            EntityPlayer entityplayer = (EntityPlayer) this.players.get(i);
++        WorldServer world = null;
++        if (entityhuman != null && entityhuman.world instanceof WorldServer) {
++            world = (WorldServer) entityhuman.world;
++        }
+ 
+-            // CraftBukkit start - Test if player receiving packet can see the source of the packet
+-            if (entityhuman != null && entityhuman instanceof EntityPlayer && !entityplayer.getBukkitEntity().canSee(((EntityPlayer) entityhuman).getBukkitEntity())) {
+-               continue;
 +        // Paper start
-+        if ((world == null || world.chunkProvider == null) && dimensionmanager != null) {
-+            world = dimensionmanager.world;
-+        }
 +        if (world == null) {
-+            LOGGER.error("Sending packet to invalid world" + entityhuman + " " + dimensionmanager + " - " + packet.getClass().getName(), new Throwable());
-+            return; // ??? shouldn't happen...
++            world = server.getWorldServer(resourcekey);
 +        }
-+        PlayerChunkMap chunkMap = world.chunkMap;
++        PlayerChunkMap chunkMap = world != null ? world.getChunkProvider().playerChunkMap : null;
 +        Object[] backingSet;
 +        if (chunkMap == null) {
 +            // Really shouldn't happen...
-+            backingSet = world.players.toArray();
++            backingSet = world != null ? world.players.toArray() : players.toArray();
 +        } else {
 +            com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> nearbyPlayers = chunkMap.playerViewDistanceBroadcastMap.getObjectsInRange(MCUtil.fastFloor(d0) >> 4, MCUtil.fastFloor(d2) >> 4);
 +            if (nearbyPlayers == null) {
 +                return;
-+            }
+             }
 +            backingSet = nearbyPlayers.getBackingSet();
 +        }
 +
 +        for (Object object : backingSet) {
 +            if (!(object instanceof EntityPlayer)) continue;
 +            EntityPlayer entityplayer = (EntityPlayer) object;
-             // Paper end
++            // Paper end
++
++            // CraftBukkit start - Test if player receiving packet can see the source of the packet
++            //if (entityhuman != null && entityhuman instanceof EntityPlayer && !entityplayer.getBukkitEntity().canSee(((EntityPlayer) entityhuman).getBukkitEntity())) { // Paper
++               //continue; // Paper
++            //} // Paper
+             // CraftBukkit end
  
-             // CraftBukkit start - Test if player receiving packet can see the source of the packet
-diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/WorldServer.java
-+++ b/src/main/java/net/minecraft/server/WorldServer.java
-@@ -0,0 +0,0 @@ public class WorldServer extends World {
-         }
-     }
-     // Paper end
-+    public final PlayerChunkMap chunkMap; // Paper
-     private final MinecraftServer server;
-     private final WorldNBTStorage dataManager;
-     public boolean savingDisabled;
-@@ -0,0 +0,0 @@ public class WorldServer extends World {
-         }, gameprofilerfiller, false, gen, env);
-         this.pvpMode = minecraftserver.getPVP();
-         worlddata.world = this;
-+        if (chunkProvider == null) { chunkMap = null; new Throwable("World created without a ChunkProvider!").printStackTrace(); } // Paper - figure out if something weird happened here
-+        else chunkMap = ((ChunkProviderServer) chunkProvider).playerChunkMap;
-         // CraftBukkit end
-         if (com.destroystokyo.paper.PaperConfig.useOptimizedTickList) {
-             this.nextTickListBlock = new com.destroystokyo.paper.server.ticklist.PaperTickList<>(this, (block) -> { // Paper - optimise TickListServer
+-            if (entityplayer != entityhuman && entityplayer.world.getDimensionKey() == resourcekey) {
++            if (entityplayer != entityhuman && entityplayer.world.getDimensionKey() == resourcekey && entityhuman instanceof EntityPlayer && !entityplayer.getBukkitEntity().canSee(((EntityPlayer) entityhuman).getBukkitEntity())) { // Paper
+                 double d4 = d0 - entityplayer.locX();
+                 double d5 = d1 - entityplayer.locY();
+                 double d6 = d2 - entityplayer.locZ();
diff --git a/Spigot-Server-Patches/POM-Changes.patch b/Spigot-Server-Patches/POM-Changes.patch
index ee23f17e5b..6be7b50705 100644
--- a/Spigot-Server-Patches/POM-Changes.patch
+++ b/Spigot-Server-Patches/POM-Changes.patch
@@ -201,7 +201,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                    Date buildDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").parse(Main.class.getPackage().getImplementationVendor()); // Paper
  
                      Calendar deadline = Calendar.getInstance();
-                     deadline.add(Calendar.DAY_OF_YEAR, -3);
+                     deadline.add(Calendar.DAY_OF_YEAR, -1);
 diff --git a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java
diff --git a/Spigot-Server-Patches/Potential-bed-API.patch b/Spigot-Server-Patches/Potential-bed-API.patch
index a7c6bebe2f..e99bfb9b92 100644
--- a/Spigot-Server-Patches/Potential-bed-API.patch
+++ b/Spigot-Server-Patches/Potential-bed-API.patch
@@ -12,20 +12,25 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
 +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
 @@ -0,0 +0,0 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
-         return null;
+         return getHandle().sleepTicks;
      }
  
 +    // Paper start - Potential bed api
 +    @Override
 +    public Location getPotentialBedLocation() {
-+        BlockPosition bed = getHandle().getBed();
++        EntityPlayer handle = (EntityPlayer) getHandle();
++        BlockPosition bed = handle.getSpawn();
 +        if (bed == null) {
 +            return null;
 +        }
-+        return new Location(getServer().getWorld(getHandle().spawnWorld), bed.getX(), bed.getY(), bed.getZ());
++
++        net.minecraft.server.WorldServer worldServer = handle.server.getWorldServer(handle.getSpawnDimension());
++        if (worldServer == null) {
++            return null;
++        }
++        return new Location(worldServer.getWorld(), bed.getX(), bed.getY(), bed.getZ());
 +    }
 +    // Paper end
-+
      @Override
-     public void setBedSpawnLocation(Location location) {
-         setBedSpawnLocation(location, false);
+     public boolean sleep(Location location, boolean force) {
+         Preconditions.checkArgument(location != null, "Location cannot be null");
diff --git a/Spigot-Server-Patches/Prevent-opening-inventories-when-frozen.patch b/Spigot-Server-Patches/Prevent-opening-inventories-when-frozen.patch
index 53a56729a9..aaf34a5dd0 100644
--- a/Spigot-Server-Patches/Prevent-opening-inventories-when-frozen.patch
+++ b/Spigot-Server-Patches/Prevent-opening-inventories-when-frozen.patch
@@ -43,8 +43,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
          String title = container.getBukkitView().getTitle();
  
--        player.playerConnection.sendPacket(new PacketPlayOutOpenWindow(container.windowId, windowType, new ChatComponentText(title)));
-+        if (!player.isFrozen()) player.playerConnection.sendPacket(new PacketPlayOutOpenWindow(container.windowId, windowType, new ChatComponentText(title))); // Paper
+-        player.playerConnection.sendPacket(new PacketPlayOutOpenWindow(container.windowId, windowType, CraftChatMessage.fromString(title)[0]));
++        if (!player.isFrozen()) player.playerConnection.sendPacket(new PacketPlayOutOpenWindow(container.windowId, windowType, CraftChatMessage.fromString(title)[0])); // Paper
          getHandle().activeContainer = container;
          getHandle().activeContainer.addSlotListener(player);
      }
@@ -52,8 +52,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          // Now open the window
          Containers<?> windowType = CraftContainer.getNotchInventoryType(inventory.getTopInventory());
          String title = inventory.getTitle();
--        player.playerConnection.sendPacket(new PacketPlayOutOpenWindow(container.windowId, windowType, new ChatComponentText(title)));
-+        if (!player.isFrozen()) player.playerConnection.sendPacket(new PacketPlayOutOpenWindow(container.windowId, windowType, new ChatComponentText(title))); // Paper
+-        player.playerConnection.sendPacket(new PacketPlayOutOpenWindow(container.windowId, windowType, CraftChatMessage.fromString(title)[0]));
++        if (!player.isFrozen()) player.playerConnection.sendPacket(new PacketPlayOutOpenWindow(container.windowId, windowType, CraftChatMessage.fromString(title)[0])); // Paper
          player.activeContainer = container;
          player.activeContainer.addSlotListener(player);
      }
diff --git a/Spigot-Server-Patches/Prevent-position-desync-in-playerconnection-causing-.patch b/Spigot-Server-Patches/Prevent-position-desync-in-playerconnection-causing-.patch
index 11e7c2b77b..a503eadde2 100644
--- a/Spigot-Server-Patches/Prevent-position-desync-in-playerconnection-causing-.patch
+++ b/Spigot-Server-Patches/Prevent-position-desync-in-playerconnection-causing-.patch
@@ -26,6 +26,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                                return; // ... thanks Mojang for letting move calls teleport across dimensions.
 +                            }
 +                            // Paper end - prevent position desync
-                             this.player.onGround = packetplayinflying.b();
                              double d12 = d8;
  
+                             d7 = d4 - this.player.locX();
diff --git a/Spigot-Server-Patches/Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch b/Spigot-Server-Patches/Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch
index d5f31067f6..22866d1f23 100644
--- a/Spigot-Server-Patches/Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch
+++ b/Spigot-Server-Patches/Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch
@@ -31,9 +31,9 @@ diff --git a/src/main/java/net/minecraft/server/Block.java b/src/main/java/net/m
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/Block.java
 +++ b/src/main/java/net/minecraft/server/Block.java
-@@ -0,0 +0,0 @@ public class Block implements IMaterial {
-     protected final SoundEffectType stepSound;
-     protected final Material material;
+@@ -0,0 +0,0 @@ public class Block extends BlockBase implements IMaterial {
+     protected final BlockStateList<Block, IBlockData> blockStateList;
+     private IBlockData blockData;
      // Paper start
 +    public final boolean isDestroyable() {
 +        return com.destroystokyo.paper.PaperConfig.allowBlockPermanentBreakingExploits ||
@@ -46,7 +46,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      public co.aikar.timings.Timing timing;
      public co.aikar.timings.Timing getTiming() {
          if (timing == null) {
-@@ -0,0 +0,0 @@ public class Block implements IMaterial {
+diff --git a/src/main/java/net/minecraft/server/BlockBase.java b/src/main/java/net/minecraft/server/BlockBase.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/net/minecraft/server/BlockBase.java
++++ b/src/main/java/net/minecraft/server/BlockBase.java
+@@ -0,0 +0,0 @@ public abstract class BlockBase {
  
      @Deprecated
      public boolean a(IBlockData iblockdata, BlockActionContext blockactioncontext) {
@@ -55,15 +59,28 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      }
  
      @Deprecated
-@@ -0,0 +0,0 @@ public class Block implements IMaterial {
+@@ -0,0 +0,0 @@ public abstract class BlockBase {
+         public Block getBlock() {
+             return (Block) this.c;
+         }
+-
++        // Paper start
++        public final boolean isDestroyable() {
++            return getBlock().isDestroyable();
++        }
++        // Paper end
+         public Material getMaterial() {
+             return this.g;
+         }
+@@ -0,0 +0,0 @@ public abstract class BlockBase {
+         }
  
-     @Deprecated
-     public EnumPistonReaction getPushReaction(IBlockData iblockdata) {
--        return this.material.getPushReaction();
-+        return !blockData.isDestroyable() ? EnumPistonReaction.BLOCK : this.material.getPushReaction(); // Paper
-     }
+         public EnumPistonReaction getPushReaction() {
+-            return this.getBlock().getPushReaction(this.p());
++            return !isDestroyable() ? EnumPistonReaction.BLOCK : this.getBlock().getPushReaction(this.p()); // Paper
+         }
  
-     public void fallOn(World world, BlockPosition blockposition, Entity entity, float f) {
+         public boolean i(IBlockAccess iblockaccess, BlockPosition blockposition) {
 diff --git a/src/main/java/net/minecraft/server/BlockPiston.java b/src/main/java/net/minecraft/server/BlockPiston.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/BlockPiston.java
@@ -82,14 +99,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          if (!world.isClientSide) {
              boolean flag = this.a(world, blockposition, enumdirection);
 @@ -0,0 +0,0 @@ public class BlockPiston extends BlockDirectional {
-             }
+             IBlockData iblockdata1 = (IBlockData) ((IBlockData) Blocks.MOVING_PISTON.getBlockData().set(BlockPistonMoving.a, enumdirection)).set(BlockPistonMoving.b, this.sticky ? BlockPropertyPistonType.STICKY : BlockPropertyPistonType.DEFAULT);
  
-             world.setTypeAndData(blockposition, (IBlockData) ((IBlockData) Blocks.MOVING_PISTON.getBlockData().set(BlockPistonMoving.a, enumdirection)).set(BlockPistonMoving.b, this.sticky ? BlockPropertyPistonType.STICKY : BlockPropertyPistonType.DEFAULT), 3);
+             world.setTypeAndData(blockposition, iblockdata1, 20);
 -            world.setTileEntity(blockposition, BlockPistonMoving.a((IBlockData) this.getBlockData().set(BlockPiston.FACING, EnumDirection.fromType1(j & 7)), enumdirection, false, true));
-+            world.setTileEntity(blockposition, BlockPistonMoving.a((IBlockData) this.getBlockData().set(BlockPiston.FACING, EnumDirection.fromType1(j & 7)), enumdirection, false, true)); // Paper - diff on change, j is facing direction
++            world.setTileEntity(blockposition, BlockPistonMoving.a((IBlockData) this.getBlockData().set(BlockPiston.FACING, EnumDirection.fromType1(j & 7)), enumdirection, false, true)); // Paper - diff on change, j is facing direction - copy this above
+             world.update(blockposition, iblockdata1.getBlock());
+             iblockdata1.a(world, blockposition, 2);
              if (this.sticky) {
-                 BlockPosition blockposition1 = blockposition.b(enumdirection.getAdjacentX() * 2, enumdirection.getAdjacentY() * 2, enumdirection.getAdjacentZ() * 2);
-                 IBlockData iblockdata1 = world.getType(blockposition1);
 @@ -0,0 +0,0 @@ public class BlockPiston extends BlockDirectional {
                      }
                  }
@@ -116,8 +133,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                              IBlockData iblockdata = this.world.getType(blockposition);
 +                            if (!iblockdata.isDestroyable()) continue; // Paper
                              Fluid fluid = iblockdata.getFluid(); // Paper
+                             Optional<Float> optional = this.k.a(this, this.world, blockposition, iblockdata, fluid);
  
-                             if (!iblockdata.isAir() || !fluid.isEmpty()) {
 @@ -0,0 +0,0 @@ public class Explosion {
                  IBlockData iblockdata = this.world.getType(blockposition);
                  Block block = iblockdata.getBlock();
@@ -127,28 +144,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                      BlockPosition blockposition1 = blockposition.immutableCopy();
  
                      this.world.getMethodProfiler().enter("explosion_blocks");
-diff --git a/src/main/java/net/minecraft/server/IBlockData.java b/src/main/java/net/minecraft/server/IBlockData.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/IBlockData.java
-+++ b/src/main/java/net/minecraft/server/IBlockData.java
-@@ -0,0 +0,0 @@ public class IBlockData extends BlockDataAbstract<Block, IBlockData> implements
-         return (CraftBlockData) cachedCraftBlockData.clone();
-     }
-     // Paper end
-+    // Paper start
-+    public final boolean isDestroyable() {
-+        return getBlock().isDestroyable();
-+    }
-+    // Paper end
- 
-     public Material getMaterial() {
-         return this.getBlock().k(this);
 diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/World.java
 +++ b/src/main/java/net/minecraft/server/World.java
 @@ -0,0 +0,0 @@ public abstract class World implements GeneratorAccess, AutoCloseable {
-     public boolean setTypeAndData(BlockPosition blockposition, IBlockData iblockdata, int i) {
+     public boolean a(BlockPosition blockposition, IBlockData iblockdata, int i, int j) {
          // CraftBukkit start - tree generation
          if (this.captureTreeGeneration) {
 +            // Paper start
diff --git a/Spigot-Server-Patches/Reduce-allocation-of-Vec3D-by-entity-tracker.patch b/Spigot-Server-Patches/Reduce-allocation-of-Vec3D-by-entity-tracker.patch
index b24552de12..7475f09739 100644
--- a/Spigot-Server-Patches/Reduce-allocation-of-Vec3D-by-entity-tracker.patch
+++ b/Spigot-Server-Patches/Reduce-allocation-of-Vec3D-by-entity-tracker.patch
@@ -37,7 +37,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                    // Paper end - remove allocation of Vec3D here
                      boolean flag4 = k < -32768L || k > 32767L || l < -32768L || l > 32767L || i1 < -32768L || i1 > 32767L;
  
-                     if (!flag4 && this.o <= 400 && !this.q && this.r == this.tracker.onGround) {
+                     if (!flag4 && this.o <= 400 && !this.q && this.r == this.tracker.isOnGround()) {
 diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
diff --git a/Spigot-Server-Patches/Remove-some-Streams-usage-in-Entity-Collision.patch b/Spigot-Server-Patches/Remove-some-Streams-usage-in-Entity-Collision.patch
deleted file mode 100644
index 0576b73640..0000000000
--- a/Spigot-Server-Patches/Remove-some-Streams-usage-in-Entity-Collision.patch
+++ /dev/null
@@ -1,178 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Aikar <aikar@aikar.co>
-Date: Sat, 9 May 2020 18:36:27 -0400
-Subject: [PATCH] Remove some Streams usage in Entity Collision
-
-While there is more down the collision system, remove some of the wrapping
-Spliterator stuff as even this wrapper stream has shown up in profiling.
-
-With other collision optimizations, we might also even avoid inner streams too.
-
-diff --git a/src/main/java/net/minecraft/server/GeneratorAccess.java b/src/main/java/net/minecraft/server/GeneratorAccess.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/GeneratorAccess.java
-+++ b/src/main/java/net/minecraft/server/GeneratorAccess.java
-@@ -0,0 +0,0 @@ public interface GeneratorAccess extends IEntityAccess, IWorldReader, VirtualLev
-         this.a((EntityHuman) null, i, blockposition, j);
-     }
- 
-+    @Override default java.util.List<VoxelShape> getEntityCollisions(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set<Entity> set, boolean returnFast) {return IEntityAccess.super.getEntityCollisions(entity, axisalignedbb, set, returnFast); } // Paper
-     @Override
-     default Stream<VoxelShape> b(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set<Entity> set) {
-         return IEntityAccess.super.b(entity, axisalignedbb, set);
-diff --git a/src/main/java/net/minecraft/server/ICollisionAccess.java b/src/main/java/net/minecraft/server/ICollisionAccess.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/ICollisionAccess.java
-+++ b/src/main/java/net/minecraft/server/ICollisionAccess.java
-@@ -0,0 +0,0 @@ public interface ICollisionAccess extends IBlockAccess {
- 
-     default boolean a(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set<Entity> set) {
-         try { if (entity != null) entity.collisionLoadChunks = true; // Paper
--        return this.c(entity, axisalignedbb, set).allMatch(VoxelShape::isEmpty);
-+        // Paper start - reduce stream usage
-+        java.util.List<VoxelShape> blockCollisions = getBlockCollision(entity, axisalignedbb, true);
-+        for (int i = 0; i < blockCollisions.size(); i++) {
-+            VoxelShape blockCollision = blockCollisions.get(i);
-+            if (!blockCollision.isEmpty()) {
-+                return false;
-+            }
-+        }
-+        return getEntityCollisions(entity, axisalignedbb, set, true).isEmpty();
-+        // Paper end
-         } finally { if (entity != null) entity.collisionLoadChunks = false; } // Paper
-     }
- 
-+    default java.util.List<VoxelShape> getEntityCollisions(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set<Entity> set, boolean returnFast) { return java.util.Collections.emptyList(); } // Paper
-     default Stream<VoxelShape> b(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set<Entity> set) {
-         return Stream.empty();
-     }
- 
-     default Stream<VoxelShape> c(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set<Entity> set) {
--        return Streams.concat(new Stream[]{this.b(entity, axisalignedbb), this.b(entity, axisalignedbb, set)});
-+        // Paper start - reduce stream usage
-+        java.util.List<VoxelShape> blockCollisions = getBlockCollision(entity, axisalignedbb, false);
-+        java.util.List<VoxelShape> entityCollisions = getEntityCollisions(entity, axisalignedbb, set, false);
-+        return Stream.concat(blockCollisions.stream(), entityCollisions.stream());
-+        // Paper end
-     }
- 
-     default Stream<VoxelShape> b(@Nullable final Entity entity, AxisAlignedBB axisalignedbb) {
-+        // Paper start - reduce stream usage
-+        java.util.List<VoxelShape> collision = getBlockCollision(entity, axisalignedbb, false);
-+        return !collision.isEmpty() ? collision.stream() : Stream.empty();
-+    }
-+
-+    default java.util.List<VoxelShape> getBlockCollision(@Nullable final Entity entity, AxisAlignedBB axisalignedbb, boolean returnFast) {
-+        // Paper end
-         int i = MathHelper.floor(axisalignedbb.minX - 1.0E-7D) - 1;
-         int j = MathHelper.floor(axisalignedbb.maxX + 1.0E-7D) + 1;
-         int k = MathHelper.floor(axisalignedbb.minY - 1.0E-7D) - 1;
-@@ -0,0 +0,0 @@ public interface ICollisionAccess extends IBlockAccess {
-         final BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();
-         final VoxelShape voxelshape = VoxelShapes.a(axisalignedbb);
- 
--        return StreamSupport.stream(new AbstractSpliterator<VoxelShape>(Long.MAX_VALUE, 1280) {
--            boolean a = entity == null;
--
--            public boolean tryAdvance(Consumer<? super VoxelShape> consumer) {
--                if (!this.a) {
--                    this.a = true;
-+        // Paper start - reduce stream usage (this part done by Aikar)
-+        java.util.List<VoxelShape> collisions = new java.util.ArrayList<>();
-+        if (true) {//return StreamSupport.stream(new AbstractSpliterator<VoxelShape>(Long.MAX_VALUE, 1280) {
-+            if (true) { //public boolean tryAdvance(Consumer<? super VoxelShape> consumer) {*/ // Paper
-+                if (entity != null) {
-+                    // Paper end
-                     VoxelShape voxelshape1 = ICollisionAccess.this.getWorldBorder().a();
-                     boolean flag = VoxelShapes.c(voxelshape1, VoxelShapes.a(entity.getBoundingBox().shrink(1.0E-7D)), OperatorBoolean.AND);
-                     boolean flag1 = VoxelShapes.c(voxelshape1, VoxelShapes.a(entity.getBoundingBox().g(1.0E-7D)), OperatorBoolean.AND);
- 
-                     if (!flag && flag1) {
--                        consumer.accept(voxelshape1);
--                        return true;
-+                        collisions.add(voxelshape1);// Paper
-+                        if (returnFast) return collisions;
-                     }
-                 }
- 
-@@ -0,0 +0,0 @@ public interface ICollisionAccess extends IBlockAccess {
-                         );
-                         if (iblockdata == null) {
-                             if (!(entity instanceof EntityPlayer) || entity.world.paperConfig.preventMovingIntoUnloadedChunks) {
--                                VoxelShape voxelshape3 = VoxelShapes.of(far ? entity.getBoundingBox() : new AxisAlignedBB(new BlockPosition(x, y, z)));
--                                consumer.accept(voxelshape3);
--                                return true;
-+                                collisions.add(VoxelShapes.of(far ? entity.getBoundingBox() : new AxisAlignedBB(new BlockPosition(x, y, z))));
-+                                if (returnFast) return collisions;
-                             }
-                         } else {
-                             //blockposition_mutableblockposition.d(k1, l1, i2); // moved up
-@@ -0,0 +0,0 @@ public interface ICollisionAccess extends IBlockAccess {
- 
-                                 if (voxelshape2 == VoxelShapes.fullCube()) {
-                                     if (axisalignedbb.intersects(x, y, z, x + 1.0D, y + 1.0D, z + 1.0D)) {
--                                        consumer.accept(voxelshape2.offset(x, y, z));
--                                        return true;
-+                                        collisions.add(voxelshape2.offset(x, y, z));
-+                                        if (returnFast) return collisions;
-                                     }
-                                 } else {
-                                     VoxelShape shape = voxelshape2.offset(x, y, z);
-                                     if (VoxelShapes.applyOperation(shape, voxelshape, OperatorBoolean.AND)) {
--                                        consumer.accept(shape);
--                                        return true;
-+                                        collisions.add(shape);
-+                                        if (returnFast) return collisions;
-                                     }
-                                     // Paper end
-                                 }
-@@ -0,0 +0,0 @@ public interface ICollisionAccess extends IBlockAccess {
-                     }
-                 }
- 
--                return false;
-+                //return false; // Paper
-             }
--        }, false);
-+        } //}, false);
-+        return collisions; // Paper
-     }
- }
-diff --git a/src/main/java/net/minecraft/server/IEntityAccess.java b/src/main/java/net/minecraft/server/IEntityAccess.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/IEntityAccess.java
-+++ b/src/main/java/net/minecraft/server/IEntityAccess.java
-@@ -0,0 +0,0 @@ public interface IEntityAccess {
-         // Paper start - remove streams from entity collision
-         if (axisalignedbb.getAverageSideLength() < 1.0E-7D) {
-             return Stream.empty();
--
-         }
-+        return getEntityCollisions(entity, axisalignedbb, set, false).stream();
-+    }
-+    default List<VoxelShape> getEntityCollisions(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set<Entity> set, boolean returnFast) {
-         AxisAlignedBB selection = axisalignedbb.grow(1.0E-7D);
-         List<Entity> entities = entity != null && entity.hardCollides() ? getEntities(entity, selection) : getHardCollidingEntities(entity, selection);
-         List<VoxelShape> shapes = new java.util.ArrayList<>();
-@@ -0,0 +0,0 @@ public interface IEntityAccess {
- 
-             if (otherEntityBox != null && selection.intersects(otherEntityBox)) {
-                 shapes.add(VoxelShapes.of(otherEntityBox));
-+                if (returnFast) return shapes;
-             }
- 
-             if (entity != null) {
-@@ -0,0 +0,0 @@ public interface IEntityAccess {
- 
-                 if (otherEntityHardBox != null && selection.intersects(otherEntityHardBox)) {
-                     shapes.add(VoxelShapes.of(otherEntityHardBox));
-+                    if (returnFast) return shapes;
-                 }
-             }
-         }
- 
--        return shapes.stream();
-+        return shapes;
-         // Paper end
-     }
- 
diff --git a/Spigot-Server-Patches/Show-Paper-in-client-crashes-server-lists-and-Mojang.patch b/Spigot-Server-Patches/Show-Paper-in-client-crashes-server-lists-and-Mojang.patch
index 6b9f14f647..e0719937b4 100644
--- a/Spigot-Server-Patches/Show-Paper-in-client-crashes-server-lists-and-Mojang.patch
+++ b/Spigot-Server-Patches/Show-Paper-in-client-crashes-server-lists-and-Mojang.patch
@@ -49,7 +49,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/org/bukkit/craftbukkit/Main.java
 +++ b/src/main/java/org/bukkit/craftbukkit/Main.java
 @@ -0,0 +0,0 @@ public class Main {
-                     deadline.add(Calendar.DAY_OF_YEAR, -3);
+                     deadline.add(Calendar.DAY_OF_YEAR, -1);
                      if (buildDate.before(deadline.getTime())) {
                          System.err.println("*** Error, this build is outdated ***");
 -                        System.err.println("*** Please download a new build as per instructions from https://www.spigotmc.org/go/outdated-spigot ***");
diff --git a/Spigot-Server-Patches/Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch b/Spigot-Server-Patches/Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch
index 0abc1fe615..da8315f553 100644
--- a/Spigot-Server-Patches/Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch
+++ b/Spigot-Server-Patches/Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch
@@ -22,13 +22,12 @@ diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/server/WorldServer.java
 +++ b/src/main/java/net/minecraft/server/WorldServer.java
-@@ -0,0 +0,0 @@ public class WorldServer extends World {
+@@ -0,0 +0,0 @@ public class WorldServer extends World implements GeneratorAccessSeed {
+         this.worldDataServer.setThundering(flag1);
      }
-     // CraftBukkit end
  
 -    @Override
 -    public BiomeBase a(int i, int j, int k) {
-+
 +    public BiomeBase getBiomeBySeed(int i, int j, int k) { return a(i, j, k); } // Paper - OBFHELPER
 +    @Override public BiomeBase a(int i, int j, int k) {
          return this.getChunkProvider().getChunkGenerator().getWorldChunkManager().getBiome(i, j, k);
diff --git a/Spigot-Server-Patches/Wait-for-Async-Tasks-during-shutdown.patch b/Spigot-Server-Patches/Wait-for-Async-Tasks-during-shutdown.patch
index dfda5c8dbb..0a4383c036 100644
--- a/Spigot-Server-Patches/Wait-for-Async-Tasks-during-shutdown.patch
+++ b/Spigot-Server-Patches/Wait-for-Async-Tasks-during-shutdown.patch
@@ -60,4 +60,4 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
      @Override
      public void reloadData() {
-         console.reload();
+         CommandReload.reload(console);
diff --git a/Spigot-Server-Patches/Workaround-for-Client-Lag-Spikes-MC-162253.patch b/Spigot-Server-Patches/Workaround-for-Client-Lag-Spikes-MC-162253.patch
index ed514bdb37..d522ba9bda 100644
--- a/Spigot-Server-Patches/Workaround-for-Client-Lag-Spikes-MC-162253.patch
+++ b/Spigot-Server-Patches/Workaround-for-Client-Lag-Spikes-MC-162253.patch
@@ -102,8 +102,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                apacket = new Packet[10];
 +            }
 +            // Paper end
-             apacket[0] = new PacketPlayOutMapChunk(chunk, 65535);
-             apacket[1] = new PacketPlayOutLightUpdate(chunk.getPos(), this.lightEngine);
+             apacket[0] = new PacketPlayOutMapChunk(chunk, 65535, true);
+             apacket[1] = new PacketPlayOutLightUpdate(chunk.getPos(), this.lightEngine, true);
 +
 +            // Paper start - Fix MC-162253
 +            final int lightMask = getLightMask(chunk);
@@ -127,7 +127,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                        continue;
 +                    }
 +
-+                    apacket[i] = new PacketPlayOutLightUpdate(new ChunkCoordIntPair(chunk.getPos().x + x, chunk.getPos().z + z), lightEngine, updateLightMask, 0);
++                    apacket[i] = new PacketPlayOutLightUpdate(new ChunkCoordIntPair(chunk.getPos().x + x, chunk.getPos().z + z), lightEngine, updateLightMask, 0, true);
 +                }
 +            }
 +        }
diff --git a/work/Bukkit b/work/Bukkit
index 6f3c5f4a5a..8edeffe67d 160000
--- a/work/Bukkit
+++ b/work/Bukkit
@@ -1 +1 @@
-Subproject commit 6f3c5f4a5a0867ef265df9d58b48bdc43079e3dd
+Subproject commit 8edeffe67d1821afd48d16cd0ec2572251f24ee8
diff --git a/work/CraftBukkit b/work/CraftBukkit
index 3f0c333870..d1fb662ec5 160000
--- a/work/CraftBukkit
+++ b/work/CraftBukkit
@@ -1 +1 @@
-Subproject commit 3f0c333870ba74705e98d19322174d6f0c10c900
+Subproject commit d1fb662ec53c4fd8bc718039b76a3e9a11346371
diff --git a/work/Spigot b/work/Spigot
index 758abbeee4..8fc58f10ab 160000
--- a/work/Spigot
+++ b/work/Spigot
@@ -1 +1 @@
-Subproject commit 758abbeee4e12f5ff65470999dd9955d0ebb49cd
+Subproject commit 8fc58f10ab171ca1979afa2065909214a0ffab32