diff --git a/patches/unapplied/api/0341-Add-Raw-Byte-Entity-Serialization.patch b/patches/api/0342-Add-Raw-Byte-Entity-Serialization.patch
similarity index 100%
rename from patches/unapplied/api/0341-Add-Raw-Byte-Entity-Serialization.patch
rename to patches/api/0342-Add-Raw-Byte-Entity-Serialization.patch
diff --git a/patches/unapplied/server/0793-Fix-Codec-log-spam.patch b/patches/removed/1.18/0793-Fix-Codec-log-spam.patch
similarity index 100%
rename from patches/unapplied/server/0793-Fix-Codec-log-spam.patch
rename to patches/removed/1.18/0793-Fix-Codec-log-spam.patch
diff --git a/patches/server/0351-Guard-against-serializing-mismatching-chunk-coordina.patch b/patches/server/0351-Guard-against-serializing-mismatching-chunk-coordina.patch
index 9bbe141d96..b625771c77 100644
--- a/patches/server/0351-Guard-against-serializing-mismatching-chunk-coordina.patch
+++ b/patches/server/0351-Guard-against-serializing-mismatching-chunk-coordina.patch
@@ -6,23 +6,29 @@ Subject: [PATCH] Guard against serializing mismatching chunk coordinate
 Should help if something dumb happens
 
 diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-index 992a7d6c4372942711a1381ac08ee13910a62cb0..37855d2183212cb043b30418740c0fd047dbc07f 100644
