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 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 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> 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> { private static final float AVERAGE_PACKETS_SMOOTHING = 0.75F; -@@ -409,9 +411,19 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -387,9 +389,19 @@ public class Connection extends SimpleChannelInboundHandler> { 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 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 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 resourcekey, DimensionType dimensionmanager, ChunkProgressListener worldloadlistener, ChunkGenerator chunkgenerator, boolean flag, long i, List 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 resourcekey, final DimensionType dimensionmanager, Supplier 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 resourcekey, final DimensionType dimensionmanager, Supplier 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 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 implements A @@ -283,7 +283,7 @@ index 976d206a17add01a31ae38b966913368cf386cb1..28c1f144f2cc8675ed61dc8144568593 void removeSectionIfEmpty(long sectionPos, EntitySection section) { if (section.isEmpty()) { this.sectionStorage.remove(sectionPos); -@@ -462,11 +521,25 @@ public class PersistentEntitySectionManager implements A +@@ -451,11 +510,25 @@ public class PersistentEntitySectionManager 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 implements A - entitysection.add(this.entity); // CraftBukkit - decompile error +@@ -467,6 +540,11 @@ public class PersistentEntitySectionManager 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 extends Behavior { @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>> 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>> 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>> entries = this.behaviors.entries; @@ -85,10 +85,10 @@ index 09998d160a6d79fdb5a5041a5d572649a1532e6a..84bd9298840ba60abbdb0675cda0458e RUN_ONE { @Override - public void apply(Stream> 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 void apply(List>> tasks, ServerLevel world, E entity, long time) { @@ -105,10 +105,10 @@ index 09998d160a6d79fdb5a5041a5d572649a1532e6a..84bd9298840ba60abbdb0675cda0458e TRY_ALL { @Override - public void apply(Stream> 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 void apply(List>> tasks, ServerLevel world, E entity, long time) { @@ -127,64 +127,8 @@ index 09998d160a6d79fdb5a5041a5d572649a1532e6a..84bd9298840ba60abbdb0675cda0458e + public abstract void apply(List>> 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 { - - @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 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 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 { - List 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 visibleMobs = new java.util.ArrayList<>(list); -- visibleMobs.removeIf(otherEntityLiving -> !Sensor.isEntityTargetable(entity, otherEntityLiving)); -+ List 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 { +@@ -21,25 +21,30 @@ public class PlayerSensor extends Sensor { @Override protected void doTick(ServerLevel world, LivingEntity entity) { -- // Paper start - remove streams in favour of lists - List 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 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 { - } - - private List getNearestVillagerBabies(LivingEntity entities) { -- return this.getVisibleEntities(entities).stream().filter(this::isVillagerBaby).collect(Collectors.toList()); -+ // Paper start - remove streams -+ List list = new java.util.ArrayList<>(); -+ List 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 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 R convertUnwrapped(final DataType 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 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 Dynamic update(final DSL.TypeReference type, final Dynamic 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 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 convert(final MapType 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> { ++ ++ 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 convert(final MapType 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 data, final Function renamer) { ++ boolean needsRename = false; ++ for (final String key : data.keys()) { ++ if (renamer.apply(key) != null) { ++ needsRename = true; ++ break; ++ } ++ } ++ ++ if (!needsRename) { ++ return; ++ } ++ ++ final List newKeys = new ArrayList<>(); ++ final List 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 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 convert(final MapType 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, MapType>> hooks = this.structureHooks.getFloor(converterVersion); ++ List, MapType>> 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 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, MapType>> hooks = this.structureHooks.getFloor(converterVersion); ++ List, MapType>> 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 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> hooks = this.structureHooks.getFloor(converterVersion); ++ List> 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 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 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 convert(final MapType root, final long sourceVersion, final long toVersion) { ++ final MapType spawnData = root.getMap("SpawnData"); ++ if (spawnData != null) { ++ final MapType 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 spawnPotential = spawnPotentials.getMap(i); ++ ++ // new format of weighted list (SpawnPotentials): ++ // root.data -> data ++ // root.weight -> weight ++ ++ final MapType entity = spawnPotential.getMap("Entity"); ++ final int weight = spawnPotential.getInt("Weight", 1); ++ spawnPotential.remove("Entity"); ++ spawnPotential.remove("Weight"); ++ spawnPotential.setInt("weight", weight); ++ ++ final MapType 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 STATUS_IS_OR_AFTER_SURFACE = new HashSet<>(Arrays.asList( ++ "surface", ++ "carvers", ++ "liquid_carvers", ++ "features", ++ "light", ++ "spawn", ++ "heightmaps", ++ "full" ++ )); ++ private static final Set STATUS_IS_OR_AFTER_NOISE = new HashSet<>(Arrays.asList( ++ "noise", ++ "surface", ++ "carvers", ++ "liquid_carvers", ++ "features", ++ "light", ++ "spawn", ++ "heightmaps", ++ "full" ++ )); ++ private static final Set 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 data) { ++ // See https://github.com/CaffeineMC/lithium-fabric/issues/279 ++ final MapType 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 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 convert(final MapType 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 dimensions = data.getMap("dimensions"); ++ if (dimensions == null) { ++ // wat ++ return null; ++ } ++ ++ // only care about overworld ++ final MapType overworld = dimensions.getMap("minecraft:overworld"); ++ if (overworld == null) { ++ // wat ++ return null; ++ } ++ ++ final MapType generator = overworld.getMap("generator"); ++ if (generator == null) { ++ // wat ++ return null; ++ } ++ ++ final String type = generator.getString("type", ""); ++ switch (type) { ++ case "minecraft:noise": { ++ final MapType 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 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 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 convert(final MapType 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 level = data.getMap("Level"); ++ ++ if (level == null) { ++ return null; ++ } ++ ++ final MapType 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[] newBiomes = createBiomeSections(level, isOverworld, minSection, isAlreadyExtended); ++ final MapType 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 allBlocks = new HashSet<>(); ++ if (sections != null) { ++ final IntOpenHashSet existingSections = new IntOpenHashSet(); ++ ++ for (int i = 0, len = sections.size(); i < len; ++i) { ++ final MapType 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 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 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 data, final long fromVersion, final long toVersion) -> { ++ final MapType dimensions = data.getMap("dimensions"); ++ ++ if (dimensions == null) { ++ return null; ++ } ++ ++ for (final String dimension : dimensions.keys()) { ++ final MapType dimensionData = dimensions.getMap(dimension); ++ if (dimensionData == null) { ++ continue; ++ } ++ ++ final MapType 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 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 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 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 data, final long fromVersion, final long toVersion) -> { ++ final MapType 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 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 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 level, final Set 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 getEmptyBlockPalette() { ++ final MapType 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 upgradeData, final int shift) { ++ if (upgradeData == null) { ++ return; ++ } ++ ++ final MapType 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 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 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 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 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 level, final int newSize, final int offset) { ++ final MapType 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 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 level) { ++ final MapType 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[] createBiomeSections(final MapType level, final boolean wantExtendedHeight, ++ final int minSection, final MutableBoolean isAlreadyExtended) { ++ final MapType[] 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 bottomCopy = createBiomeSection(biomes, 0, 15); // just want the biomes at y = 0 ++ final MapType 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 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 wrapPalette(final ListType palette) { ++ return wrapPalette(palette, null); ++ } ++ ++ private static MapType wrapPalette(final ListType palette, final long[] blockStates) { ++ final MapType ret = Types.NBT.createEmptyMap(); ++ ret.setList("palette", palette); ++ if (blockStates != null) { ++ ret.setLongs("data", blockStates); ++ } ++ ++ return ret; ++ } ++ ++ private static MapType 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 createEmptyLayer() { ++ final MapType 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 convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType dimensions = data.getMap("dimensions"); ++ ++ for (final String dimensionKey : dimensions.keys()) { ++ final MapType 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 BIOME_UPDATE = ImmutableMap.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 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 convert(final MapType root, final long sourceVersion, final long toVersion) { ++ final MapType 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 liquidTicks = level.getMap("LiquidTicks"); ++ if (liquidTicks != null) { ++ level.remove("LiquidTicks"); ++ level.setMap("fluid_ticks", liquidTicks); ++ } ++ } ++ ++ final Int2ObjectOpenHashMap 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 section = sections.getMap(i); ++ ++ final int sectionY = section.getInt("Y"); ++ if (sectionY < minSection && section.hasKey("biomes")) { ++ minSection = sectionY; ++ } ++ ++ final MapType 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 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 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 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 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 blockState) { ++ return blockState == null ? "minecraft:air" : blockState.getString("Name", "minecraft:air"); ++ } ++ ++ private static String getLiquidId(final MapType blockState) { ++ if (blockState == null) { ++ return "minecraft:empty"; ++ } ++ ++ final String name = blockState.getString("Name"); ++ if (ALWAYS_WATERLOGGED.contains(name)) { ++ return "minecraft:water"; ++ } ++ ++ final MapType 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 getState(final int x, final int y, final int z) { ++ final int index = x | (z << 4) | (y << 8); ++ return this.getState(index); ++ } ++ ++ public MapType 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 convert(final MapType root, final long sourceVersion, final long toVersion) { ++ final MapType 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 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 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 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 convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType dimensions = data.getMap("dimensions"); ++ ++ for (final String dimensionKey : dimensions.keys()) { ++ final MapType 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 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 { - 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 extends RegionFileStorage implements AutoCloseabl - int j = getVersion(dynamic); - int k = SharedConstants.getCurrentVersion().getWorldVersion(); - boolean bl = j != k; -- Dynamic dynamic2 = this.fixerUpper.update(this.type.getType(), dynamic, j, k); -+ // Paper start - route to new converter system -+ Dynamic 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 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?