diff --git a/patches/api/Ability-to-apply-mending-to-XP-API.patch b/patches/api/Ability-to-apply-mending-to-XP-API.patch
index 9101d291d7..d654150ccb 100644
--- a/patches/api/Ability-to-apply-mending-to-XP-API.patch
+++ b/patches/api/Ability-to-apply-mending-to-XP-API.patch
@@ -18,21 +18,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      public void resetPlayerWeather();
  
 +    // Paper start
-     /**
-      * Gives the player the amount of experience specified.
-      *
-      * @param amount Exp amount to give
-      */
--    public void giveExp(int amount);
-+    public default void giveExp(int amount) {
-+        giveExp(amount, false);
-+    }
 +    /**
 +     * Gives the player the amount of experience specified.
 +     *
 +     * @param amount Exp amount to give
-+     * @param applyMending Mend players items with mending, with same behavior as picking up orbs. calls {@link #applyMending(int)}
 +     */
++    public default void giveExp(int amount) {
++        giveExp(amount, false);
++    }
+     /**
+      * Gives the player the amount of experience specified.
+      *
+      * @param amount Exp amount to give
++     * @param applyMending Mend players items with mending, with same behavior as picking up orbs. calls {@link #applyMending(int)}
+      */
+-    public void giveExp(int amount);
 +    public void giveExp(int amount, boolean applyMending);
 +
 +    /**
diff --git a/patches/api/Add-critical-damage-API.patch b/patches/api/Add-critical-damage-API.patch
index 7cd516955a..28032187e0 100644
--- a/patches/api/Add-critical-damage-API.patch
+++ b/patches/api/Add-critical-damage-API.patch
@@ -32,8 +32,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          this.damager = damager;
 +        // Paper start - add critical damage API
 +        this.critical = critical;
-     }
- 
++    }
++
 +    /**
 +     * Shows this damage instance was critical.
 +     * The damage instance can be critical if the attacking player met the respective conditions.
@@ -44,9 +44,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     */
 +    public boolean isCritical() {
 +        return this.critical;
-+    }
+     }
 +    // Paper end
-+
+ 
      /**
       * Returns the entity that damaged the defender.
-      *
diff --git a/patches/api/Add-more-advancement-API.patch b/patches/api/Add-more-advancement-API.patch
index 70abec7df8..27753aceae 100644
--- a/patches/api/Add-more-advancement-API.patch
+++ b/patches/api/Add-more-advancement-API.patch
@@ -194,12 +194,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     * recipes.
       *
 -     * This includes it's name, description and other visible tags.
--     *
--     * @return a AdvancementDisplay object, or null if not set.
 +     * @return the display info
-      */
--    @Nullable
--    AdvancementDisplay getDisplay();
++     */
 +    @org.jetbrains.annotations.Nullable
 +    io.papermc.paper.advancement.AdvancementDisplay getDisplay();
 +