+index 384ee6fbc65baff381d875665fd2462dbc99683e..980c784b8e5365b62cbeef7f32af9f4383cc01e6 100644
 --- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
 +++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-@@ -75,6 +75,12 @@ public class ChunkSerializer {
+@@ -75,6 +75,18 @@ public class ChunkSerializer {
  
      public ChunkSerializer() {}
  
 +    // Paper start - guard against serializing mismatching coordinates
 +    // TODO Note: This needs to be re-checked each update
 +    public static ChunkPos getChunkCoordinate(CompoundTag chunkData) {
-+        return new ChunkPos(chunkData.getInt("xPos"), chunkData.getInt("zPos"));
++        final int dataVersion = ChunkStorage.getVersion(chunkData);
++        if (dataVersion < 2842) { // Level tag is removed after this version
++            final CompoundTag levelData = chunkData.getCompound("Level");
++            return new ChunkPos(levelData.getInt("xPos"), levelData.getInt("zPos"));
++        } else {
++            return new ChunkPos(chunkData.getInt("xPos"), chunkData.getInt("zPos"));
++        }
 +    }
 +    // Paper end
      // Paper start
      public static final class InProgressChunkHolder {
  
-@@ -100,7 +106,7 @@ public class ChunkSerializer {
+@@ -100,7 +112,7 @@ public class ChunkSerializer {
      public static InProgressChunkHolder loadChunk(ServerLevel world, PoiManager poiStorage, ChunkPos chunkPos, CompoundTag nbt, boolean distinguish) {
          java.util.ArrayDeque<Runnable> tasksToExecuteOnMain = new java.util.ArrayDeque<>();
          // Paper end
@@ -32,7 +38,7 @@ index 992a7d6c4372942711a1381ac08ee13910a62cb0..37855d2183212cb043b30418740c0fd0
          if (!Objects.equals(chunkPos, chunkcoordintpair1)) {
              ChunkSerializer.LOGGER.error("Chunk file at {} is in the wrong location; relocating. (Expected {}, got {})", chunkPos, chunkPos, chunkcoordintpair1);
 diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
-index 1fc202caf9051f12192ed479898b01b0a02eebbd..0631a5b1af38ace5ad167d1986271081bd2ee7a6 100644
+index 2eba8c2d1e978f677eaedac2e09cd1124f1d03db..a750b40be3ba5ba258ca2540ab0398deac5a6c5e 100644
 --- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
 +++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
 @@ -132,6 +132,13 @@ public class ChunkStorage implements AutoCloseable {
diff --git a/patches/server/0458-Optimize-NibbleArray-to-use-pooled-buffers.patch b/patches/server/0458-Optimize-NibbleArray-to-use-pooled-buffers.patch
index 50f950e9e5..e85cb4e1ec 100644
--- a/patches/server/0458-Optimize-NibbleArray-to-use-pooled-buffers.patch
+++ b/patches/server/0458-Optimize-NibbleArray-to-use-pooled-buffers.patch
@@ -256,10 +256,10 @@ index 81701abd11fbc4671393a76a42973f53835ca234..e8cf0088e94925934acd02ba05b9411b
  
      public String toString() {
 diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-index 0980efbc9ef092f56713c7ef776f78fd89cca818..634e44c3eac516f080b565a3b4c7691e31a3eb38 100644
+index 980c784b8e5365b62cbeef7f32af9f4383cc01e6..46beea026eec707c69194da6d1d51dc66b61f54e 100644
 --- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
 +++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-@@ -496,11 +496,11 @@ public class ChunkSerializer {
+@@ -502,11 +502,11 @@ public class ChunkSerializer {
                  }
  
                  if (nibblearray != null && !nibblearray.isEmpty()) {
diff --git a/patches/server/0478-Do-not-let-the-server-load-chunks-from-newer-version.patch b/patches/server/0478-Do-not-let-the-server-load-chunks-from-newer-version.patch
index 4826293d24..2fa64e3aff 100644
--- a/patches/server/0478-Do-not-let-the-server-load-chunks-from-newer-version.patch
+++ b/patches/server/0478-Do-not-let-the-server-load-chunks-from-newer-version.patch
@@ -9,10 +9,10 @@ the game, immediately stop the server to prevent data corruption.
 You can override this functionality at your own peril.
 
 diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-index 5c21871c7bdfce191db499860725da769dc9caac..d44ce73ea91abd7199695c417c534b6e4ca34e6a 100644
+index 46beea026eec707c69194da6d1d51dc66b61f54e..a5bd7d9a7440887d8997a96efebaf3db155263be 100644
 --- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
 +++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-@@ -103,9 +103,22 @@ public class ChunkSerializer {
+@@ -109,9 +109,22 @@ public class ChunkSerializer {
          return holder.protoChunk;
      }
  
diff --git a/patches/removed/1.18/0726-Add-Raw-Byte-Entity-Serialization.patch b/patches/server/0727-Add-Raw-Byte-Entity-Serialization.patch
similarity index 91%
rename from patches/removed/1.18/0726-Add-Raw-Byte-Entity-Serialization.patch
rename to patches/server/0727-Add-Raw-Byte-Entity-Serialization.patch
index 3f8b6c847d..6eeb7bafe0 100644
--- a/patches/removed/1.18/0726-Add-Raw-Byte-Entity-Serialization.patch
+++ b/patches/server/0727-Add-Raw-Byte-Entity-Serialization.patch
@@ -45,10 +45,10 @@ index ee50ea695585639d0ff184b675f3fb3b205b9f86..0bd800e1aeda87689a6c56ee6fadda38
      // Paper end
  }
 diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
-index 691b110e8a64081b68868456089908fe38fbc1cf..673b5192c32ee1b3c0d15462d3fadb0f31cd6a03 100644
+index 691b110e8a64081b68868456089908fe38fbc1cf..3eb5e7f7d449ccd862f42e2e131ebcddbdf79afb 100644
 --- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
 +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
-@@ -421,6 +421,29 @@ public final class CraftMagicNumbers implements UnsafeValues {
+@@ -421,6 +421,30 @@ public final class CraftMagicNumbers implements UnsafeValues {
          return CraftItemStack.asCraftMirror(net.minecraft.world.item.ItemStack.of((CompoundTag) converted.getValue()));
      }
  
@@ -69,7 +69,8 @@ index 691b110e8a64081b68868456089908fe38fbc1cf..673b5192c32ee1b3c0d15462d3fadb0f
 +
 +        CompoundTag compound = deserializeNbtFromBytes(data);
 +        int dataVersion = compound.getInt("DataVersion");
-+        compound = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.ENTITY, compound, dataVersion, getDataVersion());
++        Dynamic<Tag> converted = DataFixers.getDataFixer().update(References.ENTITY_TREE, new Dynamic<>(NbtOps.INSTANCE, compound), dataVersion, getDataVersion());
++        compound = (CompoundTag) converted.getValue();
 +        if (!preserveUUID) compound.remove("UUID"); // Generate a new UUID so we don't have to worry about deserializing the same entity twice
 +        return net.minecraft.world.entity.EntityType.create(compound, ((org.bukkit.craftbukkit.CraftWorld) world).getHandle())
 +            .orElseThrow(() -> new IllegalArgumentException("An ID was not found for the data. Did you downgrade?")).getBukkitEntity();
diff --git a/patches/server/0727-Vanilla-command-permission-fixes.patch b/patches/server/0728-Vanilla-command-permission-fixes.patch
similarity index 100%
rename from patches/server/0727-Vanilla-command-permission-fixes.patch
rename to patches/server/0728-Vanilla-command-permission-fixes.patch
diff --git a/patches/server/0728-Make-CallbackExecutor-strict-again.patch b/patches/server/0729-Make-CallbackExecutor-strict-again.patch
similarity index 100%
rename from patches/server/0728-Make-CallbackExecutor-strict-again.patch
rename to patches/server/0729-Make-CallbackExecutor-strict-again.patch
diff --git a/patches/server/0729-Do-not-allow-the-server-to-unload-chunks-at-request-.patch b/patches/server/0730-Do-not-allow-the-server-to-unload-chunks-at-request-.patch
similarity index 100%
rename from patches/server/0729-Do-not-allow-the-server-to-unload-chunks-at-request-.patch
rename to patches/server/0730-Do-not-allow-the-server-to-unload-chunks-at-request-.patch
diff --git a/patches/server/0730-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch b/patches/server/0731-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch
similarity index 100%
rename from patches/server/0730-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch
rename to patches/server/0731-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch
diff --git a/patches/server/0731-Correctly-handle-recursion-for-chunkholder-updates.patch b/patches/server/0732-Correctly-handle-recursion-for-chunkholder-updates.patch
similarity index 100%
rename from patches/server/0731-Correctly-handle-recursion-for-chunkholder-updates.patch
rename to patches/server/0732-Correctly-handle-recursion-for-chunkholder-updates.patch
diff --git a/patches/server/0732-Separate-lookup-locking-from-state-access-in-UserCac.patch b/patches/server/0733-Separate-lookup-locking-from-state-access-in-UserCac.patch
similarity index 100%
rename from patches/server/0732-Separate-lookup-locking-from-state-access-in-UserCac.patch
rename to patches/server/0733-Separate-lookup-locking-from-state-access-in-UserCac.patch
diff --git a/patches/server/0733-Fix-chunks-refusing-to-unload-at-low-TPS.patch b/patches/server/0734-Fix-chunks-refusing-to-unload-at-low-TPS.patch
similarity index 100%
rename from patches/server/0733-Fix-chunks-refusing-to-unload-at-low-TPS.patch
rename to patches/server/0734-Fix-chunks-refusing-to-unload-at-low-TPS.patch
diff --git a/patches/server/0734-Do-not-allow-ticket-level-changes-while-unloading-pl.patch b/patches/server/0735-Do-not-allow-ticket-level-changes-while-unloading-pl.patch
similarity index 100%
rename from patches/server/0734-Do-not-allow-ticket-level-changes-while-unloading-pl.patch
rename to patches/server/0735-Do-not-allow-ticket-level-changes-while-unloading-pl.patch
diff --git a/patches/server/0735-Do-not-allow-ticket-level-changes-when-updating-chun.patch b/patches/server/0736-Do-not-allow-ticket-level-changes-when-updating-chun.patch
similarity index 100%
rename from patches/server/0735-Do-not-allow-ticket-level-changes-when-updating-chun.patch
rename to patches/server/0736-Do-not-allow-ticket-level-changes-when-updating-chun.patch
diff --git a/patches/server/0736-Do-not-submit-profile-lookups-to-worldgen-threads.patch b/patches/server/0737-Do-not-submit-profile-lookups-to-worldgen-threads.patch
similarity index 100%
rename from patches/server/0736-Do-not-submit-profile-lookups-to-worldgen-threads.patch
rename to patches/server/0737-Do-not-submit-profile-lookups-to-worldgen-threads.patch
diff --git a/patches/server/0737-Log-when-the-async-catcher-is-tripped.patch b/patches/server/0738-Log-when-the-async-catcher-is-tripped.patch
similarity index 100%
rename from patches/server/0737-Log-when-the-async-catcher-is-tripped.patch
rename to patches/server/0738-Log-when-the-async-catcher-is-tripped.patch
diff --git a/patches/server/0738-Sanitize-ResourceLocation-error-logging.patch b/patches/server/0739-Sanitize-ResourceLocation-error-logging.patch
similarity index 100%
rename from patches/server/0738-Sanitize-ResourceLocation-error-logging.patch
rename to patches/server/0739-Sanitize-ResourceLocation-error-logging.patch
diff --git a/patches/server/0739-Optimise-general-POI-access.patch b/patches/server/0740-Optimise-general-POI-access.patch
similarity index 100%
rename from patches/server/0739-Optimise-general-POI-access.patch
rename to patches/server/0740-Optimise-general-POI-access.patch
diff --git a/patches/server/0740-Allow-controlled-flushing-for-network-manager.patch b/patches/server/0741-Allow-controlled-flushing-for-network-manager.patch
similarity index 100%
rename from patches/server/0740-Allow-controlled-flushing-for-network-manager.patch
rename to patches/server/0741-Allow-controlled-flushing-for-network-manager.patch
diff --git a/patches/server/0741-Add-more-async-catchers.patch b/patches/server/0742-Add-more-async-catchers.patch
similarity index 100%
rename from patches/server/0741-Add-more-async-catchers.patch
rename to patches/server/0742-Add-more-async-catchers.patch
diff --git a/patches/server/0742-Rewrite-entity-bounding-box-lookup-calls.patch b/patches/server/0743-Rewrite-entity-bounding-box-lookup-calls.patch
similarity index 99%
rename from patches/server/0742-Rewrite-entity-bounding-box-lookup-calls.patch
rename to patches/server/0743-Rewrite-entity-bounding-box-lookup-calls.patch
index 85256bd18b..2d7fa25573 100644
--- a/patches/server/0742-Rewrite-entity-bounding-box-lookup-calls.patch
+++ b/patches/server/0743-Rewrite-entity-bounding-box-lookup-calls.patch
@@ -953,7 +953,7 @@ index 5d189257f494eb12b5fd98b12da6dd09ca14f972..913b56361dece6c699ed7fad7e580d40
 +    // Paper end
  }
 diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index dcfc726ab96dccc05848219e824ad7612dbfbdab..db6c11694e6316c50a3f0a138e09542fdae45718 100644
+index 7713f26d4a97df94c27694d28881d298e4c54147..3110f8cbf65ba0fefbf78f90915ee358694d20ca 100644
 --- a/src/main/java/net/minecraft/world/entity/Entity.java
 +++ b/src/main/java/net/minecraft/world/entity/Entity.java
 @@ -386,6 +386,56 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
@@ -1013,7 +1013,7 @@ index dcfc726ab96dccc05848219e824ad7612dbfbdab..db6c11694e6316c50a3f0a138e09542f
      public Entity(EntityType<?> type, Level world) {
          this.id = Entity.ENTITY_COUNTER.incrementAndGet();
          this.passengers = ImmutableList.of();
-@@ -2233,11 +2283,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
+@@ -2242,11 +2292,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
          return InteractionResult.PASS;
      }
  
diff --git a/patches/server/0743-Execute-chunk-tasks-mid-tick.patch b/patches/server/0744-Execute-chunk-tasks-mid-tick.patch
similarity index 100%
rename from patches/server/0743-Execute-chunk-tasks-mid-tick.patch
rename to patches/server/0744-Execute-chunk-tasks-mid-tick.patch
diff --git a/patches/server/0744-Do-not-copy-visible-chunks.patch b/patches/server/0745-Do-not-copy-visible-chunks.patch
similarity index 100%
rename from patches/server/0744-Do-not-copy-visible-chunks.patch
rename to patches/server/0745-Do-not-copy-visible-chunks.patch
diff --git a/patches/server/0745-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch b/patches/server/0746-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch
similarity index 99%
rename from patches/server/0745-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch
rename to patches/server/0746-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch
index a8ae16c3cb..749c4fc580 100644
--- a/patches/server/0745-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch
+++ b/patches/server/0746-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch
@@ -10,7 +10,7 @@ hoping that at least then we don't swap chunks, and maybe recover
 them all.
 
 diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-index f58050eaa1354ace7b3558d528ab8effdd1432aa..23b3203d04cf1dd2fc3c42e2c7b287a949c81699 100644
+index a5bd7d9a7440887d8997a96efebaf3db155263be..c1f22e3e9b3ab91f05556707ace46e9c627b025e 100644
 --- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
 +++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
 @@ -66,6 +66,12 @@ import org.apache.logging.log4j.LogManager;
@@ -26,7 +26,7 @@ index f58050eaa1354ace7b3558d528ab8effdd1432aa..23b3203d04cf1dd2fc3c42e2c7b287a9
  
      public static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codec(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
      private static final Logger LOGGER = LogManager.getLogger();
-@@ -437,7 +443,7 @@ public class ChunkSerializer {
+@@ -443,7 +449,7 @@ public class ChunkSerializer {
          nbttagcompound.putInt("xPos", chunkcoordintpair.x);
          nbttagcompound.putInt("yPos", chunk.getMinSection());
          nbttagcompound.putInt("zPos", chunkcoordintpair.z);
diff --git a/patches/server/0746-Custom-table-implementation-for-blockstate-state-loo.patch b/patches/server/0747-Custom-table-implementation-for-blockstate-state-loo.patch
similarity index 100%
rename from patches/server/0746-Custom-table-implementation-for-blockstate-state-loo.patch
rename to patches/server/0747-Custom-table-implementation-for-blockstate-state-loo.patch
diff --git a/patches/server/0747-Detail-more-information-in-watchdog-dumps.patch b/patches/server/0748-Detail-more-information-in-watchdog-dumps.patch
similarity index 98%
rename from patches/server/0747-Detail-more-information-in-watchdog-dumps.patch
rename to patches/server/0748-Detail-more-information-in-watchdog-dumps.patch
index f03e054f56..5a422cba90 100644
--- a/patches/server/0747-Detail-more-information-in-watchdog-dumps.patch
+++ b/patches/server/0748-Detail-more-information-in-watchdog-dumps.patch
@@ -123,7 +123,7 @@ index 5d4f20a31ad99b4e808bb9a7aaa2153666af493f..928ac2d5b93b93aa7494374f4f344655
  
      private void tickPassenger(Entity vehicle, Entity passenger) {
 diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index db6c11694e6316c50a3f0a138e09542fdae45718..62fe626aa198d19ccfb48f47b8f771b6037cff9c 100644
+index 3110f8cbf65ba0fefbf78f90915ee358694d20ca..96794dcb87c3606e9d112d4159be8be31ad4329e 100644
 --- a/src/main/java/net/minecraft/world/entity/Entity.java
 +++ b/src/main/java/net/minecraft/world/entity/Entity.java
 @@ -860,7 +860,42 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
@@ -183,7 +183,7 @@ index db6c11694e6316c50a3f0a138e09542fdae45718..62fe626aa198d19ccfb48f47b8f771b6
      }
  
      protected boolean isHorizontalCollisionMinor(Vec3 adjustedMovement) {
-@@ -3798,7 +3840,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
+@@ -3807,7 +3849,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
      }
  
      public void setDeltaMovement(Vec3 velocity) {
@@ -193,7 +193,7 @@ index db6c11694e6316c50a3f0a138e09542fdae45718..62fe626aa198d19ccfb48f47b8f771b6
      }
  
      public void setDeltaMovement(double x, double y, double z) {
-@@ -3874,7 +3918,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
+@@ -3883,7 +3927,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
          }
          // Paper end - fix MC-4
          if (this.position.x != x || this.position.y != y || this.position.z != z) {
diff --git a/patches/server/0748-Manually-inline-methods-in-BlockPosition.patch b/patches/server/0749-Manually-inline-methods-in-BlockPosition.patch
similarity index 100%
rename from patches/server/0748-Manually-inline-methods-in-BlockPosition.patch
rename to patches/server/0749-Manually-inline-methods-in-BlockPosition.patch
diff --git a/patches/server/0749-Distance-manager-tick-timings.patch b/patches/server/0750-Distance-manager-tick-timings.patch
similarity index 100%
rename from patches/server/0749-Distance-manager-tick-timings.patch
rename to patches/server/0750-Distance-manager-tick-timings.patch
diff --git a/patches/server/0750-Name-craft-scheduler-threads-according-to-the-plugin.patch b/patches/server/0751-Name-craft-scheduler-threads-according-to-the-plugin.patch
similarity index 100%
rename from patches/server/0750-Name-craft-scheduler-threads-according-to-the-plugin.patch
rename to patches/server/0751-Name-craft-scheduler-threads-according-to-the-plugin.patch
diff --git a/patches/server/0751-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch b/patches/server/0752-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch
similarity index 100%
rename from patches/server/0751-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch
rename to patches/server/0752-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch
diff --git a/patches/server/0752-Add-packet-limiter-config.patch b/patches/server/0753-Add-packet-limiter-config.patch
similarity index 100%
rename from patches/server/0752-Add-packet-limiter-config.patch
rename to patches/server/0753-Add-packet-limiter-config.patch
diff --git a/patches/server/0753-Lag-compensate-block-breaking.patch b/patches/server/0754-Lag-compensate-block-breaking.patch
similarity index 100%
rename from patches/server/0753-Lag-compensate-block-breaking.patch
rename to patches/server/0754-Lag-compensate-block-breaking.patch
diff --git a/patches/server/0754-Use-correct-LevelStem-registry-when-loading-default-.patch b/patches/server/0755-Use-correct-LevelStem-registry-when-loading-default-.patch
similarity index 100%
rename from patches/server/0754-Use-correct-LevelStem-registry-when-loading-default-.patch
rename to patches/server/0755-Use-correct-LevelStem-registry-when-loading-default-.patch
diff --git a/patches/server/0755-Don-t-read-neighbour-chunk-data-off-disk-when-conver.patch b/patches/server/0756-Don-t-read-neighbour-chunk-data-off-disk-when-conver.patch
similarity index 100%
rename from patches/server/0755-Don-t-read-neighbour-chunk-data-off-disk-when-conver.patch
rename to patches/server/0756-Don-t-read-neighbour-chunk-data-off-disk-when-conver.patch
diff --git a/patches/server/0756-Consolidate-flush-calls-for-entity-tracker-packets.patch b/patches/server/0757-Consolidate-flush-calls-for-entity-tracker-packets.patch
similarity index 100%
rename from patches/server/0756-Consolidate-flush-calls-for-entity-tracker-packets.patch
rename to patches/server/0757-Consolidate-flush-calls-for-entity-tracker-packets.patch
diff --git a/patches/server/0757-Don-t-lookup-fluid-state-when-raytracing.patch b/patches/server/0758-Don-t-lookup-fluid-state-when-raytracing.patch
similarity index 100%
rename from patches/server/0757-Don-t-lookup-fluid-state-when-raytracing.patch
rename to patches/server/0758-Don-t-lookup-fluid-state-when-raytracing.patch
diff --git a/patches/server/0758-Time-scoreboard-search.patch b/patches/server/0759-Time-scoreboard-search.patch
similarity index 100%
rename from patches/server/0758-Time-scoreboard-search.patch
rename to patches/server/0759-Time-scoreboard-search.patch
diff --git a/patches/server/0759-Send-full-pos-packets-for-hard-colliding-entities.patch b/patches/server/0760-Send-full-pos-packets-for-hard-colliding-entities.patch
similarity index 100%
rename from patches/server/0759-Send-full-pos-packets-for-hard-colliding-entities.patch
rename to patches/server/0760-Send-full-pos-packets-for-hard-colliding-entities.patch
diff --git a/patches/server/0760-Do-not-run-raytrace-logic-for-AIR.patch b/patches/server/0761-Do-not-run-raytrace-logic-for-AIR.patch
similarity index 100%
rename from patches/server/0760-Do-not-run-raytrace-logic-for-AIR.patch
rename to patches/server/0761-Do-not-run-raytrace-logic-for-AIR.patch
diff --git a/patches/server/0761-Oprimise-map-impl-for-tracked-players.patch b/patches/server/0762-Oprimise-map-impl-for-tracked-players.patch
similarity index 100%
rename from patches/server/0761-Oprimise-map-impl-for-tracked-players.patch
rename to patches/server/0762-Oprimise-map-impl-for-tracked-players.patch
diff --git a/patches/server/0762-Optimise-BlockSoil-nearby-water-lookup.patch b/patches/server/0763-Optimise-BlockSoil-nearby-water-lookup.patch
similarity index 100%
rename from patches/server/0762-Optimise-BlockSoil-nearby-water-lookup.patch
rename to patches/server/0763-Optimise-BlockSoil-nearby-water-lookup.patch
diff --git a/patches/server/0763-Allow-removal-addition-of-entities-to-entity-ticklis.patch b/patches/server/0764-Allow-removal-addition-of-entities-to-entity-ticklis.patch
similarity index 100%
rename from patches/server/0763-Allow-removal-addition-of-entities-to-entity-ticklis.patch
rename to patches/server/0764-Allow-removal-addition-of-entities-to-entity-ticklis.patch
diff --git a/patches/server/0764-Optimise-random-block-ticking.patch b/patches/server/0765-Optimise-random-block-ticking.patch
similarity index 100%
rename from patches/server/0764-Optimise-random-block-ticking.patch
rename to patches/server/0765-Optimise-random-block-ticking.patch
diff --git a/patches/unapplied/server/0791-Optimise-non-flush-packet-sending.patch b/patches/server/0766-Optimise-non-flush-packet-sending.patch
similarity index 94%
rename from patches/unapplied/server/0791-Optimise-non-flush-packet-sending.patch
rename to patches/server/0766-Optimise-non-flush-packet-sending.patch
index c5f1223f87..f8dc75ba8c 100644
--- a/patches/unapplied/server/0791-Optimise-non-flush-packet-sending.patch
+++ b/patches/server/0766-Optimise-non-flush-packet-sending.patch
@@ -20,7 +20,7 @@ up on this optimisation before he came along.
 Locally this patch drops the entity tracker tick by a full 1.5x.
 
 diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index 300cd4d2861c7f155cc6a5bb5a0c47b0b77ff240..7c66d5d51efd3ec55f5170cf828db22e26131517 100644
+index 241b086bd096a4bc2175835b2505deda1c143f09..a1aafb037fd340dc93dd2afb758ffc7457d15f84 100644
 --- a/src/main/java/net/minecraft/network/Connection.java
 +++ b/src/main/java/net/minecraft/network/Connection.java
 @@ -49,6 +49,8 @@ import org.apache.logging.log4j.Logger;
@@ -32,7 +32,7 @@ index 300cd4d2861c7f155cc6a5bb5a0c47b0b77ff240..7c66d5d51efd3ec55f5170cf828db22e
  public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
  
      private static final float AVERAGE_PACKETS_SMOOTHING = 0.75F;
-@@ -409,9 +411,19 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -387,9 +389,19 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
          if (this.channel.eventLoop().inEventLoop()) {
              this.doSendPacket(packet, callback, enumprotocol, enumprotocol1, flush); // Paper - add flush parameter
          } else {
diff --git a/patches/unapplied/server/0792-Optimise-nearby-player-lookups.patch b/patches/server/0767-Optimise-nearby-player-lookups.patch
similarity index 81%
rename from patches/unapplied/server/0792-Optimise-nearby-player-lookups.patch
rename to patches/server/0767-Optimise-nearby-player-lookups.patch
index 7ac99c624f..0d6a80476c 100644
--- a/patches/unapplied/server/0792-Optimise-nearby-player-lookups.patch
+++ b/patches/server/0767-Optimise-nearby-player-lookups.patch
@@ -9,13 +9,13 @@ since the penalty of a map lookup could outweigh the benefits of
 searching less players (as it basically did in the outside range patch).
 
 diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
-index 4588ae8037407b81c99135863eb0c4e97c564c24..2a33071c4b69cb7b5a7e296e8fd903e3a528b210 100644
+index 7418245d5d08706ca2a1378e769abfb0de1076ed..8a7cc96f563c3fb8807d4a8a3249fc0892710d17 100644
 --- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
 +++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
-@@ -242,6 +242,12 @@ public class ChunkHolder {
-         long key = net.minecraft.server.MCUtil.getCoordinateKey(this.pos);
-         this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key);
-         this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key);
+@@ -94,6 +94,12 @@ public class ChunkHolder {
+         this.setTicketLevel(level);
+         this.changedBlocksPerSection = new ShortSet[world.getSectionsCount()];
+         this.chunkMap = (ChunkMap)playersWatchingChunkProvider; // Paper
 +        // Paper start - optimise checkDespawn
 +        LevelChunk chunk = this.getFullChunkUnchecked();
 +        if (chunk != null) {
@@ -23,16 +23,16 @@ index 4588ae8037407b81c99135863eb0c4e97c564c24..2a33071c4b69cb7b5a7e296e8fd903e3
 +        }
 +        // Paper end - optimise checkDespawn
      }
-     // Paper end - optimise isOutsideOfRange
-     long lastAutoSaveTime; // Paper - incremental autosave
+ 
+     // CraftBukkit start
 diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 6522eb4b72c50be3ff7d1b094066a1cbec88634d..10d1e1f728519ad49379895c7fd3668badcbfd5a 100644
+index 925da0baa59f742dbe727c6323cc90b65159f314..3b2db473e5eacbcf55ae8786aff5ac71388a98ee 100644
 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java
 +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -217,6 +217,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
-     public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobSpawnMap; // this map is absent from updateMaps since it's controlled at the start of the chunkproviderserver tick
-     public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerChunkTickRangeMap;
-     // Paper end - optimise PlayerChunkMap#isOutsideRange
+@@ -190,21 +190,36 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+     final CallbackExecutor chunkLoadConversionCallbackExecutor = new CallbackExecutor(); // Paper
+     // Paper start - distance maps
+     private final com.destroystokyo.paper.util.misc.PooledLinkedHashSets<ServerPlayer> pooledLinkedPlayerHashSets = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets<>();
 +    // Paper start - optimise checkDespawn
 +    public static final int GENERAL_AREA_MAP_SQUARE_RADIUS = 40;
 +    public static final double GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE = 16.0 * (GENERAL_AREA_MAP_SQUARE_RADIUS - 1);
@@ -42,40 +42,34 @@ index 6522eb4b72c50be3ff7d1b094066a1cbec88634d..10d1e1f728519ad49379895c7fd3668b
  
      void addPlayerToDistanceMaps(ServerPlayer player) {
          int chunkX = MCUtil.getChunkCoordinate(player.getX());
-@@ -237,6 +243,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
-         this.playerChunkTickRangeMap.add(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE);
-         // Paper end - optimise PlayerChunkMap#isOutsideRange
-         this.playerChunkManager.addPlayer(player); // Paper - replace chunk loader
+         int chunkZ = MCUtil.getChunkCoordinate(player.getZ());
+         // Note: players need to be explicitly added to distance maps before they can be updated
 +        // Paper start - optimise checkDespawn
 +        this.playerGeneralAreaMap.add(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS);
 +        // Paper end - optimise checkDespawn
      }
  
      void removePlayerFromDistanceMaps(ServerPlayer player) {
-@@ -250,6 +259,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
-         this.playerChunkTickRangeMap.remove(player);
-         // Paper end - optimise PlayerChunkMap#isOutsideRange
-         this.playerChunkManager.removePlayer(player); // Paper - replace chunk loader
+ 
 +        // Paper start - optimise checkDespawn
 +        this.playerGeneralAreaMap.remove(player);
 +        // Paper end - optimise checkDespawn
      }
  
      void updateMaps(ServerPlayer player) {
-@@ -268,6 +280,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
-         this.playerChunkTickRangeMap.update(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE);
-         // Paper end - optimise PlayerChunkMap#isOutsideRange
-         this.playerChunkManager.updatePlayer(player); // Paper - replace chunk loader
+         int chunkX = MCUtil.getChunkCoordinate(player.getX());
+         int chunkZ = MCUtil.getChunkCoordinate(player.getZ());
+         // Note: players need to be explicitly added to distance maps before they can be updated
 +        // Paper start - optimise checkDespawn
 +        this.playerGeneralAreaMap.update(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS);
 +        // Paper end - optimise checkDespawn
      }
      // Paper end
      // Paper start
-@@ -426,6 +441,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
-                 }
-             });
-         // Paper end - optimise PlayerChunkMap#isOutsideRange
+@@ -280,6 +295,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+         this.regionManagers.add(this.dataRegionManager);
+         // Paper end
+         this.playerMobDistanceMap = this.level.paperConfig.perPlayerMobSpawns ? new com.destroystokyo.paper.util.PlayerMobDistanceMap() : null; // Paper
 +        // Paper start - optimise checkDespawn
 +        this.playerGeneralAreaMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
 +            (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
@@ -95,32 +89,15 @@ index 6522eb4b72c50be3ff7d1b094066a1cbec88634d..10d1e1f728519ad49379895c7fd3668b
 +        // Paper end - optimise checkDespawn
      }
  
-     // Paper start - Chunk Prioritization
-@@ -739,7 +771,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
-         } else {
-             if (holder != null) {
-                 holder.setTicketLevel(level);
--                holder.updateRanges(); // Paper - optimise isOutsideOfRange
-+                // Paper - move to correct place
-             }
- 
-             if (holder != null) {
-@@ -754,6 +786,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
-                 holder = (ChunkHolder) this.pendingUnloads.remove(pos);
-                 if (holder != null) {
-                     holder.setTicketLevel(level);
-+                    holder.updateRanges(); // Paper - optimise isOutsideOfRange // Paper - move to correct place
-                 } else {
-                     holder = new ChunkHolder(new ChunkPos(pos), level, this.level, this.lightEngine, this.queueSorter, this);
-                     // Paper start
+     protected ChunkGenerator generator() {
 diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index ed696ed93251e644c2b71e468e2556d24b273933..3afb8b91dceb86e88b848462a5b8dbd5c8365239 100644
+index 5935c442a6534ad51d191a72bc8d2043fa25e2ac..8a219ecabe49d8f2564a365968891b214a090f6d 100644
 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java
 +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -392,6 +392,83 @@ public class ServerLevel extends Level implements WorldGenLevel {
-         }
+@@ -396,6 +396,83 @@ public class ServerLevel extends Level implements WorldGenLevel {
+         return this.getServer().getPlayerList().getPlayer(uuid);
      }
-     // Paper end - rewrite ticklistserver
+     // Paper end
 +    // Paper start - optimise checkDespawn
 +    public final List<ServerPlayer> playersAffectingSpawning = new java.util.ArrayList<>();
 +    // Paper end - optimise checkDespawn
@@ -201,7 +178,7 @@ index ed696ed93251e644c2b71e468e2556d24b273933..3afb8b91dceb86e88b848462a5b8dbd5
  
      // Add env and gen to constructor, WorldData -> WorldDataServer
      public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, ServerLevelData iworlddataserver, ResourceKey<Level> resourcekey, DimensionType dimensionmanager, ChunkProgressListener worldloadlistener, ChunkGenerator chunkgenerator, boolean flag, long i, List<CustomSpawner> list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) {
-@@ -492,6 +569,14 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -484,6 +561,14 @@ public class ServerLevel extends Level implements WorldGenLevel {
      }
  
      public void tick(BooleanSupplier shouldKeepTicking) {
@@ -217,10 +194,10 @@ index ed696ed93251e644c2b71e468e2556d24b273933..3afb8b91dceb86e88b848462a5b8dbd5
  
          this.handlingTick = true;
 diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java
-index 5f4556505d831ea45e576a4c4e10f99e1353684e..55d07e70a67e08bab3a7a66076c980986736e5b8 100644
+index 697c2663c4deeb8f2ad603c979ab0884ac027930..0b46066d35d9bb38d98a9d6e5ca8dbdc0ba1dc5a 100644
 --- a/src/main/java/net/minecraft/world/entity/Mob.java
 +++ b/src/main/java/net/minecraft/world/entity/Mob.java
-@@ -789,7 +789,12 @@ public abstract class Mob extends LivingEntity {
+@@ -792,7 +792,12 @@ public abstract class Mob extends LivingEntity {
          if (this.level.getDifficulty() == Difficulty.PEACEFUL && this.shouldDespawnInPeaceful()) {
              this.discard();
          } else if (!this.isPersistenceRequired() && !this.requiresCustomPersistence()) {
@@ -233,12 +210,12 @@ index 5f4556505d831ea45e576a4c4e10f99e1353684e..55d07e70a67e08bab3a7a66076c98098
 +            // Paper end - optimise checkDespawn
  
              if (entityhuman != null) {
-                 double d0 = entityhuman.distanceToSqr((Entity) this); // CraftBukkit - decompile error
+                 double d0 = entityhuman.distanceToSqr((Entity) this);
 diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 64d5e71a8a26116385cee195d86fce1ef1574a8c..f936e9f9a9fa655fa997d6862b5ed54c04169d35 100644
+index 6be462975523dae9ff436ba1643b2042ec66e554..929f48675a10fdd64cb351305389d680a16963fb 100644
 --- a/src/main/java/net/minecraft/world/level/Level.java
 +++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -247,6 +247,69 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -243,6 +243,69 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
          return ret;
      }
      // Paper end
@@ -306,13 +283,13 @@ index 64d5e71a8a26116385cee195d86fce1ef1574a8c..f936e9f9a9fa655fa997d6862b5ed54c
 +    }
 +    // Paper end - optimise checkDespawn
  
-     protected Level(WritableLevelData worlddatamutable, ResourceKey<Level> resourcekey, final DimensionType dimensionmanager, Supplier<ProfilerFiller> supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.concurrent.Executor executor) { // Paper - Anti-Xray - Pass executor
+     protected Level(WritableLevelData worlddatamutable, ResourceKey<Level> resourcekey, final DimensionType dimensionmanager, Supplier<ProfilerFiller> supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env) {
          this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot
 diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
-index 4d8251a961a9c52456db997506dd9691beaec022..55dd04816886d27a62856ac952d2fc5d15bf40e6 100644
+index 63ba93538d990fdd4c9e8c491bb715adc8d57986..e9a37fc6791366ea421f2766a36dc2e014ab7951 100644
 --- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
 +++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
-@@ -267,7 +267,7 @@ public final class NaturalSpawner {
+@@ -269,7 +269,7 @@ public final class NaturalSpawner {
                              blockposition_mutableblockposition.set(l, i, i1);
                              double d0 = (double) l + 0.5D;
                              double d1 = (double) i1 + 0.5D;
@@ -321,7 +298,7 @@ index 4d8251a961a9c52456db997506dd9691beaec022..55dd04816886d27a62856ac952d2fc5d
  
                              if (entityhuman != null) {
                                  double d2 = entityhuman.distanceToSqr(d0, (double) i, d1);
-@@ -340,7 +340,7 @@ public final class NaturalSpawner {
+@@ -342,7 +342,7 @@ public final class NaturalSpawner {
      }
  
      private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel world, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double squaredDistance) {
@@ -331,10 +308,10 @@ index 4d8251a961a9c52456db997506dd9691beaec022..55dd04816886d27a62856ac952d2fc5d
  
      private static Boolean isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, MobSpawnSettings.SpawnerData spawnEntry, BlockPos.MutableBlockPos pos, double squaredDistance) { // Paper
 diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-index 9e75ee7f43722f05dd5df4ef00f7d3e271f4c6e5..86686c24b0b7de4b4bfadbc77419a8872a8e86ee 100644
+index b92da719a5d35a60a2e13ccb0f55c41b242f9b50..a9807f9182a4d70e09486612606c7c1d0a6734ac 100644
 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
 +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -369,6 +369,93 @@ public class LevelChunk implements ChunkAccess {
+@@ -231,6 +231,93 @@ public class LevelChunk extends ChunkAccess {
          }
      }
      // Paper end
@@ -426,5 +403,5 @@ index 9e75ee7f43722f05dd5df4ef00f7d3e271f4c6e5..86686c24b0b7de4b4bfadbc77419a887
 +    }
 +    // Paper end - optimise checkDespawn
  
-     public LevelChunk(ServerLevel worldserver, ProtoChunk protoChunk, @Nullable Consumer<LevelChunk> consumer) {
-         this(worldserver, protoChunk.getPos(), protoChunk.getBiomes(), protoChunk.getUpgradeData(), protoChunk.getBlockTicks(), protoChunk.getLiquidTicks(), protoChunk.getInhabitedTime(), protoChunk.getSections(), consumer);
+     public LevelChunk(ServerLevel world, ProtoChunk protoChunk, @Nullable LevelChunk.PostLoadProcessor chunk_c) {
+         this(world, protoChunk.getPos(), protoChunk.getUpgradeData(), protoChunk.unpackBlockTicks(), protoChunk.unpackFluidTicks(), protoChunk.getInhabitedTime(), protoChunk.getSections(), chunk_c, protoChunk.getBlendingData());
diff --git a/patches/unapplied/server/0794-Optimise-WorldServer-notify.patch b/patches/server/0768-Optimise-WorldServer-notify.patch
similarity index 93%
rename from patches/unapplied/server/0794-Optimise-WorldServer-notify.patch
rename to patches/server/0768-Optimise-WorldServer-notify.patch
index 8657ce3d53..9e09b42d3b 100644
--- a/patches/unapplied/server/0794-Optimise-WorldServer-notify.patch
+++ b/patches/server/0768-Optimise-WorldServer-notify.patch
@@ -8,10 +8,10 @@ Instead, only iterate over navigators in the current region that are
 eligible for repathing.
 
 diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index bef7d5b4c8b99d2fbcd975127b16653e0f391338..19a853ceeded1c8803d182d035f0362abfa29933 100644
+index 3b2db473e5eacbcf55ae8786aff5ac71388a98ee..6fc7c1cf269466362dce91fa2cf525e67bee6c15 100644
 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java
 +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -290,15 +290,81 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -227,15 +227,81 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
      public final io.papermc.paper.chunk.SingleThreadChunkRegionManager dataRegionManager;
  
      public static final class DataRegionData implements io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionData {
@@ -93,7 +93,7 @@ index bef7d5b4c8b99d2fbcd975127b16653e0f391338..19a853ceeded1c8803d182d035f0362a
          }
  
          @Override
-@@ -308,6 +374,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -245,6 +311,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
              final DataRegionSectionData sectionData = (DataRegionSectionData)section.sectionData;
              final DataRegionData oldRegionData = oldRegion == null ? null : (DataRegionData)oldRegion.regionData;
              final DataRegionData newRegionData = (DataRegionData)newRegion.regionData;
@@ -110,10 +110,10 @@ index bef7d5b4c8b99d2fbcd975127b16653e0f391338..19a853ceeded1c8803d182d035f0362a
      }
  
 diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 89a8138e97ab6a399cfbc69cab0ecaa70bb2fe8d..c01b5611fe6946a24fe21eac6a80e3ddadf9f3c1 100644
+index 8a219ecabe49d8f2564a365968891b214a090f6d..8837c9793eb25ad88bdb4b0f6198dc7ae353b9b2 100644
 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java
 +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -1079,6 +1079,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1090,6 +1090,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
      public void tickNonPassenger(Entity entity) {
          // Paper start - log detailed entity tick information
          io.papermc.paper.util.TickThread.ensureTickThread("Cannot tick an entity off-main");
@@ -121,7 +121,7 @@ index 89a8138e97ab6a399cfbc69cab0ecaa70bb2fe8d..c01b5611fe6946a24fe21eac6a80e3dd
          try {
              if (currentlyTickingEntity.get() == null) {
                  currentlyTickingEntity.lazySet(entity);
-@@ -1524,9 +1525,19 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1504,9 +1505,19 @@ public class ServerLevel extends Level implements WorldGenLevel {
          VoxelShape voxelshape1 = newState.getCollisionShape(this, pos);
  
          if (Shapes.joinIsNotEmpty(voxelshape, voxelshape1, BooleanOp.NOT_SAME)) {
@@ -143,7 +143,7 @@ index 89a8138e97ab6a399cfbc69cab0ecaa70bb2fe8d..c01b5611fe6946a24fe21eac6a80e3dd
                  // CraftBukkit start - fix SPIGOT-6362
                  Mob entityinsentient;
                  try {
-@@ -1545,6 +1556,11 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1525,6 +1536,11 @@ public class ServerLevel extends Level implements WorldGenLevel {
                      navigationabstract.recomputePath(pos);
                  }
              }
@@ -155,7 +155,7 @@ index 89a8138e97ab6a399cfbc69cab0ecaa70bb2fe8d..c01b5611fe6946a24fe21eac6a80e3dd
  
          }
          } // Paper
-@@ -2324,10 +2340,12 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -2317,10 +2333,12 @@ public class ServerLevel extends Level implements WorldGenLevel {
  
          public void onTickingStart(Entity entity) {
              ServerLevel.this.entityTickList.add(entity);
@@ -169,7 +169,7 @@ index 89a8138e97ab6a399cfbc69cab0ecaa70bb2fe8d..c01b5611fe6946a24fe21eac6a80e3dd
  
          public void onTrackingStart(Entity entity) {
 diff --git a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
-index 61080352ef305a1f276dbc297aa680b3175a5da2..10505f32b71b723ed8dbfd9e1348a1691c9f7f18 100644
+index 5884fb42f0880585dee843b98a6ea470a1508e46..4651c2e78089ed28220e767654261c735d2c5eb1 100644
 --- a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
 +++ b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
 @@ -27,7 +27,7 @@ import net.minecraft.world.phys.Vec3;
@@ -189,8 +189,8 @@ index 61080352ef305a1f276dbc297aa680b3175a5da2..10505f32b71b723ed8dbfd9e1348a169
 +    protected boolean hasDelayedRecomputation; protected final boolean needsPathRecalculation() { return this.hasDelayedRecomputation; } // Paper - public accessor
      protected long timeLastRecompute;
      protected NodeEvaluator nodeEvaluator;
-     private BlockPos targetPos;
-@@ -49,6 +49,13 @@ public abstract class PathNavigation {
+     @Nullable
+@@ -50,6 +50,13 @@ public abstract class PathNavigation {
      public final PathFinder pathFinder;
      private boolean isStuck;
  
@@ -204,7 +204,7 @@ index 61080352ef305a1f276dbc297aa680b3175a5da2..10505f32b71b723ed8dbfd9e1348a169
      public PathNavigation(Mob mob, Level world) {
          this.mob = mob;
          this.level = world;
-@@ -405,7 +412,7 @@ public abstract class PathNavigation {
+@@ -415,7 +422,7 @@ public abstract class PathNavigation {
      }
  
      public void recomputePath(BlockPos pos) {
@@ -214,7 +214,7 @@ index 61080352ef305a1f276dbc297aa680b3175a5da2..10505f32b71b723ed8dbfd9e1348a169
              Vec3 vec3 = new Vec3(((double)node.x + this.mob.getX()) / 2.0D, ((double)node.y + this.mob.getY()) / 2.0D, ((double)node.z + this.mob.getZ()) / 2.0D);
              if (pos.closerThan(vec3, (double)(this.path.getNodeCount() - this.path.getNextNodeIndex()))) {
 diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
-index 976d206a17add01a31ae38b966913368cf386cb1..28c1f144f2cc8675ed61dc814456859309970480 100644
+index 1d61807768dd883cb82bda5d529055bc50e4d1a9..d963243431e1a75f95e673e1268faa7c2320c6b6 100644
 --- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
 +++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
 @@ -71,6 +71,65 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
@@ -283,7 +283,7 @@ index 976d206a17add01a31ae38b966913368cf386cb1..28c1f144f2cc8675ed61dc8144568593
      void removeSectionIfEmpty(long sectionPos, EntitySection<T> section) {
          if (section.isEmpty()) {
              this.sectionStorage.remove(sectionPos);
-@@ -462,11 +521,25 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
+@@ -451,11 +510,25 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
          @Override
          public void onMove() {
              BlockPos blockposition = this.entity.blockPosition();
@@ -311,8 +311,8 @@ index 976d206a17add01a31ae38b966913368cf386cb1..28c1f144f2cc8675ed61dc8144568593
  
                  if (!this.currentSection.remove(this.entity)) {
                      PersistentEntitySectionManager.LOGGER.warn("Entity {} wasn't found in section {} (moving to {})", this.entity, SectionPos.of(this.currentSectionKey), i);
-@@ -478,6 +551,11 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
-                 entitysection.add(this.entity); // CraftBukkit - decompile error
+@@ -467,6 +540,11 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
+                 entitysection.add(this.entity);
                  this.currentSection = entitysection;
                  this.currentSectionKey = i;
 +                // Paper start
diff --git a/patches/unapplied/server/0795-Remove-streams-for-villager-AI.patch b/patches/server/0769-Remove-streams-for-villager-AI.patch
similarity index 57%
rename from patches/unapplied/server/0795-Remove-streams-for-villager-AI.patch
rename to patches/server/0769-Remove-streams-for-villager-AI.patch
index 227d34f188..8257c2de40 100644
--- a/patches/unapplied/server/0795-Remove-streams-for-villager-AI.patch
+++ b/patches/server/0769-Remove-streams-for-villager-AI.patch
@@ -5,17 +5,17 @@ Subject: [PATCH] Remove streams for villager AI
 
 
 diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java b/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java
-index 09998d160a6d79fdb5a5041a5d572649a1532e6a..84bd9298840ba60abbdb0675cda0458e0d6a534a 100644
+index e644bdd3a6f7c09a44149da03587b796674fa568..c67c448e0d8bdd788b94189651304110694c95da 100644
 --- a/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java
 +++ b/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java
 @@ -30,11 +30,19 @@ public class GateBehavior<E extends LivingEntity> extends Behavior<E> {
  
      @Override
      protected boolean canStillUse(ServerLevel world, E entity, long time) {
--        return this.behaviors.stream().filter((behavior) -> {
--            return behavior.getStatus() == Behavior.Status.RUNNING;
--        }).anyMatch((behavior) -> {
--            return behavior.canStillUse(world, entity, time);
+-        return this.behaviors.stream().filter((task) -> {
+-            return task.getStatus() == Behavior.Status.RUNNING;
+-        }).anyMatch((task) -> {
+-            return task.canStillUse(world, entity, time);
 -        });
 +        // Paper start - remove streams
 +        List<ShufflingList.WeightedEntry<Behavior<? super E>>> entries = this.behaviors.entries;
@@ -43,10 +43,10 @@ index 09998d160a6d79fdb5a5041a5d572649a1532e6a..84bd9298840ba60abbdb0675cda0458e
  
      @Override
      protected void tick(ServerLevel world, E entity, long time) {
--        this.behaviors.stream().filter((behavior) -> {
--            return behavior.getStatus() == Behavior.Status.RUNNING;
--        }).forEach((behavior) -> {
--            behavior.tickOrStop(world, entity, time);
+-        this.behaviors.stream().filter((task) -> {
+-            return task.getStatus() == Behavior.Status.RUNNING;
+-        }).forEach((task) -> {
+-            task.tickOrStop(world, entity, time);
 -        });
 +        // Paper start - remove streams
 +        List<ShufflingList.WeightedEntry<Behavior<? super E>>> entries = this.behaviors.entries;
@@ -62,10 +62,10 @@ index 09998d160a6d79fdb5a5041a5d572649a1532e6a..84bd9298840ba60abbdb0675cda0458e
  
      @Override
      protected void stop(ServerLevel world, E entity, long time) {
--        this.behaviors.stream().filter((behavior) -> {
--            return behavior.getStatus() == Behavior.Status.RUNNING;
--        }).forEach((behavior) -> {
--            behavior.doStop(world, entity, time);
+-        this.behaviors.stream().filter((task) -> {
+-            return task.getStatus() == Behavior.Status.RUNNING;
+-        }).forEach((task) -> {
+-            task.doStop(world, entity, time);
 -        });
 +        // Paper start - remove streams
 +        List<ShufflingList.WeightedEntry<Behavior<? super E>>> entries = this.behaviors.entries;
@@ -85,10 +85,10 @@ index 09998d160a6d79fdb5a5041a5d572649a1532e6a..84bd9298840ba60abbdb0675cda0458e
          RUN_ONE {
              @Override
 -            public <E extends LivingEntity> void apply(Stream<Behavior<? super E>> tasks, ServerLevel world, E entity, long time) {
--                tasks.filter((behavior) -> {
--                    return behavior.getStatus() == Behavior.Status.STOPPED;
--                }).filter((behavior) -> {
--                    return behavior.tryStart(world, entity, time);
+-                tasks.filter((task) -> {
+-                    return task.getStatus() == Behavior.Status.STOPPED;
+-                }).filter((task) -> {
+-                    return task.tryStart(world, entity, time);
 -                }).findFirst();
 +            // Paper start - remove streams
 +            public <E extends LivingEntity> void apply(List<ShufflingList.WeightedEntry<Behavior<? super E>>> tasks, ServerLevel world, E entity, long time) {
@@ -105,10 +105,10 @@ index 09998d160a6d79fdb5a5041a5d572649a1532e6a..84bd9298840ba60abbdb0675cda0458e
          TRY_ALL {
              @Override
 -            public <E extends LivingEntity> void apply(Stream<Behavior<? super E>> tasks, ServerLevel world, E entity, long time) {
--                tasks.filter((behavior) -> {
--                    return behavior.getStatus() == Behavior.Status.STOPPED;
--                }).forEach((behavior) -> {
--                    behavior.tryStart(world, entity, time);
+-                tasks.filter((task) -> {
+-                    return task.getStatus() == Behavior.Status.STOPPED;
+-                }).forEach((task) -> {
+-                    task.tryStart(world, entity, time);
 -                });
 +            // Paper start - remove streams
 +            public <E extends LivingEntity> void apply(List<ShufflingList.WeightedEntry<Behavior<? super E>>> tasks, ServerLevel world, E entity, long time) {
@@ -127,64 +127,8 @@ index 09998d160a6d79fdb5a5041a5d572649a1532e6a..84bd9298840ba60abbdb0675cda0458e
 +        public abstract <E extends LivingEntity> void apply(List<ShufflingList.WeightedEntry<Behavior<? super E>>> tasks, ServerLevel world, E entity, long time); // Paper - remove streams
      }
  }
-diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/SetLookAndInteract.java b/src/main/java/net/minecraft/world/entity/ai/behavior/SetLookAndInteract.java
-index 1f59e790d62f0be8e505e339a6699ca3964aea0d..ee783bb7cbec2f58549cb95fde7cbc4c47efa1cb 100644
---- a/src/main/java/net/minecraft/world/entity/ai/behavior/SetLookAndInteract.java
-+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/SetLookAndInteract.java
-@@ -34,21 +34,42 @@ public class SetLookAndInteract extends Behavior<LivingEntity> {
- 
-     @Override
-     public boolean checkExtraStartConditions(ServerLevel world, LivingEntity entity) {
--        return this.selfFilter.test(entity) && this.getVisibleEntities(entity).stream().anyMatch(this::isMatchingTarget);
-+        // Paper start - remove streams
-+        if (!this.selfFilter.test(entity)) {
-+            return false;
-+        }
-+
-+        List<LivingEntity> visibleEntities = this.getVisibleEntities(entity);
-+        for (int i = 0; i < visibleEntities.size(); i++) {
-+            LivingEntity livingEntity = visibleEntities.get(i);
-+            if (this.isMatchingTarget(livingEntity)) {
-+                return true;
-+            }
-+        }
-+        return false;
-+        // Paper end - remove streams
-     }
- 
-     @Override
-     public void start(ServerLevel world, LivingEntity entity, long time) {
-         super.start(world, entity, time);
-         Brain<?> brain = entity.getBrain();
--        brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES).ifPresent((list) -> {
--            list.stream().filter((livingEntity2) -> {
--                return livingEntity2.distanceToSqr(entity) <= (double)this.interactionRangeSqr;
--            }).filter(this::isMatchingTarget).findFirst().ifPresent((livingEntity) -> {
--                brain.setMemory(MemoryModuleType.INTERACTION_TARGET, livingEntity);
--                brain.setMemory(MemoryModuleType.LOOK_TARGET, new EntityTracker(livingEntity, true));
--            });
--        });
-+        // Paper start - remove streams
-+        List<LivingEntity> list = brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES).orElse(null);
-+        if (list != null) {
-+            double maxRangeSquared = (double)this.interactionRangeSqr;
-+            for (int i = 0; i < list.size(); i++) {
-+                LivingEntity livingEntity2 = list.get(i);
-+                if (livingEntity2.distanceToSqr(entity) <= maxRangeSquared) {
-+                    if (this.isMatchingTarget(livingEntity2)) {
-+                        brain.setMemory(MemoryModuleType.INTERACTION_TARGET, livingEntity2);
-+                        brain.setMemory(MemoryModuleType.LOOK_TARGET, new EntityTracker(livingEntity2, true));
-+                        break;
-+                    }
-+                }
-+            }
-+        }
-+        // Paper end - remove streams
-     }
- 
-     private boolean isMatchingTarget(LivingEntity entity) {
 diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java b/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java
-index 4fa64b1e2004810906bb0b174436c8e687a75ada..aaff4038867820ab2694f036dcd3c419657be6b8 100644
+index 1bc34453933bc7590af45a5559a4fc75eb3e0c5c..204ca4fbd89bdadd902529f1f191df46fce3cace 100644
 --- a/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java
 +++ b/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java
 @@ -12,7 +12,7 @@ import java.util.Random;
@@ -225,44 +169,17 @@ index 49f3b25d28072b61f5cc97260df61df892a58714..71f2692c83feafbb31f45427e6c738cb
          brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM, Optional.ofNullable(nearest));
          // Paper end
      }
-diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
-index ffd83db0a419ab589e89feeddd3fb038d6ed5839..c6947aa93b7d2fbc23b0c0e76eed061eb03140c7 100644
---- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
-+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
-@@ -18,12 +18,19 @@ public class NearestLivingEntitySensor extends Sensor<LivingEntity> {
-         List<LivingEntity> list = world.getEntitiesOfClass(LivingEntity.class, aABB, (livingEntity2) -> {
-             return livingEntity2 != entity && livingEntity2.isAlive();
-         });
--        list.sort(Comparator.comparingDouble(entity::distanceToSqr));
-+        // Paper start - remove streams
-+        list.sort((e1, e2) -> Double.compare(entity.distanceToSqr(e1), entity.distanceToSqr(e2)));
-         Brain<?> brain = entity.getBrain();
-         brain.setMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES, list);
-         // Paper start - remove streams in favour of lists
--        List<LivingEntity> visibleMobs = new java.util.ArrayList<>(list);
--        visibleMobs.removeIf(otherEntityLiving -> !Sensor.isEntityTargetable(entity, otherEntityLiving));
-+        List<LivingEntity> visibleMobs = new java.util.ArrayList<>();
-+        for (int i = 0, len = list.size(); i < len; i++) {
-+            LivingEntity nearby = list.get(i);
-+            if (Sensor.isEntityTargetable(entity, nearby)) {
-+                visibleMobs.add(nearby);
-+            }
-+        }
-+        // Paper end - remove streams
-         brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, visibleMobs);
-         // Paper end
-     }
 diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
-index 457ea75137b8b02dc32bf1769ae8d57c470da470..217c8fd1edf664dc568ee0559a38e0bd2a36696c 100644
+index 312775d0430f793720211dc29bb293503e799d11..75d9c4f011b5a97def215784c92bb57bbb35d06b 100644
 --- a/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
 +++ b/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
-@@ -21,25 +21,31 @@ public class PlayerSensor extends Sensor<LivingEntity> {
+@@ -21,25 +21,30 @@ public class PlayerSensor extends Sensor<LivingEntity> {
  
      @Override
      protected void doTick(ServerLevel world, LivingEntity entity) {
--        // Paper start - remove streams in favour of lists
 -        List<Player> players = new java.util.ArrayList<>(world.players());
--        players.removeIf(player -> !EntitySelector.NO_SPECTATORS.test(player) || !entity.closerThan(player, 16.0D)); // Paper - removeIf only re-allocates once compared to iterator
+-        players.removeIf(player -> !EntitySelector.NO_SPECTATORS.test(player) || !entity.closerThan(player, 16.0D));
+-        players.sort(Comparator.comparingDouble(entity::distanceTo));
 +        // Paper start - remove streams
 +        List<Player> players = (List)world.getNearbyPlayers(entity, entity.getX(), entity.getY(), entity.getZ(), 16.0D, EntitySelector.NO_SPECTATORS);
 +        players.sort((e1, e2) -> Double.compare(entity.distanceToSqr(e1), entity.distanceToSqr(e2)));
@@ -296,32 +213,8 @@ index 457ea75137b8b02dc32bf1769ae8d57c470da470..217c8fd1edf664dc568ee0559a38e0bd
 -        brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER, nearest);
 -        brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER, nearestTargetable);
 -        // Paper end
-+
 +        brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER, firstTargetable);
 +        brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER, Optional.ofNullable(firstAttackable));
 +        // Paper end - remove streams
      }
  }
-diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/VillagerBabiesSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/VillagerBabiesSensor.java
-index 478010bc291fa3276aab0f66ce6283403af710ec..de39b608856bdf9bef7120a6922c01c5745f3771 100644
---- a/src/main/java/net/minecraft/world/entity/ai/sensing/VillagerBabiesSensor.java
-+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/VillagerBabiesSensor.java
-@@ -22,7 +22,17 @@ public class VillagerBabiesSensor extends Sensor<LivingEntity> {
-     }
- 
-     private List<LivingEntity> getNearestVillagerBabies(LivingEntity entities) {
--        return this.getVisibleEntities(entities).stream().filter(this::isVillagerBaby).collect(Collectors.toList());
-+        // Paper start - remove streams
-+        List<LivingEntity> list = new java.util.ArrayList<>();
-+        List<LivingEntity> visibleEntities = this.getVisibleEntities(entities);
-+        for (int i = 0; i < visibleEntities.size(); i++) {
-+            LivingEntity livingEntity = visibleEntities.get(i);
-+            if (this.isVillagerBaby(livingEntity)) {
-+                list.add(livingEntity);
-+            }
-+        }
-+        return list;
-+        // Paper end - remove streams
-     }
- 
-     private boolean isVillagerBaby(LivingEntity entity) {
diff --git a/patches/unapplied/server/0796-Rewrite-dataconverter-system.patch b/patches/server/0770-Rewrite-dataconverter-system.patch
similarity index 94%
rename from patches/unapplied/server/0796-Rewrite-dataconverter-system.patch
rename to patches/server/0770-Rewrite-dataconverter-system.patch
index cc35e27dfc..c95c2b73e9 100644
--- a/patches/unapplied/server/0796-Rewrite-dataconverter-system.patch
+++ b/patches/server/0770-Rewrite-dataconverter-system.patch
@@ -111,10 +111,10 @@ index 0000000000000000000000000000000000000000..cf9fae4451ead4860343b915fb70e3a7
 +}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/MCDataConverter.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/MCDataConverter.java
 new file mode 100644
-index 0000000000000000000000000000000000000000..64f442922a9ca26a723653acb7a5398fca6076fd
+index 0000000000000000000000000000000000000000..25f1f4c355c1b4aca12e366f100922c53b4db1c6
 --- /dev/null
 +++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/MCDataConverter.java
-@@ -0,0 +1,69 @@
+@@ -0,0 +1,90 @@
 +package ca.spottedleaf.dataconverter.minecraft;
 +
 +import ca.spottedleaf.dataconverter.converters.DataConverter;
@@ -130,6 +130,27 @@ index 0000000000000000000000000000000000000000..64f442922a9ca26a723653acb7a5398f
 +
 +    private static final LongArrayList BREAKPOINTS = MCVersionRegistry.getBreakpoints();
 +
++    public static <T> T copy(final T type) {
++        if (type instanceof CompoundTag) {
++            return (T)((CompoundTag)type).copy();
++        } else if (type instanceof JsonObject) {
++            return (T)((JsonObject)type).deepCopy();
++        }
++
++        return type;
++    }
++
++    public static <T, R> R convertUnwrapped(final DataType<T, R> type, final T data, final boolean compressedJson, final int fromVersion, final int toVersion) {
++        if (data instanceof CompoundTag) {
++            return (R)convertTag((MCDataType)type, (CompoundTag)data, fromVersion, toVersion);
++        }
++        if (data instanceof JsonObject) {
++            return (R)convertJson((MCDataType)type, (JsonObject)data, compressedJson, fromVersion, toVersion);
++        }
++
++        return convert(type, data, fromVersion, toVersion);
++    }
++
 +    public static CompoundTag convertTag(final MCDataType type, final CompoundTag data, final int fromVersion, final int toVersion) {
 +        final NBTMapType wrapped = new NBTMapType(data);
 +
@@ -186,10 +207,10 @@ index 0000000000000000000000000000000000000000..64f442922a9ca26a723653acb7a5398f
 +}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/MCVersionRegistry.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/MCVersionRegistry.java
 new file mode 100644
-index 0000000000000000000000000000000000000000..321b69f2353202b08de6c364d13340a80c0e077d
+index 0000000000000000000000000000000000000000..016420effa89f3243479a966bf7aed286e82be1c
 --- /dev/null
 +++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/MCVersionRegistry.java
-@@ -0,0 +1,332 @@
+@@ -0,0 +1,343 @@
 +package ca.spottedleaf.dataconverter.minecraft;
 +
 +import ca.spottedleaf.dataconverter.converters.DataConverter;
@@ -356,6 +377,7 @@ index 0000000000000000000000000000000000000000..321b69f2353202b08de6c364d13340a8
 +                2688,
 +                2690,
 +                2691,
++                2693,
 +                2696,
 +                2700,
 +                2701,
@@ -363,8 +385,18 @@ index 0000000000000000000000000000000000000000..321b69f2353202b08de6c364d13340a8
 +                2704,
 +                2707,
 +                2710,
-+                2717
-+                // All up to 1.17.1
++                2717,
++                2825,
++                2831,
++                2832,
++                2833,
++                2838,
++                2841,
++                2842,
++                2843,
++                2846,
++                2852,
++                // All up to 1.18-pre6
 +        };
 +        Arrays.sort(converterVersions);
 +
@@ -382,7 +414,7 @@ index 0000000000000000000000000000000000000000..321b69f2353202b08de6c364d13340a8
 +        registerSubVersion(MCVersions.V17W47A, 7);
 +
 +        // register breakpoints here
-+        // for all major releases after 1.16, add them here. this reduces the work required to determine if a breakpoint
++        // for all major releases after 1.16, add them. this reduces the work required to determine if a breakpoint
 +        // is needed for new converters
 +
 +        // Too much changed in this version.
@@ -524,10 +556,10 @@ index 0000000000000000000000000000000000000000..321b69f2353202b08de6c364d13340a8
 +}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/MCVersions.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/MCVersions.java
 new file mode 100644
-index 0000000000000000000000000000000000000000..6b954f25089aa8bb66359280d4aca596e3f8530f
+index 0000000000000000000000000000000000000000..ba8c1f056aa77d3812fb02f2a60ddd192e68984f
 --- /dev/null
 +++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/MCVersions.java
-@@ -0,0 +1,366 @@
+@@ -0,0 +1,380 @@
 +package ca.spottedleaf.dataconverter.minecraft;
 +
 +@SuppressWarnings("unused")
@@ -893,16 +925,177 @@ index 0000000000000000000000000000000000000000..6b954f25089aa8bb66359280d4aca596
 +    public static final int V1_17_1_RC1           = 2728;
 +    public static final int V1_17_1_RC2           = 2729;
 +    public static final int V1_17_1               = 2730;
++    public static final int V21W37A               = 2834;
++    public static final int V21W38A               = 2835;
++    public static final int V21W39A               = 2836;
++    public static final int V21W40A               = 2838;
++    public static final int V21W41A               = 2839;
++    public static final int V21W42A               = 2840;
++    public static final int V21W43A               = 2844;
++    public static final int V21W44A               = 2845;
++    public static final int V1_18_PRE1            = 2847;
++    public static final int V1_18_PRE2            = 2848;
++    public static final int V1_18_PRE3            = 2849;
++    public static final int V1_18_PRE4            = 2850;
++    public static final int V1_18_PRE5            = 2851;
++    public static final int V1_18_PRE6            = 2853;
++}
+diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/ReplacedDataFixerUpper.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/ReplacedDataFixerUpper.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..5ddf54649fc0ddcee1b1f6bdc6e8d7be7ae46618
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/ReplacedDataFixerUpper.java
+@@ -0,0 +1,140 @@
++package ca.spottedleaf.dataconverter.minecraft;
++
++import ca.spottedleaf.dataconverter.converters.datatypes.DataType;
++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
++import com.mojang.datafixers.DSL;
++import com.mojang.datafixers.DataFixer;
++import com.mojang.datafixers.schemas.Schema;
++import com.mojang.serialization.Dynamic;
++import net.minecraft.SharedConstants;
++import net.minecraft.util.datafix.fixes.References;
++import org.apache.logging.log4j.LogManager;
++import org.apache.logging.log4j.Logger;
++import java.util.Set;
++import java.util.concurrent.ConcurrentHashMap;
++
++public class ReplacedDataFixerUpper implements DataFixer {
++
++    protected static final Set<DSL.TypeReference> WARNED_TYPES = ConcurrentHashMap.newKeySet();
++
++    private static final Logger LOGGER = LogManager.getLogger();
++
++    public final DataFixer wrapped;
++
++    public ReplacedDataFixerUpper(final DataFixer wrapped) {
++        this.wrapped = wrapped;
++    }
++
++    @Override
++    public <T> Dynamic<T> update(final DSL.TypeReference type, final Dynamic<T> input, final int version, final int newVersion) {
++        DataType<?, ?> equivType = null;
++        boolean warn = true;
++
++        if (type == References.LEVEL) {
++            warn = false;
++        }
++        if (type == References.PLAYER) {
++            equivType = MCTypeRegistry.PLAYER;
++        }
++        if (type == References.CHUNK) {
++            equivType = MCTypeRegistry.CHUNK;
++        }
++        if (type == References.HOTBAR) {
++            warn = false;
++        }
++        if (type == References.OPTIONS) {
++            warn = false;
++        }
++        if (type == References.STRUCTURE) {
++            equivType = MCTypeRegistry.STRUCTURE;
++        }
++        if (type == References.STATS) {
++            warn = false;
++        }
++        if (type == References.SAVED_DATA) {
++            equivType = MCTypeRegistry.SAVED_DATA;
++        }
++        if (type == References.ADVANCEMENTS) {
++            warn = false;
++        }
++        if (type == References.POI_CHUNK) {
++            equivType = MCTypeRegistry.POI_CHUNK;
++        }
++        if (type == References.ENTITY_CHUNK) {
++            equivType = MCTypeRegistry.ENTITY_CHUNK;
++        }
++        if (type == References.BLOCK_ENTITY) {
++            equivType = MCTypeRegistry.TILE_ENTITY;
++        }
++        if (type == References.ITEM_STACK) {
++            equivType = MCTypeRegistry.ITEM_STACK;
++        }
++        if (type == References.BLOCK_STATE) {
++            equivType = MCTypeRegistry.BLOCK_STATE;
++        }
++        if (type == References.ENTITY_NAME) {
++            equivType = MCTypeRegistry.ENTITY_NAME;
++        }
++        if (type == References.ENTITY_TREE) {
++            equivType = MCTypeRegistry.ENTITY;
++        }
++        if (type == References.ENTITY) {
++            // NO EQUIV TYPE (this is ENTITY without passengers/riding)
++            // Only used internally for DFU, so we shouldn't get here
++        }
++        if (type == References.BLOCK_NAME) {
++            equivType = MCTypeRegistry.BLOCK_NAME;
++        }
++        if (type == References.ITEM_NAME) {
++            equivType = MCTypeRegistry.ITEM_NAME;
++        }
++        if (type == References.UNTAGGED_SPAWNER) {
++            equivType = MCTypeRegistry.UNTAGGED_SPAWNER;
++        }
++        if (type == References.STRUCTURE_FEATURE) {
++            equivType = MCTypeRegistry.STRUCTURE_FEATURE;
++        }
++        if (type == References.OBJECTIVE) {
++            warn = false;
++        }
++        if (type == References.TEAM) {
++            warn = false;
++        }
++        if (type == References.RECIPE) {
++            warn = false;
++        }
++        if (type == References.BIOME) {
++            equivType = MCTypeRegistry.BIOME;
++        }
++        if (type == References.WORLD_GEN_SETTINGS) {
++            warn = false;
++        }
++
++        if (equivType != null) {
++            if (newVersion > version) {
++                try {
++                    final Dynamic<T> ret = new Dynamic<>(input.getOps(), (T)MCDataConverter.copy(MCDataConverter.convertUnwrapped((DataType)equivType, input.getValue(), false, version, newVersion)));
++                    return ret;
++                } catch (final Exception ex) {
++                    LOGGER.error("Failed to convert data using DataConverter, falling back to DFU", new Throwable());
++                    // In dev environment this should hard fail
++                }
++
++                return this.wrapped.update(type, input, version, newVersion);
++            } else {
++                return input;
++            }
++        } else {
++            if (warn && WARNED_TYPES.add(type)) {
++                LOGGER.error("No equiv type for " + type, new Throwable());
++            }
++
++            return this.wrapped.update(type, input, version, newVersion);
++        }
++    }
++
++    @Override
++    public Schema getSchema(final int key) {
++        return this.wrapped.getSchema(key);
++    }
 +}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/advancements/ConverterAbstractAdvancementsRename.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/advancements/ConverterAbstractAdvancementsRename.java
 new file mode 100644
-index 0000000000000000000000000000000000000000..c92e198b1cc20655e404f67b2af4ca493aad2f6d
+index 0000000000000000000000000000000000000000..ae3aed21c1fccb688e9a1665e2d317a77508d157
 --- /dev/null
 +++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/advancements/ConverterAbstractAdvancementsRename.java
-@@ -0,0 +1,34 @@
+@@ -0,0 +1,28 @@
 +package ca.spottedleaf.dataconverter.minecraft.converters.advancements;
 +
 +import ca.spottedleaf.dataconverter.converters.DataConverter;
++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.RenameHelper;
 +import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
 +import ca.spottedleaf.dataconverter.types.MapType;
 +import java.util.ArrayList;
@@ -920,14 +1113,7 @@ index 0000000000000000000000000000000000000000..c92e198b1cc20655e404f67b2af4ca49
 +        MCTypeRegistry.ADVANCEMENTS.addStructureConverter(new DataConverter<>(version, subVersion) {
 +            @Override
 +            public MapType<String> convert(final MapType<String> data, final long sourceVersion, final long toVersion) {
-+                for (final String key : new ArrayList<>(data.keys())) {
-+                    final String updated = renamer.apply(key);
-+                    if (updated != null) {
-+                        final Object value = data.getGeneric(key);
-+                        data.remove(key);
-+                        data.setGeneric(updated, value);
-+                    }
-+                }
++                RenameHelper.renameKeys(data, renamer);
 +                return null;
 +            }
 +        });
@@ -2456,6 +2642,42 @@ index 0000000000000000000000000000000000000000..afad2d92f78d4727ff4440ad2778f018
 +        return null;
 +    }
 +}
+diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/helpers/AddFlagIfAbsent.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/helpers/AddFlagIfAbsent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..4ab607f946782cc483535564e86fa9753dd7897a
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/helpers/AddFlagIfAbsent.java
+@@ -0,0 +1,30 @@
++package ca.spottedleaf.dataconverter.minecraft.converters.helpers;
++
++import ca.spottedleaf.dataconverter.converters.DataConverter;
++import ca.spottedleaf.dataconverter.types.MapType;
++
++public final class AddFlagIfAbsent extends DataConverter<MapType<String>, MapType<String>> {
++
++    public final String path;
++    public final boolean dfl;
++
++    public AddFlagIfAbsent(final int toVersion, final String path, final boolean dfl) {
++        super(toVersion);
++        this.path = path;
++        this.dfl = dfl;
++    }
++
++    public AddFlagIfAbsent(final int toVersion, final int versionStep, final String path, final boolean dfl) {
++        super(toVersion, versionStep);
++        this.path = path;
++        this.dfl = dfl;
++    }
++
++    @Override
++    public MapType<String> convert(final MapType<String> data, final long sourceVersion, final long toVersion) {
++        if (!data.hasKey(this.path)) {
++            data.setBoolean(this.path, this.dfl);
++        }
++        return null;
++    }
++}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/helpers/ConverterAbstractStringValueTypeRename.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/helpers/ConverterAbstractStringValueTypeRename.java
 new file mode 100644
 index 0000000000000000000000000000000000000000..bc79670f47aaa413ea3e96ef6a32e14099ad8a58
@@ -4979,6 +5201,70 @@ index 0000000000000000000000000000000000000000..5008c6d28b7f9b730bfaf257a264edcb
 +        return ID_TO_STRING[id & 255];
 +    }
 +}
+diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/helpers/RenameHelper.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/helpers/RenameHelper.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..fb235cf3b597abb8c6557def215efac7cc1a53f5
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/helpers/RenameHelper.java
+@@ -0,0 +1,58 @@
++package ca.spottedleaf.dataconverter.minecraft.converters.helpers;
++
++import ca.spottedleaf.dataconverter.types.MapType;
++import java.util.ArrayList;
++import java.util.List;
++import java.util.function.Function;
++
++public final class RenameHelper {
++
++    // assumes no two or more entries are renamed to a single value, otherwise result will be only one of them will win
++    // and there is no defined winner in such a case
++    public static void renameKeys(final MapType<String> data, final Function<String, String> renamer) {
++        boolean needsRename = false;
++        for (final String key : data.keys()) {
++            if (renamer.apply(key) != null) {
++                needsRename = true;
++                break;
++            }
++        }
++
++        if (!needsRename) {
++            return;
++        }
++
++        final List<String> newKeys = new ArrayList<>();
++        final List<Object> newValues = new ArrayList<>();
++
++        for (final String key : new ArrayList<>(data.keys())) {
++            final String renamed = renamer.apply(key);
++
++            if (renamed != null) {
++                newValues.add(data.getGeneric(key));
++                newKeys.add(renamed);
++                data.remove(key);
++            }
++        }
++
++        // insert new keys
++        for (int i = 0, len = newKeys.size(); i < len; ++i) {
++            final String key = newKeys.get(i);
++            final Object value = newValues.get(i);
++
++            data.setGeneric(key, value);
++        }
++    }
++
++    // Clobbers anything in toKey if fromKey exists
++    public static void renameSingle(final MapType<String> data, final String fromKey, final String toKey) {
++        final Object value = data.getGeneric(fromKey);
++        if (value != null) {
++            data.remove(fromKey);
++            data.setGeneric(toKey, value);
++        }
++    }
++
++    private RenameHelper() {}
++
++}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/itemname/ConverterAbstractItemRename.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/itemname/ConverterAbstractItemRename.java
 new file mode 100644
 index 0000000000000000000000000000000000000000..94569f0ccff0d3a09eafd4ba73572d9db0a0ac5b
@@ -5561,13 +5847,14 @@ index 0000000000000000000000000000000000000000..d88b12e6b9e381ba614dc04599a44e47
 +}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/options/ConverterAbstractOptionsRename.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/options/ConverterAbstractOptionsRename.java
 new file mode 100644
-index 0000000000000000000000000000000000000000..8bfcaac8caa858d3696ed7a430f2b246857b3ac4
+index 0000000000000000000000000000000000000000..769dd8447976b66dcfc36283ede4ae16f1e4206d
 --- /dev/null
 +++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/options/ConverterAbstractOptionsRename.java
-@@ -0,0 +1,33 @@
+@@ -0,0 +1,28 @@
 +package ca.spottedleaf.dataconverter.minecraft.converters.options;
 +
 +import ca.spottedleaf.dataconverter.converters.DataConverter;
++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.RenameHelper;
 +import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
 +import ca.spottedleaf.dataconverter.types.MapType;
 +import java.util.ArrayList;
@@ -5585,13 +5872,7 @@ index 0000000000000000000000000000000000000000..8bfcaac8caa858d3696ed7a430f2b246
 +        MCTypeRegistry.OPTIONS.addStructureConverter(new DataConverter<>(version, subVersion) {
 +            @Override
 +            public MapType<String> convert(final MapType<String> data, final long sourceVersion, final long toVersion) {
-+                for (final String key : new ArrayList<>(data.keys())) {
-+                    final String updated = renamer.apply(key);
-+                    if (updated != null) {
-+                        data.setGeneric(updated, data.getGeneric(key));
-+                        data.remove(key);
-+                    }
-+                }
++                RenameHelper.renameKeys(data, renamer);
 +                return null;
 +            }
 +        });
@@ -5683,13 +5964,14 @@ index 0000000000000000000000000000000000000000..8f35cbbd78a629712f9ae3cd5d180269
 +}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/stats/ConverterAbstractStatsRename.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/stats/ConverterAbstractStatsRename.java
 new file mode 100644
-index 0000000000000000000000000000000000000000..2317d6dd7e609095187168a1fb9a684ce540ec0e
+index 0000000000000000000000000000000000000000..a1985c85aa9193699d7d20e6f4f11b6e9744ee70
 --- /dev/null
 +++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/stats/ConverterAbstractStatsRename.java
-@@ -0,0 +1,75 @@
+@@ -0,0 +1,66 @@
 +package ca.spottedleaf.dataconverter.minecraft.converters.stats;
 +
 +import ca.spottedleaf.dataconverter.converters.DataConverter;
++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.RenameHelper;
 +import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
 +import ca.spottedleaf.dataconverter.types.MapType;
 +import java.util.ArrayList;
@@ -5745,17 +6027,7 @@ index 0000000000000000000000000000000000000000..2317d6dd7e609095187168a1fb9a684c
 +                    return null;
 +                }
 +
-+                for (final String key : new ArrayList<>(custom.keys())) {
-+                    final String rename = renamer.apply(key);
-+                    if (rename == null) {
-+                        continue;
-+                    }
-+
-+                    final Object value = custom.getGeneric(key);
-+                    custom.remove(key);
-+
-+                    custom.setGeneric(rename, value);
-+                }
++                RenameHelper.renameKeys(custom, renamer);
 +
 +                return null;
 +            }
@@ -5864,10 +6136,10 @@ index 0000000000000000000000000000000000000000..99d2c2c84820295be1f8bb0b43784e58
 +}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/datatypes/IDDataType.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/datatypes/IDDataType.java
 new file mode 100644
-index 0000000000000000000000000000000000000000..885467de6a5928faafab867dda79b035868ce222
+index 0000000000000000000000000000000000000000..b2c2b4c4ae83f14639fa53e38f2c75ccd284c2d2
 --- /dev/null
 +++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/datatypes/IDDataType.java
-@@ -0,0 +1,163 @@
+@@ -0,0 +1,166 @@
 +package ca.spottedleaf.dataconverter.minecraft.datatypes;
 +
 +import ca.spottedleaf.dataconverter.converters.DataConverter;
@@ -5953,7 +6225,7 @@ index 0000000000000000000000000000000000000000..885467de6a5928faafab867dda79b035
 +                break;
 +            }
 +
-+            final List<DataHook<MapType<String>, MapType<String>>> hooks = this.structureHooks.getFloor(converterVersion);
++            List<DataHook<MapType<String>, MapType<String>>> hooks = this.structureHooks.getFloor(converterVersion);
 +
 +            if (hooks != null) {
 +                for (int k = 0, klen = hooks.size(); k < klen; ++k) {
@@ -5969,6 +6241,9 @@ index 0000000000000000000000000000000000000000..885467de6a5928faafab867dda79b035
 +                ret = data = replace;
 +            }
 +
++            // possibly new data format, update hooks
++            hooks = this.structureHooks.getFloor(toVersion);
++
 +            if (hooks != null) {
 +                for (int klen = hooks.size(), k = klen - 1; k >= 0; --k) {
 +                    final MapType<String> postReplace = hooks.get(k).postHook(data, fromVersion, toVersion);
@@ -6033,10 +6308,10 @@ index 0000000000000000000000000000000000000000..885467de6a5928faafab867dda79b035
 +}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/datatypes/MCDataType.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/datatypes/MCDataType.java
 new file mode 100644
-index 0000000000000000000000000000000000000000..e997192e13286ff910885a8e5074a283c1269ea4
+index 0000000000000000000000000000000000000000..76a6e3efa5c69150e8f5e0063cb6357bed1bffae
 --- /dev/null
 +++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/datatypes/MCDataType.java
-@@ -0,0 +1,126 @@
+@@ -0,0 +1,129 @@
 +package ca.spottedleaf.dataconverter.minecraft.datatypes;
 +
 +import ca.spottedleaf.dataconverter.converters.DataConverter;
@@ -6104,7 +6379,7 @@ index 0000000000000000000000000000000000000000..e997192e13286ff910885a8e5074a283
 +                break;
 +            }
 +
-+            final List<DataHook<MapType<String>, MapType<String>>> hooks = this.structureHooks.getFloor(converterVersion);
++            List<DataHook<MapType<String>, MapType<String>>> hooks = this.structureHooks.getFloor(converterVersion);
 +
 +            if (hooks != null) {
 +                for (int k = 0, klen = hooks.size(); k < klen; ++k) {
@@ -6120,6 +6395,9 @@ index 0000000000000000000000000000000000000000..e997192e13286ff910885a8e5074a283
 +                ret = data = replace;
 +            }
 +
++            // possibly new data format, update hooks
++            hooks = this.structureHooks.getFloor(toVersion);
++
 +            if (hooks != null) {
 +                for (int klen = hooks.size(), k = klen - 1; k >= 0; --k) {
 +                    final MapType<String> postReplace = hooks.get(k).postHook(data, fromVersion, toVersion);
@@ -6165,10 +6443,10 @@ index 0000000000000000000000000000000000000000..e997192e13286ff910885a8e5074a283
 +}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/datatypes/MCTypeRegistry.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/datatypes/MCTypeRegistry.java
 new file mode 100644
-index 0000000000000000000000000000000000000000..2f5a9e04e3797673f49a035eebfedd1b90c81b07
+index 0000000000000000000000000000000000000000..40fa4f9b8ad8c658ec43e1b4a9d3dec7de4744da
 --- /dev/null
 +++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/datatypes/MCTypeRegistry.java
-@@ -0,0 +1,316 @@
+@@ -0,0 +1,339 @@
 +package ca.spottedleaf.dataconverter.minecraft.datatypes;
 +
 +import ca.spottedleaf.dataconverter.minecraft.versions.V100;
@@ -6276,6 +6554,7 @@ index 0000000000000000000000000000000000000000..2f5a9e04e3797673f49a035eebfedd1b
 +import ca.spottedleaf.dataconverter.minecraft.versions.V2688;
 +import ca.spottedleaf.dataconverter.minecraft.versions.V2690;
 +import ca.spottedleaf.dataconverter.minecraft.versions.V2691;
++import ca.spottedleaf.dataconverter.minecraft.versions.V2693;
 +import ca.spottedleaf.dataconverter.minecraft.versions.V2696;
 +import ca.spottedleaf.dataconverter.minecraft.versions.V2700;
 +import ca.spottedleaf.dataconverter.minecraft.versions.V2701;
@@ -6283,6 +6562,16 @@ index 0000000000000000000000000000000000000000..2f5a9e04e3797673f49a035eebfedd1b
 +import ca.spottedleaf.dataconverter.minecraft.versions.V2707;
 +import ca.spottedleaf.dataconverter.minecraft.versions.V2710;
 +import ca.spottedleaf.dataconverter.minecraft.versions.V2717;
++import ca.spottedleaf.dataconverter.minecraft.versions.V2825;
++import ca.spottedleaf.dataconverter.minecraft.versions.V2831;
++import ca.spottedleaf.dataconverter.minecraft.versions.V2832;
++import ca.spottedleaf.dataconverter.minecraft.versions.V2833;
++import ca.spottedleaf.dataconverter.minecraft.versions.V2838;
++import ca.spottedleaf.dataconverter.minecraft.versions.V2841;
++import ca.spottedleaf.dataconverter.minecraft.versions.V2842;
++import ca.spottedleaf.dataconverter.minecraft.versions.V2843;
++import ca.spottedleaf.dataconverter.minecraft.versions.V2846;
++import ca.spottedleaf.dataconverter.minecraft.versions.V2852;
 +import ca.spottedleaf.dataconverter.minecraft.versions.V501;
 +import ca.spottedleaf.dataconverter.minecraft.versions.V502;
 +import ca.spottedleaf.dataconverter.minecraft.versions.V505;
@@ -6473,6 +6762,7 @@ index 0000000000000000000000000000000000000000..2f5a9e04e3797673f49a035eebfedd1b
 +        V2688.register();
 +        V2690.register();
 +        V2691.register();
++        V2693.register();
 +        V2696.register();
 +        V2700.register();
 +        V2701.register();
@@ -6481,16 +6771,27 @@ index 0000000000000000000000000000000000000000..2f5a9e04e3797673f49a035eebfedd1b
 +        V2707.register();
 +        V2710.register();
 +        V2717.register();
++        // V1.18
++        V2825.register();
++        V2831.register();
++        V2832.register();
++        V2833.register();
++        V2838.register();
++        V2841.register();
++        V2842.register();
++        V2843.register();
++        V2846.register();
++        V2852.register();
 +    }
 +
 +    private MCTypeRegistry() {}
 +}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/datatypes/MCValueType.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/datatypes/MCValueType.java
 new file mode 100644
-index 0000000000000000000000000000000000000000..c7ace3088f8059a3bae12ec21604b2b7417a7b90
+index 0000000000000000000000000000000000000000..13c1381261909ef672fbeb665907f01f2d5c1ced
 --- /dev/null
 +++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/datatypes/MCValueType.java
-@@ -0,0 +1,83 @@
+@@ -0,0 +1,86 @@
 +package ca.spottedleaf.dataconverter.minecraft.datatypes;
 +
 +import ca.spottedleaf.dataconverter.converters.DataConverter;
@@ -6545,7 +6846,7 @@ index 0000000000000000000000000000000000000000..c7ace3088f8059a3bae12ec21604b2b7
 +                break;
 +            }
 +
-+            final List<DataHook<Object, Object>> hooks = this.structureHooks.getFloor(converterVersion);
++            List<DataHook<Object, Object>> hooks = this.structureHooks.getFloor(converterVersion);
 +
 +            if (hooks != null) {
 +                for (int k = 0, klen = hooks.size(); k < klen; ++k) {
@@ -6561,6 +6862,9 @@ index 0000000000000000000000000000000000000000..c7ace3088f8059a3bae12ec21604b2b7
 +                ret = converted;
 +            }
 +
++            // possibly new data format, update hooks
++            hooks = this.structureHooks.getFloor(toVersion);
++
 +            if (hooks != null) {
 +                for (int k = 0, klen = hooks.size(); k < klen; ++k) {
 +                    final Object replace = hooks.get(k).postHook(ret == null ? data : ret, fromVersion, toVersion);
@@ -11883,7 +12187,7 @@ index 0000000000000000000000000000000000000000..d905043c4f3071e8dc340ac2afe2b81a
 +}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2202.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2202.java
 new file mode 100644
-index 0000000000000000000000000000000000000000..b64183295fea0550ebd6e69e476155c8783f4120
+index 0000000000000000000000000000000000000000..0bb378ac8e8d0a087359361281644a7f39cecfbe
 --- /dev/null
 +++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2202.java
 @@ -0,0 +1,50 @@
@@ -11911,7 +12215,7 @@ index 0000000000000000000000000000000000000000..b64183295fea0550ebd6e69e476155c8
 +
 +                final int[] oldBiomes = level.getInts("Biomes");
 +
-+                if (oldBiomes == null) {
++                if (oldBiomes == null || oldBiomes.length != 256) {
 +                    return null;
 +                }
 +
@@ -11924,7 +12228,7 @@ index 0000000000000000000000000000000000000000..b64183295fea0550ebd6e69e476155c8
 +                        int k = (j << 2) + 2;
 +                        int l = (n << 2) + 2;
 +                        int m = l << 4 | k;
-+                        newBiomes[n << 2 | j] = m < oldBiomes.length ? oldBiomes[m] : -1;
++                        newBiomes[n << 2 | j] = oldBiomes[m];
 +                    }
 +                }
 +
@@ -14501,6 +14805,27 @@ index 0000000000000000000000000000000000000000..3841780d52c2e242609fc076efa5902c
 +        ConverterAbstractBlockRename.registerAndFixJigsaw(VERSION, RENAMES::get);
 +    }
 +}
+diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2693.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2693.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..deac34afe6a3681db9a7630ad6526f71d4dd6e1f
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2693.java
+@@ -0,0 +1,15 @@
++package ca.spottedleaf.dataconverter.minecraft.versions;
++
++import ca.spottedleaf.dataconverter.minecraft.MCVersions;
++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.AddFlagIfAbsent;
++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
++
++public final class V2693 {
++
++    protected static final int VERSION = MCVersions.V21W05B + 1;
++
++    public static void register() {
++        MCTypeRegistry.WORLD_GEN_SETTINGS.addStructureConverter(new AddFlagIfAbsent(VERSION, "has_increased_height_already", false));
++    }
++
++}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2696.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2696.java
 new file mode 100644
 index 0000000000000000000000000000000000000000..0094154c1da7cb95120e01bceeb836ca7ab68e25
@@ -14568,10 +14893,10 @@ index 0000000000000000000000000000000000000000..c37142033061a3e4865686ee64d8f15f
 +}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2701.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2701.java
 new file mode 100644
-index 0000000000000000000000000000000000000000..47eb9e3b454af3ae5d88be5107c68bebc001a82b
+index 0000000000000000000000000000000000000000..9d6b03410c4665e19a2a35226d11f77b2cae3bbf
 --- /dev/null
 +++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2701.java
-@@ -0,0 +1,209 @@
+@@ -0,0 +1,203 @@
 +package ca.spottedleaf.dataconverter.minecraft.versions;
 +
 +import ca.spottedleaf.dataconverter.converters.DataConverter;
@@ -14632,12 +14957,6 @@ index 0000000000000000000000000000000000000000..47eb9e3b454af3ae5d88be5107c68beb
 +                        continue;
 +                    }
 +
-+                    final String featureName = feature.getString("name");
-+
-+                    if (featureName == null) {
-+                        continue;
-+                    }
-+
 +                    final String replacement = convertToString(feature);
 +
 +                    if (replacement != null) {
@@ -14822,13 +15141,14 @@ index 0000000000000000000000000000000000000000..53e45b14c05dab35cd5725998458d47e
 +}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2707.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2707.java
 new file mode 100644
-index 0000000000000000000000000000000000000000..9acd9ed381063f93bfe74cf78db5cb8b87dfbcd5
+index 0000000000000000000000000000000000000000..74c1df97036059b3a5147f7cf94752ef4516a33d
 --- /dev/null
 +++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2707.java
-@@ -0,0 +1,18 @@
+@@ -0,0 +1,21 @@
 +package ca.spottedleaf.dataconverter.minecraft.versions;
 +
 +import ca.spottedleaf.dataconverter.minecraft.MCVersions;
++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.AddFlagIfAbsent;
 +import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
 +import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists;
 +
@@ -14841,6 +15161,8 @@ index 0000000000000000000000000000000000000000..9acd9ed381063f93bfe74cf78db5cb8b
 +    }
 +
 +    public static void register() {
++        MCTypeRegistry.WORLD_GEN_SETTINGS.addStructureConverter(new AddFlagIfAbsent(VERSION, "has_increased_height_already", true));
++
 +        registerMob("minecraft:marker"); // ?????????????
 +    }
 +}
@@ -14893,6 +15215,1503 @@ index 0000000000000000000000000000000000000000..8678ba95b5abe96b399a310623078f88
 +    }
 +
 +}
+diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2825.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2825.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..c2d2b7c10e5b988b1111b20b778c475a12bef353
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2825.java
+@@ -0,0 +1,15 @@
++package ca.spottedleaf.dataconverter.minecraft.versions;
++
++import ca.spottedleaf.dataconverter.minecraft.MCVersions;
++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.AddFlagIfAbsent;
++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
++
++public final class V2825 {
++
++    protected static final int VERSION = MCVersions.V1_17_1 + 95;
++
++    public static void register() {
++        MCTypeRegistry.WORLD_GEN_SETTINGS.addStructureConverter(new AddFlagIfAbsent(VERSION, "has_increased_height_already", false));
++    }
++
++}
+diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2831.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2831.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..d28ade80499dce882a9a84309a2a0da527fe01a0
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2831.java
+@@ -0,0 +1,69 @@
++package ca.spottedleaf.dataconverter.minecraft.versions;
++
++import ca.spottedleaf.dataconverter.converters.DataConverter;
++import ca.spottedleaf.dataconverter.minecraft.MCVersions;
++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
++import ca.spottedleaf.dataconverter.minecraft.walkers.generic.WalkerUtils;
++import ca.spottedleaf.dataconverter.types.ListType;
++import ca.spottedleaf.dataconverter.types.MapType;
++import ca.spottedleaf.dataconverter.types.ObjectType;
++import ca.spottedleaf.dataconverter.types.Types;
++
++public final class V2831 {
++
++    protected static final int VERSION = MCVersions.V1_17_1 + 101;
++
++    public static void register() {
++        MCTypeRegistry.UNTAGGED_SPAWNER.addStructureWalker(VERSION, (final MapType<String> root, final long fromVersion, final long toVersion) -> {
++            final ListType spawnPotentials = root.getList("SpawnPotentials", ObjectType.MAP);
++            if (spawnPotentials != null) {
++                for (int i = 0, len = spawnPotentials.size(); i < len; ++i) {
++                    final MapType<String> spawnPotential = spawnPotentials.getMap(i);
++
++                    WalkerUtils.convert(MCTypeRegistry.ENTITY, spawnPotential.getMap("data"), "entity", fromVersion, toVersion);
++                }
++            }
++
++            WalkerUtils.convert(MCTypeRegistry.ENTITY, root.getMap("SpawnData"), "entity", fromVersion, toVersion);
++
++            return null;
++        });
++
++        MCTypeRegistry.UNTAGGED_SPAWNER.addStructureConverter(new DataConverter<>(VERSION) {
++            @Override
++            public MapType<String> convert(final MapType<String> root, final long sourceVersion, final long toVersion) {
++                final MapType<String> spawnData = root.getMap("SpawnData");
++                if (spawnData != null) {
++                    final MapType<String> wrapped = Types.NBT.createEmptyMap();
++                    root.setMap("SpawnData", wrapped);
++
++                    wrapped.setMap("entity", spawnData);
++                }
++
++                final ListType spawnPotentials = root.getList("SpawnPotentials", ObjectType.MAP);
++                if (spawnPotentials != null) {
++                    for (int i = 0, len = spawnPotentials.size(); i < len; ++i) {
++                        final MapType<String> spawnPotential = spawnPotentials.getMap(i);
++
++                        // new format of weighted list (SpawnPotentials):
++                        // root.data -> data
++                        // root.weight -> weight
++
++                        final MapType<String> entity = spawnPotential.getMap("Entity");
++                        final int weight = spawnPotential.getInt("Weight", 1);
++                        spawnPotential.remove("Entity");
++                        spawnPotential.remove("Weight");
++                        spawnPotential.setInt("weight", weight);
++
++                        final MapType<String> data = Types.NBT.createEmptyMap();
++                        spawnPotential.setMap("data", data);
++
++                        data.setMap("entity", entity);
++                    }
++                }
++
++                return null;
++            }
++        });
++    }
++}
+diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2832.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2832.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..68971097a9d9a1be63258518985d406d2a3392d8
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2832.java
+@@ -0,0 +1,924 @@
++package ca.spottedleaf.dataconverter.minecraft.versions;
++
++import ca.spottedleaf.dataconverter.converters.DataConverter;
++import ca.spottedleaf.dataconverter.minecraft.MCVersions;
++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.RenameHelper;
++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
++import ca.spottedleaf.dataconverter.minecraft.walkers.generic.WalkerUtils;
++import ca.spottedleaf.dataconverter.types.ListType;
++import ca.spottedleaf.dataconverter.types.MapType;
++import ca.spottedleaf.dataconverter.types.ObjectType;
++import ca.spottedleaf.dataconverter.types.Types;
++import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap;
++import it.unimi.dsi.fastutil.ints.IntIterator;
++import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
++import org.apache.commons.lang3.mutable.MutableBoolean;
++import org.apache.logging.log4j.LogManager;
++import org.apache.logging.log4j.Logger;
++import java.util.Arrays;
++import java.util.BitSet;
++import java.util.HashSet;
++import java.util.Set;
++
++public final class V2832 {
++
++    protected static final Logger LOGGER = LogManager.getLogger();
++
++    protected static final int VERSION = MCVersions.V1_17_1 + 102;
++
++    private static final String[] BIOMES_BY_ID = new String[256]; // rip datapacks
++    static {
++        BIOMES_BY_ID[0] = "minecraft:ocean";
++        BIOMES_BY_ID[1] = "minecraft:plains";
++        BIOMES_BY_ID[2] = "minecraft:desert";
++        BIOMES_BY_ID[3] = "minecraft:mountains";
++        BIOMES_BY_ID[4] = "minecraft:forest";
++        BIOMES_BY_ID[5] = "minecraft:taiga";
++        BIOMES_BY_ID[6] = "minecraft:swamp";
++        BIOMES_BY_ID[7] = "minecraft:river";
++        BIOMES_BY_ID[8] = "minecraft:nether_wastes";
++        BIOMES_BY_ID[9] = "minecraft:the_end";
++        BIOMES_BY_ID[10] = "minecraft:frozen_ocean";
++        BIOMES_BY_ID[11] = "minecraft:frozen_river";
++        BIOMES_BY_ID[12] = "minecraft:snowy_tundra";
++        BIOMES_BY_ID[13] = "minecraft:snowy_mountains";
++        BIOMES_BY_ID[14] = "minecraft:mushroom_fields";
++        BIOMES_BY_ID[15] = "minecraft:mushroom_field_shore";
++        BIOMES_BY_ID[16] = "minecraft:beach";
++        BIOMES_BY_ID[17] = "minecraft:desert_hills";
++        BIOMES_BY_ID[18] = "minecraft:wooded_hills";
++        BIOMES_BY_ID[19] = "minecraft:taiga_hills";
++        BIOMES_BY_ID[20] = "minecraft:mountain_edge";
++        BIOMES_BY_ID[21] = "minecraft:jungle";
++        BIOMES_BY_ID[22] = "minecraft:jungle_hills";
++        BIOMES_BY_ID[23] = "minecraft:jungle_edge";
++        BIOMES_BY_ID[24] = "minecraft:deep_ocean";
++        BIOMES_BY_ID[25] = "minecraft:stone_shore";
++        BIOMES_BY_ID[26] = "minecraft:snowy_beach";
++        BIOMES_BY_ID[27] = "minecraft:birch_forest";
++        BIOMES_BY_ID[28] = "minecraft:birch_forest_hills";
++        BIOMES_BY_ID[29] = "minecraft:dark_forest";
++        BIOMES_BY_ID[30] = "minecraft:snowy_taiga";
++        BIOMES_BY_ID[31] = "minecraft:snowy_taiga_hills";
++        BIOMES_BY_ID[32] = "minecraft:giant_tree_taiga";
++        BIOMES_BY_ID[33] = "minecraft:giant_tree_taiga_hills";
++        BIOMES_BY_ID[34] = "minecraft:wooded_mountains";
++        BIOMES_BY_ID[35] = "minecraft:savanna";
++        BIOMES_BY_ID[36] = "minecraft:savanna_plateau";
++        BIOMES_BY_ID[37] = "minecraft:badlands";
++        BIOMES_BY_ID[38] = "minecraft:wooded_badlands_plateau";
++        BIOMES_BY_ID[39] = "minecraft:badlands_plateau";
++        BIOMES_BY_ID[40] = "minecraft:small_end_islands";
++        BIOMES_BY_ID[41] = "minecraft:end_midlands";
++        BIOMES_BY_ID[42] = "minecraft:end_highlands";
++        BIOMES_BY_ID[43] = "minecraft:end_barrens";
++        BIOMES_BY_ID[44] = "minecraft:warm_ocean";
++        BIOMES_BY_ID[45] = "minecraft:lukewarm_ocean";
++        BIOMES_BY_ID[46] = "minecraft:cold_ocean";
++        BIOMES_BY_ID[47] = "minecraft:deep_warm_ocean";
++        BIOMES_BY_ID[48] = "minecraft:deep_lukewarm_ocean";
++        BIOMES_BY_ID[49] = "minecraft:deep_cold_ocean";
++        BIOMES_BY_ID[50] = "minecraft:deep_frozen_ocean";
++        BIOMES_BY_ID[127] = "minecraft:the_void";
++        BIOMES_BY_ID[129] = "minecraft:sunflower_plains";
++        BIOMES_BY_ID[130] = "minecraft:desert_lakes";
++        BIOMES_BY_ID[131] = "minecraft:gravelly_mountains";
++        BIOMES_BY_ID[132] = "minecraft:flower_forest";
++        BIOMES_BY_ID[133] = "minecraft:taiga_mountains";
++        BIOMES_BY_ID[134] = "minecraft:swamp_hills";
++        BIOMES_BY_ID[140] = "minecraft:ice_spikes";
++        BIOMES_BY_ID[149] = "minecraft:modified_jungle";
++        BIOMES_BY_ID[151] = "minecraft:modified_jungle_edge";
++        BIOMES_BY_ID[155] = "minecraft:tall_birch_forest";
++        BIOMES_BY_ID[156] = "minecraft:tall_birch_hills";
++        BIOMES_BY_ID[157] = "minecraft:dark_forest_hills";
++        BIOMES_BY_ID[158] = "minecraft:snowy_taiga_mountains";
++        BIOMES_BY_ID[160] = "minecraft:giant_spruce_taiga";
++        BIOMES_BY_ID[161] = "minecraft:giant_spruce_taiga_hills";
++        BIOMES_BY_ID[162] = "minecraft:modified_gravelly_mountains";
++        BIOMES_BY_ID[163] = "minecraft:shattered_savanna";
++        BIOMES_BY_ID[164] = "minecraft:shattered_savanna_plateau";
++        BIOMES_BY_ID[165] = "minecraft:eroded_badlands";
++        BIOMES_BY_ID[166] = "minecraft:modified_wooded_badlands_plateau";
++        BIOMES_BY_ID[167] = "minecraft:modified_badlands_plateau";
++        BIOMES_BY_ID[168] = "minecraft:bamboo_jungle";
++        BIOMES_BY_ID[169] = "minecraft:bamboo_jungle_hills";
++        BIOMES_BY_ID[170] = "minecraft:soul_sand_valley";
++        BIOMES_BY_ID[171] = "minecraft:crimson_forest";
++        BIOMES_BY_ID[172] = "minecraft:warped_forest";
++        BIOMES_BY_ID[173] = "minecraft:basalt_deltas";
++        BIOMES_BY_ID[174] = "minecraft:dripstone_caves";
++        BIOMES_BY_ID[175] = "minecraft:lush_caves";
++        BIOMES_BY_ID[177] = "minecraft:meadow";
++        BIOMES_BY_ID[178] = "minecraft:grove";
++        BIOMES_BY_ID[179] = "minecraft:snowy_slopes";
++        BIOMES_BY_ID[180] = "minecraft:snowcapped_peaks";
++        BIOMES_BY_ID[181] = "minecraft:lofty_peaks";
++        BIOMES_BY_ID[182] = "minecraft:stony_peaks";
++    }
++
++    private static final String[] HEIGHTMAP_TYPES = new String[] {
++            "WORLD_SURFACE_WG",
++            "WORLD_SURFACE",
++            "WORLD_SURFACE_IGNORE_SNOW",
++            "OCEAN_FLOOR_WG",
++            "OCEAN_FLOOR",
++            "MOTION_BLOCKING",
++            "MOTION_BLOCKING_NO_LEAVES"
++    };
++
++    private static final Set<String> STATUS_IS_OR_AFTER_SURFACE = new HashSet<>(Arrays.asList(
++            "surface",
++            "carvers",
++            "liquid_carvers",
++            "features",
++            "light",
++            "spawn",
++            "heightmaps",
++            "full"
++    ));
++    private static final Set<String> STATUS_IS_OR_AFTER_NOISE = new HashSet<>(Arrays.asList(
++            "noise",
++            "surface",
++            "carvers",
++            "liquid_carvers",
++            "features",
++            "light",
++            "spawn",
++            "heightmaps",
++            "full"
++    ));
++    private static final Set<String> BLOCKS_BEFORE_FEATURE_STATUS = new HashSet<>(Arrays.asList(
++            "minecraft:air",
++            "minecraft:basalt",
++            "minecraft:bedrock",
++            "minecraft:blackstone",
++            "minecraft:calcite",
++            "minecraft:cave_air",
++            "minecraft:coarse_dirt",
++            "minecraft:crimson_nylium",
++            "minecraft:dirt",
++            "minecraft:end_stone",
++            "minecraft:grass_block",
++            "minecraft:gravel",
++            "minecraft:ice",
++            "minecraft:lava",
++            "minecraft:mycelium",
++            "minecraft:nether_wart_block",
++            "minecraft:netherrack",
++            "minecraft:orange_terracotta",
++            "minecraft:packed_ice",
++            "minecraft:podzol",
++            "minecraft:powder_snow",
++            "minecraft:red_sand",
++            "minecraft:red_sandstone",
++            "minecraft:sand",
++            "minecraft:sandstone",
++            "minecraft:snow_block",
++            "minecraft:soul_sand",
++            "minecraft:soul_soil",
++            "minecraft:stone",
++            "minecraft:terracotta",
++            "minecraft:warped_nylium",
++            "minecraft:warped_wart_block",
++            "minecraft:water",
++            "minecraft:white_terracotta"
++    ));
++
++    private static int getObjectsPerValue(final long[] val) {
++        return (4096 + val.length - 1) / (val.length); // expression is invalid if it returns > 64
++    }
++
++    private static long[] resize(final long[] val, final int oldBitsPerObject, final int newBitsPerObject) {
++        final long oldMask = (1L << oldBitsPerObject) - 1; // works even if bitsPerObject == 64
++        final long newMask = (1L << newBitsPerObject) - 1;
++        final int oldObjectsPerValue = 64 / oldBitsPerObject;
++        final int newObjectsPerValue = 64 / newBitsPerObject;
++
++        if (newBitsPerObject == oldBitsPerObject) {
++            return val;
++        }
++
++        final int items = 4096;
++
++        final long[] ret = new long[(items + newObjectsPerValue - 1) / newObjectsPerValue];
++
++        final int expectedSize = ((items + oldObjectsPerValue - 1) / oldObjectsPerValue);
++        if (val.length != expectedSize) {
++            throw new IllegalStateException("Expected size: " + expectedSize + ", got: " + val.length);
++        }
++
++        int shift = 0;
++        int idx = 0;
++        long newCurr = 0L;
++
++        int currItem = 0;
++        for (int i = 0; i < val.length; ++i) {
++            final long oldCurr = val[i];
++
++            for (int objIdx = 0; currItem < items && objIdx + oldBitsPerObject <= 64; objIdx += oldBitsPerObject, ++currItem) {
++                final long value = (oldCurr >> objIdx) & oldMask;
++
++                if ((value & newMask) != value) {
++                    throw new IllegalStateException("Old data storage has values that cannot be moved into new palette (would erase data)!");
++                }
++
++                newCurr |= value << shift;
++                shift += newBitsPerObject;
++
++                if (shift + newBitsPerObject > 64) { // will next write overflow?
++                    // must move to next idx
++                    ret[idx++] = newCurr;
++                    shift = 0;
++                    newCurr = 0L;
++                }
++            }
++        }
++
++        // don't forget to write the last one
++        if (shift != 0) {
++            ret[idx] = newCurr;
++        }
++
++        return ret;
++    }
++
++    private static void fixLithiumChunks(final MapType<String> data) {
++        // See https://github.com/CaffeineMC/lithium-fabric/issues/279
++        final MapType<String> level = data.getMap("Level");
++        if (level == null) {
++            return;
++        }
++
++        final int chunkX = level.getInt("xPos");
++        final int chunkZ = level.getInt("zPos");
++
++        final ListType sections = level.getList("Sections", ObjectType.MAP);
++        if (sections == null) {
++            return;
++        }
++
++        for (int i = 0, len = sections.size(); i < len; ++i) {
++            final MapType<String> section = sections.getMap(i);
++
++            final int sectionY = section.getInt("Y");
++
++            final ListType palette = section.getList("Palette", ObjectType.MAP);
++            final long[] blockStates = section.getLongs("BlockStates");
++
++            if (palette == null || blockStates == null) {
++                continue;
++            }
++
++            final int expectedBits = Math.max(4, ceilLog2(palette.size()));
++            final int gotObjectsPerValue = getObjectsPerValue(blockStates);
++            final int gotBits = 64 / gotObjectsPerValue;
++
++            if (expectedBits == gotBits) {
++                continue;
++            }
++
++            try {
++                section.setLongs("BlockStates", resize(blockStates, gotBits, expectedBits));
++            } catch (final Exception ex) {
++                LOGGER.fatal("Failed to rewrite mismatched palette and data storage for section y: " + sectionY
++                        + " for chunk [" + chunkX + "," + chunkZ + "], palette entries: " + palette.size() + ", data storage size: "
++                        + blockStates.length,
++                        ex
++                );
++            }
++        }
++    }
++
++    public static void register() {
++        // See V2551 for the layout of world gen settings
++        MCTypeRegistry.WORLD_GEN_SETTINGS.addStructureConverter(new DataConverter<>(VERSION) {
++            @Override
++            public MapType<String> convert(final MapType<String> data, final long sourceVersion, final long toVersion) {
++                // converters were added to older versions note whether the world has increased height already or not
++                final boolean noHeightFlag = !data.hasKey("has_increased_height_already");
++                final boolean hasIncreasedHeight = data.getBoolean("has_increased_height_already", true);
++                data.remove("has_increased_height_already");
++
++                final MapType<String> dimensions = data.getMap("dimensions");
++                if (dimensions == null) {
++                    // wat
++                    return null;
++                }
++
++                // only care about overworld
++                final MapType<String> overworld = dimensions.getMap("minecraft:overworld");
++                if (overworld == null) {
++                    // wat
++                    return null;
++                }
++
++                final MapType<String> generator = overworld.getMap("generator");
++                if (generator == null) {
++                    // wat
++                    return null;
++                }
++
++                final String type = generator.getString("type", "");
++                switch (type) {
++                    case "minecraft:noise": {
++                        final MapType<String> biomeSource = generator.getMap("biome_source");
++                        final String sourceType = biomeSource.getString("type");
++
++                        boolean largeBiomes = false;
++
++                        if ("minecraft:vanilla_layered".equals(sourceType) || (noHeightFlag && "minecraft:multi_noise".equals(sourceType))) {
++                            largeBiomes = biomeSource.getBoolean("large_biomes");
++
++                            final MapType<String> newBiomeSource = Types.NBT.createEmptyMap();
++                            generator.setMap("biome_source", newBiomeSource);
++
++                            newBiomeSource.setString("preset", "minecraft:overworld");
++                            newBiomeSource.setString("type", "minecraft:multi_noise");
++                        }
++
++                        if (largeBiomes) {
++                            if ("minecraft:overworld".equals(generator.getString("settings"))) {
++                                generator.setString("settings", "minecraft:large_biomes");
++                            }
++                        }
++
++                        break;
++                    }
++                    case "minecraft:flat": {
++                        if (!hasIncreasedHeight) {
++                            final MapType<String> settings = generator.getMap("settings");
++                            if (settings == null) {
++                                break;
++                            }
++
++                            updateLayers(settings.getList("layers", ObjectType.MAP));
++                        }
++                        break;
++                    }
++                    default:
++                        // do nothing
++                        break;
++                }
++
++                return null;
++            }
++        });
++
++
++        // It looks like DFU will only support worlds in the old height format or the new one, any custom one isn't supported
++        // and by not supported I mean it will just treat it as the old format... maybe at least throw in that case?
++        MCTypeRegistry.CHUNK.addStructureConverter(new DataConverter<>(VERSION) {
++            @Override
++            public MapType<String> convert(final MapType<String> data, final long sourceVersion, final long toVersion) {
++                // The below covers padPaletteEntries - this was written BEFORE that code was added to the datafixer -
++                // and this still works, so I'm keeping it. Don't fix what isn't broken.
++                fixLithiumChunks(data); // See https://github.com/CaffeineMC/lithium-fabric/issues/279
++
++                final MapType<String> level = data.getMap("Level");
++
++                if (level == null) {
++                    return null;
++                }
++
++                final MapType<String> context = data.getMap("__context"); // Passed through by ChunkStorage
++                final String dimension = context == null ? "" : context.getString("dimension", "");
++                final String generator = context == null ? "" : context.getString("generator", "");
++                final boolean isOverworld = "minecraft:overworld".equals(dimension);
++                final int minSection = isOverworld ? -4 : 0;
++                final MutableBoolean isAlreadyExtended = new MutableBoolean();
++
++                final MapType<String>[] newBiomes = createBiomeSections(level, isOverworld, minSection, isAlreadyExtended);
++                final MapType<String> wrappedEmptyBlockPalette = getEmptyBlockPalette();
++
++                final ListType sections = level.getList("Sections", ObjectType.MAP);
++
++                // must update sections for two things:
++                // 1. the biomes are now stored per section, so we must insert the biomes palette into each section (and create them if they don't exist)
++                // 2. each section must now have block states (or at least DFU is ensuring they do, but current code does not require)
++                V2841.SimplePaletteReader bottomSection = null;
++                final Set<String> allBlocks = new HashSet<>();
++                if (sections != null) {
++                    final IntOpenHashSet existingSections = new IntOpenHashSet();
++
++                    for (int i = 0, len = sections.size(); i < len; ++i) {
++                        final MapType<String> section = sections.getMap(i);
++
++                        final int y = section.getInt("Y");
++                        final int sectionIndex = y - minSection;
++
++                        existingSections.add(y);
++
++                        // add in relevant biome section
++                        if (sectionIndex >= 0 && sectionIndex < newBiomes.length) {
++                            // exclude out of bounds sections (i.e the light sections above and below the world)
++                            section.setMap("biomes", newBiomes[sectionIndex]);
++                        }
++
++                        // update palette
++                        final ListType palette = section.getList("Palette", ObjectType.MAP);
++                        final long[] blockStates = section.getLongs("BlockStates");
++
++                        section.remove("Palette");
++                        section.remove("BlockStates");
++
++                        if (palette != null) {
++                            for (int j = 0, len2 = palette.size(); j < len2; ++j) {
++                                allBlocks.add(V2841.getBlockId(palette.getMap(j)));
++                            }
++                        }
++
++                        final MapType<String> palettedContainer;
++                        if (palette != null && blockStates != null) {
++                            // only if both exist, same as DFU, same as legacy chunk loading code
++                            section.setMap("block_states", palettedContainer = wrapPaletteOptimised(palette, blockStates));
++                        } else {
++                            section.setMap("block_states", palettedContainer = wrappedEmptyBlockPalette.copy()); // must write a palette now, copy so that later edits do not edit them all
++                        }
++
++                        if (section.getInt("Y", Integer.MAX_VALUE) == 0) {
++                            bottomSection = new V2841.SimplePaletteReader(palettedContainer.getList("palette", ObjectType.MAP), palettedContainer.getLongs("data"));
++                        }
++                    }
++
++                    // all existing sections updated, now we must create new sections just for the biomes migration
++                    for (int sectionIndex = 0; sectionIndex < newBiomes.length; ++sectionIndex) {
++                        final int sectionY = sectionIndex + minSection;
++                        if (!existingSections.add(sectionY)) {
++                            // exists already
++                            continue;
++                        }
++
++                        final MapType<String> newSection = Types.NBT.createEmptyMap();
++                        sections.addMap(newSection);
++
++                        newSection.setByte("Y", (byte)sectionY);
++                        // must write a palette now, copy so that later edits do not edit them all
++                        newSection.setMap("block_states", wrappedEmptyBlockPalette.copy());
++
++                        newSection.setGeneric("biomes", newBiomes[sectionIndex]);
++                    }
++                }
++
++                // update status so interpolation can take place
++                predictChunkStatusBeforeSurface(level, allBlocks);
++
++                // done with sections, update the rest of the chunk
++                updateChunkData(level, isOverworld, isAlreadyExtended.getValue(), "minecraft:noise".equals(generator), bottomSection);
++
++                return null;
++            }
++        });
++
++        MCTypeRegistry.WORLD_GEN_SETTINGS.addStructureWalker(VERSION, (final MapType<String> data, final long fromVersion, final long toVersion) -> {
++            final MapType<String> dimensions = data.getMap("dimensions");
++
++            if (dimensions == null) {
++                return null;
++            }
++
++            for (final String dimension : dimensions.keys()) {
++                final MapType<String> dimensionData = dimensions.getMap(dimension);
++                if (dimensionData == null) {
++                    continue;
++                }
++
++                final MapType<String> generator = dimensionData.getMap("generator");
++                if (generator == null) {
++                    continue;
++                }
++
++                final String type = generator.getString("type");
++                if (type == null) {
++                    continue;
++                }
++
++                switch (type) {
++                    case "minecraft:flat": {
++                        final MapType<String> settings = generator.getMap("settings");
++                        if (settings == null) {
++                            continue;
++                        }
++
++                        WalkerUtils.convert(MCTypeRegistry.BIOME, settings, "biome", fromVersion, toVersion);
++
++                        final ListType layers = settings.getList("layers", ObjectType.MAP);
++                        if (layers != null) {
++                            for (int i = 0, len = layers.size(); i < len; ++i) {
++                                WalkerUtils.convert(MCTypeRegistry.BLOCK_NAME, layers.getMap(i), "block", fromVersion, toVersion);
++                            }
++                        }
++
++                        break;
++                    }
++                    case "minecraft:noise": {
++                        final MapType<String> settings = generator.getMap("settings");
++                        if (settings != null) {
++                            WalkerUtils.convert(MCTypeRegistry.BLOCK_NAME, settings, "default_block", fromVersion, toVersion);
++                            WalkerUtils.convert(MCTypeRegistry.BLOCK_NAME, settings, "default_fluid", fromVersion, toVersion);
++                        }
++
++                        final MapType<String> biomeSource = generator.getMap("biome_source");
++                        if (biomeSource != null) {
++                            final String biomeSourceType = biomeSource.getString("type", "");
++                            switch (biomeSourceType) {
++                                case "minecraft:fixed": {
++                                    WalkerUtils.convert(MCTypeRegistry.BIOME, biomeSource, "biome", fromVersion, toVersion);
++                                    break;
++                                }
++
++                                case "minecraft:multi_noise": {
++                                    // preset is absent, no type for namespaced string
++
++                                    // Vanilla's schema is _still_ wrong. It should be DSL.fields("biomes", DSL.list(DSL.fields("biome")))
++                                    // But it just contains the list part. That obviously can never be the case, because
++                                    // the root object is a compound, not a list.
++
++                                    final ListType biomes = biomeSource.getList("biomes", ObjectType.MAP);
++                                    if (biomes != null) {
++                                        for (int i = 0, len = biomes.size(); i < len; ++i) {
++                                            WalkerUtils.convert(MCTypeRegistry.BIOME, biomes.getMap(i), "biome", fromVersion, toVersion);
++                                        }
++                                    }
++                                    break;
++                                }
++
++                                case "minecraft:checkerboard": {
++                                    WalkerUtils.convertList(MCTypeRegistry.BIOME, biomeSource, "biomes", fromVersion, toVersion);
++                                    break;
++                                }
++                            }
++                        }
++
++                        break;
++                    }
++                }
++            }
++
++            return null;
++        });
++
++        MCTypeRegistry.CHUNK.addStructureWalker(VERSION, (final MapType<String> data, final long fromVersion, final long toVersion) -> {
++            final MapType<String> level = data.getMap("Level");
++            if (level == null) {
++                return null;
++            }
++
++            WalkerUtils.convertList(MCTypeRegistry.ENTITY, level, "Entities", fromVersion, toVersion);
++            WalkerUtils.convertList(MCTypeRegistry.TILE_ENTITY, level, "TileEntities", fromVersion, toVersion);
++
++            final ListType tileTicks = level.getList("TileTicks", ObjectType.MAP);
++            if (tileTicks != null) {
++                for (int i = 0, len = tileTicks.size(); i < len; ++i) {
++                    final MapType<String> tileTick = tileTicks.getMap(i);
++                    WalkerUtils.convert(MCTypeRegistry.BLOCK_NAME, tileTick, "i", fromVersion, toVersion);
++                }
++            }
++
++            final ListType sections = level.getList("Sections", ObjectType.MAP);
++            if (sections != null) {
++                for (int i = 0, len = sections.size(); i < len; ++i) {
++                    final MapType<String> section = sections.getMap(i);
++
++                    WalkerUtils.convertList(MCTypeRegistry.BIOME, section.getMap("biomes"), "palette", fromVersion, toVersion);
++                    WalkerUtils.convertList(MCTypeRegistry.BLOCK_STATE, section.getMap("block_states"), "palette", fromVersion, toVersion);
++                }
++            }
++
++            WalkerUtils.convertValues(MCTypeRegistry.STRUCTURE_FEATURE, level.getMap("Structures"), "Starts", fromVersion, toVersion);
++
++            return null;
++        });
++    }
++
++    private static void predictChunkStatusBeforeSurface(final MapType<String> level, final Set<String> chunkBlocks) {
++        final String status = level.getString("Status", "empty");
++        if (STATUS_IS_OR_AFTER_SURFACE.contains(status)) {
++            return;
++        }
++
++        chunkBlocks.remove("minecraft:air");
++        final boolean chunkNotEmpty = !chunkBlocks.isEmpty();
++        chunkBlocks.removeAll(BLOCKS_BEFORE_FEATURE_STATUS);
++        final boolean chunkFeatureStatus = !chunkBlocks.isEmpty();
++
++        final String update;
++        if (chunkFeatureStatus) {
++            update = "liquid_carvers";
++        } else if (!"noise".equals(status) && !chunkNotEmpty) {
++            update = "biomes".equals(status) ? "structure_references" : status;
++        } else {
++            update = "noise";
++        }
++
++        level.setString("Status", update);
++    }
++
++    private static MapType<String> getEmptyBlockPalette() {
++        final MapType<String> airBlockState = Types.NBT.createEmptyMap();
++        airBlockState.setString("Name", "minecraft:air");
++
++        final ListType emptyBlockPalette = Types.NBT.createEmptyList();
++        emptyBlockPalette.addMap(airBlockState);
++
++        return V2832.wrapPalette(emptyBlockPalette);
++    }
++
++    private static void shiftUpgradeData(final MapType<String> upgradeData, final int shift) {
++        if (upgradeData == null) {
++            return;
++        }
++
++        final MapType<String> indices = upgradeData.getMap("Indices");
++        if (indices == null) {
++            return;
++        }
++
++        RenameHelper.renameKeys(indices, (final String input) -> {
++            return Integer.toString(Integer.parseInt(input) + shift);
++        });
++    }
++
++    private static void updateChunkData(final MapType<String> level, final boolean wantExtendedHeight, final boolean isAlreadyExtended,
++                                        final boolean onNoiseGenerator, final V2841.SimplePaletteReader bottomSection) {
++        level.remove("Biomes");
++        if (!wantExtendedHeight) {
++            padCarvingMasks(level, 16, 0);
++            return;
++        }
++
++        if (isAlreadyExtended) {
++            padCarvingMasks(level, 24, 0);
++            return;
++        }
++
++        offsetHeightmaps(level);
++        addEmptyListPadding(level, "Lights");
++        addEmptyListPadding(level, "LiquidsToBeTicked");
++        addEmptyListPadding(level, "PostProcessing");
++        addEmptyListPadding(level, "ToBeTicked");
++        shiftUpgradeData(level.getMap("UpgradeData"), 4); // https://bugs.mojang.com/browse/MC-238076 - fixed now, Mojang fix is identical. No change required.
++        padCarvingMasks(level, 24, 4);
++
++        if (!onNoiseGenerator) {
++            return;
++        }
++
++        final String status = level.getString("Status");
++        if (status == null || "empty".equals(status)) {
++            return;
++        }
++
++        final MapType<String> blendingData = Types.NBT.createEmptyMap();
++        level.setMap("blending_data", blendingData);
++
++        blendingData.setBoolean("old_noise", STATUS_IS_OR_AFTER_NOISE.contains(status));
++
++        if (bottomSection == null) {
++            return;
++        }
++
++        final BitSet missingBedrock = new BitSet(256);
++        boolean hasBedrock = status.equals("noise");
++
++        for (int z = 0; z <= 15; ++z) {
++            for (int x = 0; x <= 15; ++x) {
++                final MapType<String> state = bottomSection.getState(x, 0, z);
++                final String blockId = V2841.getBlockId(state);
++                final boolean isBedrock = state != null && "minecraft:bedrock".equals(blockId);
++                final boolean isAir = state != null && "minecraft:air".equals(blockId);
++                if (isAir) {
++                    missingBedrock.set((z << 4) | x);
++                }
++
++                hasBedrock |= isBedrock;
++            }
++        }
++
++        if (hasBedrock && missingBedrock.cardinality() != missingBedrock.size()) {
++            final String targetStatus = "full".equals(status) ? "heightmaps" : status;
++
++            final MapType<String> belowZeroRetrogen = Types.NBT.createEmptyMap();
++            level.setMap("below_zero_retrogen", belowZeroRetrogen);
++
++            belowZeroRetrogen.setString("target_status", targetStatus);
++            belowZeroRetrogen.setLongs("missing_bedrock", missingBedrock.toLongArray());
++
++            level.setString("Status", "empty");
++        }
++
++        level.setBoolean("isLightOn", false);
++    }
++
++    private static void padCarvingMasks(final MapType<String> level, final int newSize, final int offset) {
++        final MapType<String> carvingMasks = level.getMap("CarvingMasks");
++        if (carvingMasks == null) {
++            // if empty, DFU still writes
++            level.setMap("CarvingMasks", Types.NBT.createEmptyMap());
++            return;
++        }
++
++        for (final String key : carvingMasks.keys()) {
++            final long[] old = BitSet.valueOf(carvingMasks.getBytes(key)).toLongArray();
++            final long[] newVal = new long[64 * newSize];
++
++            System.arraycopy(old, 0, newVal, 64 * offset, old.length);
++
++            carvingMasks.setLongs(key, newVal); // no CME: key exists already
++        }
++    }
++
++    private static void addEmptyListPadding(final MapType<String> level, final String path) {
++        ListType list = level.getListUnchecked(path);
++        if (list != null && list.size() == 24) {
++            return;
++        }
++
++        if (list == null) {
++            // difference from DFU: Don't create the damn thing!
++            return;
++        }
++
++
++        // offset the section array to the new format
++        for (int i = 0; i < 4; ++i) {
++            // always create new copies, so that modifying one doesn't modify ALL of them!
++            list.addList(0, Types.NBT.createEmptyList()); // add below
++            list.addList(Types.NBT.createEmptyList()); // add above
++        }
++    }
++
++    private static void offsetHeightmaps(final MapType<String> level) {
++        final MapType<String> heightmaps = level.getMap("Heightmaps");
++        if (heightmaps == null) {
++            return;
++        }
++
++        for (final String key : HEIGHTMAP_TYPES) {
++            offsetHeightmap(heightmaps.getLongs(key));
++        }
++    }
++
++    private static void offsetHeightmap(final long[] heightmap) {
++        if (heightmap == null) {
++            return;
++        }
++
++        // heightmaps are configured to have 9 bits per value, with 256 total values
++        // heightmaps are also relative to the lowest position
++        for (int idx = 0, len = heightmap.length; idx < len; ++idx) {
++            long curr = heightmap[idx];
++            long next = 0L;
++
++            for (int objIdx = 0; objIdx + 9 <= 64; objIdx += 9) {
++                final long value = (curr >> objIdx) & 511L;
++                if (value != 0L) {
++                    final long offset = Math.min(511L, value + 64L);
++
++                    next |= (offset << objIdx);
++                }
++            }
++
++            heightmap[idx] = next;
++        }
++    }
++
++    private static MapType<String>[] createBiomeSections(final MapType<String> level, final boolean wantExtendedHeight,
++                                                         final int minSection, final MutableBoolean isAlreadyExtended) {
++        final MapType<String>[] ret = new MapType[wantExtendedHeight ? 24 : 16];
++
++        final int[] biomes = level.getInts("Biomes");
++        if (biomes == null) {
++            final ListType palette = Types.NBT.createEmptyList();
++            palette.addString("minecraft:plains");
++
++            for (int i = 0; i < ret.length; ++i) {
++                ret[i] = wrapPalette(palette.copy()); // copy palette so that later possible modifications don't trash all sections
++            }
++
++            return ret;
++        }
++
++        final boolean isExtended = biomes.length == 1536; // magic value for 24 sections of biomes (24 * 4^3)
++        isAlreadyExtended.setValue(isExtended);
++
++        if (isExtended) {
++            for (int sectionIndex = 0; sectionIndex < 24; ++sectionIndex) {
++                ret[sectionIndex] = createBiomeSection(biomes, sectionIndex * 64, -1); // -1 is all 1s
++            }
++        } else {
++            for (int sectionY = 0; sectionY < 16; ++sectionY) {
++                ret[sectionY - minSection] = createBiomeSection(biomes, sectionY * 64, -1); // -1 is all 1s
++            }
++
++            if (wantExtendedHeight) {
++                // must set the new sections at top and bottom
++                final MapType<String> bottomCopy = createBiomeSection(biomes, 0, 15); // just want the biomes at y = 0
++                final MapType<String> topCopy = createBiomeSection(biomes, 1008, 15); // just want the biomes at y = 252
++
++                for (int sectionIndex = 0; sectionIndex < 4; ++sectionIndex) {
++                    ret[sectionIndex] = bottomCopy.copy(); // copy palette so that later possible modifications don't trash all sections
++                }
++
++                for (int sectionIndex = 20; sectionIndex < 24; ++sectionIndex) {
++                    ret[sectionIndex] = topCopy.copy(); // copy palette so that later possible modifications don't trash all sections
++                }
++            }
++        }
++
++        return ret;
++    }
++
++    private static MapType<String> createBiomeSection(final int[] biomes, final int offset, final int mask) {
++        final Int2IntLinkedOpenHashMap paletteId = new Int2IntLinkedOpenHashMap();
++
++        for (int idx = 0; idx < 64; ++idx) {
++            final int biome = biomes[offset + (idx & mask)];
++            paletteId.putIfAbsent(biome, paletteId.size());
++        }
++
++        final ListType paletteString = Types.NBT.createEmptyList();
++        for (final IntIterator iterator = paletteId.keySet().iterator(); iterator.hasNext();) {
++            final int biomeId = iterator.nextInt();
++            final String biome = biomeId >= 0 && biomeId < BIOMES_BY_ID.length ? BIOMES_BY_ID[biomeId] : null;
++            paletteString.addString(biome == null ? "minecraft:plains" : biome);
++        }
++
++        final int bitsPerObject = ceilLog2(paletteString.size());
++        if (bitsPerObject == 0) {
++            return wrapPalette(paletteString);
++        }
++
++        // manually create packed integer data
++        final int objectsPerValue = 64 / bitsPerObject;
++        final long[] packed = new long[(64 + objectsPerValue - 1) / objectsPerValue];
++
++        int shift = 0;
++        int idx = 0;
++        long curr = 0;
++
++        for (int biome_idx = 0; biome_idx < 64; ++biome_idx) {
++            final int biome = biomes[offset + (biome_idx & mask)];
++
++            curr |= ((long)paletteId.get(biome)) << shift;
++
++            shift += bitsPerObject;
++
++            if (shift + bitsPerObject > 64) { // will next write overflow?
++                // must move to next idx
++                packed[idx++] = curr;
++                shift = 0;
++                curr = 0L;
++            }
++        }
++
++        // don't forget to write the last one
++        if (shift != 0) {
++            packed[idx] = curr;
++        }
++
++        return wrapPalette(paletteString, packed);
++    }
++
++    private static MapType<String> wrapPalette(final ListType palette) {
++        return wrapPalette(palette, null);
++    }
++
++    private static MapType<String> wrapPalette(final ListType palette, final long[] blockStates) {
++        final MapType<String> ret = Types.NBT.createEmptyMap();
++        ret.setList("palette", palette);
++        if (blockStates != null) {
++            ret.setLongs("data", blockStates);
++        }
++
++        return ret;
++    }
++
++    private static MapType<String> wrapPaletteOptimised(final ListType palette, final long[] blockStates) {
++        if (palette.size() == 1) {
++            return wrapPalette(palette);
++        }
++
++        return wrapPalette(palette, blockStates);
++    }
++
++    public static int ceilLog2(final int value) {
++        return value == 0 ? 0 : Integer.SIZE - Integer.numberOfLeadingZeros(value - 1); // see doc of numberOfLeadingZeros
++    }
++
++    private static void updateLayers(final ListType layers) {
++        if (layers == null) {
++            return;
++        }
++
++        layers.addMap(0, createEmptyLayer()); // add at the bottom
++    }
++
++    private static MapType<String> createEmptyLayer() {
++        final MapType<String> ret = Types.NBT.createEmptyMap();
++        ret.setInt("height", 64);
++        ret.setString("block", "minecraft:air");
++
++        return ret;
++    }
++}
+diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2833.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2833.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..4bdac86810c51e9f87ea82ba9f6c6d8ae8ce2bdf
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2833.java
+@@ -0,0 +1,30 @@
++package ca.spottedleaf.dataconverter.minecraft.versions;
++
++import ca.spottedleaf.dataconverter.converters.DataConverter;
++import ca.spottedleaf.dataconverter.minecraft.MCVersions;
++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
++import ca.spottedleaf.dataconverter.types.MapType;
++
++public final class V2833 {
++
++    protected static final int VERSION = MCVersions.V1_17_1 + 103;
++
++    public static void register() {
++        MCTypeRegistry.WORLD_GEN_SETTINGS.addStructureConverter(new DataConverter<>(VERSION) {
++            @Override
++            public MapType<String> convert(final MapType<String> data, final long sourceVersion, final long toVersion) {
++                final MapType<String> dimensions = data.getMap("dimensions");
++
++                for (final String dimensionKey : dimensions.keys()) {
++                    final MapType<String> dimension = dimensions.getMap(dimensionKey);
++                    if (!dimension.hasKey("type")) {
++                        throw new IllegalStateException("Unable load old custom worlds.");
++                    }
++                }
++
++                return null;
++            }
++        });
++    }
++
++}
+diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2838.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2838.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..586e711163e2bdea110442dd181289fc06f6f7f1
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2838.java
+@@ -0,0 +1,56 @@
++package ca.spottedleaf.dataconverter.minecraft.versions;
++
++import ca.spottedleaf.dataconverter.minecraft.MCVersions;
++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.ConverterAbstractStringValueTypeRename;
++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
++import com.google.common.collect.ImmutableMap;
++
++public final class V2838 {
++
++    protected static final int VERSION = MCVersions.V21W40A;
++
++    public static final ImmutableMap<String, String> BIOME_UPDATE = ImmutableMap.<String, String>builder()
++            .put("minecraft:badlands_plateau", "minecraft:badlands")
++            .put("minecraft:bamboo_jungle_hills", "minecraft:bamboo_jungle")
++            .put("minecraft:birch_forest_hills", "minecraft:birch_forest")
++            .put("minecraft:dark_forest_hills", "minecraft:dark_forest")
++            .put("minecraft:desert_hills", "minecraft:desert")
++            .put("minecraft:desert_lakes", "minecraft:desert")
++            .put("minecraft:giant_spruce_taiga_hills", "minecraft:old_growth_spruce_taiga")
++            .put("minecraft:giant_spruce_taiga", "minecraft:old_growth_spruce_taiga")
++            .put("minecraft:giant_tree_taiga_hills", "minecraft:old_growth_pine_taiga")
++            .put("minecraft:giant_tree_taiga", "minecraft:old_growth_pine_taiga")
++            .put("minecraft:gravelly_mountains", "minecraft:windswept_gravelly_hills")
++            .put("minecraft:jungle_edge", "minecraft:sparse_jungle")
++            .put("minecraft:jungle_hills", "minecraft:jungle")
++            .put("minecraft:modified_badlands_plateau", "minecraft:badlands")
++            .put("minecraft:modified_gravelly_mountains", "minecraft:windswept_gravelly_hills")
++            .put("minecraft:modified_jungle_edge", "minecraft:sparse_jungle")
++            .put("minecraft:modified_jungle", "minecraft:jungle")
++            .put("minecraft:modified_wooded_badlands_plateau", "minecraft:wooded_badlands")
++            .put("minecraft:mountain_edge", "minecraft:windswept_hills")
++            .put("minecraft:mountains", "minecraft:windswept_hills")
++            .put("minecraft:mushroom_field_shore", "minecraft:mushroom_fields")
++            .put("minecraft:shattered_savanna", "minecraft:windswept_savanna")
++            .put("minecraft:shattered_savanna_plateau", "minecraft:windswept_savanna")
++            .put("minecraft:snowy_mountains", "minecraft:snowy_plains")
++            .put("minecraft:snowy_taiga_hills", "minecraft:snowy_taiga")
++            .put("minecraft:snowy_taiga_mountains", "minecraft:snowy_taiga")
++            .put("minecraft:snowy_tundra", "minecraft:snowy_plains")
++            .put("minecraft:stone_shore", "minecraft:stony_shore")
++            .put("minecraft:swamp_hills", "minecraft:swamp")
++            .put("minecraft:taiga_hills", "minecraft:taiga")
++            .put("minecraft:taiga_mountains", "minecraft:taiga")
++            .put("minecraft:tall_birch_forest", "minecraft:old_growth_birch_forest")
++            .put("minecraft:tall_birch_hills", "minecraft:old_growth_birch_forest")
++            .put("minecraft:wooded_badlands_plateau", "minecraft:wooded_badlands")
++            .put("minecraft:wooded_hills", "minecraft:forest")
++            .put("minecraft:wooded_mountains", "minecraft:windswept_forest")
++            .put("minecraft:lofty_peaks", "minecraft:jagged_peaks")
++            .put("minecraft:snowcapped_peaks", "minecraft:frozen_peaks")
++            .build();
++
++    public static void register() {
++        ConverterAbstractStringValueTypeRename.register(VERSION, MCTypeRegistry.BIOME, BIOME_UPDATE::get);
++    }
++}
+diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2841.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2841.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..41b41ff084662bbc2e323713473e4e13b8e50cd7
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2841.java
+@@ -0,0 +1,205 @@
++package ca.spottedleaf.dataconverter.minecraft.versions;
++
++import ca.spottedleaf.dataconverter.converters.DataConverter;
++import ca.spottedleaf.dataconverter.minecraft.MCVersions;
++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
++import ca.spottedleaf.dataconverter.types.ListType;
++import ca.spottedleaf.dataconverter.types.MapType;
++import ca.spottedleaf.dataconverter.types.ObjectType;
++import ca.spottedleaf.dataconverter.types.Types;
++import ca.spottedleaf.dataconverter.util.IntegerUtil;
++import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
++import java.util.Arrays;
++import java.util.HashSet;
++import java.util.Set;
++
++public final class V2841 {
++
++    protected static final int VERSION = MCVersions.V21W42A + 1;
++
++    protected static final Set<String> ALWAYS_WATERLOGGED = new HashSet<>(Arrays.asList(
++            "minecraft:bubble_column",
++            "minecraft:kelp",
++            "minecraft:kelp_plant",
++            "minecraft:seagrass",
++            "minecraft:tall_seagrass"
++    ));
++
++    public static void register() {
++        MCTypeRegistry.CHUNK.addStructureConverter(new DataConverter<>(VERSION) {
++            @Override
++            public MapType<String> convert(final MapType<String> root, final long sourceVersion, final long toVersion) {
++                final MapType<String> level = root.getMap("Level");
++                if (level == null) {
++                    return null;
++                }
++
++                {
++                    // Why it's renamed here and not the next data version is beyond me.
++                    final MapType<String> liquidTicks = level.getMap("LiquidTicks");
++                    if (liquidTicks != null) {
++                        level.remove("LiquidTicks");
++                        level.setMap("fluid_ticks", liquidTicks);
++                    }
++                }
++
++                final Int2ObjectOpenHashMap<SimplePaletteReader> sectionBlocks = new Int2ObjectOpenHashMap<>();
++                final ListType sections = level.getList("Sections", ObjectType.MAP);
++                int minSection = 0; // TODO wtf is this
++                if (sections != null) {
++                    for (int i = 0, len = sections.size(); i < len; ++i) {
++                        final MapType<String> section = sections.getMap(i);
++
++                        final int sectionY = section.getInt("Y");
++                        if (sectionY < minSection && section.hasKey("biomes")) {
++                            minSection = sectionY;
++                        }
++
++                        final MapType<String> blockStates = section.getMap("block_states");
++                        if (blockStates == null) {
++                            continue;
++                        }
++
++                        sectionBlocks.put(sectionY, new SimplePaletteReader(section.getList("palette", ObjectType.MAP), section.getLongs("data")));
++                    }
++                }
++
++                level.setByte("yPos", (byte)minSection); // TODO ???????????????????????????????????????
++
++                if (level.hasKey("fluid_ticks") || level.hasKey("TileTicks")) {
++                    return null;
++                }
++
++                final int sectionX = level.getInt("xPos");
++                final int sectionZ = level.getInt("zPos");
++
++                final ListType fluidTicks = level.getList("LiquidsToBeTicked", ObjectType.LIST);
++                final ListType blockTicks = level.getList("ToBeTicked", ObjectType.LIST);
++                level.remove("LiquidsToBeTicked");
++                level.remove("ToBeTicked");
++
++                level.setList("fluid_ticks", migrateTickList(fluidTicks, false, sectionBlocks, sectionX, minSection, sectionZ));
++                level.setList("TileTicks", migrateTickList(blockTicks, true, sectionBlocks, sectionX, minSection, sectionZ));
++
++                return null;
++            }
++        });
++    }
++
++    public static ListType migrateTickList(final ListType ticks, final boolean blockTicks, final Int2ObjectOpenHashMap<SimplePaletteReader> sectionBlocks,
++                                           final int sectionX, final int minSection, final int sectionZ) {
++        final ListType ret = Types.NBT.createEmptyList();
++
++        if (ticks == null) {
++            return ret;
++        }
++
++        for (int sectionIndex = 0, totalSections = ticks.size(); sectionIndex < totalSections; ++sectionIndex) {
++            final int sectionY = sectionIndex + minSection;
++            final ListType sectionTicks = ticks.getList(sectionIndex);
++            final SimplePaletteReader palette = sectionBlocks.get(sectionY);
++
++            for (int i = 0, len = sectionTicks.size(); i < len; ++i) {
++                final int localIndex = sectionTicks.getShort(i) & 0xFFFF;
++                final MapType<String> blockState = palette == null ? null : palette.getState(localIndex);
++                final String subjectId = blockTicks ? getBlockId(blockState) : getLiquidId(blockState);
++
++                ret.addMap(createNewTick(subjectId, localIndex, sectionX, sectionY, sectionZ));
++            }
++        }
++
++        return ret;
++    }
++
++    public static MapType<String> createNewTick(final String subjectId, final int localIndex, final int sectionX, final int sectionY, final int sectionZ) {
++        final int newX = (localIndex & 15) + (sectionX << 4);
++        final int newZ = ((localIndex >> 4) & 15) + (sectionZ << 4);
++        final int newY = ((localIndex >> 8) & 15) + (sectionY << 4);
++
++        final MapType<String> ret = Types.NBT.createEmptyMap();
++
++        ret.setString("i", subjectId);
++        ret.setInt("x", newX);
++        ret.setInt("y", newY);
++        ret.setInt("z", newZ);
++        ret.setInt("t", 0);
++        ret.setInt("p", 0);
++
++        return ret;
++    }
++
++    public static String getBlockId(final MapType<String> blockState) {
++        return blockState == null ? "minecraft:air" : blockState.getString("Name", "minecraft:air");
++    }
++
++    private static String getLiquidId(final MapType<String> blockState) {
++        if (blockState == null) {
++            return "minecraft:empty";
++        }
++
++        final String name = blockState.getString("Name");
++        if (ALWAYS_WATERLOGGED.contains(name)) {
++            return "minecraft:water";
++        }
++
++        final MapType<String> properties = blockState.getMap("Properties");
++        if ("minecraft:water".equals(name)) {
++            return properties != null && properties.getInt("level") == 0 ? "minecraft:water" : "minecraft:flowing_water";
++        } else if ("minecraft:lava".equals(name)) {
++            return properties != null && properties.getInt("level") == 0 ? "minecraft:lava" : "minecraft:flowing_lava";
++        }
++
++        return (properties != null && properties.getBoolean("waterlogged")) ? "minecraft:water" : "minecraft:empty";
++    }
++
++    public static final class SimplePaletteReader {
++
++        public final ListType palette;
++        public final long[] data;
++        private final int bitsPerValue;
++        private final long mask;
++        private final int valuesPerLong;
++
++        public SimplePaletteReader(final ListType palette, final long[] data) {
++            this.palette = palette == null ? null : (palette.size() == 0 ? null : palette);
++            this.data = data;
++            this.bitsPerValue = Math.max(4, IntegerUtil.ceilLog2(this.palette == null ? 0 : this.palette.size()));
++            this.mask = (1L << this.bitsPerValue) - 1L;
++            this.valuesPerLong = (int)(64L / this.bitsPerValue);
++        }
++
++        public MapType<String> getState(final int x, final int y, final int z) {
++            final int index = x | (z << 4) | (y << 8);
++            return this.getState(index);
++        }
++
++        public MapType<String> getState(final int index) {
++            final ListType palette = this.palette;
++            if (palette == null) {
++                return null;
++            }
++
++            final int paletteSize = palette.size();
++            if (paletteSize == 1) {
++                return palette.getMap(0);
++            }
++
++            // x86 div computes mod. no loss here using mod
++            // if needed, can compute magic mul and shift for div values using IntegerUtil
++            final int dataIndex = index / this.valuesPerLong;
++            final int localIndex = (index % this.valuesPerLong) * this.bitsPerValue;
++            final long[] data = this.data;
++            if (dataIndex < 0 || dataIndex >= data.length) {
++                return null;
++            }
++
++            final long value = data[dataIndex];
++            final int paletteIndex = (int)((value >>> localIndex) & this.mask);
++            if (paletteIndex < 0 || paletteIndex >= paletteSize) {
++                return null;
++            }
++
++            return palette.getMap(paletteIndex);
++        }
++    }
++}
+diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2842.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2842.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..f06e24bb87baf01b1386fb7a6af1ea04f4d6f2ef
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2842.java
+@@ -0,0 +1,76 @@
++package ca.spottedleaf.dataconverter.minecraft.versions;
++
++import ca.spottedleaf.dataconverter.converters.DataConverter;
++import ca.spottedleaf.dataconverter.minecraft.MCVersions;
++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.RenameHelper;
++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
++import ca.spottedleaf.dataconverter.minecraft.walkers.generic.WalkerUtils;
++import ca.spottedleaf.dataconverter.types.ListType;
++import ca.spottedleaf.dataconverter.types.MapType;
++import ca.spottedleaf.dataconverter.types.ObjectType;
++
++public final class V2842 {
++
++    protected static final int VERSION = MCVersions.V21W42A + 2;
++
++    public static void register() {
++        MCTypeRegistry.CHUNK.addStructureConverter(new DataConverter<>(VERSION) {
++            @Override
++            public MapType<String> convert(final MapType<String> root, final long sourceVersion, final long toVersion) {
++                final MapType<String> level = root.getMap("Level");
++                root.remove("Level");
++
++                if (!root.isEmpty()) {
++                    for (final String key : root.keys()) {
++                        if (level.hasKey(key)) {
++                            // Don't clobber level's data
++                            continue;
++                        }
++                        level.setGeneric(key, root.getGeneric(key));
++                    }
++                }
++
++                // Rename top level first
++                RenameHelper.renameSingle(level, "TileEntities", "block_entities");
++                RenameHelper.renameSingle(level, "TileTicks", "block_ticks");
++                RenameHelper.renameSingle(level, "Entities", "entities");
++                RenameHelper.renameSingle(level, "Sections", "sections");
++                RenameHelper.renameSingle(level, "Structures", "structures");
++
++                // 2nd level
++                final MapType<String> structures = level.getMap("structures");
++                if (structures != null) {
++                    RenameHelper.renameSingle(structures, "Starts", "starts");
++                }
++
++                return level; // Level is now root tag.
++            }
++        });
++
++        MCTypeRegistry.CHUNK.addStructureWalker(VERSION, (final MapType<String> data, final long fromVersion, final long toVersion) -> {
++            WalkerUtils.convertList(MCTypeRegistry.ENTITY, data, "entities", fromVersion, toVersion);
++            WalkerUtils.convertList(MCTypeRegistry.TILE_ENTITY, data, "block_entities", fromVersion, toVersion);
++
++            final ListType blockTicks = data.getList("block_ticks", ObjectType.MAP);
++            if (blockTicks != null) {
++                for (int i = 0, len = blockTicks.size(); i < len; ++i) {
++                    WalkerUtils.convert(MCTypeRegistry.BLOCK_NAME, blockTicks.getMap(i), "i", fromVersion, toVersion);
++                }
++            }
++
++            final ListType sections = data.getList("sections", ObjectType.MAP);
++            if (sections != null) {
++                for (int i = 0, len = sections.size(); i < len; ++i) {
++                    final MapType<String> section = sections.getMap(i);
++
++                    WalkerUtils.convertList(MCTypeRegistry.BIOME, section.getMap("biomes"), "palette", fromVersion, toVersion);
++                    WalkerUtils.convertList(MCTypeRegistry.BLOCK_STATE, section.getMap("block_states"), "palette", fromVersion, toVersion);
++                }
++            }
++
++            WalkerUtils.convertValues(MCTypeRegistry.STRUCTURE_FEATURE, data.getMap("structures"), "starts", fromVersion, toVersion);
++
++            return null;
++        });
++    }
++}
+diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2843.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2843.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..5d69a84a1d4f74a961210561c3258a4ed5e4c4d2
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2843.java
+@@ -0,0 +1,15 @@
++package ca.spottedleaf.dataconverter.minecraft.versions;
++
++import ca.spottedleaf.dataconverter.minecraft.MCVersions;
++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.ConverterAbstractStringValueTypeRename;
++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
++import java.util.Map;
++
++public final class V2843 {
++
++    protected static final int VERSION = MCVersions.V21W42A + 3;
++
++    public static void register() {
++        ConverterAbstractStringValueTypeRename.register(VERSION, MCTypeRegistry.BIOME, Map.of("minecraft:deep_warm_ocean", "minecraft:warm_ocean")::get);
++    }
++}
+diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2846.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2846.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..236327249d2b95b799b90172d457601167492249
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2846.java
+@@ -0,0 +1,18 @@
++package ca.spottedleaf.dataconverter.minecraft.versions;
++
++import ca.spottedleaf.dataconverter.minecraft.MCVersions;
++import ca.spottedleaf.dataconverter.minecraft.converters.advancements.ConverterAbstractAdvancementsRename;
++import com.google.common.collect.ImmutableMap;
++
++public final class V2846 {
++
++    protected static final int VERSION = MCVersions.V21W44A + 1;
++
++    public static void register() {
++        ConverterAbstractAdvancementsRename.register(VERSION, ImmutableMap.of(
++                "minecraft:husbandry/play_jukebox_in_meadows", "minecraft:adventure/play_jukebox_in_meadows",
++                "minecraft:adventure/caves_and_cliff", "minecraft:adventure/fall_from_world_height",
++                "minecraft:adventure/ride_strider_in_overworld_lava", "minecraft:nether/ride_strider_in_overworld_lava"
++        )::get);
++    }
++}
+diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2852.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2852.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..94ab7be8c34d2ebb557df5a0864130f7f12c2185
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2852.java
+@@ -0,0 +1,29 @@
++package ca.spottedleaf.dataconverter.minecraft.versions;
++
++import ca.spottedleaf.dataconverter.converters.DataConverter;
++import ca.spottedleaf.dataconverter.minecraft.MCVersions;
++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
++import ca.spottedleaf.dataconverter.types.MapType;
++
++public final class V2852 {
++
++    protected static final int VERSION = MCVersions.V1_18_PRE5 + 1;
++
++    public static void register() {
++        MCTypeRegistry.WORLD_GEN_SETTINGS.addStructureConverter(new DataConverter<>(VERSION) {
++            @Override
++            public MapType<String> convert(final MapType<String> data, final long sourceVersion, final long toVersion) {
++                final MapType<String> dimensions = data.getMap("dimensions");
++
++                for (final String dimensionKey : dimensions.keys()) {
++                    final MapType<String> dimension = dimensions.getMap(dimensionKey);
++                    if (!dimension.hasKey("type")) {
++                        throw new IllegalStateException("Unable load old custom worlds.");
++                    }
++                }
++
++                return null;
++            }
++        });
++    }
++}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V501.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V501.java
 new file mode 100644
 index 0000000000000000000000000000000000000000..6ab2bf99d72983fc2742a1f6f2f7fa671611526d
@@ -16534,12 +18353,13 @@ index 0000000000000000000000000000000000000000..e66b4e0f7cdb032b545ace7ba852ad79
 +}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/generic/WalkerUtils.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/generic/WalkerUtils.java
 new file mode 100644
-index 0000000000000000000000000000000000000000..8f231d4f0999a52d9c19b4669daa94c7881933d0
+index 0000000000000000000000000000000000000000..1e81a1e46a9c0ffceb564a7b1fc4d1b51009f3f7
 --- /dev/null
 +++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/generic/WalkerUtils.java
-@@ -0,0 +1,130 @@
+@@ -0,0 +1,127 @@
 +package ca.spottedleaf.dataconverter.minecraft.walkers.generic;
 +
++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.RenameHelper;
 +import ca.spottedleaf.dataconverter.minecraft.datatypes.MCDataType;
 +import ca.spottedleaf.dataconverter.minecraft.datatypes.MCValueType;
 +import ca.spottedleaf.dataconverter.types.ObjectType;
@@ -16634,13 +18454,9 @@ index 0000000000000000000000000000000000000000..8f231d4f0999a52d9c19b4669daa94c7
 +            return;
 +        }
 +
-+        for (final String key : new ArrayList<>(data.keys())) {
-+            final String updated = (String)type.convert(key, fromVersion, toVersion);
-+            if (updated != null) {
-+                data.setGeneric(updated, data.getGeneric(key));
-+                data.remove(key);
-+            }
-+        }
++        RenameHelper.renameKeys(data, (final String input) -> {
++            return (String)type.convert(input, fromVersion, toVersion);
++        });
 +    }
 +
 +    public static void convertValues(final MCDataType type, final MapType<String> data, final String path, final long fromVersion, final long toVersion) {
@@ -19373,6 +21189,235 @@ index 0000000000000000000000000000000000000000..de9d632489609136c712a9adaee941fd
 +        return this.val[index];
 +    }
 +}
+diff --git a/src/main/java/ca/spottedleaf/dataconverter/util/IntegerUtil.java b/src/main/java/ca/spottedleaf/dataconverter/util/IntegerUtil.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..b5ebf6bbe1a3711111cf045ee8b46c934c7e4563
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/dataconverter/util/IntegerUtil.java
+@@ -0,0 +1,223 @@
++package ca.spottedleaf.dataconverter.util;
++
++public final class IntegerUtil {
++    public static final int HIGH_BIT_U32 = Integer.MIN_VALUE;
++    public static final long HIGH_BIT_U64 = Long.MIN_VALUE;
++
++    public static int ceilLog2(final int value) {
++        return Integer.SIZE - Integer.numberOfLeadingZeros(value - 1); // see doc of numberOfLeadingZeros
++    }
++
++    public static long ceilLog2(final long value) {
++        return Long.SIZE - Long.numberOfLeadingZeros(value - 1); // see doc of numberOfLeadingZeros
++    }
++
++    public static int floorLog2(final int value) {
++        // xor is optimized subtract for 2^n -1
++        // note that (2^n -1) - k = (2^n -1) ^ k for k <= (2^n - 1)
++        return (Integer.SIZE - 1) ^ Integer.numberOfLeadingZeros(value); // see doc of numberOfLeadingZeros
++    }
++
++    public static int floorLog2(final long value) {
++        // xor is optimized subtract for 2^n -1
++        // note that (2^n -1) - k = (2^n -1) ^ k for k <= (2^n - 1)
++        return (Long.SIZE - 1) ^ Long.numberOfLeadingZeros(value); // see doc of numberOfLeadingZeros
++    }
++
++    public static int roundCeilLog2(final int value) {
++        // optimized variant of 1 << (32 - leading(val - 1))
++        // given
++        // 1 << n = HIGH_BIT_32 >>> (31 - n) for n [0, 32)
++        // 1 << (32 - leading(val - 1)) = HIGH_BIT_32 >>> (31 - (32 - leading(val - 1)))
++        // HIGH_BIT_32 >>> (31 - (32 - leading(val - 1)))
++        // HIGH_BIT_32 >>> (31 - 32 + leading(val - 1))
++        // HIGH_BIT_32 >>> (-1 + leading(val - 1))
++        return HIGH_BIT_U32 >>> (Integer.numberOfLeadingZeros(value - 1) - 1);
++    }
++
++    public static long roundCeilLog2(final long value) {
++        // see logic documented above
++        return HIGH_BIT_U64 >>> (Long.numberOfLeadingZeros(value - 1) - 1);
++    }
++
++    public static int roundFloorLog2(final int value) {
++        // optimized variant of 1 << (31 - leading(val))
++        // given
++        // 1 << n = HIGH_BIT_32 >>> (31 - n) for n [0, 32)
++        // 1 << (31 - leading(val)) = HIGH_BIT_32 >> (31 - (31 - leading(val)))
++        // HIGH_BIT_32 >> (31 - (31 - leading(val)))
++        // HIGH_BIT_32 >> (31 - 31 + leading(val))
++        return HIGH_BIT_U32 >>> Integer.numberOfLeadingZeros(value);
++    }
++
++    public static long roundFloorLog2(final long value) {
++        // see logic documented above
++        return HIGH_BIT_U64 >>> Long.numberOfLeadingZeros(value);
++    }
++
++    public static boolean isPowerOfTwo(final int n) {
++        // 2^n has one bit
++        // note: this rets true for 0 still
++        return IntegerUtil.getTrailingBit(n) == n;
++    }
++
++    public static boolean isPowerOfTwo(final long n) {
++        // 2^n has one bit
++        // note: this rets true for 0 still
++        return IntegerUtil.getTrailingBit(n) == n;
++    }
++
++    public static int getTrailingBit(final int n) {
++        return -n & n;
++    }
++
++    public static long getTrailingBit(final long n) {
++        return -n & n;
++    }
++
++    public static int trailingZeros(final int n) {
++        return Integer.numberOfTrailingZeros(n);
++    }
++
++    public static int trailingZeros(final long n) {
++        return Long.numberOfTrailingZeros(n);
++    }
++
++    // from hacker's delight (signed division magic value)
++    public static int getDivisorMultiple(final long numbers) {
++        return (int)(numbers >>> 32);
++    }
++
++    // from hacker's delight (signed division magic value)
++    public static int getDivisorShift(final long numbers) {
++        return (int)numbers;
++    }
++
++    public static long getDivisorNumbers(final int d) {
++        final int ad = Math.abs(d);
++
++        if (ad < 2) {
++            throw new IllegalArgumentException("|number| must be in [2, 2^31 -1], not: " + d);
++        }
++
++        final int two31 = 0x80000000;
++        final long mask = 0xFFFFFFFFL; // mask for enforcing unsigned behaviour
++
++        int p = 31;
++
++        // all these variables are UNSIGNED!
++        int t = two31 + (d >>> 31);
++        int anc = t - 1 - (int)((t & mask)%ad);
++        int q1 = (int)((two31 & mask)/(anc & mask));
++        int r1 = two31 - q1*anc;
++        int q2 = (int)((two31 & mask)/(ad & mask));
++        int r2 = two31 - q2*ad;
++        int delta;
++
++        do {
++            p = p + 1;
++            q1 = 2*q1;                        // Update q1 = 2**p/|nc|.
++            r1 = 2*r1;                        // Update r1 = rem(2**p, |nc|).
++            if ((r1 & mask) >= (anc & mask)) {// (Must be an unsigned comparison here)
++                q1 = q1 + 1;
++                r1 = r1 - anc;
++            }
++            q2 = 2*q2;                       // Update q2 = 2**p/|d|.
++            r2 = 2*r2;                       // Update r2 = rem(2**p, |d|).
++            if ((r2 & mask) >= (ad & mask)) {// (Must be an unsigned comparison here)
++                q2 = q2 + 1;
++                r2 = r2 - ad;
++            }
++            delta = ad - r2;
++        } while ((q1 & mask) < (delta & mask) || (q1 == delta && r1 == 0));
++
++        int magicNum = q2 + 1;
++        if (d < 0) {
++            magicNum = -magicNum;
++        }
++        int shift = p - 32;
++        return ((long)magicNum << 32) | shift;
++    }
++
++    public static int branchlessAbs(final int val) {
++        // -n = -1 ^ n + 1
++        final int mask = val >> (Integer.SIZE - 1); // -1 if < 0, 0 if >= 0
++        return (mask ^ val) - mask; // if val < 0, then (0 ^ val) - 0 else (-1 ^ val) + 1
++    }
++
++    public static long branchlessAbs(final long val) {
++        // -n = -1 ^ n + 1
++        final long mask = val >> (Long.SIZE - 1); // -1 if < 0, 0 if >= 0
++        return (mask ^ val) - mask; // if val < 0, then (0 ^ val) - 0 else (-1 ^ val) + 1
++    }
++
++    //https://github.com/skeeto/hash-prospector for hash functions
++
++    //score = ~590.47984224483832
++    public static int hash0(int x) {
++        x *= 0x36935555;
++        x ^= x >>> 16;
++        return x;
++    }
++
++    //score = ~310.01596637036749
++    public static int hash1(int x) {
++        x ^= x >>> 15;
++        x *= 0x356aaaad;
++        x ^= x >>> 17;
++        return x;
++    }
++
++    public static int hash2(int x) {
++        x ^= x >>> 16;
++        x *= 0x7feb352d;
++        x ^= x >>> 15;
++        x *= 0x846ca68b;
++        x ^= x >>> 16;
++        return x;
++    }
++
++    public static int hash3(int x) {
++        x ^= x >>> 17;
++        x *= 0xed5ad4bb;
++        x ^= x >>> 11;
++        x *= 0xac4c1b51;
++        x ^= x >>> 15;
++        x *= 0x31848bab;
++        x ^= x >>> 14;
++        return x;
++    }
++
++    //score = ~365.79959673201887
++    public static long hash1(long x) {
++        x ^= x >>> 27;
++        x *= 0xb24924b71d2d354bL;
++        x ^= x >>> 28;
++        return x;
++    }
++
++    //h2 hash
++    public static long hash2(long x) {
++        x ^= x >>> 32;
++        x *= 0xd6e8feb86659fd93L;
++        x ^= x >>> 32;
++        x *= 0xd6e8feb86659fd93L;
++        x ^= x >>> 32;
++        return x;
++    }
++
++    public static long hash3(long x) {
++        x ^= x >>> 45;
++        x *= 0xc161abe5704b6c79L;
++        x ^= x >>> 41;
++        x *= 0xe3e5389aedbc90f7L;
++        x ^= x >>> 56;
++        x *= 0x1f9aba75a52db073L;
++        x ^= x >>> 53;
++        return x;
++    }
++
++    private IntegerUtil() {
++        throw new RuntimeException();
++    }
++}
 diff --git a/src/main/java/ca/spottedleaf/dataconverter/util/Long2IntArraySortedMap.java b/src/main/java/ca/spottedleaf/dataconverter/util/Long2IntArraySortedMap.java
 new file mode 100644
 index 0000000000000000000000000000000000000000..94705bb141b550589faa9a0408402d8636c61907
@@ -19567,81 +21612,25 @@ index 0000000000000000000000000000000000000000..967ad1186cbc81a76a4958ea99d4eff3
 +        return correct.equals(value) ? null : correct;
 +    }
 +}
-diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
-index b889dbad607b6508fb4987d21d3be691a5b37072..747e2a31f055d84d4321c8241e1b6c2918557e91 100644
---- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
-@@ -75,7 +75,7 @@ public class ChunkStorage implements AutoCloseable {
-         boolean flag = true;
+diff --git a/src/main/java/net/minecraft/util/datafix/DataFixers.java b/src/main/java/net/minecraft/util/datafix/DataFixers.java
+index b08d32bc80b4a65ebb980366a3e717c3b0e1bdab..4c5d893c8c29a8395ecb769b20772ba80f66d4ac 100644
+--- a/src/main/java/net/minecraft/util/datafix/DataFixers.java
++++ b/src/main/java/net/minecraft/util/datafix/DataFixers.java
+@@ -86,9 +86,16 @@ public class DataFixers {
+         DataFixerBuilder datafixerbuilder = new DataFixerBuilder(SharedConstants.getCurrentVersion().getWorldVersion());
  
-         // CraftBukkit start
--        if (i < 1466) {
-+        if (false && i < 1466) { // Paper - no longer needed, data converter system handles it now
-             CompoundTag level = nbttagcompound.getCompound("Level");
-             if (level.getBoolean("TerrainPopulated") && !level.getBoolean("LightPopulated")) {
-                 ServerChunkCache cps = (generatoraccess == null) ? null : ((ServerLevel) generatoraccess).getChunkSource();
-@@ -87,7 +87,7 @@ public class ChunkStorage implements AutoCloseable {
-         // CraftBukkit end
- 
-         if (i < 1493) {
--            nbttagcompound = NbtUtils.update(this.fixerUpper, DataFixTypes.CHUNK, nbttagcompound, i, 1493);
-+            ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.CHUNK, nbttagcompound, i, 1493); // Paper - replace chunk converter
-             if (nbttagcompound.getCompound("Level").getBoolean("hasLegacyStructureData")) {
-                 synchronized (this.persistentDataLock) { // Paper - Async chunk loading
-                 if (this.legacyStructureHandler == null) {
-@@ -99,7 +99,7 @@ public class ChunkStorage implements AutoCloseable {
-             }
-         }
- 
--        nbttagcompound = NbtUtils.update(this.fixerUpper, DataFixTypes.CHUNK, nbttagcompound, Math.max(1493, i));
-+        nbttagcompound = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.CHUNK, nbttagcompound, Math.max(1493, i), SharedConstants.getCurrentVersion().getWorldVersion()); // Paper - replace chunk converter
-         if (i < SharedConstants.getCurrentVersion().getWorldVersion()) {
-             nbttagcompound.putInt("DataVersion", SharedConstants.getCurrentVersion().getWorldVersion());
-         }
-diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
-index 0e13a1f898a793799416056bd468851013f9c5cb..916f93b097a65f95e830fe5e1567c85d304f808f 100644
---- a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
-@@ -148,7 +148,7 @@ public class EntityStorage implements EntityPersistentStorage<Entity> {
-     private CompoundTag upgradeChunkTag(CompoundTag chunkTag) {
-         int i = EntityStorage.getVersion(chunkTag);
- 
--        return NbtUtils.update(this.fixerUpper, DataFixTypes.ENTITY_CHUNK, chunkTag, i);
-+        return ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.ENTITY_CHUNK, chunkTag, i, SharedConstants.getCurrentVersion().getWorldVersion()); // Paper - route to new converter system
+         DataFixers.addFixers(datafixerbuilder);
+-        return datafixerbuilder.build(Util.bootstrapExecutor());
++        return redirectToDataconverter(datafixerbuilder, Util.bootstrapExecutor()); // Paper
      }
  
-     public static int getVersion(CompoundTag chunkTag) {
-diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
-index e2d1149cbe75b0689c9f816b87ebb7ba0d6f56c8..a7a51aa31628d9dbc30f43ef74022b0cd917be7c 100644
---- a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
-@@ -135,7 +135,15 @@ public class SectionStorage<R> extends RegionFileStorage implements AutoCloseabl
-             int j = getVersion(dynamic);
-             int k = SharedConstants.getCurrentVersion().getWorldVersion();
-             boolean bl = j != k;
--            Dynamic<T> dynamic2 = this.fixerUpper.update(this.type.getType(), dynamic, j, k);
-+            // Paper start - route to new converter system
-+            Dynamic<T> dynamic2;
++    // Paper start
++    private static DataFixer redirectToDataconverter(final DataFixerBuilder instance, final java.util.concurrent.Executor executor) {
++        final DataFixer wrap = instance.build(executor);
++        return new ca.spottedleaf.dataconverter.minecraft.ReplacedDataFixerUpper(wrap);
++    }
++    // Paper end
 +
-+            if (this.type.getType() == net.minecraft.util.datafix.fixes.References.POI_CHUNK) {
-+                dynamic2 = new Dynamic<>(dynamic.getOps(), (T)ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.POI_CHUNK, (CompoundTag)dynamic.getValue(), j, k));
-+            } else {
-+                dynamic2 = this.fixerUpper.update(this.type.getType(), dynamic, j, k);
-+            }
-+            // Paper end - route to new converter system
-             OptionalDynamic<T> optionalDynamic = dynamic2.get("Sections");
- 
-             for(int l = this.levelHeightAccessor.getMinSection(); l < this.levelHeightAccessor.getMaxSection(); ++l) {
-diff --git a/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java b/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java
-index 6727468946ea5f60bd80549f827a7c2b9a42b98b..35c39aed9583275ef25d32c783715798b52bdb63 100644
---- a/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java
-+++ b/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java
-@@ -93,7 +93,7 @@ public class PlayerDataStorage {
-             // CraftBukkit end
-             int i = nbttagcompound.contains("DataVersion", 3) ? nbttagcompound.getInt("DataVersion") : -1;
- 
--            player.load(NbtUtils.update(this.fixerUpper, DataFixTypes.PLAYER, nbttagcompound, i));
-+            player.load(ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.PLAYER, nbttagcompound, i, net.minecraft.SharedConstants.getCurrentVersion().getWorldVersion())); // Paper - replace player converter
-         }
- 
-         return nbttagcompound;
+     public static DataFixer getDataFixer() {
+         return DataFixers.DATA_FIXER;
+     }
diff --git a/todo.txt b/todo.txt
index be6bbf98d0..a563b5f0e2 100644
--- a/todo.txt
+++ b/todo.txt
@@ -29,3 +29,5 @@ check ChunkHolder#updateFutures async catcher
 
 
 leaf: check mid tick chunk task diff in ServerChunkCache
+optimize nearby player lookups - look at patch and updateranges diff in chunkmap (why is it in this patch)
+GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE_SQUARED is unused?