@@ -209,10 +205,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     * completes the advancement. Will return the same as
 +     * {@link io.papermc.paper.advancement.AdvancementDisplay#displayName()} when an
 +     * {@link io.papermc.paper.advancement.AdvancementDisplay} is present.
-+     *
+      *
+-     * @return a AdvancementDisplay object, or null if not set.
 +     * @return the display name
 +     * @see io.papermc.paper.advancement.AdvancementDisplay#displayName()
-+     */
+      */
+-    @Nullable
+-    AdvancementDisplay getDisplay();
 +    @NotNull net.kyori.adventure.text.Component displayName();
 +
 +    /**
diff --git a/patches/api/Adventure.patch b/patches/api/Adventure.patch
index 6dc37b19a0..e562bb6ea3 100644
--- a/patches/api/Adventure.patch
+++ b/patches/api/Adventure.patch
@@ -939,21 +939,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      }
  
 +    // Paper start
-     /**
-      * Gets the default message that is displayed when the server is stopped.
-      *
-      * @return the shutdown message
-      */
-+    public static net.kyori.adventure.text.@Nullable Component shutdownMessage() {
-+        return server.shutdownMessage();
-+    }
-+    // Paper end
 +    /**
 +     * Gets the default message that is displayed when the server is stopped.
 +     *
 +     * @return the shutdown message
-+     * @deprecated in favour of {@link #shutdownMessage()}
 +     */
++    public static net.kyori.adventure.text.@Nullable Component shutdownMessage() {
++        return server.shutdownMessage();
++    }
++    // Paper end
+     /**
+      * Gets the default message that is displayed when the server is stopped.
+      *
+      * @return the shutdown message
++     * @deprecated in favour of {@link #shutdownMessage()}
+      */
      @Nullable
 +    @Deprecated // Paper
      public static String getShutdownMessage() {
@@ -1238,16 +1238,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      Inventory createInventory(@Nullable InventoryHolder owner, int size) throws IllegalArgumentException;
  
 +    // Paper start
-     /**
-      * Creates an empty inventory of type {@link InventoryType#CHEST} with the
-      * specified size and title.
-@@ -0,0 +0,0 @@ public interface Server extends PluginMessageRecipient {
-      * @throws IllegalArgumentException if the size is not a multiple of 9
-      */
-     @NotNull
-+    Inventory createInventory(@Nullable InventoryHolder owner, int size, net.kyori.adventure.text.@NotNull Component title) throws IllegalArgumentException;
-+    // Paper end
-+
 +    /**
 +     * Creates an empty inventory of type {@link InventoryType#CHEST} with the
 +     * specified size and title.
@@ -1258,10 +1248,22 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     *     viewed
 +     * @return a new inventory
 +     * @throws IllegalArgumentException if the size is not a multiple of 9
-+     * @deprecated in favour of {@link #createInventory(InventoryHolder, int, net.kyori.adventure.text.Component)}
 +     */
-+    @Deprecated // Paper
 +    @NotNull
++    Inventory createInventory(@Nullable InventoryHolder owner, int size, net.kyori.adventure.text.@NotNull Component title) throws IllegalArgumentException;
++    // Paper end
++
+     /**
+      * Creates an empty inventory of type {@link InventoryType#CHEST} with the
+      * specified size and title.
+@@ -0,0 +0,0 @@ public interface Server extends PluginMessageRecipient {
+      *     viewed
+      * @return a new inventory
+      * @throws IllegalArgumentException if the size is not a multiple of 9
++     * @deprecated in favour of {@link #createInventory(InventoryHolder, int, net.kyori.adventure.text.Component)}
+      */
++    @Deprecated // Paper
+     @NotNull
      Inventory createInventory(@Nullable InventoryHolder owner, int size, @NotNull String title) throws IllegalArgumentException;
  
 +    // Paper start
@@ -1311,19 +1313,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      String getMotd();
  
 +    // Paper start
-     /**
-      * Gets the default message that is displayed when the server is stopped.
-      *
-      * @return the shutdown message
-      */
-+    net.kyori.adventure.text.@Nullable Component shutdownMessage();
-+    // Paper end
 +    /**
 +     * Gets the default message that is displayed when the server is stopped.
 +     *
 +     * @return the shutdown message
-+     * @deprecated in favour of {@link #shutdownMessage()}
 +     */
++    net.kyori.adventure.text.@Nullable Component shutdownMessage();
++    // Paper end
+     /**
+      * Gets the default message that is displayed when the server is stopped.
+      *
+      * @return the shutdown message
++     * @deprecated in favour of {@link #shutdownMessage()}
+      */
      @Nullable
 +    @Deprecated // Paper
      String getShutdownMessage();
@@ -1521,49 +1523,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
   * Represents a captured state of either a SignPost or a WallSign.
   */
  public interface Sign extends TileState, Colorable {
--
 +    // Paper start
-     /**
-      * Gets all the lines of text currently on this sign.
-      *
-      * @return Array of Strings containing each line of text
-      */
-     @NotNull
--    public String[] getLines();
-+    public java.util.List<net.kyori.adventure.text.Component> lines();
- 
-     /**
-      * Gets the line of text at the specified index.
-@@ -0,0 +0,0 @@ public interface Sign extends TileState, Colorable {
-      * For example, getLine(0) will return the first line of text.
-      *
-      * @param index Line number to get the text from, starting at 0
--     * @return Text on the given line
-      * @throws IndexOutOfBoundsException Thrown when the line does not exist
-+     * @return Text on the given line
-      */
-     @NotNull
--    public String getLine(int index) throws IndexOutOfBoundsException;
-+    public net.kyori.adventure.text.Component line(int index) throws IndexOutOfBoundsException;
- 
-     /**
-      * Sets the line of text at the specified index.
-@@ -0,0 +0,0 @@ public interface Sign extends TileState, Colorable {
-      * @param line New text to set at the specified index
-      * @throws IndexOutOfBoundsException If the index is out of the range 0..3
-      */
-+    public void line(int index, net.kyori.adventure.text.@NotNull Component line) throws IndexOutOfBoundsException;
-+    // Paper end
-+
 +    /**
 +     * Gets all the lines of text currently on this sign.
 +     *
 +     * @return Array of Strings containing each line of text
-+     * @deprecated in favour of {@link #lines()}
 +     */
 +    @NotNull
-+    @Deprecated // Paper
-+    public String[] getLines();
++    public java.util.List<net.kyori.adventure.text.Component> lines();
 +
 +    /**
 +     * Gets the line of text at the specified index.
@@ -1571,13 +1538,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     * For example, getLine(0) will return the first line of text.
 +     *
 +     * @param index Line number to get the text from, starting at 0
-+     * @return Text on the given line
 +     * @throws IndexOutOfBoundsException Thrown when the line does not exist
-+     * @deprecated in favour of {@link #line(int)}
++     * @return Text on the given line
 +     */
 +    @NotNull
-+    @Deprecated // Paper
-+    public String getLine(int index) throws IndexOutOfBoundsException;
++    public net.kyori.adventure.text.Component line(int index) throws IndexOutOfBoundsException;
 +
 +    /**
 +     * Sets the line of text at the specified index.
@@ -1588,8 +1553,38 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     * @param index Line number to set the text at, starting from 0
 +     * @param line New text to set at the specified index
 +     * @throws IndexOutOfBoundsException If the index is out of the range 0..3
-+     * @deprecated in favour of {@link #line(int, net.kyori.adventure.text.Component)}
 +     */
++    public void line(int index, net.kyori.adventure.text.@NotNull Component line) throws IndexOutOfBoundsException;
++    // Paper end
+ 
+     /**
+      * Gets all the lines of text currently on this sign.
+      *
+      * @return Array of Strings containing each line of text
++     * @deprecated in favour of {@link #lines()}
+      */
+     @NotNull
++    @Deprecated // Paper
+     public String[] getLines();
+ 
+     /**
+@@ -0,0 +0,0 @@ public interface Sign extends TileState, Colorable {
+      * @param index Line number to get the text from, starting at 0
+      * @return Text on the given line
+      * @throws IndexOutOfBoundsException Thrown when the line does not exist
++     * @deprecated in favour of {@link #line(int)}
+      */
+     @NotNull
++    @Deprecated // Paper
+     public String getLine(int index) throws IndexOutOfBoundsException;
+ 
+     /**
+@@ -0,0 +0,0 @@ public interface Sign extends TileState, Colorable {
+      * @param index Line number to set the text at, starting from 0
+      * @param line New text to set at the specified index
+      * @throws IndexOutOfBoundsException If the index is out of the range 0..3
++     * @deprecated in favour of {@link #line(int, net.kyori.adventure.text.Component)}
+      */
 +    @Deprecated // Paper
      public void setLine(int index, @NotNull String line) throws IndexOutOfBoundsException;
  
@@ -2137,24 +2132,42 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      public void sendEquipmentChange(@NotNull LivingEntity entity, @NotNull Map<EquipmentSlot, ItemStack> items);
  
 +    // Paper start
-     /**
-      * Send a sign change. This fakes a sign change packet for a user at
-      * a certain location. This will not actually change the world in any way.
-@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
-      * @throws IllegalArgumentException if location is null
-      * @throws IllegalArgumentException if lines is non-null and has a length less than 4
-      */
--    public void sendSignChange(@NotNull Location loc, @Nullable String[] lines) throws IllegalArgumentException;
++    /**
++     * Send a sign change. This fakes a sign change packet for a user at
++     * a certain location. This will not actually change the world in any way.
++     * This method will use a sign at the location's block or a faked sign
++     * sent via
++     * {@link #sendBlockChange(org.bukkit.Location, org.bukkit.Material, byte)}.
++     * <p>
++     * If the client does not have a sign at the given location it will
++     * display an error message to the user.
++     *
++     * @param loc the location of the sign
++     * @param lines the new text on the sign or null to clear it
++     * @throws IllegalArgumentException if location is null
++     * @throws IllegalArgumentException if lines is non-null and has a length less than 4
++     */
 +    default void sendSignChange(@NotNull Location loc, @Nullable java.util.List<? extends net.kyori.adventure.text.Component> lines) throws IllegalArgumentException {
 +        this.sendSignChange(loc, lines, DyeColor.BLACK);
 +    }
- 
-     /**
-      * Send a sign change. This fakes a sign change packet for a user at
-@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
-      * @throws IllegalArgumentException if dyeColor is null
-      * @throws IllegalArgumentException if lines is non-null and has a length less than 4
-      */
++
++    /**
++     * Send a sign change. This fakes a sign change packet for a user at
++     * a certain location. This will not actually change the world in any way.
++     * This method will use a sign at the location's block or a faked sign
++     * sent via
++     * {@link #sendBlockChange(org.bukkit.Location, org.bukkit.Material, byte)}.
++     * <p>
++     * If the client does not have a sign at the given location it will
++     * display an error message to the user.
++     *
++     * @param loc the location of the sign
++     * @param lines the new text on the sign or null to clear it
++     * @param dyeColor the color of the sign
++     * @throws IllegalArgumentException if location is null
++     * @throws IllegalArgumentException if dyeColor is null
++     * @throws IllegalArgumentException if lines is non-null and has a length less than 4
++     */
 +    default void sendSignChange(@NotNull Location loc, @Nullable java.util.List<? extends net.kyori.adventure.text.Component> lines, @NotNull DyeColor dyeColor) throws IllegalArgumentException {
 +        this.sendSignChange(loc, lines, dyeColor, false);
 +    }
@@ -2202,43 +2215,25 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        throws IllegalArgumentException;
 +    // Paper end
 +
-+    /**
-+     * Send a sign change. This fakes a sign change packet for a user at
-+     * a certain location. This will not actually change the world in any way.
-+     * This method will use a sign at the location's block or a faked sign
-+     * sent via
-+     * {@link #sendBlockChange(org.bukkit.Location, org.bukkit.Material, byte)}.
-+     * <p>
-+     * If the client does not have a sign at the given location it will
-+     * display an error message to the user.
-+     *
-+     * @param loc the location of the sign
-+     * @param lines the new text on the sign or null to clear it
-+     * @throws IllegalArgumentException if location is null
-+     * @throws IllegalArgumentException if lines is non-null and has a length less than 4
+     /**
+      * Send a sign change. This fakes a sign change packet for a user at
+      * a certain location. This will not actually change the world in any way.
+@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+      * @param lines the new text on the sign or null to clear it
+      * @throws IllegalArgumentException if location is null
+      * @throws IllegalArgumentException if lines is non-null and has a length less than 4
 +     * @deprecated in favour of {@link #sendSignChange(org.bukkit.Location, java.util.List)}
-+     */
+      */
 +    @Deprecated // Paper
-+    public void sendSignChange(@NotNull Location loc, @Nullable String[] lines) throws IllegalArgumentException;
-+
-+    /**
-+     * Send a sign change. This fakes a sign change packet for a user at
-+     * a certain location. This will not actually change the world in any way.
-+     * This method will use a sign at the location's block or a faked sign
-+     * sent via
-+     * {@link #sendBlockChange(org.bukkit.Location, org.bukkit.Material, byte)}.
-+     * <p>
-+     * If the client does not have a sign at the given location it will
-+     * display an error message to the user.
-+     *
-+     * @param loc the location of the sign
-+     * @param lines the new text on the sign or null to clear it
-+     * @param dyeColor the color of the sign
-+     * @throws IllegalArgumentException if location is null
-+     * @throws IllegalArgumentException if dyeColor is null
-+     * @throws IllegalArgumentException if lines is non-null and has a length less than 4
+     public void sendSignChange(@NotNull Location loc, @Nullable String[] lines) throws IllegalArgumentException;
+ 
+     /**
+@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+      * @throws IllegalArgumentException if location is null
+      * @throws IllegalArgumentException if dyeColor is null
+      * @throws IllegalArgumentException if lines is non-null and has a length less than 4
 +     * @deprecated in favour of {@link #sendSignChange(org.bukkit.Location, java.util.List, org.bukkit.DyeColor)}
-+     */
+      */
 +    @Deprecated // Paper
      public void sendSignChange(@NotNull Location loc, @Nullable String[] lines, @NotNull DyeColor dyeColor) throws IllegalArgumentException;
  
@@ -2269,6 +2264,25 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      public void setResourcePack(@NotNull String url, @Nullable byte[] hash, @Nullable String prompt);
  
 +    // Paper start
+     /**
+      * Request that the player's client download and switch resource packs.
+      * <p>
+@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+      * @param hash The sha1 hash sum of the resource pack file which is used
+      *     to apply a cached version of the pack directly without downloading
+      *     if it is available. Hast to be 20 bytes long!
++     * @param prompt The optional custom prompt message to be shown to client.
++     * @throws IllegalArgumentException Thrown if the URL is null.
++     * @throws IllegalArgumentException Thrown if the URL is too long. The
++     *     length restriction is an implementation specific arbitrary value.
++     * @throws IllegalArgumentException Thrown if the hash is not 20 bytes
++     *     long.
++     */
++    default void setResourcePack(@NotNull String url, byte @Nullable [] hash, net.kyori.adventure.text.@Nullable Component prompt) {
++        this.setResourcePack(url, hash, prompt, false);
++    }
++    // Paper end
++
 +    /**
 +     * Request that the player's client download and switch resource packs.
 +     * <p>
@@ -2298,35 +2312,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     *     pack correctly.
 +     * </ul>
 +     *
++     * @deprecated in favour of {@link #setResourcePack(String, byte[], Component, boolean)}
 +     * @param url The URL from which the client will download the resource
 +     *     pack. The string must contain only US-ASCII characters and should
 +     *     be encoded as per RFC 1738.
 +     * @param hash The sha1 hash sum of the resource pack file which is used
 +     *     to apply a cached version of the pack directly without downloading
 +     *     if it is available. Hast to be 20 bytes long!
-+     * @param prompt The optional custom prompt message to be shown to client.
-+     * @throws IllegalArgumentException Thrown if the URL is null.
-+     * @throws IllegalArgumentException Thrown if the URL is too long. The
-+     *     length restriction is an implementation specific arbitrary value.
-+     * @throws IllegalArgumentException Thrown if the hash is not 20 bytes
-+     *     long.
-+     */
-+    default void setResourcePack(@NotNull String url, byte @Nullable [] hash, net.kyori.adventure.text.@Nullable Component prompt) {
-+        this.setResourcePack(url, hash, prompt, false);
-+    }
-+    // Paper end
-+
-     /**
-      * Request that the player's client download and switch resource packs.
-      * <p>
-@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
-      *     pack correctly.
-      * </ul>
-      *
-+     * @deprecated in favour of {@link #setResourcePack(String, byte[], Component, boolean)}
-      * @param url The URL from which the client will download the resource
-      *     pack. The string must contain only US-ASCII characters and should
-      *     be encoded as per RFC 1738.
+      * @param force If true, the client will be disconnected from the server
+      *     when it declines to use the resource pack.
+      * @throws IllegalArgumentException Thrown if the URL is null.
 @@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
       * @throws IllegalArgumentException Thrown if the hash is not 20 bytes
       *     long.
@@ -2592,50 +2587,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      }
  
 +    // Paper start
-     /**
-      * Gets all of the lines of text from the sign involved in this event.
-      *
-      * @return the String array for the sign's lines new text
-      */
--    @NotNull
--    public String[] getLines() {
--        return lines;
-+    public @NotNull java.util.List<net.kyori.adventure.text.Component> lines() {
-+        return this.adventure$lines;
-     }
- 
-     /**
-@@ -0,0 +0,0 @@ public class SignChangeEvent extends BlockEvent implements Cancellable {
-      * @throws IndexOutOfBoundsException thrown when the provided index is {@literal > 3
-      *     or < 0}
-      */
--    @Nullable
--    public String getLine(int index) throws IndexOutOfBoundsException {
--        return lines[index];
-+    public net.kyori.adventure.text.@Nullable Component line(int index) throws IndexOutOfBoundsException {
-+        return this.adventure$lines.get(index);
-     }
- 
-     /**
-@@ -0,0 +0,0 @@ public class SignChangeEvent extends BlockEvent implements Cancellable {
-      * @throws IndexOutOfBoundsException thrown when the provided index is {@literal > 3
-      *     or < 0}
-      */
-+    public void line(int index, net.kyori.adventure.text.@Nullable Component line) throws IndexOutOfBoundsException {
-+        this.adventure$lines.set(index, line);
-+    }
-+    // Paper end
-+
 +    /**
 +     * Gets all of the lines of text from the sign involved in this event.
 +     *
 +     * @return the String array for the sign's lines new text
-+     * @deprecated in favour of {@link #lines()}
 +     */
-+    @NotNull
-+    @Deprecated // Paper
-+    public String[] getLines() {
-+        return adventure$lines.stream().map(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection()::serialize).toArray(String[]::new); // Paper
++    public @NotNull java.util.List<net.kyori.adventure.text.Component> lines() {
++        return this.adventure$lines;
 +    }
 +
 +    /**
@@ -2646,12 +2604,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     *     provided index
 +     * @throws IndexOutOfBoundsException thrown when the provided index is {@literal > 3
 +     *     or < 0}
-+     * @deprecated in favour of {@link #line(int)}
 +     */
-+    @Nullable
-+    @Deprecated // Paper
-+    public String getLine(int index) throws IndexOutOfBoundsException {
-+        return net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(this.adventure$lines.get(index)); // Paper
++    public net.kyori.adventure.text.@Nullable Component line(int index) throws IndexOutOfBoundsException {
++        return this.adventure$lines.get(index);
 +    }
 +
 +    /**
@@ -2661,8 +2616,46 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     * @param line text to set
 +     * @throws IndexOutOfBoundsException thrown when the provided index is {@literal > 3
 +     *     or < 0}
-+     * @deprecated in favour of {@link #line(int, net.kyori.adventure.text.Component)}
 +     */
++    public void line(int index, net.kyori.adventure.text.@Nullable Component line) throws IndexOutOfBoundsException {
++        this.adventure$lines.set(index, line);
++    }
++    // Paper end
++
+     /**
+      * Gets all of the lines of text from the sign involved in this event.
+      *
+      * @return the String array for the sign's lines new text
++     * @deprecated in favour of {@link #lines()}
+      */
+     @NotNull
++    @Deprecated // Paper
+     public String[] getLines() {
+-        return lines;
++        return adventure$lines.stream().map(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection()::serialize).toArray(String[]::new); // Paper
+     }
+ 
+     /**
+@@ -0,0 +0,0 @@ public class SignChangeEvent extends BlockEvent implements Cancellable {
+      *     provided index
+      * @throws IndexOutOfBoundsException thrown when the provided index is {@literal > 3
+      *     or < 0}
++     * @deprecated in favour of {@link #line(int)}
+      */
+     @Nullable
++    @Deprecated // Paper
+     public String getLine(int index) throws IndexOutOfBoundsException {
+-        return lines[index];
++        return net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(this.adventure$lines.get(index)); // Paper
+     }
+ 
+     /**
+@@ -0,0 +0,0 @@ public class SignChangeEvent extends BlockEvent implements Cancellable {
+      * @param line text to set
+      * @throws IndexOutOfBoundsException thrown when the provided index is {@literal > 3
+      *     or < 0}
++     * @deprecated in favour of {@link #line(int, net.kyori.adventure.text.Component)}
+      */
 +    @Deprecated // Paper
      public void setLine(int index, @Nullable String line) throws IndexOutOfBoundsException {
 -        lines[index] = line;
@@ -2687,11 +2680,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    public PlayerDeathEvent(@NotNull final Player player, @NotNull final List<ItemStack> drops, final int droppedExp, @Nullable final net.kyori.adventure.text.Component adventure$deathMessage) {
 +        this(player, drops, droppedExp, 0, adventure$deathMessage, null);
 +    }
- 
++
 +    public PlayerDeathEvent(@NotNull final Player player, @NotNull final List<ItemStack> drops, final int droppedExp, final int newExp, @Nullable final net.kyori.adventure.text.Component adventure$deathMessage, @Nullable String deathMessage) {
 +        this(player, drops, droppedExp, newExp, 0, 0, adventure$deathMessage, deathMessage);
 +    }
-+
+ 
 +    public PlayerDeathEvent(@NotNull final Player player, @NotNull final List<ItemStack> drops, final int droppedExp, final int newExp, final int newTotalExp, final int newLevel, @Nullable final net.kyori.adventure.text.Component adventure$deathMessage, @Nullable String deathMessage) {
 +        super(player, drops, droppedExp);
 +        this.newExp = newExp;
@@ -2728,54 +2721,51 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      }
  
 +    // Paper start
-     /**
-      * Set the death message that will appear to everyone on the server.
-      *
-      * @param deathMessage Message to appear to other players on the server.
-      */
--    public void setDeathMessage(@Nullable String deathMessage) {
--        this.deathMessage = deathMessage;
++    /**
++     * Set the death message that will appear to everyone on the server.
++     *
++     * @param deathMessage Message to appear to other players on the server.
++     */
 +    public void deathMessage(net.kyori.adventure.text.@Nullable Component deathMessage) {
 +        this.deathMessage = null;
 +        this.adventure$deathMessage = deathMessage;
-     }
- 
-     /**
-@@ -0,0 +0,0 @@ public class PlayerDeathEvent extends EntityDeathEvent {
-      *
-      * @return Message to appear to other players on the server.
-      */
--    @Nullable
--    public String getDeathMessage() {
--        return deathMessage;
++    }
++
++    /**
++     * Get the death message that will appear to everyone on the server.
++     *
++     * @return Message to appear to other players on the server.
++     */
 +    public net.kyori.adventure.text.@Nullable Component deathMessage() {
 +        return this.adventure$deathMessage;
 +    }
 +    // Paper end
 +
-+    /**
-+     * Set the death message that will appear to everyone on the server.
-+     *
-+     * @param deathMessage Message to appear to other players on the server.
+     /**
+      * Set the death message that will appear to everyone on the server.
+      *
+      * @param deathMessage Message to appear to other players on the server.
 +     * @deprecated in favour of {@link #deathMessage(net.kyori.adventure.text.Component)}
-+     */
+      */
 +    @Deprecated // Paper
-+    public void setDeathMessage(@Nullable String deathMessage) {
-+        this.deathMessage = deathMessage;
+     public void setDeathMessage(@Nullable String deathMessage) {
+         this.deathMessage = deathMessage;
 +        this.adventure$deathMessage = deathMessage != null ? net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(deathMessage) : null; // Paper
      }
  
-+    /**
-+     * Get the death message that will appear to everyone on the server.
-+     *
-+     * @return Message to appear to other players on the server.
+     /**
+      * Get the death message that will appear to everyone on the server.
+      *
+      * @return Message to appear to other players on the server.
 +     * @deprecated in favour of {@link #deathMessage()}
-+     */
-+    @Nullable
+      */
+     @Nullable
 +    @Deprecated // Paper
-+    public String getDeathMessage() {
+     public String getDeathMessage() {
+-        return deathMessage;
 +        return this.deathMessage != null ? this.deathMessage : (this.adventure$deathMessage != null ? getDeathMessageString(this.adventure$deathMessage) : null); // Paper
-+    }
+     }
+-
 +    // Paper start //TODO: add translation API to drop String deathMessage in favor of just Adventure
 +    private static String getDeathMessageString(net.kyori.adventure.text.Component component) {
 +        return net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(component);
@@ -2884,26 +2874,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
       */
 -    public void setKickMessage(@NotNull final String message) {
 +    public void kickMessage(@NotNull final net.kyori.adventure.text.Component message) {
-         this.message = message;
-     }
- 
--    /**
--     * Allows the player to log in
--     */
--    public void allow() {
--        result = Result.ALLOWED;
--        message = "";
--    }
--
-     /**
-      * Disallows the player from logging in, with the given reason
-      *
-      * @param result New result for disallowing the player
-      * @param message Kick message to display to the user
-      */
--    public void disallow(@NotNull final Result result, @NotNull final String message) {
++        this.message = message;
++    }
++
++    /**
++     * Disallows the player from logging in, with the given reason
++     *
++     * @param result New result for disallowing the player
++     * @param message Kick message to display to the user
++     */
 +    public void disallow(@NotNull final Result result, @NotNull final net.kyori.adventure.text.Component message) {
-         this.result = result;
++        this.result = result;
          this.message = message;
      }
  
@@ -2945,31 +2926,31 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    public void setKickMessage(@NotNull final String message) {
 +        this.message = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message); // Paper
 +    }
-+
-+    /**
-+     * Allows the player to log in
-+     */
-+    public void allow() {
-+        result = Result.ALLOWED;
-+        message = net.kyori.adventure.text.Component.empty(); // Paper
-+    }
-+
-+    /**
-+     * Disallows the player from logging in, with the given reason
-+     *
-+     * @param result New result for disallowing the player
-+     * @param message Kick message to display to the user
-+     * @deprecated in favour of {@link #disallow(org.bukkit.event.player.AsyncPlayerPreLoginEvent.Result, net.kyori.adventure.text.Component)}
-+     */
-+    @Deprecated // Paper
-+    public void disallow(@NotNull final Result result, @NotNull final String message) {
-+        this.result = result;
-+        this.message = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message); // Paper
-+    }
 +
      /**
-      * Disallows the player from logging in, with the given reason
+      * Allows the player to log in
+      */
+     public void allow() {
+         result = Result.ALLOWED;
+-        message = "";
++        message = net.kyori.adventure.text.Component.empty(); // Paper
+     }
+ 
+     /**
+@@ -0,0 +0,0 @@ public class AsyncPlayerPreLoginEvent extends Event {
       *
+      * @param result New result for disallowing the player
+      * @param message Kick message to display to the user
++     * @deprecated in favour of {@link #disallow(org.bukkit.event.player.AsyncPlayerPreLoginEvent.Result, net.kyori.adventure.text.Component)}
+      */
++    @Deprecated // Paper
+     public void disallow(@NotNull final Result result, @NotNull final String message) {
+         this.result = result;
+-        this.message = message;
++        this.message = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message); // Paper
+     }
+ 
+     /**
 @@ -0,0 +0,0 @@ public class AsyncPlayerPreLoginEvent extends Event {
      @Deprecated
      public void disallow(@NotNull final PlayerPreLoginEvent.Result result, @NotNull final String message) {
@@ -3013,70 +2994,65 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  public class PlayerJoinEvent extends PlayerEvent {
      private static final HandlerList handlers = new HandlerList();
 -    private String joinMessage;
--
--    public PlayerJoinEvent(@NotNull final Player playerJoined, @Nullable final String joinMessage) {
 +    // Paper start
 +    private net.kyori.adventure.text.Component joinMessage;
 +    public PlayerJoinEvent(@NotNull final Player playerJoined, @Nullable final net.kyori.adventure.text.Component joinMessage) {
-         super(playerJoined);
-         this.joinMessage = joinMessage;
-     }
++        super(playerJoined);
++        this.joinMessage = joinMessage;
++    }
  
 +    @Deprecated // Paper end
-+    public PlayerJoinEvent(@NotNull final Player playerJoined, @Nullable final String joinMessage) {
-+        super(playerJoined);
+     public PlayerJoinEvent(@NotNull final Player playerJoined, @Nullable final String joinMessage) {
+         super(playerJoined);
 +        this.joinMessage = joinMessage != null ? net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(joinMessage) : null; // Paper end
 +    }
 +
 +    // Paper start
-     /**
-      * Gets the join message to send to all online players
-      *
-      * @return string join message. Can be null
-      */
--    @Nullable
--    public String getJoinMessage() {
--        return joinMessage;
-+    public net.kyori.adventure.text.@Nullable Component joinMessage() {
-+        return this.joinMessage;
-     }
- 
-     /**
-@@ -0,0 +0,0 @@ public class PlayerJoinEvent extends PlayerEvent {
-      *
-      * @param joinMessage join message. If null, no message will be sent
-      */
--    public void setJoinMessage(@Nullable String joinMessage) {
-+    public void joinMessage(net.kyori.adventure.text.@Nullable Component joinMessage) {
-         this.joinMessage = joinMessage;
-     }
-+    // Paper end
-+
 +    /**
 +     * Gets the join message to send to all online players
 +     *
 +     * @return string join message. Can be null
-+     * @deprecated in favour of {@link #joinMessage()}
 +     */
-+    @Nullable
-+    @Deprecated // Paper
-+    public String getJoinMessage() {
-+        return this.joinMessage == null ? null : net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(this.joinMessage); // Paper
++    public net.kyori.adventure.text.@Nullable Component joinMessage() {
++        return this.joinMessage;
 +    }
 +
 +    /**
 +     * Sets the join message to send to all online players
 +     *
 +     * @param joinMessage join message. If null, no message will be sent
-+     * @deprecated in favour of {@link #joinMessage(net.kyori.adventure.text.Component)}
 +     */
++    public void joinMessage(net.kyori.adventure.text.@Nullable Component joinMessage) {
+         this.joinMessage = joinMessage;
+     }
++    // Paper end
+ 
+     /**
+      * Gets the join message to send to all online players
+      *
+      * @return string join message. Can be null
++     * @deprecated in favour of {@link #joinMessage()}
+      */
+     @Nullable
 +    @Deprecated // Paper
-+    public void setJoinMessage(@Nullable String joinMessage) {
+     public String getJoinMessage() {
+-        return joinMessage;
++        return this.joinMessage == null ? null : net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(this.joinMessage); // Paper
+     }
+ 
+     /**
+      * Sets the join message to send to all online players
+      *
+      * @param joinMessage join message. If null, no message will be sent
++     * @deprecated in favour of {@link #joinMessage(net.kyori.adventure.text.Component)}
+      */
++    @Deprecated // Paper
+     public void setJoinMessage(@Nullable String joinMessage) {
+-        this.joinMessage = joinMessage;
 +        this.joinMessage = joinMessage != null ? net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(joinMessage) : null; // Paper
-+    }
+     }
  
      @NotNull
-     @Override
 diff --git a/src/main/java/org/bukkit/event/player/PlayerKickEvent.java b/src/main/java/org/bukkit/event/player/PlayerKickEvent.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/org/bukkit/event/player/PlayerKickEvent.java
@@ -3252,8 +3228,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
       */
 +    @Deprecated // Paper
      public PlayerLoginEvent(@NotNull final Player player, @NotNull String hostname, @NotNull final InetAddress address, @NotNull final Result result, @NotNull final String message, @NotNull final InetAddress realAddress) { // Spigot
-+        this(player, hostname, address, realAddress); // Spigot
-+        this.result = result;
+         this(player, hostname, address, realAddress); // Spigot
+         this.result = result;
 +        this.message = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message); // Paper
 +    }
 +
@@ -3270,11 +3246,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     * @param realAddress the actual, unspoofed connecting address
 +     */
 +    public PlayerLoginEvent(@NotNull final Player player, @NotNull String hostname, @NotNull final InetAddress address, @NotNull final Result result, @NotNull final net.kyori.adventure.text.Component message, @NotNull final InetAddress realAddress) { // Spigot
-         this(player, hostname, address, realAddress); // Spigot
-         this.result = result;
-         this.message = message;
-     }
- 
++        this(player, hostname, address, realAddress); // Spigot
++        this.result = result;
++        this.message = message;
++    }
++
 +    /**
 +     * Gets the current kick message that will be used if getResult() !=
 +     * Result.ALLOWED
@@ -3291,13 +3267,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     * @param message New kick message
 +     */
 +    public void kickMessage(net.kyori.adventure.text.@NotNull Component message) {
-+        this.message = message;
-+    }
+         this.message = message;
+     }
 +    // Paper end
-+
+ 
      // Spigot start
      /**
-      * Gets the connection address of this player, regardless of whether it has been spoofed or not.
 @@ -0,0 +0,0 @@ public class PlayerLoginEvent extends PlayerEvent {
       * Result.ALLOWED
       *
@@ -3403,25 +3378,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          this.message = message;
      }
  
--    /**
--     * Allows the player to log in
--     */
--    public void allow() {
--        result = Result.ALLOWED;
--        message = "";
--    }
--
-     /**
-      * Disallows the player from logging in, with the given reason
-      *
-      * @param result New result for disallowing the player
-      * @param message Kick message to display to the user
-      */
--    public void disallow(@NotNull final Result result, @NotNull final String message) {
++    /**
++     * Disallows the player from logging in, with the given reason
++     *
++     * @param result New result for disallowing the player
++     * @param message Kick message to display to the user
++     */
 +    public void disallow(@NotNull final Result result, @NotNull final net.kyori.adventure.text.Component message) {
-         this.result = result;
-         this.message = message;
-     }
++        this.result = result;
++        this.message = message;
++    }
 +    // Paper end
 +    /**
 +     * Gets the current kick message that will be used if getResult() !=
@@ -3447,29 +3413,30 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        this.message = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message); // Paper
 +    }
 +
-+    /**
-+     * Allows the player to log in
-+     */
-+    public void allow() {
-+        result = Result.ALLOWED;
+     /**
+      * Allows the player to log in
+      */
+     public void allow() {
+         result = Result.ALLOWED;
+-        message = "";
 +        message = net.kyori.adventure.text.Component.empty(); // Paper
-+    }
-+
-+    /**
-+     * Disallows the player from logging in, with the given reason
-+     *
-+     * @param result New result for disallowing the player
-+     * @param message Kick message to display to the user
-+     * @deprecated in favour of {@link #disallow(org.bukkit.event.player.PlayerPreLoginEvent.Result, net.kyori.adventure.text.Component)}
-+     */
-+    @Deprecated // Paper
-+    public void disallow(@NotNull final Result result, @NotNull final String message) {
-+        this.result = result;
-+        this.message = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message); // Paper
-+    }
+     }
+ 
+     /**
+@@ -0,0 +0,0 @@ public class PlayerPreLoginEvent extends Event {
+      *
+      * @param result New result for disallowing the player
+      * @param message Kick message to display to the user
++     * @deprecated in favour of {@link #disallow(org.bukkit.event.player.PlayerPreLoginEvent.Result, net.kyori.adventure.text.Component)}
+      */
++    @Deprecated // Paper
+     public void disallow(@NotNull final Result result, @NotNull final String message) {
+         this.result = result;
+-        this.message = message;
++        this.message = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message); // Paper
+     }
  
      /**
-      * Gets the player's name.
 diff --git a/src/main/java/org/bukkit/event/player/PlayerQuitEvent.java b/src/main/java/org/bukkit/event/player/PlayerQuitEvent.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/org/bukkit/event/player/PlayerQuitEvent.java
@@ -3483,59 +3450,60 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
 +    @Deprecated // Paper
      public PlayerQuitEvent(@NotNull final Player who, @Nullable final String quitMessage) {
-+        super(who);
+         super(who);
 +        this.quitMessage = quitMessage != null ? net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(quitMessage) : null; // Paper
 +    }
 +    // Paper start
 +    public PlayerQuitEvent(@NotNull final Player who, @Nullable final net.kyori.adventure.text.Component quitMessage) {
-         super(who);
-         this.quitMessage = quitMessage;
-     }
-@@ -0,0 +0,0 @@ public class PlayerQuitEvent extends PlayerEvent {
-      *
-      * @return string quit message
-      */
--    @Nullable
--    public String getQuitMessage() {
-+    public net.kyori.adventure.text.@Nullable Component quitMessage() {
-         return quitMessage;
-     }
- 
-@@ -0,0 +0,0 @@ public class PlayerQuitEvent extends PlayerEvent {
-      *
-      * @param quitMessage quit message
-      */
--    public void setQuitMessage(@Nullable String quitMessage) {
-+    public void quitMessage(net.kyori.adventure.text.@Nullable Component quitMessage) {
-         this.quitMessage = quitMessage;
-     }
-+    // Paper end
++        super(who);
++        this.quitMessage = quitMessage;
++    }
 +
 +    /**
 +     * Gets the quit message to send to all online players
 +     *
 +     * @return string quit message
-+     * @deprecated in favour of {@link #quitMessage()}
 +     */
-+    @Nullable
-+    @Deprecated // Paper
-+    public String getQuitMessage() {
-+        return this.quitMessage == null ? null : net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(this.quitMessage); // Paper
++    public net.kyori.adventure.text.@Nullable Component quitMessage() {
++        return quitMessage;
 +    }
 +
 +    /**
 +     * Sets the quit message to send to all online players
 +     *
 +     * @param quitMessage quit message
-+     * @deprecated in favour of {@link #quitMessage(net.kyori.adventure.text.Component)}
 +     */
++    public void quitMessage(net.kyori.adventure.text.@Nullable Component quitMessage) {
+         this.quitMessage = quitMessage;
+     }
++    // Paper end
+ 
+     /**
+      * Gets the quit message to send to all online players
+      *
+      * @return string quit message
++     * @deprecated in favour of {@link #quitMessage()}
+      */
+     @Nullable
 +    @Deprecated // Paper
-+    public void setQuitMessage(@Nullable String quitMessage) {
+     public String getQuitMessage() {
+-        return quitMessage;
++        return this.quitMessage == null ? null : net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(this.quitMessage); // Paper
+     }
+ 
+     /**
+      * Sets the quit message to send to all online players
+      *
+      * @param quitMessage quit message
++     * @deprecated in favour of {@link #quitMessage(net.kyori.adventure.text.Component)}
+      */
++    @Deprecated // Paper
+     public void setQuitMessage(@Nullable String quitMessage) {
+-        this.quitMessage = quitMessage;
 +        this.quitMessage = quitMessage != null ? net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(quitMessage) : null; // Paper
-+    }
+     }
  
      @NotNull
-     @Override
 diff --git a/src/main/java/org/bukkit/event/server/BroadcastMessageEvent.java b/src/main/java/org/bukkit/event/server/BroadcastMessageEvent.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/org/bukkit/event/server/BroadcastMessageEvent.java
@@ -3649,9 +3617,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    @Deprecated // Paper
      protected ServerListPingEvent(@NotNull final String hostname, @NotNull final InetAddress address, @NotNull final String motd, final int maxPlayers) {
          super(true);
-+        this.numPlayers = MAGIC_PLAYER_COUNT;
-+        this.hostname = hostname;
-+        this.address = address;
+         this.numPlayers = MAGIC_PLAYER_COUNT;
+         this.hostname = hostname;
+         this.address = address;
 +        this.motd = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(motd); // Paper
 +        this.maxPlayers = maxPlayers;
 +    }
@@ -3665,10 +3633,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        Preconditions.checkArgument(numPlayers >= 0, "Cannot have negative number of players online (%s)", numPlayers);
 +        this.hostname = hostname;
 +        this.address = address;
-+        this.motd = motd;
+         this.motd = motd;
 +        this.numPlayers = numPlayers;
-+        this.maxPlayers = maxPlayers;
-+    }
+         this.maxPlayers = maxPlayers;
+     }
 +    /**
 +     * This constructor is intended for implementations that provide the
 +     * {@link #iterator()} method, thus provided the {@link #getNumPlayers()}
@@ -3695,12 +3663,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     * @param maxPlayers the max number of players
 +     */
 +    protected ServerListPingEvent(final @NotNull String hostname, final @NotNull InetAddress address, final net.kyori.adventure.text.@NotNull Component motd, final int maxPlayers) {
-         this.numPlayers = MAGIC_PLAYER_COUNT;
-         this.hostname = hostname;
-         this.address = address;
-         this.motd = motd;
-         this.maxPlayers = maxPlayers;
-     }
++        this.numPlayers = MAGIC_PLAYER_COUNT;
++        this.hostname = hostname;
++        this.address = address;
++        this.motd = motd;
++        this.maxPlayers = maxPlayers;
++    }
 +    /**
 +     * Get the message of the day message.
 +     *
@@ -3859,8 +3827,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      boolean hasPages();
  
 +    // Paper start
-     /**
--     * Gets the specified page in the book. The given page must exist.
++    /**
 +     * Gets the title of the book.
 +     * <p>
 +     * Plugins should check that hasTitle() returns true before calling this
@@ -3906,55 +3873,34 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
 +    /**
 +     * Gets the specified page in the book. The page must exist.
-      * <p>
-      * Pages are 1-indexed.
-      *
-      * @param page the page number to get, in range [1, getPageCount()]
-      * @return the page from the book
-      */
--    @NotNull
--    String getPage(int page);
++     * <p>
++     * Pages are 1-indexed.
++     *
++     * @param page the page number to get, in range [1, getPageCount()]
++     * @return the page from the book
++     */
 +    net.kyori.adventure.text.@NotNull Component page(int page);
- 
-     /**
-      * Sets the specified page in the book. Pages of the book must be
-@@ -0,0 +0,0 @@ public interface BookMeta extends ItemMeta {
-      * @param page the page number to set, in range [1, getPageCount()]
-      * @param data the data to set for that page
-      */
--    void setPage(int page, @NotNull String data);
--
--    /**
--     * Gets all the pages in the book.
--     *
--     * @return list of all the pages in the book
--     */
--    @NotNull
--    List<String> getPages();
--
--    /**
--     * Clears the existing book pages, and sets the book to use the provided
--     * pages. Maximum 100 pages with 256 characters per page.
--     *
--     * @param pages A list of pages to set the book to use
--     */
--    void setPages(@NotNull List<String> pages);
--
--    /**
--     * Clears the existing book pages, and sets the book to use the provided
--     * pages. Maximum 50 pages with 256 characters per page.
--     *
--     * @param pages A list of strings, each being a page
--     */
--    void setPages(@NotNull String... pages);
++
++    /**
++     * Sets the specified page in the book. Pages of the book must be
++     * contiguous.
++     * <p>
++     * The data can be up to 256 characters in length, additional characters
++     * are truncated.
++     * <p>
++     * Pages are 1-indexed.
++     *
++     * @param page the page number to set, in range [1, getPageCount()]
++     * @param data the data to set for that page
++     */
 +    void page(int page, net.kyori.adventure.text.@NotNull Component data);
- 
-     /**
-      * Adds new pages to the end of the book. Up to a maximum of 50 pages with
-@@ -0,0 +0,0 @@ public interface BookMeta extends ItemMeta {
-      *
-      * @param pages A list of strings, each being a page
-      */
++
++    /**
++     * Adds new pages to the end of the book. Up to a maximum of 50 pages with
++     * 256 characters per page.
++     *
++     * @param pages A list of strings, each being a page
++     */
 +    void addPages(net.kyori.adventure.text.@NotNull Component @NotNull ... pages);
 +
 +    interface BookMetaBuilder extends Builder {
@@ -3983,72 +3929,66 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
 +    // Paper end
 +
-+    /**
-+     * Gets the specified page in the book. The given page must exist.
-+     * <p>
-+     * Pages are 1-indexed.
-+     *
-+     * @param page the page number to get, in range [1, getPageCount()]
-+     * @return the page from the book
+     /**
+      * Gets the specified page in the book. The given page must exist.
+      * <p>
+@@ -0,0 +0,0 @@ public interface BookMeta extends ItemMeta {
+      *
+      * @param page the page number to get, in range [1, getPageCount()]
+      * @return the page from the book
 +     * @deprecated in favour of {@link #page(int)}
-+     */
-+    @NotNull
+      */
+     @NotNull
 +    @Deprecated // Paper
-+    String getPage(int page);
-+
-+    /**
-+     * Sets the specified page in the book. Pages of the book must be
-+     * contiguous.
-+     * <p>
-+     * The data can be up to 256 characters in length, additional characters
-+     * are truncated.
-+     * <p>
-+     * Pages are 1-indexed.
-+     *
-+     * @param page the page number to set, in range [1, getPageCount()]
-+     * @param data the data to set for that page
+     String getPage(int page);
+ 
+     /**
+@@ -0,0 +0,0 @@ public interface BookMeta extends ItemMeta {
+      *
+      * @param page the page number to set, in range [1, getPageCount()]
+      * @param data the data to set for that page
 +     * @deprecated in favour of {@link #page(int, net.kyori.adventure.text.Component)}
-+     */
+      */
 +    @Deprecated // Paper
-+    void setPage(int page, @NotNull String data);
-+
-+    /**
-+     * Gets all the pages in the book.
-+     *
-+     * @return list of all the pages in the book
+     void setPage(int page, @NotNull String data);
+ 
+     /**
+      * Gets all the pages in the book.
+      *
+      * @return list of all the pages in the book
 +     * @deprecated in favour of {@link #pages()}
-+     */
-+    @NotNull
+      */
+     @NotNull
 +    @Deprecated // Paper
-+    List<String> getPages();
-+
-+    /**
-+     * Clears the existing book pages, and sets the book to use the provided
-+     * pages. Maximum 100 pages with 256 characters per page.
-+     *
-+     * @param pages A list of pages to set the book to use
+     List<String> getPages();
+ 
+     /**
+@@ -0,0 +0,0 @@ public interface BookMeta extends ItemMeta {
+      * pages. Maximum 100 pages with 256 characters per page.
+      *
+      * @param pages A list of pages to set the book to use
 +     * @deprecated in favour of {@link #pages(List)}
-+     */
+      */
 +    @Deprecated // Paper
-+    void setPages(@NotNull List<String> pages);
-+
-+    /**
-+     * Clears the existing book pages, and sets the book to use the provided
-+     * pages. Maximum 50 pages with 256 characters per page.
-+     *
-+     * @param pages A list of strings, each being a page
+     void setPages(@NotNull List<String> pages);
+ 
+     /**
+@@ -0,0 +0,0 @@ public interface BookMeta extends ItemMeta {
+      * pages. Maximum 50 pages with 256 characters per page.
+      *
+      * @param pages A list of strings, each being a page
 +     * @deprecated in favour of {@link #pages(net.kyori.adventure.text.Component...)}
-+     */
+      */
 +    @Deprecated // Paper
-+    void setPages(@NotNull String... pages);
-+
-+    /**
-+     * Adds new pages to the end of the book. Up to a maximum of 50 pages with
-+     * 256 characters per page.
-+     *
-+     * @param pages A list of strings, each being a page
+     void setPages(@NotNull String... pages);
+ 
+     /**
+@@ -0,0 +0,0 @@ public interface BookMeta extends ItemMeta {
+      * 256 characters per page.
+      *
+      * @param pages A list of strings, each being a page
 +     * @deprecated in favour of {@link #addPages(net.kyori.adventure.text.Component...)}
-+     */
+      */
 +    @Deprecated // Paper
      void addPage(@NotNull String... pages);
  
@@ -4239,6 +4179,24 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        this(x, y, direction, type, visible, (String) null); // Paper
      }
  
+     /**
+@@ -0,0 +0,0 @@ public final class MapCursor {
+      * @param type The type (color/style) of the map cursor.
+      * @param visible Whether the cursor is visible by default.
+      * @param caption cursor caption
+-     * @deprecated Magic value
++     * @deprecated Magic value. Use {@link #MapCursor(byte, byte, byte, byte, boolean, net.kyori.adventure.text.Component)}
+      */
+     @Deprecated
+     public MapCursor(byte x, byte y, byte direction, byte type, boolean visible, @Nullable String caption) {
+@@ -0,0 +0,0 @@ public final class MapCursor {
+         setDirection(direction);
+         setRawType(type);
+         this.visible = visible;
+-        this.caption = caption;
++        this.caption = caption == null ? null : net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(caption); // Paper
+     }
++    // Paper start
 +    /**
 +     * Initialize the map cursor.
 +     *
@@ -4248,35 +4206,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     * @param type The type (color/style) of the map cursor.
 +     * @param visible Whether the cursor is visible by default.
 +     * @param caption cursor caption
-+     * @deprecated Magic value. Use {@link #MapCursor(byte, byte, byte, byte, boolean, net.kyori.adventure.text.Component)}
++     * @deprecated Magic value
 +     */
 +    @Deprecated
-+    public MapCursor(byte x, byte y, byte direction, byte type, boolean visible, @Nullable String caption) {
-+        this.x = x;
-+        this.y = y;
-+        setDirection(direction);
-+        setRawType(type);
-+        this.visible = visible;
-+        this.caption = caption == null ? null : net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(caption); // Paper
-+    }
-+    // Paper start
-     /**
-      * Initialize the map cursor.
-      *
-@@ -0,0 +0,0 @@ public final class MapCursor {
-      * @deprecated Magic value
-      */
-     @Deprecated
--    public MapCursor(byte x, byte y, byte direction, byte type, boolean visible, @Nullable String caption) {
--        this.x = x;
--        this.y = y;
 +    public MapCursor(byte x, byte y, byte direction, byte type, boolean visible, net.kyori.adventure.text.@Nullable Component caption) {
 +        this.x = x; this.y = y; this.visible = visible; this.caption = caption;
-         setDirection(direction);
-         setRawType(type);
--        this.visible = visible;
--        this.caption = caption;
-     }
++        setDirection(direction);
++        setRawType(type);
++    }
 +    /**
 +     * Initialize the map cursor.
 +     *
@@ -4310,11 +4247,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      }
  
 +    // Paper start
-     /**
-      * Gets the caption on this cursor.
-      *
-      * @return caption
-      */
++    /**
++     * Gets the caption on this cursor.
++     *
++     * @return caption
++     */
 +    public net.kyori.adventure.text.@Nullable Component caption() {
 +        return this.caption;
 +    }
@@ -4327,12 +4264,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        this.caption = caption;
 +    }
 +    // Paper end
-+    /**
-+     * Gets the caption on this cursor.
-+     *
-+     * @return caption
+     /**
+      * Gets the caption on this cursor.
+      *
+      * @return caption
 +     * @deprecated in favour of {@link #caption()}
-+     */
+      */
      @Nullable
 +    @Deprecated // Paper
      public String getCaption() {
@@ -4445,14 +4382,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
       */
      @NotNull
      String getName() throws IllegalStateException;
--
 +    // Paper start
-     /**
-      * Gets the name displayed to players for this objective
-      *
-      * @return this objective's display name
-      * @throws IllegalStateException if this objective has been unregistered
-      */
++    /**
++     * Gets the name displayed to players for this objective
++     *
++     * @return this objective's display name
++     * @throws IllegalStateException if this objective has been unregistered
++     */
 +    net.kyori.adventure.text.@NotNull Component displayName() throws IllegalStateException;
 +    /**
 +     * Sets the name displayed to players for this objective.
@@ -4465,14 +4401,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     */
 +    void displayName(net.kyori.adventure.text.@Nullable Component displayName) throws IllegalStateException, IllegalArgumentException;
 +    // Paper end
-+
-+    /**
-+     * Gets the name displayed to players for this objective
-+     *
-+     * @return this objective's display name
-+     * @throws IllegalStateException if this objective has been unregistered
+ 
+     /**
+      * Gets the name displayed to players for this objective
+      *
+      * @return this objective's display name
+      * @throws IllegalStateException if this objective has been unregistered
 +     * @deprecated in favour of {@link #displayName()}
-+     */
+      */
      @NotNull
 +    @Deprecated // Paper
      String getDisplayName() throws IllegalStateException;
@@ -4631,14 +4567,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
       */
      @NotNull
      String getName() throws IllegalStateException;
--
 +    // Paper start
-     /**
-      * Gets the name displayed to entries for this team
-      *
-      * @return Team display name
-      * @throws IllegalStateException if this team has been unregistered
-      */
++    /**
++     * Gets the name displayed to entries for this team
++     *
++     * @return Team display name
++     * @throws IllegalStateException if this team has been unregistered
++     */
 +    net.kyori.adventure.text.@NotNull Component displayName() throws IllegalStateException;
 +
 +    /**
@@ -4716,14 +4651,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     */
 +    void color(net.kyori.adventure.text.format.@Nullable NamedTextColor color);
 +    // Paper end
-+
-+    /**
-+     * Gets the name displayed to entries for this team
-+     *
-+     * @return Team display name
-+     * @throws IllegalStateException if this team has been unregistered
+ 
+     /**
+      * Gets the name displayed to entries for this team
+      *
+      * @return Team display name
+      * @throws IllegalStateException if this team has been unregistered
 +     * @deprecated in favour of {@link #displayName()}
-+     */
+      */
      @NotNull
 +    @Deprecated // Paper
      String getDisplayName() throws IllegalStateException;
diff --git a/patches/api/EntityRegainHealthEvent-isFastRegen-API.patch b/patches/api/EntityRegainHealthEvent-isFastRegen-API.patch
index 90bb66c279..626f262300 100644
--- a/patches/api/EntityRegainHealthEvent-isFastRegen-API.patch
+++ b/patches/api/EntityRegainHealthEvent-isFastRegen-API.patch
@@ -25,8 +25,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          this.amount = amount;
          this.regainReason = regainReason;
 +        this.isFastRegen = isFastRegen; // Paper
-     }
- 
++    }
++
 +    // Paper start - Add getter for isFastRegen
 +    /**
 +     * Is this event a result of the fast regeneration mechanic
@@ -35,9 +35,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     */
 +    public boolean isFastRegen() {
 +        return isFastRegen;
-+    }
+     }
 +    // Paper end
-+
+ 
      /**
       * Gets the amount of regained health
-      *
diff --git a/patches/api/EntityShootBowEvent-consumeArrow-and-getArrowItem-AP.patch b/patches/api/EntityShootBowEvent-consumeArrow-and-getArrowItem-AP.patch
index eb2a965cd6..7798907f76 100644
--- a/patches/api/EntityShootBowEvent-consumeArrow-and-getArrowItem-AP.patch
+++ b/patches/api/EntityShootBowEvent-consumeArrow-and-getArrowItem-AP.patch
@@ -18,7 +18,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    public void setConsumeArrow(boolean consumeArrow) {
 +        this.setConsumeItem(consumeArrow);
 +    }
- 
++
 +    @Deprecated
 +    public boolean getConsumeArrow() {
 +        return this.shouldConsumeItem();
@@ -33,7 +33,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    public EntityShootBowEvent(@NotNull final LivingEntity shooter, @Nullable final ItemStack bow, @NotNull final Entity projectile, final float force) {
 +        this(shooter, bow, new ItemStack(org.bukkit.Material.AIR), projectile, force);
 +    }
-+
+ 
 +    @Deprecated
 +    public EntityShootBowEvent(@NotNull final LivingEntity shooter, @Nullable final ItemStack bow, @NotNull ItemStack arrowItem, @NotNull final Entity projectile, final float force) {
 +        this(shooter, bow, arrowItem, projectile, EquipmentSlot.HAND, force, true);
diff --git a/patches/api/Expand-world-key-API.patch b/patches/api/Expand-world-key-API.patch
index d72f764b2b..070f843b12 100644
--- a/patches/api/Expand-world-key-API.patch
+++ b/patches/api/Expand-world-key-API.patch
@@ -110,14 +110,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      public WorldCreator(@NotNull String name) {
 -        if (name == null) {
 -            throw new IllegalArgumentException("World name cannot be null");
--        }
--
--        this.name = name;
--        this.seed = (new Random()).nextLong();
 +        // Paper start
 +        this(name, getWorldKey(name));
-     }
- 
++    }
++
 +    private static NamespacedKey getWorldKey(String name) {
 +        final String mainLevelName = Bukkit.getUnsafe().getMainLevelName();
 +        if (name.equals(mainLevelName)) {
@@ -128,9 +124,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            return NamespacedKey.minecraft("the_end");
 +        } else {
 +            return NamespacedKey.minecraft(name.toLowerCase(java.util.Locale.ENGLISH).replace(" ", "_"));
-+        }
+         }
 +    }
-+
+ 
+-        this.name = name;
 +    /**
 +     * Creates an empty WorldCreator for the given world name and key
 +     *
@@ -142,7 +139,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            throw new IllegalArgumentException("World name and key cannot be null");
 +        }
 +        this.name = levelName;
-+        this.seed = (new Random()).nextLong();
+         this.seed = (new Random()).nextLong();
 +        this.key = worldKey;
 +    }
 +
@@ -186,9 +183,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    @NotNull
 +    public static WorldCreator ofKey(@NotNull NamespacedKey worldKey) {
 +        return new WorldCreator(worldKey);
-+    }
+     }
 +    // Paper end
-+
+ 
      /**
       * Copies the options from the specified world
-      *
diff --git a/patches/api/Improve-PortalEvents.patch b/patches/api/Improve-PortalEvents.patch
index 8556fa47eb..fd6fea0745 100644
--- a/patches/api/Improve-PortalEvents.patch
+++ b/patches/api/Improve-PortalEvents.patch
@@ -23,8 +23,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          super(entity, from, to);
          this.searchRadius = searchRadius;
 +        this.type = org.bukkit.PortalType.CUSTOM; // Paper
-     }
- 
++    }
++
 +    // Paper start
 +    public EntityPortalEvent(@NotNull Entity entity, @NotNull Location from, @Nullable Location to, int searchRadius, final @NotNull org.bukkit.PortalType portalType) {
 +        super(entity, from, to);
@@ -63,12 +63,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    @Override
 +    public void setTo(@Nullable final Location to) {
 +        super.setTo(to);
-+    }
+     }
 +    // Paper end
-+
+ 
      /**
       * Set the Block radius to search in for available portals.
-      *
 diff --git a/src/main/java/org/bukkit/event/player/PlayerPortalEvent.java b/src/main/java/org/bukkit/event/player/PlayerPortalEvent.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/org/bukkit/event/player/PlayerPortalEvent.java
diff --git a/patches/api/Inventory-removeItemAnySlot.patch b/patches/api/Inventory-removeItemAnySlot.patch
index 6d6b95a34f..f4d1e8431f 100644
--- a/patches/api/Inventory-removeItemAnySlot.patch
+++ b/patches/api/Inventory-removeItemAnySlot.patch
@@ -16,6 +16,25 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     * Removes the given ItemStacks from the storage contents of the inventory.
 +     * For removing ItemStacks from the inventories that have other content groups,
 +     * like Player inventories, see {@link #removeItemAnySlot(ItemStack...)}.
+      * <p>
+      * It will try to remove 'as much as possible' from the types and amounts
+      * you give as arguments.
+@@ -0,0 +0,0 @@ public interface Inventory extends Iterable<ItemStack> {
+      * @param items The ItemStacks to remove
+      * @return A HashMap containing items that couldn't be removed.
+      * @throws IllegalArgumentException if items is null
++     * @see #removeItemAnySlot(ItemStack...)
+      */
+     @NotNull
+     public HashMap<Integer, ItemStack> removeItem(@NotNull ItemStack... items) throws IllegalArgumentException;
+ 
++    // Paper start
++    /**
++     * Searches all possible inventory slots in order to remove the given ItemStacks.
++     * <p>
++     * Similar to {@link Inventory#removeItem(ItemStack...)} in behavior, except this
++     * method will check all possible slots in the inventory, rather than just the main
++     * storage contents.
 +     * <p>
 +     * It will try to remove 'as much as possible' from the types and amounts
 +     * you give as arguments.
@@ -32,28 +51,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     * @param items The ItemStacks to remove
 +     * @return A HashMap containing items that couldn't be removed.
 +     * @throws IllegalArgumentException if items is null
-+     * @see #removeItemAnySlot(ItemStack...)
 +     */
 +    @NotNull
-+    public HashMap<Integer, ItemStack> removeItem(@NotNull ItemStack... items) throws IllegalArgumentException;
-+
-+    // Paper start
-+    /**
-+     * Searches all possible inventory slots in order to remove the given ItemStacks.
-+     * <p>
-+     * Similar to {@link Inventory#removeItem(ItemStack...)} in behavior, except this
-+     * method will check all possible slots in the inventory, rather than just the main
-+     * storage contents.
-      * <p>
-      * It will try to remove 'as much as possible' from the types and amounts
-      * you give as arguments.
-@@ -0,0 +0,0 @@ public interface Inventory extends Iterable<ItemStack> {
-      * @throws IllegalArgumentException if items is null
-      */
-     @NotNull
--    public HashMap<Integer, ItemStack> removeItem(@NotNull ItemStack... items) throws IllegalArgumentException;
 +    public HashMap<Integer, ItemStack> removeItemAnySlot(@NotNull ItemStack... items) throws IllegalArgumentException;
 +    // Paper end
- 
++
      /**
       * Returns all ItemStacks from the inventory
+      *
diff --git a/patches/api/Paper-Plugins.patch b/patches/api/Paper-Plugins.patch
index 71587d11eb..d4b3dba87f 100644
--- a/patches/api/Paper-Plugins.patch
+++ b/patches/api/Paper-Plugins.patch
@@ -2194,7 +2194,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
 -        javaPlugin.init(loader, loader.server, description, dataFolder, file, this);
 +        javaPlugin.init(null, org.bukkit.Bukkit.getServer(), description, dataFolder, file, this); // Paper
-     }
++    }
 +
 +    // Paper start
 +    @Override
@@ -2219,7 +2219,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            Class<? extends org.bukkit.configuration.serialization.ConfigurationSerializable> serializable = clazz.asSubclass(org.bukkit.configuration.serialization.ConfigurationSerializable.class);
 +            org.bukkit.configuration.serialization.ConfigurationSerialization.unregisterClass(serializable);
 +        }
-+    }
+     }
 +
 +    @Override
 +    public @Nullable io.papermc.paper.plugin.provider.classloader.PluginClassLoaderGroup getGroup() {
diff --git a/patches/api/Test-changes.patch b/patches/api/Test-changes.patch
index 8606c8ae36..c96d72c436 100644
--- a/patches/api/Test-changes.patch
+++ b/patches/api/Test-changes.patch
@@ -288,12 +288,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                return true;
 +            }
 +        }
- 
--    private static boolean isWellAnnotated(@Nullable List<AnnotationNode> annotations) {
++
 +        return false;
 +    }
 +    // Paper end
-+
+ 
+-    private static boolean isWellAnnotated(@Nullable List<AnnotationNode> annotations) {
 +    private static boolean isWellAnnotated(@Nullable List<? extends AnnotationNode> annotations) { // Paper
          if (annotations == null) {
              return false;
diff --git a/patches/api/Timings-v2.patch b/patches/api/Timings-v2.patch
index 3ea0a26aac..6b1c3bc64a 100644
--- a/patches/api/Timings-v2.patch
+++ b/patches/api/Timings-v2.patch
@@ -3400,12 +3400,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          public void sendMessage(@NotNull net.md_5.bungee.api.ChatMessageType position, @Nullable java.util.UUID sender, @NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
              throw new UnsupportedOperationException("Not supported yet.");
 +
-         }
++        }
 +
 +        // Paper start
 +        public int getPing() {
 +            throw new UnsupportedOperationException( "Not supported yet." );
-+        }
+         }
 +        // Paper end
      }
  
@@ -3611,8 +3611,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      public CustomTimingsHandler(@NotNull String name) {
 -        this(name, null);
 -    }
-+        Timing timing;
- 
+-
 -    public CustomTimingsHandler(@NotNull String name, @Nullable CustomTimingsHandler parent) {
 -        this.name = name;
 -        this.parent = parent;
@@ -3635,16 +3634,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -            long avg = time / count;
 -
 -            printStream.println("    " + timings.name + " Time: " + time + " Count: " + count + " Avg: " + avg + " Violations: " + timings.violations);
-+        new AuthorNagException("Deprecated use of CustomTimingsHandler. Please Switch to Timings.of ASAP").printStackTrace();
-+        try {
-+            final Method ofSafe = TimingsManager.class.getDeclaredMethod("getHandler", String.class, String.class, Timing.class);
-+            ofSafe.setAccessible(true);
-+            timing = (Timing) ofSafe.invoke(null,"Minecraft", "(Deprecated API) " + name, null);
-+        } catch (Exception e) {
-+            e.printStackTrace();
-+            Bukkit.getLogger().log(Level.SEVERE, "This handler could not be registered");
-+            timing = Timings.NULL_HANDLER;
-         }
+-        }
 -        printStream.println("# Version " + Bukkit.getVersion());
 -        int entities = 0;
 -        int livingEntities = 0;
@@ -3654,9 +3644,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        }
 -        printStream.println("# Entities " + entities);
 -        printStream.println("# LivingEntities " + livingEntities);
-+        handler = timing;
-     }
- 
+-    }
+-
 -    /**
 -     * Resets all timings.
 -     */
@@ -3668,9 +3657,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        }
 -        TimingsCommand.timingStart = System.nanoTime();
 -    }
-+    public void startTiming() { handler.startTiming(); }
-+    public void stopTiming() { handler.stopTiming(); }
- 
+-
 -    /**
 -     * Ticked every tick by CraftBukkit to count the number of times a timer
 -     * caused TPS loss.
@@ -3686,7 +3673,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -            }
 -        }
 -    }
--
++        Timing timing;
+ 
 -    /**
 -     * Starts timing to track a section of code.
 -     */
@@ -3697,9 +3685,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -            if (parent != null && ++parent.timingDepth == 1) {
 -                parent.start = start;
 -            }
--        }
--    }
--
++        new AuthorNagException("Deprecated use of CustomTimingsHandler. Please Switch to Timings.of ASAP").printStackTrace();
++        try {
++            final Method ofSafe = TimingsManager.class.getDeclaredMethod("getHandler", String.class, String.class, Timing.class);
++            ofSafe.setAccessible(true);
++            timing = (Timing) ofSafe.invoke(null,"Minecraft", "(Deprecated API) " + name, null);
++        } catch (Exception e) {
++            e.printStackTrace();
++            Bukkit.getLogger().log(Level.SEVERE, "This handler could not be registered");
++            timing = Timings.NULL_HANDLER;
+         }
++        handler = timing;
+     }
+ 
 -    /**
 -     * Stops timing a section of code.
 -     */
@@ -3718,7 +3716,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -            }
 -        }
 -    }
--
++    public void startTiming() { handler.startTiming(); }
++    public void stopTiming() { handler.stopTiming(); }
+ 
 -    /**
 -     * Reset this timer, setting all values to zero.
 -     */
diff --git a/patches/server/Add-PlayerKickEvent-causes.patch b/patches/server/Add-PlayerKickEvent-causes.patch
index 896f0a7985..5f617fe18c 100644
--- a/patches/server/Add-PlayerKickEvent-causes.patch
+++ b/patches/server/Add-PlayerKickEvent-causes.patch
@@ -146,13 +146,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      public void disconnect(final Component reason) {
 -        this.disconnect(PaperAdventure.asAdventure(reason));
 +        this.disconnect(PaperAdventure.asAdventure(reason), org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN);
++    }
++
++    public void disconnect(final Component reason, PlayerKickEvent.Cause cause) {
++        this.disconnect(PaperAdventure.asAdventure(reason), cause);
      }
  
 -    public void disconnect(net.kyori.adventure.text.Component reason) {
-+    public void disconnect(final Component reason, PlayerKickEvent.Cause cause) {
-+        this.disconnect(PaperAdventure.asAdventure(reason), cause);
-+    }
-+
 +    public void disconnect(net.kyori.adventure.text.Component reason, org.bukkit.event.player.PlayerKickEvent.Cause cause) {
          // Paper end
          // CraftBukkit start - fire PlayerKickEvent
diff --git a/patches/server/Add-PlayerUseUnknownEntityEvent.patch b/patches/server/Add-PlayerUseUnknownEntityEvent.patch
index 4f805e11af..5c15202c7d 100644
--- a/patches/server/Add-PlayerUseUnknownEntityEvent.patch
+++ b/patches/server/Add-PlayerUseUnknownEntityEvent.patch
@@ -48,8 +48,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                }
 +            });
 +        }
- 
-     }
++
++    }
  
 +    private void callPlayerUseUnknownEntityEvent(ServerboundInteractPacket packet, InteractionHand hand) {
 +        this.cserver.getPluginManager().callEvent(new com.destroystokyo.paper.event.player.PlayerUseUnknownEntityEvent(
@@ -58,9 +58,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            packet.getActionType() == ServerboundInteractPacket.ActionType.ATTACK,
 +            hand == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND
 +        ));
-+    }
+     }
 +    // Paper end
-+
+ 
      @Override
      public void handleClientCommand(ServerboundClientCommandPacket packet) {
-         PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel());
diff --git a/patches/server/Add-Plugin-Tickets-to-API-Chunk-Methods.patch b/patches/server/Add-Plugin-Tickets-to-API-Chunk-Methods.patch
index 75f9234a3f..cbb6022325 100644
--- a/patches/server/Add-Plugin-Tickets-to-API-Chunk-Methods.patch
+++ b/patches/server/Add-Plugin-Tickets-to-API-Chunk-Methods.patch
@@ -60,17 +60,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        }
 +        return chunk.bukkitChunk;
 +        // Paper end
-     }
- 
++    }
++
 +    // Paper start
 +    private void addTicket(int x, int z) {
 +        io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> world.getChunkSource().addRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 0, Unit.INSTANCE)); // Paper
-+    }
+     }
 +    // Paper end
-+
+ 
      @Override
      public Chunk getChunkAt(Block block) {
-         Preconditions.checkArgument(block != null, "null block");
 @@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
      public boolean unloadChunkRequest(int x, int z) {
          org.spigotmc.AsyncCatcher.catchOp("chunk unload"); // Spigot
diff --git a/patches/server/Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch b/patches/server/Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch
index 6e0d522f02..3497a939e3 100644
--- a/patches/server/Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch
+++ b/patches/server/Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch
@@ -35,19 +35,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay");
 +        }
 +        // Paper end
-     }
- 
++    }
++
 +    // Paper start
 +    @Override
 +    public void addAdditionalSaveData(CompoundTag nbt) {
 +        super.addAdditionalSaveData(nbt);
 +        nbt.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay);
-+    }
+     }
 +    // Paper end
-+
+ 
      @Override
      public void setItemSlot(EquipmentSlot slot, ItemStack stack) {
-         super.setItemSlot(slot, stack);
 diff --git a/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java
diff --git a/patches/server/Add-more-Witch-API.patch b/patches/server/Add-more-Witch-API.patch
index d2bc35cd11..e521347307 100644
--- a/patches/server/Add-more-Witch-API.patch
+++ b/patches/server/Add-more-Witch-API.patch
@@ -39,9 +39,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -                    if (!this.isSilent()) {
 -                        this.level.playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.WITCH_DRINK, this.getSoundSource(), 1.0F, 0.8F + this.random.nextFloat() * 0.4F);
 -                    }
- 
--                    AttributeInstance attributemodifiable = this.getAttribute(Attributes.MOVEMENT_SPEED);
 -
+-                    AttributeInstance attributemodifiable = this.getAttribute(Attributes.MOVEMENT_SPEED);
+ 
 -                    attributemodifiable.removeModifier(Witch.SPEED_MODIFIER_DRINKING);
 -                    attributemodifiable.addTransientModifier(Witch.SPEED_MODIFIER_DRINKING);
                  }
diff --git a/patches/server/Add-more-advancement-API.patch b/patches/server/Add-more-advancement-API.patch
index 7aac2ff3a3..2650b5ff1d 100644
--- a/patches/server/Add-more-advancement-API.patch
+++ b/patches/server/Add-more-advancement-API.patch
@@ -137,12 +137,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -    public AdvancementDisplay getDisplay() {
 -        if (this.handle.getDisplay() == null) {
 -            return null;
--        }
--
--        return new CraftAdvancementDisplay(this.handle.getDisplay());
 +    public io.papermc.paper.advancement.AdvancementDisplay getDisplay() {
 +        return this.handle.getDisplay() == null ? null : this.handle.getDisplay().paper;
-     }
++    }
 +
 +    @Deprecated @io.papermc.paper.annotation.DoNotUse
 +    public AdvancementDisplay getDisplay0() { // May be called by plugins via Commodore
@@ -164,10 +161,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        final var children = com.google.common.collect.ImmutableList.<org.bukkit.advancement.Advancement>builder();
 +        for (Advancement advancement : this.handle.getChildren()) {
 +            children.add(advancement.bukkit);
-+        }
+         }
 +        return children.build();
 +    }
-+
+ 
+-        return new CraftAdvancementDisplay(this.handle.getDisplay());
 +    @Override
 +    public org.bukkit.advancement.Advancement getRoot() {
 +        Advancement advancement = this.handle;
@@ -175,7 +173,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            advancement = advancement.getParent();
 +        }
 +        return advancement.bukkit;
-+    }
+     }
 +    // Paper end
  }
 diff --git a/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancementDisplay.java b/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancementDisplay.java
diff --git a/patches/server/Adventure.patch b/patches/server/Adventure.patch
index d843cdee26..e32d01bff3 100644
--- a/patches/server/Adventure.patch
+++ b/patches/server/Adventure.patch
@@ -2322,14 +2322,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -    @Deprecated
 -    public void disconnect(Component reason) {
 -        this.disconnect(CraftChatMessage.fromComponent(reason));
--    }
--    // CraftBukkit end
--
-     public void disconnect(String s) {
++    public void disconnect(String s) {
 +        // Paper start
 +        this.disconnect(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(s));
-+    }
-+
+     }
+-    // CraftBukkit end
+ 
+-    public void disconnect(String s) {
 +    public void disconnect(final Component reason) {
 +        this.disconnect(PaperAdventure.asAdventure(reason));
 +    }
@@ -2810,13 +2809,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      }
  
 +    // Paper start
-     @Override
++    @Override
 +    public net.kyori.adventure.text.Component shutdownMessage() {
 +        String msg = getShutdownMessage();
 +        return msg != null ? net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(msg) : null;
 +    }
 +    // Paper end
-+    @Override
+     @Override
 +    @Deprecated // Paper
      public String getShutdownMessage() {
          return this.configuration.getString("settings.shutdown-message");
@@ -2893,12 +2892,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      }
  
 +    // Paper start
-     @Override
++    @Override
 +    public Merchant createMerchant(net.kyori.adventure.text.Component title) {
 +        return new org.bukkit.craftbukkit.inventory.CraftMerchantCustom(title == null ? InventoryType.MERCHANT.defaultTitle() : title);
 +    }
 +    // Paper end
-+    @Override
+     @Override
 +    @Deprecated // Paper
      public Merchant createMerchant(String title) {
          return new CraftMerchantCustom(title == null ? InventoryType.MERCHANT.getDefaultTitle() : title);
diff --git a/patches/server/Allow-adding-items-to-BlockDropItemEvent.patch b/patches/server/Allow-adding-items-to-BlockDropItemEvent.patch
index 5666d16b45..4367155fda 100644
--- a/patches/server/Allow-adding-items-to-BlockDropItemEvent.patch
+++ b/patches/server/Allow-adding-items-to-BlockDropItemEvent.patch
@@ -31,13 +31,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                    Entity item = ((org.bukkit.craftbukkit.entity.CraftItem) bukkit).getHandle();
 +                    item.level.addFreshEntity(item);
 +                }
-             }
++            }
 +        } else {
 +            for (Item bukkit : list) {
 +                if (bukkit.isValid()) {
 +                    bukkit.remove();
 +                }
-+            }
+             }
 +            // Paper end
          }
      }
diff --git a/patches/server/Always-parse-protochunk-light-sources-unless-it-is-m.patch b/patches/server/Always-parse-protochunk-light-sources-unless-it-is-m.patch
index addcc42cca..26076c8d0f 100644
--- a/patches/server/Always-parse-protochunk-light-sources-unless-it-is-m.patch
+++ b/patches/server/Always-parse-protochunk-light-sources-unless-it-is-m.patch
@@ -21,14 +21,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                // Paper start - let's make sure the implementation isn't as slow as possible
 +                int offX = chunkPos.x << 4;
 +                int offZ = chunkPos.z << 4;
- 
--                while (iterator.hasNext()) {
--                    BlockPos blockposition = (BlockPos) iterator.next();
++
 +                int minChunkSection = io.papermc.paper.util.WorldUtil.getMinSection(world);
 +                int maxChunkSection = io.papermc.paper.util.WorldUtil.getMaxSection(world);
- 
--                    if (((ChunkAccess) object1).getBlockState(blockposition).getLightEmission() != 0) {
--                        protochunk.addLight(blockposition);
++
 +                LevelChunkSection[] sections = achunksection;
 +                for (int sectionY = minChunkSection; sectionY <= maxChunkSection; ++sectionY) {
 +                    LevelChunkSection section = sections[sectionY - minChunkSection];
@@ -37,12 +33,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                        continue;
 +                    }
 +                    int offY = sectionY << 4;
-+
+ 
+-                while (iterator.hasNext()) {
+-                    BlockPos blockposition = (BlockPos) iterator.next();
 +                    for (int index = 0; index < (16 * 16 * 16); ++index) {
 +                        if (section.states.get(index).getLightEmission() <= 0) {
 +                            continue;
 +                        }
-+
+ 
+-                    if (((ChunkAccess) object1).getBlockState(blockposition).getLightEmission() != 0) {
+-                        protochunk.addLight(blockposition);
 +                        // index = x | (z << 4) | (y << 8)
 +                        protochunk.addLight(new BlockPos(offX | (index & 15), offY | (index >>> 8), offZ | ((index >>> 4) & 15)));
                      }
diff --git a/patches/server/Anti-Xray.patch b/patches/server/Anti-Xray.patch
index 7ee5261208..570bfe23b2 100644
--- a/patches/server/Anti-Xray.patch
+++ b/patches/server/Anti-Xray.patch
@@ -1388,8 +1388,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        this.addPresetValues();
 +        return object == null ? -1 : data2.palette.idFor(object);
 +        // Paper end
-     }
- 
++    }
++
 +    // Paper start - Anti-Xray - Add preset values
 +    private void addPresetValues() {
 +        if (this.presetValues != null && this.data.configuration().factory() != PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY) {
@@ -1397,12 +1397,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                this.data.palette.idFor(presetValue);
 +            }
 +        }
-+    }
+     }
 +    // Paper end
-+
+ 
      public T getAndSet(int x, int y, int z, T value) {
          this.acquire();
- 
 @@ -0,0 +0,0 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
              data.palette.read(buf);
              buf.readLongArray(data.storage.getRaw());
@@ -1415,10 +1414,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      }
  
 +    // Paper start - Anti-Xray - Add chunk packet info
++    @Override
++    @Deprecated @io.papermc.paper.annotation.DoNotUse public void write(FriendlyByteBuf buf) { this.write(buf, null, 0); }
      @Override
 -    public void write(FriendlyByteBuf buf) {
-+    @Deprecated @io.papermc.paper.annotation.DoNotUse public void write(FriendlyByteBuf buf) { this.write(buf, null, 0); }
-+    @Override
 +    public void write(FriendlyByteBuf buf, @Nullable com.destroystokyo.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int bottomBlockY) {
          this.acquire();
  
diff --git a/patches/server/AsyncTabCompleteEvent.patch b/patches/server/AsyncTabCompleteEvent.patch
index b2eec8b74f..d88698d78a 100644
--- a/patches/server/AsyncTabCompleteEvent.patch
+++ b/patches/server/AsyncTabCompleteEvent.patch
@@ -49,6 +49,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          if (stringreader.canRead() && stringreader.peek() == '/') {
              stringreader.skip();
          }
+-
+-        ParseResults<CommandSourceStack> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
+-
+-        this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
+-            if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
+-            this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions));
 +            final String command = packet.getCommand();
 +            final com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event = new com.destroystokyo.paper.event.server.AsyncTabCompleteEvent(this.getCraftPlayer(), command, true, null);
 +            event.callEvent();
@@ -56,14 +62,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            // If the event isn't handled, we can assume that we have no completions, and so we'll ask the server
 +            if (!event.isHandled()) {
 +                if (!event.isCancelled()) {
- 
--        ParseResults<CommandSourceStack> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
++
 +                    this.server.scheduleOnMain(() -> { // This needs to be on main
 +                        ParseResults<CommandSourceStack> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
- 
--        this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
--            if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
--            this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions));
++
 +                        this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
 +                            if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
 +                            this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions));
diff --git a/patches/server/Attempt-to-recalculate-regionfile-header-if-it-is-co.patch b/patches/server/Attempt-to-recalculate-regionfile-header-if-it-is-co.patch
index 2f43f9b6a1..e56d40017e 100644
--- a/patches/server/Attempt-to-recalculate-regionfile-header-if-it-is-co.patch
+++ b/patches/server/Attempt-to-recalculate-regionfile-header-if-it-is-co.patch
@@ -509,7 +509,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                          } else {
 -                            this.usedSectors.force(i1, j1);
 +                            //this.usedSectors.force(i1, j1); // Paper - move this down so we can check if it fails to allocate
-                         }
++                        }
 +                        // Paper start - recalculate header on header corruption
 +                        if (offset < 2 || sectorLength <= 0 || ((long)offset * 4096L) > regionFileSize) {
 +                            if (canRecalcHeader) {
@@ -532,7 +532,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                        boolean failedToAllocate = !this.usedSectors.tryAllocate(offset, sectorLength);
 +                        if (failedToAllocate) {
 +                            LOGGER.error("Overlapping allocation by local chunk (" + (headerLocation & 31) + "," + (headerLocation >>> 5) + ") in regionfile " + this.regionFile.toAbsolutePath());
-+                        }
+                         }
 +                        if (failedToAllocate & !canRecalcHeader) {
 +                            // location = chunkX | (chunkZ << 5);
 +                            LOGGER.error("Detected invalid header for regionfile " + this.regionFile.toAbsolutePath() +
diff --git a/patches/server/Configurable-Keep-Spawn-Loaded-range-per-world.patch b/patches/server/Configurable-Keep-Spawn-Loaded-range-per-world.patch
index 01ee3a8a64..4482a894c8 100644
--- a/patches/server/Configurable-Keep-Spawn-Loaded-range-per-world.patch
+++ b/patches/server/Configurable-Keep-Spawn-Loaded-range-per-world.patch
@@ -31,18 +31,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        // CraftBukkit start
 -        if (worldserver.getWorld().getKeepSpawnInMemory()) {
 -            chunkproviderserver.addRegionTicket(TicketType.START, new ChunkPos(blockposition), 11, Unit.INSTANCE);
+-
+-            while (chunkproviderserver.getTickingGenerated() != 441) {
+-                // this.nextTickTime = SystemUtils.getMillis() + 10L;
+-                this.executeModerately();
+-            }
+-        }
 +        // Paper start - configurable spawn reason
 +        int radiusBlocks = worldserver.paperConfig().spawn.keepSpawnLoadedRange * 16;
 +        int radiusChunks = radiusBlocks / 16 + ((radiusBlocks & 15) != 0 ? 1 : 0);
 +        int totalChunks = ((radiusChunks) * 2 + 1);
 +        totalChunks *= totalChunks;
 +        worldloadlistener.setChunkRadius(radiusBlocks / 16);
- 
--            while (chunkproviderserver.getTickingGenerated() != 441) {
--                // this.nextTickTime = SystemUtils.getMillis() + 10L;
--                this.executeModerately();
--            }
--        }
++
 +        worldserver.addTicketsForSpawn(radiusBlocks, blockposition);
 +        // Paper end
  
diff --git a/patches/server/Entity-Activation-Range-2.0.patch b/patches/server/Entity-Activation-Range-2.0.patch
index cf39a011f3..2b0b6da9ca 100644
--- a/patches/server/Entity-Activation-Range-2.0.patch
+++ b/patches/server/Entity-Activation-Range-2.0.patch
@@ -264,14 +264,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        // Paper start
 +        if (this.getUnhappyCounter() > 0) {
 +            this.setUnhappyCounter(this.getUnhappyCounter() - 1);
-         }
++        }
 +        if (this.isEffectiveAi()) {
 +            if (level.spigotConfig.tickInactiveVillagers) {
 +                this.customServerAiStep();
 +            } else {
 +                this.mobTick(true);
 +            }
-+        }
+         }
 +        maybeDecayGossip();
 +        // Paper end
 +
@@ -569,16 +569,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -    public static boolean checkEntityImmunities(Entity entity)
 +    public static int checkEntityImmunities(Entity entity) // Paper - return # of ticks to get immunity
      {
--        // quick checks.
--        if ( entity.wasTouchingWater || entity.remainingFireTicks > 0 )
--        {
--            return true;
 +        // Paper start
 +        SpigotWorldConfig config = entity.level.spigotConfig;
 +        int inactiveWakeUpImmunity = checkInactiveWakeup(entity);
 +        if (inactiveWakeUpImmunity > -1) {
 +            return inactiveWakeUpImmunity;
-         }
++        }
 +        if (entity.remainingFireTicks > 0) {
 +            return 2;
 +        }
@@ -587,16 +583,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        }
 +        long inactiveFor = MinecraftServer.currentTick - entity.activatedTick;
 +        // Paper end
-+        // quick checks.
+         // quick checks.
+-        if ( entity.wasTouchingWater || entity.remainingFireTicks > 0 )
 +        if ( (entity.activationType != ActivationType.WATER && entity.wasTouchingWater && entity.isPushedByFluid()) ) // Paper
-+        {
+         {
+-            return true;
 +            return 100; // Paper
 +        }
 +        // Paper start
 +        if ( !entity.isOnGround() || entity.getDeltaMovement().horizontalDistanceSqr() > 9.999999747378752E-6D )
 +        {
 +            return 100;
-+        }
+         }
 +        // Paper end
          if ( !( entity instanceof AbstractArrow ) )
          {
@@ -626,8 +624,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              {
 -                return true;
 +                return 20; // Paper
-             }
--            if ( entity instanceof Villager && ( (Villager) entity ).canBreed() )
++            }
 +            // Paper start
 +            if (entity instanceof Bee) {
 +                Bee bee = (Bee)entity;
@@ -655,7 +652,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                        return config.villagersWorkImmunityFor;
 +                    }
 +                }
-+            }
+             }
+-            if ( entity instanceof Villager && ( (Villager) entity ).canBreed() )
 +            if ( entity instanceof Llama && ( (Llama) entity ).inCaravan() )
              {
 -                return true;
@@ -679,11 +677,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              if (entity instanceof Creeper && ((Creeper) entity).isIgnited()) { // isExplosive
 -                return true;
 +                return 20; // Paper
-             }
++            }
 +            // Paper start
 +            if (entity instanceof Mob && ((Mob) entity).targetSelector.hasTasks() ) {
 +                return 0;
-+            }
+             }
 +            if (entity instanceof Pillager) {
 +                Pillager pillager = (Pillager) entity;
 +                // TODO:?
diff --git a/patches/server/Entity-Origin-API.patch b/patches/server/Entity-Origin-API.patch
index df175f257c..f8faf46245 100644
--- a/patches/server/Entity-Origin-API.patch
+++ b/patches/server/Entity-Origin-API.patch
@@ -37,12 +37,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    private org.bukkit.util.Vector origin;
 +    @javax.annotation.Nullable
 +    private UUID originWorld;
- 
++
 +    public void setOrigin(@javax.annotation.Nonnull Location location) {
 +        this.origin = location.toVector();
 +        this.originWorld = location.getWorld().getUID();
 +    }
-+
+ 
 +    @javax.annotation.Nullable
 +    public org.bukkit.util.Vector getOriginVector() {
 +        return this.origin != null ? this.origin.clone() : null;
diff --git a/patches/server/ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch b/patches/server/ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch
index b8bfcade0a..0c3033c257 100644
--- a/patches/server/ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch
+++ b/patches/server/ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch
@@ -32,7 +32,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    public java.util.UUID sourceEntityId;
 +    public java.util.UUID triggerEntityId;
 +    public org.bukkit.entity.ExperienceOrb.SpawnReason spawnReason = org.bukkit.entity.ExperienceOrb.SpawnReason.UNKNOWN;
- 
++
 +    private void loadPaperNBT(CompoundTag nbttagcompound) {
 +        if (!nbttagcompound.contains("Paper.ExpData", 10)) { // 10 = compound
 +            return;
@@ -66,7 +66,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        }
 +        nbttagcompound.put("Paper.ExpData", comp);
 +    }
-+
+ 
 +    @io.papermc.paper.annotation.DoNotUse
 +    @Deprecated
      public ExperienceOrb(Level world, double x, double y, double z, int amount) {
diff --git a/patches/server/Fix-GameProfileCache-concurrency.patch b/patches/server/Fix-GameProfileCache-concurrency.patch
index d6654c804d..02c7fbd628 100644
--- a/patches/server/Fix-GameProfileCache-concurrency.patch
+++ b/patches/server/Fix-GameProfileCache-concurrency.patch
@@ -112,8 +112,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        return ImmutableList.copyOf(this.profilesByUUID.values()).stream().sorted(Comparator.comparing(GameProfileCache.GameProfileInfo::getLastAccess).reversed()).limit((long) limit);
 +        // Paper start - allow better concurrency
 +        return this.listTopMRUProfiles(limit).stream();
-     }
- 
++    }
++
 +    private List<GameProfileCache.GameProfileInfo> listTopMRUProfiles(int limit) {
 +        try {
 +            this.stateLock.lock();
@@ -121,9 +121,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        } finally {
 +            this.stateLock.unlock();
 +        }
-+    }
+     }
 +    // Paper end
-+
+ 
      private static JsonElement writeGameProfile(GameProfileCache.GameProfileInfo entry, DateFormat dateFormat) {
          JsonObject jsonobject = new JsonObject();
- 
diff --git a/patches/server/Fix-World-isChunkGenerated-calls.patch b/patches/server/Fix-World-isChunkGenerated-calls.patch
index fc71f0fbf1..87217f196a 100644
--- a/patches/server/Fix-World-isChunkGenerated-calls.patch
+++ b/patches/server/Fix-World-isChunkGenerated-calls.patch
@@ -197,6 +197,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        if (chunk instanceof ImposterProtoChunk) {
 -            // We then cycle through again to get the full chunk immediately, rather than after the ticket addition
 -            chunk = this.world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true);
+-        }
 +        if (!generate) {
 +            ChunkAccess immediate = world.getChunkSource().getChunkAtImmediately(x, z);
 +            if (immediate == null) {
@@ -210,7 +211,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                world.getChunk(x, z); // make sure we're at ticket level 32 or lower
 +                return true;
 +            }
-+
+ 
+-        if (chunk instanceof net.minecraft.world.level.chunk.LevelChunk) {
+-            this.world.getChunkSource().addRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 1, Unit.INSTANCE);
+-            return true;
 +            net.minecraft.world.level.chunk.storage.RegionFile file;
 +            try {
 +                file = world.getChunkSource().chunkMap.regionFileCache.getRegionFile(chunkPos, false);
@@ -232,11 +236,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            // we do this so we do not re-read the chunk data on disk
          }
  
--        if (chunk instanceof net.minecraft.world.level.chunk.LevelChunk) {
--            this.world.getChunkSource().addRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 1, Unit.INSTANCE);
--            return true;
--        }
--
 -        return false;
 +        world.getChunkSource().addRegionTicket(TicketType.PLUGIN, chunkPos, 1, Unit.INSTANCE);
 +        world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true);
diff --git a/patches/server/Fix-for-large-move-vectors-crashing-server.patch b/patches/server/Fix-for-large-move-vectors-crashing-server.patch
index 2f3fbcaddd..58915421d8 100644
--- a/patches/server/Fix-for-large-move-vectors-crashing-server.patch
+++ b/patches/server/Fix-for-large-move-vectors-crashing-server.patch
@@ -27,13 +27,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                  double d8 = d5 - this.vehicleFirstGoodZ;
                  double d9 = entity.getDeltaMovement().lengthSqr();
 -                double d10 = d6 * d6 + d7 * d7 + d8 * d8;
+-
 +                // Paper start - fix large move vectors killing the server
 +                double currDeltaX = toX - fromX;
 +                double currDeltaY = toY - fromY;
 +                double currDeltaZ = toZ - fromZ;
 +                double d10 = Math.max(d6 * d6 + d7 * d7 + d8 * d8, (currDeltaX * currDeltaX + currDeltaY * currDeltaY + currDeltaZ * currDeltaZ) - 1);
 +                // Paper end - fix large move vectors killing the server
- 
++
 +                // Paper start - fix large move vectors killing the server
 +                double otherFieldX = d3 - this.vehicleLastGoodX;
 +                double otherFieldY = d4 - this.vehicleLastGoodY - 1.0E-6D;
diff --git a/patches/server/Fix-upstreams-block-state-factories.patch b/patches/server/Fix-upstreams-block-state-factories.patch
index e0abbd47a5..645a2542e3 100644
--- a/patches/server/Fix-upstreams-block-state-factories.patch
+++ b/patches/server/Fix-upstreams-block-state-factories.patch
@@ -328,21 +328,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      private static <T extends BlockEntity, B extends CraftBlockEntityState<T>> void register(
 -            Material blockType,
+-            Class<B> blockStateType,
+-            BiFunction<World, T, B> blockStateConstructor,
+-            BiFunction<BlockPos, net.minecraft.world.level.block.state.BlockState, T> tileEntityConstructor
+-    ) {
+-        CraftBlockStates.register(Collections.singletonList(blockType), blockStateType, blockStateConstructor, tileEntityConstructor);
+-    }
+-
+-    private static <T extends BlockEntity, B extends CraftBlockEntityState<T>> void register(
+-            List<Material> blockTypes,
 +            net.minecraft.world.level.block.entity.BlockEntityType<? extends T> blockEntityType, // Paper
              Class<B> blockStateType,
 -            BiFunction<World, T, B> blockStateConstructor,
 -            BiFunction<BlockPos, net.minecraft.world.level.block.state.BlockState, T> tileEntityConstructor
 +            BiFunction<World, T, B> blockStateConstructor // Paper
      ) {
--        CraftBlockStates.register(Collections.singletonList(blockType), blockStateType, blockStateConstructor, tileEntityConstructor);
--    }
--
--    private static <T extends BlockEntity, B extends CraftBlockEntityState<T>> void register(
--            List<Material> blockTypes,
--            Class<B> blockStateType,
--            BiFunction<World, T, B> blockStateConstructor,
--            BiFunction<BlockPos, net.minecraft.world.level.block.state.BlockState, T> tileEntityConstructor
--    ) {
 -        BlockStateFactory<B> factory = new BlockEntityStateFactory<>(blockStateType, blockStateConstructor, tileEntityConstructor);
 -        for (Material blockType : blockTypes) {
 -            CraftBlockStates.register(blockType, factory);
diff --git a/patches/server/Further-improve-server-tick-loop.patch b/patches/server/Further-improve-server-tick-loop.patch
index 50fd471e5c..bd90305098 100644
--- a/patches/server/Further-improve-server-tick-loop.patch
+++ b/patches/server/Further-improve-server-tick-loop.patch
@@ -185,10 +185,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
 +        for ( int i = 0; i < tps.length; i++) {
 +            tpsAvg[i] = TicksPerSecondCommand.format( tps[i] );
-         }
--        sender.sendMessage( sb.substring( 0, sb.length() - 2 ) );
--        sender.sendMessage(ChatColor.GOLD + "Current Memory Usage: " + ChatColor.GREEN + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024 * 1024)) + "/" + (Runtime.getRuntime().totalMemory() / (1024 * 1024)) + " mb (Max: "
--                + (Runtime.getRuntime().maxMemory() / (1024 * 1024)) + " mb)");
++        }
 +        sender.sendMessage(ChatColor.GOLD + "TPS from last 1m, 5m, 15m: " + org.apache.commons.lang.StringUtils.join(tpsAvg, ", "));
 +        if (args.length > 0 && args[0].equals("mem") && sender.hasPermission("bukkit.command.tpsmemory")) {
 +            sender.sendMessage(ChatColor.GOLD + "Current Memory Usage: " + ChatColor.GREEN + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024 * 1024)) + "/" + (Runtime.getRuntime().totalMemory() / (1024 * 1024)) + " mb (Max: " + (Runtime.getRuntime().maxMemory() / (1024 * 1024)) + " mb)");
@@ -196,7 +193,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                sender.sendMessage(ChatColor.RED + "Warning: " + ChatColor.GOLD + " Memory usage on modern garbage collectors is not a stable value and it is perfectly normal to see it reach max. Please do not pay it much attention.");
 +                hasShownMemoryWarning = true;
 +            }
-+        }
+         }
+-        sender.sendMessage( sb.substring( 0, sb.length() - 2 ) );
+-        sender.sendMessage(ChatColor.GOLD + "Current Memory Usage: " + ChatColor.GREEN + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024 * 1024)) + "/" + (Runtime.getRuntime().totalMemory() / (1024 * 1024)) + " mb (Max: "
+-                + (Runtime.getRuntime().maxMemory() / (1024 * 1024)) + " mb)");
 +        // Paper end
  
          return true;
diff --git a/patches/server/Handle-Item-Meta-Inconsistencies.patch b/patches/server/Handle-Item-Meta-Inconsistencies.patch
index 80e9871245..f20e245e8f 100644
--- a/patches/server/Handle-Item-Meta-Inconsistencies.patch
+++ b/patches/server/Handle-Item-Meta-Inconsistencies.patch
@@ -130,14 +130,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        ListTag list = CraftItemStack.getEnchantmentList(this.handle), listCopy;
 -        if (list == null) {
 -            return 0;
-+        // Paper start - replace entire method
-+        int level = getEnchantmentLevel(ench);
-+        if (level > 0) {
-+            final ItemMeta itemMeta = this.getItemMeta();
-+            if (itemMeta == null) return 0;
-+            itemMeta.removeEnchant(ench);
-+            this.setItemMeta(itemMeta);
-         }
+-        }
 -        int index = Integer.MIN_VALUE;
 -        int level = Integer.MIN_VALUE;
 -        int size = list.size();
@@ -169,7 +162,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -            if (i != index) {
 -                listCopy.add(list.get(i));
 -            }
--        }
++        // Paper start - replace entire method
++        int level = getEnchantmentLevel(ench);
++        if (level > 0) {
++            final ItemMeta itemMeta = this.getItemMeta();
++            if (itemMeta == null) return 0;
++            itemMeta.removeEnchant(ench);
++            this.setItemMeta(itemMeta);
+         }
 -        this.handle.getTag().put(ENCHANTMENTS.NBT, listCopy);
 +        // Paper end
  
diff --git a/patches/server/Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch b/patches/server/Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch
index 04571c41c5..eee71069e2 100644
--- a/patches/server/Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch
+++ b/patches/server/Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch
@@ -24,7 +24,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    // Paper start - optimise collisions
 +    static final UnsafeList<AABB> TEMP_COLLISION_LIST = new UnsafeList<>(1024);
 +    static boolean tempCollisionListInUse;
- 
++
 +    public static UnsafeList<AABB> getTempCollisionList() {
 +        if (!Bukkit.isPrimaryThread() || tempCollisionListInUse) {
 +            return new UnsafeList<>(16);
@@ -40,7 +40,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        ((UnsafeList)list).setSize(0);
 +        tempCollisionListInUse = false;
 +    }
-+
+ 
 +    static final UnsafeList<Entity> TEMP_GET_ENTITIES_LIST = new UnsafeList<>(1024);
 +    static boolean tempGetEntitiesListInUse;
 +
@@ -1277,26 +1277,29 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        boolean flag1 = movement.y != vec3d1.y;
 -        boolean flag2 = movement.z != vec3d1.z;
 -        boolean flag3 = this.onGround || flag1 && movement.y < 0.0D;
+-
+-        if (this.maxUpStep() > 0.0F && flag3 && (flag || flag2)) {
+-            Vec3 vec3d2 = Entity.collideBoundingBox(this, new Vec3(movement.x, (double) this.maxUpStep(), movement.z), axisalignedbb, this.level, list);
+-            Vec3 vec3d3 = Entity.collideBoundingBox(this, new Vec3(0.0D, (double) this.maxUpStep(), 0.0D), axisalignedbb.expandTowards(movement.x, 0.0D, movement.z), this.level, list);
+-
+-            if (vec3d3.y < (double) this.maxUpStep()) {
+-                Vec3 vec3d4 = Entity.collideBoundingBox(this, new Vec3(movement.x, 0.0D, movement.z), axisalignedbb.move(vec3d3), this.level, list).add(vec3d3);
+-
+-                if (vec3d4.horizontalDistanceSqr() > vec3d2.horizontalDistanceSqr()) {
+-                    vec3d2 = vec3d4;
 +        // Paper start - optimise collisions
 +        // This is a copy of vanilla's except that it uses strictly AABB math
 +        if (movement.x == 0.0 && movement.y == 0.0 && movement.z == 0.0) {
 +            return movement;
 +        }
- 
--        if (this.maxUpStep() > 0.0F && flag3 && (flag || flag2)) {
--            Vec3 vec3d2 = Entity.collideBoundingBox(this, new Vec3(movement.x, (double) this.maxUpStep(), movement.z), axisalignedbb, this.level, list);
--            Vec3 vec3d3 = Entity.collideBoundingBox(this, new Vec3(0.0D, (double) this.maxUpStep(), 0.0D), axisalignedbb.expandTowards(movement.x, 0.0D, movement.z), this.level, list);
++
 +        final Level world = this.level;
 +        final AABB currBoundingBox = this.getBoundingBox();
- 
--            if (vec3d3.y < (double) this.maxUpStep()) {
--                Vec3 vec3d4 = Entity.collideBoundingBox(this, new Vec3(movement.x, 0.0D, movement.z), axisalignedbb.move(vec3d3), this.level, list).add(vec3d3);
++
 +        if (io.papermc.paper.util.CollisionUtil.isEmpty(currBoundingBox)) {
 +            return movement;
 +        }
- 
--                if (vec3d4.horizontalDistanceSqr() > vec3d2.horizontalDistanceSqr()) {
--                    vec3d2 = vec3d4;
++
 +        final List<AABB> potentialCollisions = io.papermc.paper.util.CachedLists.getTempCollisionList();
 +        try {
 +            final double stepHeight = (double)this.maxUpStep();
@@ -1323,16 +1326,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
 -            if (vec3d2.horizontalDistanceSqr() > vec3d1.horizontalDistanceSqr()) {
 -                return vec3d2.add(Entity.collideBoundingBox(this, new Vec3(0.0D, -vec3d2.y + movement.y, 0.0D), axisalignedbb.move(vec3d2), this.level, list));
--            }
--        }
 +            io.papermc.paper.util.CollisionUtil.getCollisions(world, this, collisionBox, potentialCollisions, false, this.level.paperConfig().chunks.preventMovingIntoUnloadedChunks,
 +                false, false, null, null);
- 
--        return vec3d1;
++
 +            if (io.papermc.paper.util.CollisionUtil.isCollidingWithBorderEdge(world.getWorldBorder(), collisionBox)) {
 +                io.papermc.paper.util.CollisionUtil.addBoxesToIfIntersects(world.getWorldBorder().getCollisionShape(), collisionBox, potentialCollisions);
-+            }
-+
+             }
+-        }
+ 
+-        return vec3d1;
 +            final Vec3 limitedMoveVector = io.papermc.paper.util.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisions);
 +
 +            if (stepHeight > 0.0
diff --git a/patches/server/Implement-regenerateChunk.patch b/patches/server/Implement-regenerateChunk.patch
index d09e54ae62..0e3c7e2c5f 100644
--- a/patches/server/Implement-regenerateChunk.patch
+++ b/patches/server/Implement-regenerateChunk.patch
@@ -25,6 +25,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        /*
 -        if (!unloadChunk0(x, z, false)) {
 -            return false;
+-        }
+-
+-        final long chunkKey = ChunkCoordIntPair.pair(x, z);
+-        world.getChunkProvider().unloadQueue.remove(chunkKey);
 +        // Paper start - implement regenerateChunk method
 +        final ServerLevel serverLevel = this.world;
 +        final net.minecraft.server.level.ServerChunkCache serverChunkCache = serverLevel.getChunkSource();
@@ -33,10 +37,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        for (final BlockPos blockPos : BlockPos.betweenClosed(chunkPos.getMinBlockX(), serverLevel.getMinBuildHeight(), chunkPos.getMinBlockZ(), chunkPos.getMaxBlockX(), serverLevel.getMaxBuildHeight() - 1, chunkPos.getMaxBlockZ())) {
 +            levelChunk.removeBlockEntity(blockPos);
 +            serverLevel.setBlock(blockPos, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState(), 16);
-         }
- 
--        final long chunkKey = ChunkCoordIntPair.pair(x, z);
--        world.getChunkProvider().unloadQueue.remove(chunkKey);
++        }
++
 +        for (final ChunkStatus chunkStatus : REGEN_CHUNK_STATUSES) {
 +            final List<ChunkAccess> list = new ArrayList<>();
 +            final int range = Math.max(1, chunkStatus.getRange());
diff --git a/patches/server/Improve-death-events.patch b/patches/server/Improve-death-events.patch
index c93a2d9844..546f3fd86d 100644
--- a/patches/server/Improve-death-events.patch
+++ b/patches/server/Improve-death-events.patch
@@ -119,13 +119,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            // Paper - moved into if below
              if (this.level instanceof ServerLevel) {
 -                if (entity == null || entity.wasKilled((ServerLevel) this.level, this)) {
--                    this.gameEvent(GameEvent.ENTITY_DIE);
--                    this.dropAllDeathLoot(damageSource);
--                    this.createWitherRose(entityliving);
--                }
 +                // Paper - move below into if for onKill
- 
--                this.level.broadcastEntityEvent(this, (byte) 3);
++
 +                // Paper start
 +                org.bukkit.event.entity.EntityDeathEvent deathEvent = this.dropAllDeathLoot(damageSource);
 +                if (deathEvent == null || !deathEvent.isCancelled()) {
@@ -153,11 +148,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                    if (entity != null) {
 +                        entity.wasKilled((ServerLevel) this.level, this);
 +                    }
-+                    this.gameEvent(GameEvent.ENTITY_DIE);
+                     this.gameEvent(GameEvent.ENTITY_DIE);
+-                    this.dropAllDeathLoot(damageSource);
+-                    this.createWitherRose(entityliving);
 +                } else {
 +                    this.dead = false;
 +                    this.setHealth((float) deathEvent.getReviveHealth());
-+                }
+                 }
+-
+-                this.level.broadcastEntityEvent(this, (byte) 3);
 +                // Paper end
 +                this.createWitherRose(entityliving);
              }
diff --git a/patches/server/Improved-Watchdog-Support.patch b/patches/server/Improved-Watchdog-Support.patch
index 754dc3ce10..f8e0d113f0 100644
--- a/patches/server/Improved-Watchdog-Support.patch
+++ b/patches/server/Improved-Watchdog-Support.patch
@@ -405,9 +405,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            tryPreloadClass(net.minecraft.world.level.lighting.LayerLightEventListener.class.getName());
 +            tryPreloadClass(net.minecraft.util.ExceptionCollector.class.getName());
 +            // Paper end
-         }
-     }
- 
++        }
++    }
++
 +    // Paper start
 +    private static void tryPreloadClass(String className) {
 +        tryPreloadClass(className, true);
@@ -417,13 +417,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            Class.forName(className);
 +        } catch (ClassNotFoundException e) {
 +            if (printError) System.err.println("An expected class  " + className + " was not found for preloading: " + e.getMessage());
-+        }
-+    }
+         }
+     }
 +    // Paper end
-+
+ 
      private static List<String> asList(String... params) {
          return Arrays.asList(params);
-     }
 diff --git a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
diff --git a/patches/server/MC-Utils.patch b/patches/server/MC-Utils.patch
index b8bfb9dd99..53b32292e3 100644
--- a/patches/server/MC-Utils.patch
+++ b/patches/server/MC-Utils.patch
@@ -7674,12 +7674,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    public ChunkAccess getChunkIfLoadedImmediately(int x, int z) {
 +        throw new UnsupportedOperationException("Not supported yet.");
 +    }
- 
++
 +    @Override
 +    public BlockState getBlockStateIfLoaded(BlockPos blockposition) {
 +        throw new UnsupportedOperationException("Not supported yet.");
 +    }
-+
+ 
 +    @Override
 +    public FluidState getFluidIfLoaded(BlockPos blockposition) {
 +        throw new UnsupportedOperationException("Not supported yet.");
diff --git a/patches/server/More-Teleport-API.patch b/patches/server/More-Teleport-API.patch
index 917c224c0c..cb558468a8 100644
--- a/patches/server/More-Teleport-API.patch
+++ b/patches/server/More-Teleport-API.patch
@@ -55,14 +55,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        if (flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS) && this.entity.isVehicle() && location.getWorld() != this.getWorld()) {
 +            return false;
 +        }
- 
--        if (this.entity.isVehicle() || this.entity.isRemoved()) {
++
 +        // Don't allow to teleport between worlds if remaining on vehicle
 +        if (!dismount && this.entity.isPassenger() && location.getWorld() != this.getWorld()) {
 +            return false;
 +        }
 +        // Paper end
-+
+ 
+-        if (this.entity.isVehicle() || this.entity.isRemoved()) {
 +        if ((!ignorePassengers && this.entity.isVehicle()) || this.entity.isRemoved()) { // Paper - Teleport passenger API
              return false;
          }
diff --git a/patches/server/Optimise-BlockSoil-nearby-water-lookup.patch b/patches/server/Optimise-BlockSoil-nearby-water-lookup.patch
index 8d5ba6cdb8..7f10504b90 100644
--- a/patches/server/Optimise-BlockSoil-nearby-water-lookup.patch
+++ b/patches/server/Optimise-BlockSoil-nearby-water-lookup.patch
@@ -15,16 +15,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      private static boolean isNearWater(LevelReader world, BlockPos pos) {
 -        Iterator iterator = BlockPos.betweenClosed(pos.offset(-4, 0, -4), pos.offset(4, 1, 4)).iterator();
-+        // Paper start - remove abstract block iteration
-+        int xOff = pos.getX();
-+        int yOff = pos.getY();
-+        int zOff = pos.getZ();
- 
+-
 -        BlockPos blockposition1;
 -
 -        do {
 -            if (!iterator.hasNext()) {
 -                return false;
++        // Paper start - remove abstract block iteration
++        int xOff = pos.getX();
++        int yOff = pos.getY();
++        int zOff = pos.getZ();
++
 +        for (int dz = -4; dz <= 4; ++dz) {
 +            int z = dz + zOff;
 +            for (int dx = -4; dx <= 4; ++dx) {
diff --git a/patches/server/Optimise-chunk-tick-iteration.patch b/patches/server/Optimise-chunk-tick-iteration.patch
index f6c8c98675..61aa1b9543 100644
--- a/patches/server/Optimise-chunk-tick-iteration.patch
+++ b/patches/server/Optimise-chunk-tick-iteration.patch
@@ -170,13 +170,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                  }
 +                // Paper start - optimise chunk tick iteration
 +                }
-             }
++            }
 +
 +            } finally {
 +                if (iterator1 instanceof io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator safeIterator) {
 +                    safeIterator.finishedIterating();
 +                }
-+            }
+             }
 +            // Paper end - optimise chunk tick iteration
              this.level.timings.chunkTicks.stopTiming(); // Paper
              gameprofilerfiller.popPush("customSpawners");
@@ -186,15 +186,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                  } // Paper - timings
              }
 -
-+            gameprofilerfiller.pop();
-+            // Paper start - use set of chunks requiring updates, rather than iterating every single one loaded
-             gameprofilerfiller.popPush("broadcast");
+-            gameprofilerfiller.popPush("broadcast");
 -            list.forEach((chunkproviderserver_a1) -> {
 -                this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
 -                chunkproviderserver_a1.holder.broadcastChanges(chunkproviderserver_a1.chunk);
 -                this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
 -            });
--            gameprofilerfiller.pop();
+             gameprofilerfiller.pop();
++            // Paper start - use set of chunks requiring updates, rather than iterating every single one loaded
++            gameprofilerfiller.popPush("broadcast");
 +            this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
 +            if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) {
 +                ReferenceOpenHashSet<ChunkHolder> copy = this.chunkMap.needsChangeBroadcasting.clone();
diff --git a/patches/server/Optimise-random-block-ticking.patch b/patches/server/Optimise-random-block-ticking.patch
index 7c8e6d63f2..2702d153e0 100644
--- a/patches/server/Optimise-random-block-ticking.patch
+++ b/patches/server/Optimise-random-block-ticking.patch
@@ -173,14 +173,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          if (randomTickSpeed > 0) {
 -            LevelChunkSection[] achunksection = chunk.getSections();
 -            int j1 = achunksection.length;
-+            LevelChunkSection[] sections = chunk.getSections();
-+            int minSection = io.papermc.paper.util.WorldUtil.getMinSection(this);
-+            for (int sectionIndex = 0; sectionIndex < sections.length; ++sectionIndex) {
-+                LevelChunkSection section = sections[sectionIndex];
-+                if (section == null || section.tickingList.size() == 0) {
-+                    continue;
-+                }
- 
+-
 -            for (int k1 = 0; k1 < j1; ++k1) {
 -                LevelChunkSection chunksection = achunksection[k1];
 -
@@ -192,35 +185,40 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -
 -                        gameprofilerfiller.push("randomTick");
 -                        BlockState iblockdata3 = chunksection.getBlockState(blockposition2.getX() - j, blockposition2.getY() - l1, blockposition2.getZ() - k);
--
++            LevelChunkSection[] sections = chunk.getSections();
++            int minSection = io.papermc.paper.util.WorldUtil.getMinSection(this);
++            for (int sectionIndex = 0; sectionIndex < sections.length; ++sectionIndex) {
++                LevelChunkSection section = sections[sectionIndex];
++                if (section == null || section.tickingList.size() == 0) {
++                    continue;
++                }
+ 
 -                        if (iblockdata3.isRandomlyTicking()) {
 -                            iblockdata3.randomTick(this, blockposition2, this.random);
 -                        }
--
--                        FluidState fluid = iblockdata3.getFluidState();
--
--                        if (fluid.isRandomlyTicking()) {
--                            fluid.randomTick(this, blockposition2, this.random);
--                        }
--
--                        gameprofilerfiller.pop();
 +                int yPos = (sectionIndex + minSection) << 4;
 +                for (int a = 0; a < randomTickSpeed; ++a) {
 +                    int tickingBlocks = section.tickingList.size();
 +                    int index = this.randomTickRandom.nextInt(16 * 16 * 16);
 +                    if (index >= tickingBlocks) {
 +                        continue;
-                     }
-+
++                    }
+ 
+-                        FluidState fluid = iblockdata3.getFluidState();
 +                    long raw = section.tickingList.getRaw(index);
 +                    int location = com.destroystokyo.paper.util.maplist.IBlockDataList.getLocationFromRaw(raw);
 +                    int randomX = location & 15;
 +                    int randomY = ((location >>> (4 + 4)) & 255) | yPos;
 +                    int randomZ = (location >>> 4) & 15;
-+
+ 
+-                        if (fluid.isRandomlyTicking()) {
+-                            fluid.randomTick(this, blockposition2, this.random);
+-                        }
 +                    BlockPos blockposition2 = blockposition.set(j + randomX, randomY, k + randomZ);
 +                    BlockState iblockdata = com.destroystokyo.paper.util.maplist.IBlockDataList.getBlockDataFromRaw(raw);
-+
+ 
+-                        gameprofilerfiller.pop();
+-                    }
 +                    iblockdata.randomTick(this, blockposition2, this.randomTickRandom);
 +                    // We drop the fluid tick since LAVA is ALREADY TICKED by the above method (See LiquidBlock).
 +                    // TODO CHECK ON UPDATE (ping the Canadian)
@@ -379,14 +377,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      public void recalcBlockCounts() {
 -        class a implements PalettedContainer.CountConsumer<BlockState> {
-+        // Paper start - unfuck this
-+        this.tickingList.clear();
-+        this.nonEmptyBlockCount = 0;
-+        this.tickingBlockCount = 0;
-+        this.tickingFluidCount = 0;
-+        this.states.forEachLocation((BlockState iblockdata, int i) -> {
-+            FluidState fluid = iblockdata.getFluidState();
- 
+-
 -            public int nonEmptyBlockCount;
 -            public int tickingBlockCount;
 -            public int tickingFluidCount;
@@ -401,31 +392,38 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -                    if (iblockdata.isRandomlyTicking()) {
 -                        this.tickingBlockCount += i;
 -                    }
++        // Paper start - unfuck this
++        this.tickingList.clear();
++        this.nonEmptyBlockCount = 0;
++        this.tickingBlockCount = 0;
++        this.tickingFluidCount = 0;
++        this.states.forEachLocation((BlockState iblockdata, int i) -> {
++            FluidState fluid = iblockdata.getFluidState();
++
 +            if (!iblockdata.isAir()) {
 +                this.nonEmptyBlockCount = (short) (this.nonEmptyBlockCount + 1);
 +                if (iblockdata.isRandomlyTicking()) {
 +                    this.tickingBlockCount = (short)(this.tickingBlockCount + 1);
 +                    this.tickingList.add(i, iblockdata);
                  }
--
++            }
+ 
 -                if (!fluid.isEmpty()) {
 -                    this.nonEmptyBlockCount += i;
 -                    if (fluid.isRandomlyTicking()) {
 -                        this.tickingFluidCount += i;
 -                    }
--                }
++            if (!fluid.isEmpty()) {
++                this.nonEmptyBlockCount = (short) (this.nonEmptyBlockCount + 1);
++                if (fluid.isRandomlyTicking()) {
++                    this.tickingFluidCount = (short) (this.tickingFluidCount + 1);
+                 }
 -
              }
 -        }
  
 -        a a0 = new a();
-+            if (!fluid.isEmpty()) {
-+                this.nonEmptyBlockCount = (short) (this.nonEmptyBlockCount + 1);
-+                if (fluid.isRandomlyTicking()) {
-+                    this.tickingFluidCount = (short) (this.tickingFluidCount + 1);
-+                }
-+            }
- 
+-
 -        this.states.count(a0);
 -        this.nonEmptyBlockCount = (short) a0.nonEmptyBlockCount;
 -        this.tickingBlockCount = (short) a0.tickingBlockCount;
diff --git a/patches/server/Optimize-GoalSelector-Goal.Flag-Set-operations.patch b/patches/server/Optimize-GoalSelector-Goal.Flag-Set-operations.patch
index cd37697cd3..fc27b5d667 100644
--- a/patches/server/Optimize-GoalSelector-Goal.Flag-Set-operations.patch
+++ b/patches/server/Optimize-GoalSelector-Goal.Flag-Set-operations.patch
@@ -74,27 +74,26 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        this.availableGoals.removeIf((wrappedGoal) -> {
 -            return wrappedGoal.getGoal() == goal;
 -        });
+-    }
+-
+-    private static boolean goalContainsAnyFlags(WrappedGoal goal, EnumSet<Goal.Flag> controls) {
+-        for(Goal.Flag flag : goal.getFlags()) {
+-            if (controls.contains(flag)) {
+-                return true;
 +        // Paper start - remove streams from pathfindergoalselector
 +        for (java.util.Iterator<WrappedGoal> iterator = this.availableGoals.iterator(); iterator.hasNext();) {
 +            WrappedGoal goalWrapped = iterator.next();
 +            if (goalWrapped.getGoal() != goal) {
 +                continue;
-+            }
+             }
 +            if (goalWrapped.isRunning()) {
 +                goalWrapped.stop();
 +            }
 +            iterator.remove();
-+        }
+         }
 +        // Paper end - remove streams from pathfindergoalselector
-     }
++    }
  
--    private static boolean goalContainsAnyFlags(WrappedGoal goal, EnumSet<Goal.Flag> controls) {
--        for(Goal.Flag flag : goal.getFlags()) {
--            if (controls.contains(flag)) {
--                return true;
--            }
--        }
--
 -        return false;
 +    private static boolean goalContainsAnyFlags(WrappedGoal goal, com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<Goal.Flag> controls) {
 +        return goal.getFlags().hasCommonElements(controls); // Paper
diff --git a/patches/server/Optimize-Hoppers.patch b/patches/server/Optimize-Hoppers.patch
index 35eed1c2eb..091074c67f 100644
--- a/patches/server/Optimize-Hoppers.patch
+++ b/patches/server/Optimize-Hoppers.patch
@@ -281,18 +281,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -                    if (!iinventory.getItem(i).isEmpty()) {
 -                        ItemStack itemstack = iinventory.getItem(i).copy();
 -                        // ItemStack itemstack1 = addItem(iinventory, iinventory1, iinventory.removeItem(i, 1), enumdirection);
-+                // Paper start - replace logic; MAKE SURE TO CHECK FOR DIFFS ON UPDATES
-+                return hopperPush(world, iinventory1, enumdirection, hopper);
-+                // for (int i = 0; i < iinventory.getContainerSize(); ++i) {
-+                //     if (!iinventory.getItem(i).isEmpty()) {
-+                //         ItemStack itemstack = iinventory.getItem(i).copy();
-+                //         // ItemStack itemstack1 = addItem(iinventory, iinventory1, iinventory.removeItem(i, 1), enumdirection);
- 
+-
 -                        // CraftBukkit start - Call event when pushing items into other inventories
 -                        CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
-+                //         // CraftBukkit start - Call event when pushing items into other inventories
-+                //         CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
- 
+-
 -                        Inventory destinationInventory;
 -                        // Have to special case large chests as they work oddly
 -                        if (iinventory1 instanceof CompoundContainer) {
@@ -302,16 +294,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -                        } else {
 -                            destinationInventory = new CraftInventory(iinventory);
 -                        }
-+                //         Inventory destinationInventory;
-+                //        // Have to special case large chests as they work oddly
-+                //         if (iinventory1 instanceof CompoundContainer) {
-+                //             destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory1);
-+                //         } else if (iinventory1.getOwner() != null) {
-+                //             destinationInventory = iinventory1.getOwner().getInventory();
-+                //         } else {
-+                //             destinationInventory = new CraftInventory(iinventory);
-+                //         }
- 
+-
 -                        InventoryMoveItemEvent event = new InventoryMoveItemEvent(iinventory.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true);
 -                        world.getCraftServer().getPluginManager().callEvent(event);
 -                        if (event.isCancelled()) {
@@ -321,6 +304,26 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -                        }
 -                        int origCount = event.getItem().getAmount(); // Spigot
 -                        ItemStack itemstack1 = HopperBlockEntity.addItem(iinventory, iinventory1, CraftItemStack.asNMSCopy(event.getItem()), enumdirection);
++                // Paper start - replace logic; MAKE SURE TO CHECK FOR DIFFS ON UPDATES
++                return hopperPush(world, iinventory1, enumdirection, hopper);
++                // for (int i = 0; i < iinventory.getContainerSize(); ++i) {
++                //     if (!iinventory.getItem(i).isEmpty()) {
++                //         ItemStack itemstack = iinventory.getItem(i).copy();
++                //         // ItemStack itemstack1 = addItem(iinventory, iinventory1, iinventory.removeItem(i, 1), enumdirection);
++
++                //         // CraftBukkit start - Call event when pushing items into other inventories
++                //         CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
++
++                //         Inventory destinationInventory;
++                //        // Have to special case large chests as they work oddly
++                //         if (iinventory1 instanceof CompoundContainer) {
++                //             destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory1);
++                //         } else if (iinventory1.getOwner() != null) {
++                //             destinationInventory = iinventory1.getOwner().getInventory();
++                //         } else {
++                //             destinationInventory = new CraftInventory(iinventory);
++                //         }
++
 +                //         InventoryMoveItemEvent event = new InventoryMoveItemEvent(iinventory.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true);
 +                //         world.getCraftServer().getPluginManager().callEvent(event);
 +                //         if (event.isCancelled()) {
@@ -408,14 +411,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -            // ItemStack itemstack2 = addItem(iinventory, ihopper, iinventory.removeItem(i, 1), (EnumDirection) null);
 -            // CraftBukkit start - Call event on collection of items from inventories into the hopper
 -            CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
-+        // Paper start - replace pull logic; MAKE SURE TO CHECK FOR DIFFS WHEN UPDATING
-+        if (!itemstack.isEmpty() && HopperBlockEntity.canTakeItemFromContainer(ihopper, iinventory, itemstack, i, enumdirection)) { // If this logic changes, update above. this is left unused incase reflective plugins
-+            return hopperPull(world, ihopper, iinventory, itemstack, i);
-+            // ItemStack itemstack1 = itemstack.copy();
-+            // // ItemStack itemstack2 = addItem(iinventory, ihopper, iinventory.removeItem(i, 1), (EnumDirection) null);
-+            // // CraftBukkit start - Call event on collection of items from inventories into the hopper
-+            // CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
- 
+-
 -            Inventory sourceInventory;
 -            // Have to special case large chests as they work oddly
 -            if (iinventory instanceof CompoundContainer) {
@@ -425,6 +421,38 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -            } else {
 -                sourceInventory = new CraftInventory(iinventory);
 -            }
+-
+-            InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack.clone(), ihopper.getOwner().getInventory(), false);
+-
+-            Bukkit.getServer().getPluginManager().callEvent(event);
+-            if (event.isCancelled()) {
+-                iinventory.setItem(i, itemstack1);
+-
+-                if (ihopper instanceof HopperBlockEntity) {
+-                    ((HopperBlockEntity) ihopper).setCooldown(world.spigotConfig.hopperTransfer); // Spigot
+-                }
+-
+-                return false;
+-            }
+-            int origCount = event.getItem().getAmount(); // Spigot
+-            ItemStack itemstack2 = HopperBlockEntity.addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null);
+-            // CraftBukkit end
+-
+-            if (itemstack2.isEmpty()) {
+-                iinventory.setChanged();
+-                return true;
+-            }
+-
+-            itemstack1.shrink(origCount - itemstack2.getCount()); // Spigot
+-            iinventory.setItem(i, itemstack1);
++        // Paper start - replace pull logic; MAKE SURE TO CHECK FOR DIFFS WHEN UPDATING
++        if (!itemstack.isEmpty() && HopperBlockEntity.canTakeItemFromContainer(ihopper, iinventory, itemstack, i, enumdirection)) { // If this logic changes, update above. this is left unused incase reflective plugins
++            return hopperPull(world, ihopper, iinventory, itemstack, i);
++            // ItemStack itemstack1 = itemstack.copy();
++            // // ItemStack itemstack2 = addItem(iinventory, ihopper, iinventory.removeItem(i, 1), (EnumDirection) null);
++            // // CraftBukkit start - Call event on collection of items from inventories into the hopper
++            // CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
++
 +            // Inventory sourceInventory;
 +            // // Have to special case large chests as they work oddly
 +            // if (iinventory instanceof CompoundContainer) {
@@ -434,46 +462,28 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            // } else {
 +            //     sourceInventory = new CraftInventory(iinventory);
 +            // }
- 
--            InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack.clone(), ihopper.getOwner().getInventory(), false);
++
 +            // InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack.clone(), ihopper.getOwner().getInventory(), false);
- 
--            Bukkit.getServer().getPluginManager().callEvent(event);
--            if (event.isCancelled()) {
--                iinventory.setItem(i, itemstack1);
++
 +            // Bukkit.getServer().getPluginManager().callEvent(event);
 +            // if (event.isCancelled()) {
 +            //     iinventory.setItem(i, itemstack1);
- 
--                if (ihopper instanceof HopperBlockEntity) {
--                    ((HopperBlockEntity) ihopper).setCooldown(world.spigotConfig.hopperTransfer); // Spigot
--                }
++
 +                // if (ihopper instanceof HopperBlockEntity) {
 +                //     ((HopperBlockEntity) ihopper).setCooldown(world.spigotConfig.hopperTransfer); // Spigot
 +                // }
- 
--                return false;
--            }
--            int origCount = event.getItem().getAmount(); // Spigot
--            ItemStack itemstack2 = HopperBlockEntity.addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null);
--            // CraftBukkit end
++
 +                // return false;
 +            // }
 +            // int origCount = event.getItem().getAmount(); // Spigot
 +            // ItemStack itemstack2 = HopperBlockEntity.addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null);
 +            // // CraftBukkit end
- 
--            if (itemstack2.isEmpty()) {
--                iinventory.setChanged();
--                return true;
--            }
++
 +            // if (itemstack2.isEmpty()) {
 +            //     iinventory.setChanged();
 +            //     return true;
 +            // }
- 
--            itemstack1.shrink(origCount - itemstack2.getCount()); // Spigot
--            iinventory.setItem(i, itemstack1);
++
 +            // itemstack1.shrink(origCount - itemstack2.getCount()); // Spigot
 +            // iinventory.setItem(i, itemstack1);
 +            // Paper end
diff --git a/patches/server/Optimize-Network-Manager-and-add-advanced-packet-sup.patch b/patches/server/Optimize-Network-Manager-and-add-advanced-packet-sup.patch
index 9740ab4edf..9c6d5cc03f 100644
--- a/patches/server/Optimize-Network-Manager-and-add-advanced-packet-sup.patch
+++ b/patches/server/Optimize-Network-Manager-and-add-advanced-packet-sup.patch
@@ -107,22 +107,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      public void send(Packet<?> packet, @Nullable PacketSendListener callbacks) {
 -        if (this.isConnected()) {
 -            this.flushQueue();
--            this.sendPacket(packet, callbacks);
--        } else {
--            this.queue.add(new Connection.PacketHolder(packet, callbacks));
 +        // Paper start - handle oversized packets better
 +        boolean connected = this.isConnected();
 +        if (!connected && !preparing) {
 +            return; // Do nothing
-         }
++        }
 +        packet.onPacketDispatch(getPlayer());
 +        if (connected && (InnerUtil.canSendImmediate(this, packet) || (
 +            io.papermc.paper.util.MCUtil.isMainThread() && packet.isReady() && this.queue.isEmpty() &&
 +            (packet.getExtraPackets() == null || packet.getExtraPackets().isEmpty())
 +        ))) {
-+            this.sendPacket(packet, callbacks);
+             this.sendPacket(packet, callbacks);
+-        } else {
+-            this.queue.add(new Connection.PacketHolder(packet, callbacks));
 +            return;
-+        }
+         }
 +        // write the packets to the queue, then flush - antixray hooks there already
 +        java.util.List<Packet> extraPackets = InnerUtil.buildExtraPackets(packet);
 +        boolean hasExtraPackets = extraPackets != null && !extraPackets.isEmpty();
@@ -185,10 +184,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      }
  
 -    private void flushQueue() {
--        try { // Paper - add pending task queue
--        if (this.channel != null && this.channel.isOpen()) {
--            Queue queue = this.queue;
--
 +    // Paper start - rewrite this to be safer if ran off main thread
 +    private boolean flushQueue() { // void -> boolean
 +        if (!isConnected()) {
@@ -198,20 +193,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            return processQueue();
 +        } else if (isPending) {
 +            // Should only happen during login/status stages
-             synchronized (this.queue) {
--                Connection.PacketHolder networkmanager_queuedpacket;
--
--                while ((networkmanager_queuedpacket = (Connection.PacketHolder) this.queue.poll()) != null) {
--                    this.sendPacket(networkmanager_queuedpacket.packet, networkmanager_queuedpacket.listener);
--                }
--
++            synchronized (this.queue) {
 +                return this.processQueue();
-             }
-         }
++            }
++        }
 +        return false;
 +    }
 +    private boolean processQueue() {
-+        try { // Paper - add pending task queue
+         try { // Paper - add pending task queue
+-        if (this.channel != null && this.channel.isOpen()) {
+-            Queue queue = this.queue;
 +        if (this.queue.isEmpty()) return true;
 +        // If we are on main, we are safe here in that nothing else should be processing queue off main anymore
 +        // But if we are not on main due to login/status, the parent is synchronized on packetQueue
@@ -223,13 +214,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            if (queued == null) {
 +                return true;
 +            }
-+
+ 
+-            synchronized (this.queue) {
+-                Connection.PacketHolder networkmanager_queuedpacket;
 +            // Paper start - checking isConsumed flag and skipping packet sending
 +            if (queued.isConsumed()) {
 +                continue;
 +            }
 +            // Paper end - checking isConsumed flag and skipping packet sending
-+
+ 
+-                while ((networkmanager_queuedpacket = (Connection.PacketHolder) this.queue.poll()) != null) {
+-                    this.sendPacket(networkmanager_queuedpacket.packet, networkmanager_queuedpacket.listener);
 +            Packet<?> packet = queued.packet;
 +            if (!packet.isReady()) {
 +                return false;
@@ -237,9 +232,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                iterator.remove();
 +                if (queued.tryMarkConsumed()) { // Paper - try to mark isConsumed flag for de-duplicating packet
 +                    this.sendPacket(packet, queued.listener);
-+                }
-+            }
-+        }
+                 }
+-
+             }
+         }
 +        return true;
          } finally { // Paper start - add pending task queue
              Runnable r;
diff --git a/patches/server/Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch b/patches/server/Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch
index ae100f8b12..40ab4c05ab 100644
--- a/patches/server/Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch
+++ b/patches/server/Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch
@@ -136,17 +136,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        int chunkRange = level.spigotConfig.mobSpawnRange;
 -        chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange;
 -        chunkRange = (chunkRange > 8) ? 8 : chunkRange;
-+    // Paper start - optimise anyPlayerCloseEnoughForSpawning
-+    final boolean anyPlayerCloseEnoughForSpawning(ChunkPos chunkcoordintpair, boolean reducedRange) {
-+        return this.anyPlayerCloseEnoughForSpawning(this.getUpdatingChunkIfPresent(chunkcoordintpair.toLong()), chunkcoordintpair, reducedRange);
-+    }
- 
+-
 -        final int finalChunkRange = chunkRange; // Paper for lambda below
 -        //double blockRange = (reducedRange) ? Math.pow(chunkRange << 4, 2) : 16384.0D; // Paper - use from event
 -        double blockRange = 16384.0D; // Paper
 -        // Spigot end
 -        long i = chunkcoordintpair.toLong();
--
++    // Paper start - optimise anyPlayerCloseEnoughForSpawning
++    final boolean anyPlayerCloseEnoughForSpawning(ChunkPos chunkcoordintpair, boolean reducedRange) {
++        return this.anyPlayerCloseEnoughForSpawning(this.getUpdatingChunkIfPresent(chunkcoordintpair.toLong()), chunkcoordintpair, reducedRange);
++    }
+ 
 -        if (!this.distanceManager.hasPlayersNearby(i)) {
 +    final boolean anyPlayerCloseEnoughForSpawning(ChunkHolder playerchunk, ChunkPos chunkcoordintpair, boolean reducedRange) {
 +        // this function is so hot that removing the map lookup call can have an order of magnitude impact on its performance
@@ -158,11 +158,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -            Iterator iterator = this.playerMap.getPlayers(i).iterator();
 -
 -            ServerPlayer entityplayer;
--
++        }
++        Object[] backingSet = playersInRange.getBackingSet();
+ 
 -            do {
 -                if (!iterator.hasNext()) {
 -                    return false;
--                }
++        if (reducedRange) {
++            for (int i = 0, len = backingSet.length; i < len; ++i) {
++                Object raw = backingSet[i];
++                if (!(raw instanceof ServerPlayer player)) {
++                    continue;
+                 }
 -
 -                entityplayer = (ServerPlayer) iterator.next();
 -                // Paper start - add PlayerNaturallySpawnCreaturesEvent
@@ -172,24 +179,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -                    event = entityplayer.playerNaturallySpawnedEvent;
 -                    if (event == null || event.isCancelled()) return false;
 -                    blockRange = (double) ((event.getSpawnRadius() << 4) * (event.getSpawnRadius() << 4));
--                }
++                // don't check spectator and whatnot, already handled by mob spawn map update
++                if (euclideanDistanceSquared(chunkcoordintpair, player) < player.lastEntitySpawnRadiusSquared) {
++                    return true; // in range
+                 }
 -                // Paper end
 -            } while (!this.playerIsCloseEnoughForSpawning(entityplayer, chunkcoordintpair, blockRange)); // Spigot
 -
 -            return true;
-         }
-+        Object[] backingSet = playersInRange.getBackingSet();
-+
-+        if (reducedRange) {
-+            for (int i = 0, len = backingSet.length; i < len; ++i) {
-+                Object raw = backingSet[i];
-+                if (!(raw instanceof ServerPlayer player)) {
-+                    continue;
-+                }
-+                // don't check spectator and whatnot, already handled by mob spawn map update
-+                if (euclideanDistanceSquared(chunkcoordintpair, player) < player.lastEntitySpawnRadiusSquared) {
-+                    return true; // in range
-+                }
 +            }
 +        } else {
 +            final double range = (DistanceManager.MOB_SPAWN_RANGE * 16) * (DistanceManager.MOB_SPAWN_RANGE * 16);
@@ -204,7 +201,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                    return true; // in range
 +                }
 +            }
-+        }
+         }
 +        // no players in range
 +        return false;
 +        // Paper end - optimise anyPlayerCloseEnoughForSpawning
diff --git a/patches/server/Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch b/patches/server/Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch
index f459c0ed43..cf327b7ac5 100644
--- a/patches/server/Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch
+++ b/patches/server/Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch
@@ -103,7 +103,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        int k = pos.getZ();
 +        return this.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ());
 +    }
- 
++
 +    @Override
 +    public BlockState getBlockState(final int x, final int y, final int z) {
 +        return this.getBlockStateFinal(x, y, z);
@@ -116,7 +116,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        }
 +        // Inlined ChunkSection.getType() and DataPaletteBlock.a(int,int,int)
 +        return this.sections[i].states.get((y & 15) << 8 | (z & 15) << 4 | x & 15);
-+
+ 
 +    }
 +
 +    public BlockState getBlockState_unused(int i, int j, int k) {
diff --git a/patches/server/Pillager-patrol-spawn-settings-and-per-player-option.patch b/patches/server/Pillager-patrol-spawn-settings-and-per-player-option.patch
index 7b047766ac..bc6257ed45 100644
--- a/patches/server/Pillager-patrol-spawn-settings-and-per-player-option.patch
+++ b/patches/server/Pillager-patrol-spawn-settings-and-per-player-option.patch
@@ -44,7 +44,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            // Random player selection moved up for per player spawning and configuration
 +            int j = world.players().size();
 +            if (j < 1) {
-+                return 0;
+                 return 0;
 +            }
 +
 +            net.minecraft.server.level.ServerPlayer entityhuman = world.players().get(randomsource.nextInt(j));
@@ -56,16 +56,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            if (world.paperConfig().entities.behavior.pillagerPatrols.spawnDelay.perPlayer) {
 +                --entityhuman.patrolSpawnDelay;
 +                patrolSpawnDelay = entityhuman.patrolSpawnDelay;
-+            } else {
+             } else {
+-                this.nextTick += 12000 + randomsource.nextInt(1200);
+-                long i = world.getDayTime() / 24000L;
 +                this.nextTick--;
 +                patrolSpawnDelay = this.nextTick;
 +            }
 +
 +            if (patrolSpawnDelay > 0) {
-                 return 0;
-             } else {
--                this.nextTick += 12000 + randomsource.nextInt(1200);
--                long i = world.getDayTime() / 24000L;
++                return 0;
++            } else {
 +                long days;
 +                if (world.paperConfig().entities.behavior.pillagerPatrols.start.perPlayer) {
 +                    days = entityhuman.getStats().getValue(net.minecraft.stats.Stats.CUSTOM.get(net.minecraft.stats.Stats.PLAY_TIME)) / 24000L; // PLAY_ONE_MINUTE is actually counting in ticks, a misnomer by Mojang
diff --git a/patches/server/Player-affects-spawning-API.patch b/patches/server/Player-affects-spawning-API.patch
index 6e3de69ebc..1877634781 100644
--- a/patches/server/Player-affects-spawning-API.patch
+++ b/patches/server/Player-affects-spawning-API.patch
@@ -145,13 +145,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      public String getLocale() {
          return this.getHandle().locale;
 +
-     }
- 
++    }
++
 +    // Paper start
 +    public void setAffectsSpawning(boolean affects) {
 +        this.getHandle().affectsSpawning = affects;
-+    }
-+
+     }
+ 
 +    @Override
 +    public boolean getAffectsSpawning() {
 +        return this.getHandle().affectsSpawning;
diff --git a/patches/server/Player.setPlayerProfile-API.patch b/patches/server/Player.setPlayerProfile-API.patch
index 25c5cf4948..b99f6cde4d 100644
--- a/patches/server/Player.setPlayerProfile-API.patch
+++ b/patches/server/Player.setPlayerProfile-API.patch
@@ -127,11 +127,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                bukkitPlayer.unregisterEntity(self);
 +            }
 +        }
- 
--        server.getPluginManager().callEvent(new PlayerShowEntityEvent(this, entity));
++
 +        // Set the game profile here, we should have unregistered the entity via iterating all player entities above.
 +        self.gameProfile = gameProfile;
-+
+ 
+-        server.getPluginManager().callEvent(new PlayerShowEntityEvent(this, entity));
 +        // Re-register the game profile for all players
 +        for (ServerPlayer player : players) {
 +            CraftPlayer bukkitPlayer = player.getBukkitEntity();
diff --git a/patches/server/Prevent-chunk-loading-from-Fluid-Flowing.patch b/patches/server/Prevent-chunk-loading-from-Fluid-Flowing.patch
index 0c7904e1e2..9fb4af40fa 100644
--- a/patches/server/Prevent-chunk-loading-from-Fluid-Flowing.patch
+++ b/patches/server/Prevent-chunk-loading-from-Fluid-Flowing.patch
@@ -58,14 +58,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              short short0 = FlowingFluid.getCacheKey(pos, blockposition1);
 -            Pair<BlockState, FluidState> pair = (Pair) short2objectmap.computeIfAbsent(short0, (short1) -> {
 -                BlockState iblockdata1 = world.getBlockState(blockposition1);
+-
+-                return Pair.of(iblockdata1, iblockdata1.getFluidState());
+-            });
 +            // Paper start
 +            Pair pair = (Pair) short2objectmap.get(short0);
 +            if (pair == null) {
 +                BlockState iblockdatax = world.getBlockStateIfLoaded(blockposition1);
 +                if (iblockdatax == null) continue;
- 
--                return Pair.of(iblockdata1, iblockdata1.getFluidState());
--            });
++
 +                pair = Pair.of(iblockdatax, iblockdatax.getFluidState());
 +                short2objectmap.put(short0, pair);
 +            }
diff --git a/patches/server/Properly-handle-async-calls-to-restart-the-server.patch b/patches/server/Properly-handle-async-calls-to-restart-the-server.patch
index 7533f2eb02..545abd0049 100644
--- a/patches/server/Properly-handle-async-calls-to-restart-the-server.patch
+++ b/patches/server/Properly-handle-async-calls-to-restart-the-server.patch
@@ -98,10 +98,24 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            if ( isRestarting )
              {
 -                System.out.println( "Attempting to restart with " + restartScript );
--
++                System.out.println( "Attempting to restart with " + SpigotConfig.restartScript );
++            } else
++            {
++                System.out.println( "Startup script '" + SpigotConfig.restartScript + "' does not exist! Stopping server." );
++            }
++            // Stop the watchdog
++            WatchdogThread.doStop();
+ 
 -                // Disable Watchdog
 -                WatchdogThread.doStop();
--
++            shutdownServer( isRestarting );
++            // Paper end
++        } catch ( Exception ex )
++        {
++            ex.printStackTrace();
++        }
++    }
+ 
 -                // Kick all players
 -                for ( ServerPlayer p : (List<ServerPlayer>) MinecraftServer.getServer().getPlayerList().players )
 -                {
@@ -116,73 +130,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -                }
 -                // Close the socket so we can rebind with the new process
 -                MinecraftServer.getServer().getConnection().stop();
--
--                // Give time for it to kick in
--                try
--                {
--                    Thread.sleep( 100 );
--                } catch ( InterruptedException ex )
--                {
--                }
--
--                // Actually shutdown
--                try
--                {
--                    MinecraftServer.getServer().close();
--                } catch ( Throwable t )
--                {
--                }
--
--                // This will be done AFTER the server has completely halted
--                Thread shutdownHook = new Thread()
--                {
--                    @Override
--                    public void run()
--                    {
--                        try
--                        {
--                            String os = System.getProperty( "os.name" ).toLowerCase(java.util.Locale.ENGLISH);
--                            if ( os.contains( "win" ) )
--                            {
--                                Runtime.getRuntime().exec( "cmd /c start " + restartScript );
--                            } else
--                            {
--                                Runtime.getRuntime().exec( "sh " + restartScript );
--                            }
--                        } catch ( Exception e )
--                        {
--                            e.printStackTrace();
--                        }
--                    }
--                };
--
--                shutdownHook.setDaemon( true );
--                Runtime.getRuntime().addShutdownHook( shutdownHook );
-+                System.out.println( "Attempting to restart with " + SpigotConfig.restartScript );
-             } else
-             {
-                 System.out.println( "Startup script '" + SpigotConfig.restartScript + "' does not exist! Stopping server." );
--
--                // Actually shutdown
--                try
--                {
--                    MinecraftServer.getServer().close();
--                } catch ( Throwable t )
--                {
--                }
-             }
--            System.exit( 0 );
-+            // Stop the watchdog
-+            WatchdogThread.doStop();
-+
-+            shutdownServer( isRestarting );
-+            // Paper end
-         } catch ( Exception ex )
-         {
-             ex.printStackTrace();
-         }
-     }
-+
 +    // Paper start - sync copied from above with minor changes, async added
 +    private static void shutdownServer(boolean isRestarting)
 +    {
@@ -200,9 +147,23 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            } catch ( InterruptedException ex )
 +            {
 +            }
-+
+ 
+-                // Give time for it to kick in
+-                try
+-                {
+-                    Thread.sleep( 100 );
+-                } catch ( InterruptedException ex )
+-                {
+-                }
 +            closeSocket();
-+
+ 
+-                // Actually shutdown
+-                try
+-                {
+-                    MinecraftServer.getServer().close();
+-                } catch ( Throwable t )
+-                {
+-                }
 +            // Actually shutdown
 +            try
 +            {
@@ -213,7 +174,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
 +            // Actually stop the JVM
 +            System.exit( 0 );
-+
+ 
+-                // This will be done AFTER the server has completely halted
+-                Thread shutdownHook = new Thread()
 +        } else
 +        {
 +            // Mark the server to shutdown at the end of the tick
@@ -265,32 +228,63 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            {
 +                @Override
 +                public void run()
-+                {
+                 {
+-                    @Override
+-                    public void run()
 +                    try
-+                    {
+                     {
+-                        try
 +                        String os = System.getProperty( "os.name" ).toLowerCase(java.util.Locale.ENGLISH);
 +                        if ( os.contains( "win" ) )
-+                        {
+                         {
+-                            String os = System.getProperty( "os.name" ).toLowerCase(java.util.Locale.ENGLISH);
+-                            if ( os.contains( "win" ) )
+-                            {
+-                                Runtime.getRuntime().exec( "cmd /c start " + restartScript );
+-                            } else
+-                            {
+-                                Runtime.getRuntime().exec( "sh " + restartScript );
+-                            }
+-                        } catch ( Exception e )
 +                            Runtime.getRuntime().exec( "cmd /c start " + restartScript );
 +                        } else
-+                        {
+                         {
+-                            e.printStackTrace();
 +                            Runtime.getRuntime().exec( "sh " + restartScript );
-+                        }
+                         }
 +                    } catch ( Exception e )
 +                    {
 +                        e.printStackTrace();
-+                    }
-+                }
+                     }
+-                };
+-
+-                shutdownHook.setDaemon( true );
+-                Runtime.getRuntime().addShutdownHook( shutdownHook );
+-            } else
+-            {
+-                System.out.println( "Startup script '" + SpigotConfig.restartScript + "' does not exist! Stopping server." );
+-
+-                // Actually shutdown
+-                try
+-                {
+-                    MinecraftServer.getServer().close();
+-                } catch ( Throwable t )
+-                {
+                 }
+-            }
+-            System.exit( 0 );
+-        } catch ( Exception ex )
 +            };
 +
 +            shutdownHook.setDaemon( true );
 +            Runtime.getRuntime().addShutdownHook( shutdownHook );
 +            return true;
 +        } else
-+        {
+         {
+-            ex.printStackTrace();
 +            return false;
-+        }
-+    }
+         }
+     }
 +    // Paper end
 +
  }
diff --git a/patches/server/Rewrite-chunk-system.patch b/patches/server/Rewrite-chunk-system.patch
index 757e14f19d..c502969c88 100644
--- a/patches/server/Rewrite-chunk-system.patch
+++ b/patches/server/Rewrite-chunk-system.patch
@@ -13176,15 +13176,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -            entityplayer.connection.send(packet);
 -        });
 -    }
-+        // Paper start - per player view distance
-+        // there can be potential desync with player's last mapped section and the view distance map, so use the
-+        // view distance map here.
-+        com.destroystokyo.paper.util.misc.PlayerAreaMap viewDistanceMap = this.chunkMap.playerChunkManager.broadcastMap; // Paper - replace old player chunk manager
-+        com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> players = viewDistanceMap.getObjectsInRange(this.pos);
-+        if (players == null) {
-+            return;
-+        }
- 
+-
 -    public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getOrScheduleFuture(ChunkStatus targetStatus, ChunkMap chunkStorage) {
 -        int i = targetStatus.getIndex();
 -        CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = (CompletableFuture) this.futures.get(i);
@@ -13194,7 +13186,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -
 -            if (either == null) {
 -                String s = "value in future for status: " + targetStatus + " was incorrectly set to null at chunk: " + this.pos;
--
++        // Paper start - per player view distance
++        // there can be potential desync with player's last mapped section and the view distance map, so use the
++        // view distance map here.
++        com.destroystokyo.paper.util.misc.PlayerAreaMap viewDistanceMap = this.chunkMap.playerChunkManager.broadcastMap; // Paper - replace old player chunk manager
++        com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> players = viewDistanceMap.getObjectsInRange(this.pos);
++        if (players == null) {
++            return;
++        }
+ 
 -                throw chunkStorage.debugFuturesAndCreateReportedException(new IllegalStateException("null value previously set for chunk status"), s);
 +        Object[] backingSet = players.getBackingSet();
 +        for (int i = 0, len = backingSet.length; i < len; ++i) {
@@ -13258,9 +13258,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      public final int getTicketLevel() { // Paper - final for inline
 -        return this.ticketLevel;
-+        return this.newChunkHolder.getTicketLevel(); // Paper - rewrite chunk system
-     }
- 
+-    }
+-
 -    public int getQueueLevel() {
 -        return this.queueLevel;
 -    }
@@ -13286,8 +13285,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -                completablefuture1.complete(null); // CraftBukkit - decompile error
 -            });
 -        });
--    }
--
++        return this.newChunkHolder.getTicketLevel(); // Paper - rewrite chunk system
+     }
+ 
 -    private void demoteFullChunk(ChunkMap playerchunkmap, ChunkHolder.FullChunkStatus playerchunk_state) {
 -        this.pendingFullStateConfirmation.cancel(false);
 -        playerchunkmap.onFullChunkStatusChange(this.pos, playerchunk_state);
@@ -13823,16 +13823,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        } finally {
 -            super.close();
 -        }
--
 +        throw new UnsupportedOperationException("Use ServerChunkCache#close"); // Paper - rewrite chunk system
-     }
++    }
  
 +    // Paper start - rewrite chunk system
 +    protected void saveIncrementally() {
 +        this.level.chunkTaskScheduler.chunkHolderManager.autoSave(); // Paper - rewrite chunk system
-+    }
+     }
 +    // Paper end - - rewrite chunk system
-+
+ 
      protected void saveAllChunks(boolean flush) {
 -        if (flush) {
 -            List<ChunkHolder> list = (List) io.papermc.paper.chunk.system.ChunkSystem.getVisibleChunkHolders(this.level).stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).collect(Collectors.toList()); // Paper
@@ -14492,25 +14491,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        Set<ServerPlayer> set = this.playerMap.getPlayers(chunkPos.toLong());
 -        Builder<ServerPlayer> builder = ImmutableList.builder();
 -        Iterator iterator = set.iterator();
--
--        while (iterator.hasNext()) {
--            ServerPlayer entityplayer = (ServerPlayer) iterator.next();
--            SectionPos sectionposition = entityplayer.getLastSectionPos();
--
--            if (onlyOnWatchDistanceEdge && ChunkMap.isChunkOnRangeBorder(chunkPos.x, chunkPos.z, sectionposition.x(), sectionposition.z(), this.viewDistance) || !onlyOnWatchDistanceEdge && ChunkMap.isChunkInRange(chunkPos.x, chunkPos.z, sectionposition.x(), sectionposition.z(), this.viewDistance)) {
--                builder.add(entityplayer);
--            }
 +        // Paper start - per player view distance
 +        // there can be potential desync with player's last mapped section and the view distance map, so use the
 +        // view distance map here.
 +        com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> players = this.playerChunkManager.broadcastMap.getObjectsInRange(chunkPos);
 +        if (players == null) {
 +            return java.util.Collections.emptyList();
-         }
++        }
  
--        return builder.build();
+-        while (iterator.hasNext()) {
+-            ServerPlayer entityplayer = (ServerPlayer) iterator.next();
+-            SectionPos sectionposition = entityplayer.getLastSectionPos();
 +        List<ServerPlayer> ret = new java.util.ArrayList<>(players.size());
-+
+ 
+-            if (onlyOnWatchDistanceEdge && ChunkMap.isChunkOnRangeBorder(chunkPos.x, chunkPos.z, sectionposition.x(), sectionposition.z(), this.viewDistance) || !onlyOnWatchDistanceEdge && ChunkMap.isChunkInRange(chunkPos.x, chunkPos.z, sectionposition.x(), sectionposition.z(), this.viewDistance)) {
+-                builder.add(entityplayer);
 +        Object[] backingSet = players.getBackingSet();
 +        for (int i = 0, len = backingSet.length; i < len; ++i) {
 +            if (!(backingSet[i] instanceof ServerPlayer player)) {
@@ -14518,10 +14513,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            }
 +            if (!this.playerChunkManager.isChunkSent(player, chunkPos.x, chunkPos.z, onlyOnWatchDistanceEdge)) {
 +                continue;
-+            }
+             }
 +            ret.add(player);
-+        }
-+
+         }
+ 
+-        return builder.build();
 +        return ret;
 +        // Paper end - per player view distance
      }
@@ -14887,8 +14883,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -    private void dumpTickets(String path) {
 -        try {
 -            FileOutputStream fileoutputstream = new FileOutputStream(new File(path));
-+    // Paper - rewrite chunk system
- 
+-
 -            try {
 -                ObjectIterator objectiterator = this.tickets.long2ObjectEntrySet().iterator();
 -
@@ -14909,7 +14904,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -                } catch (Throwable throwable1) {
 -                    throwable.addSuppressed(throwable1);
 -                }
--
++    // Paper - rewrite chunk system
+ 
 -                throw throwable;
 -            }
 -
@@ -15129,23 +15125,29 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -            ChunkHolder.FullChunkStatus oldChunkState = ChunkHolder.getFullChunkStatus(playerchunk.oldTicketLevel);
 -            ChunkHolder.FullChunkStatus currentChunkState = ChunkHolder.getFullChunkStatus(playerchunk.getTicketLevel());
 -            currentlyUnloading = (oldChunkState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && !currentChunkState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER));
-+        boolean needsFullScheduling = leastStatus == ChunkStatus.FULL && (chunkHolder == null || !chunkHolder.getChunkStatus().isOrAfter(ChunkHolder.FullChunkStatus.BORDER));
-+
-+        if ((chunkHolder == null || chunkHolder.getTicketLevel() > minLevel || needsFullScheduling) && !create) {
-+            return ChunkHolder.UNLOADED_CHUNK_FUTURE;
-         }
+-        }
 -        if (create && !currentlyUnloading) {
 -            // CraftBukkit end
 -            this.distanceManager.addTicket(TicketType.UNKNOWN, chunkcoordintpair, l, chunkcoordintpair);
 -            if (this.chunkAbsent(playerchunk, l)) {
 -                ProfilerFiller gameprofilerfiller = this.level.getProfiler();
- 
+-
 -                gameprofilerfiller.push("chunkLoad");
 -                this.runDistanceManagerUpdates();
 -                playerchunk = this.getVisibleChunkIfPresent(k);
 -                gameprofilerfiller.pop();
 -                if (this.chunkAbsent(playerchunk, l)) {
 -                    throw (IllegalStateException) Util.pauseInIde(new IllegalStateException("No chunk holder after ticket has been added"));
+-                }
+-            }
++        boolean needsFullScheduling = leastStatus == ChunkStatus.FULL && (chunkHolder == null || !chunkHolder.getChunkStatus().isOrAfter(ChunkHolder.FullChunkStatus.BORDER));
++
++        if ((chunkHolder == null || chunkHolder.getTicketLevel() > minLevel || needsFullScheduling) && !create) {
++            return ChunkHolder.UNLOADED_CHUNK_FUTURE;
+         }
+ 
+-        return this.chunkAbsent(playerchunk, l) ? ChunkHolder.UNLOADED_CHUNK_FUTURE : playerchunk.getOrScheduleFuture(leastStatus, this.chunkMap);
+-    }
 +        io.papermc.paper.chunk.system.scheduling.NewChunkHolder.ChunkCompletion chunkCompletion = chunkHolder == null ? null : chunkHolder.getLastChunkCompletion();
 +        if (needsFullScheduling || chunkCompletion == null || !chunkCompletion.genStatus().isOrAfter(leastStatus)) {
 +            // schedule
@@ -15155,10 +15157,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                    ret.complete(Either.right(ChunkHolder.ChunkLoadingFailure.UNLOADED));
 +                } else {
 +                    ret.complete(Either.left(chunk));
-                 }
--            }
++                }
 +            };
-+
+ 
+-    private boolean chunkAbsent(@Nullable ChunkHolder holder, int maxLevel) {
+-        return holder == null || holder.oldTicketLevel > maxLevel; // CraftBukkit using oldTicketLevel for isLoaded checks
 +            this.level.chunkTaskScheduler.scheduleChunkLoad(
 +                chunkX, chunkZ, leastStatus, true,
 +                isUrgent ? ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.BLOCKING : ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL,
@@ -15169,17 +15172,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        } else {
 +            // can return now
 +            return CompletableFuture.completedFuture(Either.left(chunkCompletion.chunk()));
-         }
--
--        return this.chunkAbsent(playerchunk, l) ? ChunkHolder.UNLOADED_CHUNK_FUTURE : playerchunk.getOrScheduleFuture(leastStatus, this.chunkMap);
++        }
 +        // Paper end - rewrite chunk system
      }
  
--    private boolean chunkAbsent(@Nullable ChunkHolder holder, int maxLevel) {
--        return holder == null || holder.oldTicketLevel > maxLevel; // CraftBukkit using oldTicketLevel for isLoaded checks
--    }
 +    // Paper - rewrite chunk system
- 
++
      @Override
      public boolean hasChunk(int x, int z) {
 -        ChunkHolder playerchunk = this.getVisibleChunkIfPresent((new ChunkPos(x, z)).toLong());
@@ -16534,21 +16532,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -                if (result.size() >= limit) {
 -                    return AbortableIterationConsumer.Continuation.ABORT;
 -                }
-+        // Paper start - optimise this call
-+        //TODO use limit
-+        if (filter instanceof net.minecraft.world.entity.EntityType entityTypeTest) {
-+            ((ServerLevel) this).getEntityLookup().getEntities(entityTypeTest, box, result, predicate);
-+        } else {
-+            Predicate<? super T> test = (obj) -> {
-+                return filter.tryCast(obj) != null;
-+            };
-+            predicate = predicate == null ? test : test.and((Predicate) predicate);
-+            Class base;
-+            if (filter == null || (base = filter.getBaseClass()) == null || base == Entity.class) {
-+                ((ServerLevel) this).getEntityLookup().getEntities((Entity) null, box, (List) result, (Predicate)predicate);
-+            } else {
-+                ((ServerLevel) this).getEntityLookup().getEntities(base, null, box, (List) result, (Predicate)predicate); // Paper - optimise this call
-             }
+-            }
 -
 -            if (entity instanceof EnderDragon) {
 -                EnderDragon entityenderdragon = (EnderDragon) entity;
@@ -16566,7 +16550,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -                        }
 -                    }
 -                }
--            }
++        // Paper start - optimise this call
++        //TODO use limit
++        if (filter instanceof net.minecraft.world.entity.EntityType entityTypeTest) {
++            ((ServerLevel) this).getEntityLookup().getEntities(entityTypeTest, box, result, predicate);
++        } else {
++            Predicate<? super T> test = (obj) -> {
++                return filter.tryCast(obj) != null;
++            };
++            predicate = predicate == null ? test : test.and((Predicate) predicate);
++            Class base;
++            if (filter == null || (base = filter.getBaseClass()) == null || base == Entity.class) {
++                ((ServerLevel) this).getEntityLookup().getEntities((Entity) null, box, (List) result, (Predicate)predicate);
++            } else {
++                ((ServerLevel) this).getEntityLookup().getEntities(base, null, box, (List) result, (Predicate)predicate); // Paper - optimise this call
+             }
 -
 -            return AbortableIterationConsumer.Continuation.CONTINUE;
 -        });
@@ -16974,9 +16972,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
 -            return protochunk1;
 +            return new InProgressChunkHolder(protochunk1, tasksToExecuteOnMain); // Paper - Async chunk loading
-         }
-     }
- 
++        }
++    }
++
 +    // Paper start - async chunk save for unload
 +    public record AsyncSaveData(
 +        Tag blockTickList, // non-null if we had to go to the server's tick list
@@ -16998,7 +16996,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            if (blockEntityNbt != null) {
 +                blockEntitiesSerialized.add(blockEntityNbt);
 +            }
-+        }
+         }
 +
 +        return new AsyncSaveData(
 +            tickLists.get(BLOCK_TICKS_TAG),
@@ -17006,12 +17004,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            blockEntitiesSerialized,
 +            world.getGameTime()
 +        );
-+    }
+     }
 +    // Paper end
-+
+ 
      private static void logErrors(ChunkPos chunkPos, int y, String message) {
          ChunkSerializer.LOGGER.error("Recoverable errors when loading section [" + chunkPos.x + ", " + y + ", " + chunkPos.z + "]: " + message);
-     }
 @@ -0,0 +0,0 @@ public class ChunkSerializer {
      // CraftBukkit end
  
@@ -17151,11 +17148,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        } catch (Throwable thr) {
 +            return CompletableFuture.failedFuture(thr);
 +        }
-     }
++    }
 +    @Nullable
 +    public CompoundTag readSync(ChunkPos chunkPos) throws IOException {
 +        return this.regionFileCache.read(chunkPos);
-+    }
+     }
 +    // Paper end - async chunk io
  
 -    public void write(ChunkPos chunkPos, CompoundTag nbt) {
@@ -17242,17 +17239,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -            }
 -        }, this.entityDeserializerQueue::tell);
 +        throw new UnsupportedOperationException(); // Paper - rewrite chunk system - copy out read logic into readEntities
-     }
- 
--    private static ChunkPos readChunkPos(CompoundTag chunkNbt) {
++    }
++
 +    // Paper start - rewrite chunk system
 +    public static List<Entity> readEntities(ServerLevel level, CompoundTag compoundTag) {
 +        ListTag listTag = compoundTag.getList("Entities", 10);
 +        List<Entity> list = EntityType.loadEntitiesRecursive(listTag, level).collect(ImmutableList.toImmutableList());
 +        return list;
-+    }
+     }
 +    // Paper end - rewrite chunk system
-+
+ 
+-    private static ChunkPos readChunkPos(CompoundTag chunkNbt) {
 +    public static ChunkPos readChunkPos(CompoundTag chunkNbt) { // Paper - public
          int[] is = chunkNbt.getIntArray("Position");
          return new ChunkPos(is[0], is[1]);
@@ -17294,10 +17291,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          }
      }
  
--    @Override
--    public void flush(boolean sync) {
--        this.worker.synchronize(sync).join();
--        this.entityDeserializerQueue.runAll();
 +    // Paper start - rewrite chunk system
 +    public static void copyEntities(final CompoundTag from, final CompoundTag into) {
 +        if (from == null) {
@@ -17311,9 +17304,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        final ListTag entitiesInto = into.getList("Entities", net.minecraft.nbt.Tag.TAG_COMPOUND);
 +        into.put("Entities", entitiesInto); // this is in case into doesn't have any entities
 +        entitiesInto.addAll(0, entitiesFrom.copy()); // need to copy, this is coming from the save thread
-     }
- 
--    private CompoundTag upgradeChunkTag(CompoundTag chunkNbt) {
++    }
++
 +    public static CompoundTag saveEntityChunk(List<Entity> entities, ChunkPos chunkPos, ServerLevel level) {
 +        return saveEntityChunk0(entities, chunkPos, level, false);
 +    }
@@ -17339,11 +17331,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    }
 +    // Paper end - rewrite chunk system
 +
-+    @Override
-+    public void flush(boolean sync) {
+     @Override
+     public void flush(boolean sync) {
+-        this.worker.synchronize(sync).join();
+-        this.entityDeserializerQueue.runAll();
 +        throw new UnsupportedOperationException(); // Paper - rewrite chunk system
-+    }
-+
+     }
+ 
+-    private CompoundTag upgradeChunkTag(CompoundTag chunkNbt) {
 +    public static CompoundTag upgradeChunkTag(CompoundTag chunkNbt) { // Paper - public and static
          int i = NbtUtils.getDataVersion(chunkNbt, -1);
          return ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.ENTITY_CHUNK, chunkNbt, i, net.minecraft.SharedConstants.getCurrentVersion().getDataVersion().getVersion()); // Paper - route to new converter system
diff --git a/patches/server/Starlight.patch b/patches/server/Starlight.patch
index da37862087..68e8f830e9 100644
--- a/patches/server/Starlight.patch
+++ b/patches/server/Starlight.patch
@@ -4545,13 +4545,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        this.hasSkyLight = hasBlockLight; // Nice variable name.
 +        this.theLightEngine = new ca.spottedleaf.starlight.common.light.StarLightInterface(chunkProvider, this.hasSkyLight, this.hasBlockLight, this);
 +        // Paper end - replace light engine impl
-     }
- 
++    }
++
 +    // Paper start - replace light engine impl
 +    protected final ChunkAccess getChunk(final int chunkX, final int chunkZ) {
 +        return ((ServerLevel)this.theLightEngine.getWorld()).getChunkSource().getChunkAtImmediately(chunkX, chunkZ);
-+    }
-+
+     }
+ 
 +    protected long relightCounter;
 +
 +    public int relight(java.util.Set<ChunkPos> chunks_param,
@@ -5167,13 +5167,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                } else if (flag1) {
 +                    skyNibbles[y - minSection] = new ca.spottedleaf.starlight.common.light.SWMRNibbleArray(null, sectionData.getInt(SKYLIGHT_STATE_TAG));
 +                    // Paper end - rewrite the light engine
-                 }
++                }
 +
 +                // Paper start - rewrite the light engine
 +                } catch (Exception ex) {
 +                    LOGGER.warn("Failed to load light data for chunk " + chunkPos + " in world '" + world.getWorld().getName() + "', light will be regenerated", ex);
 +                    flag = false;
-+                }
+                 }
 +                // Paper end - rewrite light engine
              }
          }
diff --git a/patches/server/Stinger-API.patch b/patches/server/Stinger-API.patch
index c8353e290b..a0b14cfb94 100644
--- a/patches/server/Stinger-API.patch
+++ b/patches/server/Stinger-API.patch
@@ -17,12 +17,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    public int getBeeStingerCooldown() {
 +        return getHandle().removeStingerTime;
 +    }
- 
++
 +    @Override
 +    public void setBeeStingerCooldown(int ticks) {
 +        getHandle().removeStingerTime = ticks;
 +    }
-+
+ 
 +    @Override
 +    public int getBeeStingersInBody() {
 +        return getHandle().getStingerCount();
diff --git a/patches/server/Stop-copy-on-write-operations-for-updating-light-dat.patch b/patches/server/Stop-copy-on-write-operations-for-updating-light-dat.patch
index e8b618a3b1..7302583027 100644
--- a/patches/server/Stop-copy-on-write-operations-for-updating-light-dat.patch
+++ b/patches/server/Stop-copy-on-write-operations-for-updating-light-dat.patch
@@ -271,12 +271,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
      protected static final class SkyDataLayerStorageMap extends DataLayerStorageMap<SkyLightSectionStorage.SkyDataLayerStorageMap> {
          int currentLowestY;
 -        final Long2IntOpenHashMap topSections;
-+        private final com.destroystokyo.paper.util.map.QueuedChangesMapLong2Int otherData; // Paper - avoid copying light data
- 
+-
 -        public SkyDataLayerStorageMap(Long2ObjectOpenHashMap<DataLayer> arrays, Long2IntOpenHashMap columnToTopSection, int minSectionY) {
 -            super(arrays);
 -            this.topSections = columnToTopSection;
 -            columnToTopSection.defaultReturnValue(minSectionY);
++        private final com.destroystokyo.paper.util.map.QueuedChangesMapLong2Int otherData; // Paper - avoid copying light data
++
 +        // Paper start - avoid copying light data
 +        public SkyDataLayerStorageMap(com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<DataLayer> arrays, com.destroystokyo.paper.util.map.QueuedChangesMapLong2Int columnToTopSection, int minSectionY, boolean isVisible) {
 +            super(arrays, isVisible);
diff --git a/patches/server/Stop-large-look-changes-from-crashing-the-server.patch b/patches/server/Stop-large-look-changes-from-crashing-the-server.patch
index 3ac2871e0d..c39710f4e1 100644
--- a/patches/server/Stop-large-look-changes-from-crashing-the-server.patch
+++ b/patches/server/Stop-large-look-changes-from-crashing-the-server.patch
@@ -16,18 +16,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        while (this.getYRot() - this.yRotO < -180.0F) {
 -            this.yRotO -= 360.0F;
 -        }
-+        // Paper start - stop large pitch and yaw changes from crashing the server
-+        this.yRotO += Math.round((this.getYRot() - this.yRotO) / 360.0F) * 360.0F;
- 
+-
 -        while (this.getYRot() - this.yRotO >= 180.0F) {
 -            this.yRotO += 360.0F;
 -        }
-+        this.yBodyRotO += Math.round((this.yBodyRot - this.yBodyRotO) / 360.0F) * 360.0F;
++        // Paper start - stop large pitch and yaw changes from crashing the server
++        this.yRotO += Math.round((this.getYRot() - this.yRotO) / 360.0F) * 360.0F;
  
 -        while (this.yBodyRot - this.yBodyRotO < -180.0F) {
 -            this.yBodyRotO -= 360.0F;
 -        }
-+        this.xRotO += Math.round((this.getXRot() - this.xRotO) / 360.0F) * 360.0F;
++        this.yBodyRotO += Math.round((this.yBodyRot - this.yBodyRotO) / 360.0F) * 360.0F;
  
 -        while (this.yBodyRot - this.yBodyRotO >= 180.0F) {
 -            this.yBodyRotO += 360.0F;
@@ -36,7 +35,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        while (this.getXRot() - this.xRotO < -180.0F) {
 -            this.xRotO -= 360.0F;
 -        }
--
++        this.xRotO += Math.round((this.getXRot() - this.xRotO) / 360.0F) * 360.0F;
+ 
 -        while (this.getXRot() - this.xRotO >= 180.0F) {
 -            this.xRotO += 360.0F;
 -        }
diff --git a/patches/server/Use-TerminalConsoleAppender-for-console-improvements.patch b/patches/server/Use-TerminalConsoleAppender-for-console-improvements.patch
index fe9cd30794..3f2fd691de 100644
--- a/patches/server/Use-TerminalConsoleAppender-for-console-improvements.patch
+++ b/patches/server/Use-TerminalConsoleAppender-for-console-improvements.patch
@@ -397,9 +397,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
              if (offers == null) {
 -                return cursor;
 +                return; // Paper - Method returns void
-             }
--            candidates.addAll(offers);
- 
++            }
++
 +            // Paper start - JLine update
 +            for (String completion : offers) {
 +                if (completion.isEmpty()) {
@@ -407,9 +406,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                }
 +
 +                candidates.add(new Candidate(completion));
-+            }
+             }
+-            candidates.addAll(offers);
 +            // Paper end
-+
+ 
 +            // Paper start - JLine handles cursor now
 +            /*
              final int lastSpace = buffer.lastIndexOf(' ');
diff --git a/patches/server/add-DragonEggFormEvent.patch b/patches/server/add-DragonEggFormEvent.patch
index d149a50f84..7f2d18df5a 100644
--- a/patches/server/add-DragonEggFormEvent.patch
+++ b/patches/server/add-DragonEggFormEvent.patch
@@ -25,10 +25,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                //this.level.setBlockAndUpdate(this.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, EndPodiumFeature.END_PODIUM_LOCATION), Blocks.DRAGON_EGG.defaultBlockState());
 +            } else {
 +                eggEvent.setCancelled(true);
-             }
++            }
 +            if (eggEvent.callEvent()) {
 +                eggEvent.getNewState().update(true);
-+            }
+             }
 +            // Paper end - DragonEggFormEvent
  
              this.previouslyKilled = true;