diff --git a/build-data/paper.at b/build-data/paper.at index 09fa424d9f..fc7d783869 100644 --- a/build-data/paper.at +++ b/build-data/paper.at @@ -19,9 +19,6 @@ public org.spigotmc.SpigotWorldConfig getList(Ljava/lang/String;Ljava/lang/Objec public org.spigotmc.SpigotWorldConfig getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; # MC Utils -public net.minecraft.core.Vec3i setX(I)Lnet/minecraft/core/Vec3i; -public net.minecraft.core.Vec3i setY(I)Lnet/minecraft/core/Vec3i; -public net.minecraft.core.Vec3i setZ(I)Lnet/minecraft/core/Vec3i; public net.minecraft.server.level.ServerChunkCache mainThread public net.minecraft.server.level.ServerLevel chunkSource public org.bukkit.craftbukkit.inventory.CraftItemStack handle diff --git a/patches/api/0013-Add-player-view-distance-API.patch b/patches/api/0013-Add-player-view-distance-API.patch deleted file mode 100644 index b259ef44da..0000000000 --- a/patches/api/0013-Add-player-view-distance-API.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Byteflux -Date: Mon, 29 Feb 2016 18:05:37 -0600 -Subject: [PATCH] Add player view distance API - - -diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index 1786b6c230cabaf962b27f9d95c49b42e2b5cc67..28ef62e9e04501dbf4fe7b0fec52b1435621cffb 100644 ---- a/src/main/java/org/bukkit/entity/Player.java -+++ b/src/main/java/org/bukkit/entity/Player.java -@@ -1490,6 +1490,28 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM - * @param affects Whether the player can affect mob spawning - */ - public void setAffectsSpawning(boolean affects); -+ -+ /** -+ * Gets the view distance for this player -+ * -+ * @return the player's view distance -+ * @deprecated This is unimplemented and will throw an exception at runtime. The {@link org.bukkit.World World}-based methods still work. -+ * @see org.bukkit.World#getViewDistance() -+ * @see org.bukkit.World#getNoTickViewDistance() -+ */ -+ @Deprecated -+ public int getViewDistance(); -+ -+ /** -+ * Sets the view distance for this player -+ * -+ * @param viewDistance the player's view distance -+ * @deprecated This is unimplemented and will throw an exception at runtime. The {@link org.bukkit.World World}-based methods still work. -+ * @see org.bukkit.World#setViewDistance(int) -+ * @see org.bukkit.World#setNoTickViewDistance(int) -+ */ -+ @Deprecated -+ public void setViewDistance(int viewDistance); - // Paper end - - /** diff --git a/patches/api/0013-Add-view-distance-API.patch b/patches/api/0013-Add-view-distance-API.patch new file mode 100644 index 0000000000..1756987f00 --- /dev/null +++ b/patches/api/0013-Add-view-distance-API.patch @@ -0,0 +1,132 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Byteflux +Date: Mon, 29 Feb 2016 18:05:37 -0600 +Subject: [PATCH] Add view distance API + +Add per player no-tick, tick, and send view distances. + +Also add send/no-tick view distance to World. + +diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java +index 92e8b0b1ccebdc2646337114793ea9f3e7759d25..5d01ba43ead1c5d257e38645380359c81f3de849 100644 +--- a/src/main/java/org/bukkit/World.java ++++ b/src/main/java/org/bukkit/World.java +@@ -2379,6 +2379,51 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient + int getViewDistance(); + // Spigot end + ++ // Paper start - view distance api ++ /** ++ * Sets the view distance for this world. ++ * @param viewDistance view distance in [2, 32] ++ */ ++ void setViewDistance(int viewDistance); ++ ++ /** ++ * Returns the no-tick view distance for this world. ++ *

++ * No-tick view distance is the view distance where chunks will load, however the chunks and their entities will not ++ * be set to tick. ++ *

++ * @return The no-tick view distance for this world. ++ */ ++ int getNoTickViewDistance(); ++ ++ /** ++ * Sets the no-tick view distance for this world. ++ *

++ * No-tick view distance is the view distance where chunks will load, however the chunks and their entities will not ++ * be set to tick. ++ *

++ * @param viewDistance view distance in [2, 32] ++ */ ++ void setNoTickViewDistance(int viewDistance); ++ ++ /** ++ * Gets the sending view distance for this world. ++ *

++ * Sending view distance is the view distance where chunks will load in for players in this world. ++ *

++ * @return The sending view distance for this world. ++ */ ++ public int getSendViewDistance(); ++ ++ /** ++ * Sets the sending view distance for this world. ++ *

++ * Sending view distance is the view distance where chunks will load in for players in this world. ++ *

++ * @param viewDistance view distance in [2, 32] or -1 ++ */ ++ public void setSendViewDistance(int viewDistance); ++ // Paper end - view distance api + // Spigot start + public class Spigot { + +diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java +index 1786b6c230cabaf962b27f9d95c49b42e2b5cc67..d65f35573700078a90283c37d698778a751e44a2 100644 +--- a/src/main/java/org/bukkit/entity/Player.java ++++ b/src/main/java/org/bukkit/entity/Player.java +@@ -1490,6 +1490,62 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM + * @param affects Whether the player can affect mob spawning + */ + public void setAffectsSpawning(boolean affects); ++ ++ /** ++ * Gets the view distance for this player ++ * ++ * @return the player's view distance ++ * @see org.bukkit.World#getViewDistance() ++ * @see org.bukkit.World#getNoTickViewDistance() ++ */ ++ public int getViewDistance(); ++ ++ /** ++ * Sets the view distance for this player ++ * ++ * @param viewDistance the player's view distance ++ * @see org.bukkit.World#setViewDistance(int) ++ * @see org.bukkit.World#setNoTickViewDistance(int) ++ */ ++ public void setViewDistance(int viewDistance); ++ ++ /** ++ * Gets the no-ticking view distance for this player. ++ *

++ * No-tick view distance is the view distance where chunks will load, however the chunks and their entities will not ++ * be set to tick. ++ *

++ * @return The no-tick view distance for this player. ++ */ ++ public int getNoTickViewDistance(); ++ ++ /** ++ * Sets the no-ticking view distance for this player. ++ *

++ * No-tick view distance is the view distance where chunks will load, however the chunks and their entities will not ++ * be set to tick. ++ *

++ * @param viewDistance view distance in [2, 32] or -1 ++ */ ++ public void setNoTickViewDistance(int viewDistance); ++ ++ /** ++ * Gets the sending view distance for this player. ++ *

++ * Sending view distance is the view distance where chunks will load in for players. ++ *

++ * @return The sending view distance for this player. ++ */ ++ public int getSendViewDistance(); ++ ++ /** ++ * Sets the sending view distance for this player. ++ *

++ * Sending view distance is the view distance where chunks will load in for players. ++ *

++ * @param viewDistance view distance in [2, 32] or -1 ++ */ ++ public void setSendViewDistance(int viewDistance); + // Paper end + + /** diff --git a/patches/api/0024-Complete-resource-pack-API.patch b/patches/api/0024-Complete-resource-pack-API.patch index cf062ab9bb..ef8c0bf5fe 100644 --- a/patches/api/0024-Complete-resource-pack-API.patch +++ b/patches/api/0024-Complete-resource-pack-API.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Complete resource pack API diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index 9c9c9e504cb4db80e760207f6a27fd822a01ca3c..ea119d5f6f29cdb93a5586357375b476caec5655 100644 +index 9d56c51b64dfd70dbc83b32729c38615141930d5..bbdcb00642d6d91787f0c4184434de15b0e31646 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java @@ -1220,7 +1220,9 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM @@ -18,7 +18,7 @@ index 9c9c9e504cb4db80e760207f6a27fd822a01ca3c..ea119d5f6f29cdb93a5586357375b476 public void setResourcePack(@NotNull String url); /** -@@ -1697,6 +1699,124 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM +@@ -1731,6 +1733,124 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM default net.kyori.adventure.text.event.HoverEvent asHoverEvent(final @NotNull java.util.function.UnaryOperator op) { return net.kyori.adventure.text.event.HoverEvent.showEntity(op.apply(net.kyori.adventure.text.event.HoverEvent.ShowEntity.of(this.getType().getKey(), this.getUniqueId(), this.displayName()))); } diff --git a/patches/api/0044-Add-String-based-Action-Bar-API.patch b/patches/api/0044-Add-String-based-Action-Bar-API.patch index 015008e751..8345e62459 100644 --- a/patches/api/0044-Add-String-based-Action-Bar-API.patch +++ b/patches/api/0044-Add-String-based-Action-Bar-API.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add String based Action Bar API diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index ea119d5f6f29cdb93a5586357375b476caec5655..9d5c5b5f7d7ae4f0278410833d9d385242872498 100644 +index bbdcb00642d6d91787f0c4184434de15b0e31646..638e106375a4be5bd49ddbe8ce32b1c4eeb32800 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java @@ -3,6 +3,7 @@ package org.bukkit.entity; @@ -68,7 +68,7 @@ index ea119d5f6f29cdb93a5586357375b476caec5655..9d5c5b5f7d7ae4f0278410833d9d3852 public default void sendMessage(net.md_5.bungee.api.ChatMessageType position, net.md_5.bungee.api.chat.BaseComponent... components) { spigot().sendMessage(position, components); } -@@ -1888,6 +1924,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM +@@ -1922,6 +1958,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM /** * Sends the component to the specified screen position of this player * @@ -76,7 +76,7 @@ index ea119d5f6f29cdb93a5586357375b476caec5655..9d5c5b5f7d7ae4f0278410833d9d3852 * @param position the screen position * @param component the components to send * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component} -@@ -1900,6 +1937,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM +@@ -1934,6 +1971,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM /** * Sends an array of components as a single message to the specified screen position of this player * diff --git a/patches/api/0090-Player.setPlayerProfile-API.patch b/patches/api/0090-Player.setPlayerProfile-API.patch index d9039254d4..7239c2ef3d 100644 --- a/patches/api/0090-Player.setPlayerProfile-API.patch +++ b/patches/api/0090-Player.setPlayerProfile-API.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Player.setPlayerProfile API This can be useful for changing name or skins after a player has logged in. diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index 30e7cd8e35549022d68e9ca206e55a2537694b14..d12d7e421eb634a9a3b5e35f8970995ec7786055 100644 +index befcda55ba8b12d5116f4afe9984c2871fefe8f2..8c29b5bc634e017c3abe5dd9c0fc55ffd2d54c11 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java @@ -4,6 +4,7 @@ import java.net.InetSocketAddress; @@ -17,7 +17,7 @@ index 30e7cd8e35549022d68e9ca206e55a2537694b14..d12d7e421eb634a9a3b5e35f8970995e import org.bukkit.DyeColor; import org.bukkit.Effect; import org.bukkit.GameMode; -@@ -1874,6 +1875,20 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM +@@ -1908,6 +1909,20 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM * was {@link org.bukkit.event.player.PlayerResourcePackStatusEvent.Status#SUCCESSFULLY_LOADED} */ boolean hasResourcePack(); diff --git a/patches/api/0148-Expose-attack-cooldown-methods-for-Player.patch b/patches/api/0148-Expose-attack-cooldown-methods-for-Player.patch index 39e299c2f8..99e6d0e6fe 100644 --- a/patches/api/0148-Expose-attack-cooldown-methods-for-Player.patch +++ b/patches/api/0148-Expose-attack-cooldown-methods-for-Player.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Expose attack cooldown methods for Player diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index 09d7ce67f711d48e63d71b6b8f19047aaa09d24d..f42bf3bd0423850e7b7cba75b6c0d1d2c8421de0 100644 +index 6687f3020dc217c63a6bd6ec916855bd505261ea..fa9bcf16d74bd4001fe7cc5e4947b09808048996 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java -@@ -2049,6 +2049,26 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM +@@ -2083,6 +2083,26 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM * @param profile The new profile to use */ void setPlayerProfile(@NotNull PlayerProfile profile); diff --git a/patches/api/0190-Add-ThrownEggHatchEvent.patch b/patches/api/0189-Add-ThrownEggHatchEvent.patch similarity index 100% rename from patches/api/0190-Add-ThrownEggHatchEvent.patch rename to patches/api/0189-Add-ThrownEggHatchEvent.patch diff --git a/patches/api/0189-World-view-distance-api.patch b/patches/api/0189-World-view-distance-api.patch deleted file mode 100644 index 9024e2115e..0000000000 --- a/patches/api/0189-World-view-distance-api.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Spottedleaf -Date: Tue, 5 May 2020 21:28:01 -0700 -Subject: [PATCH] World view distance api - - -diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java -index d4912e83ed9e3166501458035269d480469b6c5d..157f4d6b587c382e1f5c558e0a0f8c1b486be7a4 100644 ---- a/src/main/java/org/bukkit/World.java -+++ b/src/main/java/org/bukkit/World.java -@@ -3428,6 +3428,34 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient - int getViewDistance(); - // Spigot end - -+ // Paper start - view distance api -+ /** -+ * Sets the view distance for this world. -+ * @param viewDistance view distance in [2, 32] -+ */ -+ void setViewDistance(int viewDistance); -+ -+ /** -+ * Returns the no-tick view distance for this world. -+ *

-+ * No-tick view distance is the view distance where chunks will load, however the chunks and their entities will not -+ * be set to tick. -+ *

-+ * @return The no-tick view distance for this world. -+ */ -+ int getNoTickViewDistance(); -+ -+ /** -+ * Sets the no-tick view distance for this world. -+ *

-+ * No-tick view distance is the view distance where chunks will load, however the chunks and their entities will not -+ * be set to tick. -+ *

-+ * @param viewDistance view distance in [2, 32] -+ */ -+ void setNoTickViewDistance(int viewDistance); -+ // Paper end - view distance api -+ - // Spigot start - public class Spigot { - diff --git a/patches/api/0191-Entity-Jump-API.patch b/patches/api/0190-Entity-Jump-API.patch similarity index 100% rename from patches/api/0191-Entity-Jump-API.patch rename to patches/api/0190-Entity-Jump-API.patch diff --git a/patches/api/0192-add-hand-to-BlockMultiPlaceEvent.patch b/patches/api/0191-add-hand-to-BlockMultiPlaceEvent.patch similarity index 100% rename from patches/api/0192-add-hand-to-BlockMultiPlaceEvent.patch rename to patches/api/0191-add-hand-to-BlockMultiPlaceEvent.patch diff --git a/patches/api/0193-Add-tick-times-API.patch b/patches/api/0192-Add-tick-times-API.patch similarity index 100% rename from patches/api/0193-Add-tick-times-API.patch rename to patches/api/0192-Add-tick-times-API.patch diff --git a/patches/api/0194-Expose-MinecraftServer-isRunning.patch b/patches/api/0193-Expose-MinecraftServer-isRunning.patch similarity index 100% rename from patches/api/0194-Expose-MinecraftServer-isRunning.patch rename to patches/api/0193-Expose-MinecraftServer-isRunning.patch diff --git a/patches/api/0195-Add-Raw-Byte-ItemStack-Serialization.patch b/patches/api/0194-Add-Raw-Byte-ItemStack-Serialization.patch similarity index 96% rename from patches/api/0195-Add-Raw-Byte-ItemStack-Serialization.patch rename to patches/api/0194-Add-Raw-Byte-ItemStack-Serialization.patch index 9508269c7c..55d10224f5 100644 --- a/patches/api/0195-Add-Raw-Byte-ItemStack-Serialization.patch +++ b/patches/api/0194-Add-Raw-Byte-ItemStack-Serialization.patch @@ -20,7 +20,7 @@ index d6897f43a0692e031bed8a212d9a637ef548cc60..e348034288c74ab80360086d71f0b7f6 // Paper end } diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java -index 1da4ccef66e42fafd5ae975780c6f521137868be..93ab6407ff09fa5e90e2d8e92fd43a4f95d9a6fa 100644 +index aacf8ea85909299355d16cad0386072ec542a70e..b80ef2e5c23764ee68f809268185492bf5577913 100644 --- a/src/main/java/org/bukkit/inventory/ItemStack.java +++ b/src/main/java/org/bukkit/inventory/ItemStack.java @@ -629,6 +629,30 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, net.kyor diff --git a/patches/api/0196-Disable-Sync-Events-firing-Async-errors-during-shutd.patch b/patches/api/0195-Disable-Sync-Events-firing-Async-errors-during-shutd.patch similarity index 100% rename from patches/api/0196-Disable-Sync-Events-firing-Async-errors-during-shutd.patch rename to patches/api/0195-Disable-Sync-Events-firing-Async-errors-during-shutd.patch diff --git a/patches/api/0197-Make-JavaPluginLoader-thread-safe.patch b/patches/api/0196-Make-JavaPluginLoader-thread-safe.patch similarity index 100% rename from patches/api/0197-Make-JavaPluginLoader-thread-safe.patch rename to patches/api/0196-Make-JavaPluginLoader-thread-safe.patch diff --git a/patches/api/0198-Add-Player-Client-Options-API.patch b/patches/api/0197-Add-Player-Client-Options-API.patch similarity index 97% rename from patches/api/0198-Add-Player-Client-Options-API.patch rename to patches/api/0197-Add-Player-Client-Options-API.patch index be003da944..ade4127a65 100644 --- a/patches/api/0198-Add-Player-Client-Options-API.patch +++ b/patches/api/0197-Add-Player-Client-Options-API.patch @@ -193,7 +193,7 @@ index 0000000000000000000000000000000000000000..f7f171c4ee0b8339b2f8fbe82442d65f + } +} diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index f42bf3bd0423850e7b7cba75b6c0d1d2c8421de0..768eca64aba4446fc341b71c12ff538169399f76 100644 +index fa9bcf16d74bd4001fe7cc5e4947b09808048996..23588bf0642810e775f556e0e7f3945433f9f280 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java @@ -2,6 +2,7 @@ package org.bukkit.entity; @@ -204,7 +204,7 @@ index f42bf3bd0423850e7b7cba75b6c0d1d2c8421de0..768eca64aba4446fc341b71c12ff5381 import com.destroystokyo.paper.Title; // Paper import net.kyori.adventure.text.Component; import com.destroystokyo.paper.profile.PlayerProfile; // Paper -@@ -2069,6 +2070,12 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM +@@ -2103,6 +2104,12 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM * Reset the cooldown counter to 0, effectively starting the cooldown period. */ void resetCooldown(); diff --git a/patches/api/0199-Add-PlayerAttackEntityCooldownResetEvent.patch b/patches/api/0198-Add-PlayerAttackEntityCooldownResetEvent.patch similarity index 100% rename from patches/api/0199-Add-PlayerAttackEntityCooldownResetEvent.patch rename to patches/api/0198-Add-PlayerAttackEntityCooldownResetEvent.patch diff --git a/patches/api/0200-Fix-Potion-toItemStack-swapping-the-extended-and-upg.patch b/patches/api/0199-Fix-Potion-toItemStack-swapping-the-extended-and-upg.patch similarity index 100% rename from patches/api/0200-Fix-Potion-toItemStack-swapping-the-extended-and-upg.patch rename to patches/api/0199-Fix-Potion-toItemStack-swapping-the-extended-and-upg.patch diff --git a/patches/api/0201-Add-item-slot-convenience-methods.patch b/patches/api/0200-Add-item-slot-convenience-methods.patch similarity index 100% rename from patches/api/0201-Add-item-slot-convenience-methods.patch rename to patches/api/0200-Add-item-slot-convenience-methods.patch diff --git a/patches/api/0202-Villager-Restocks-API.patch b/patches/api/0201-Villager-Restocks-API.patch similarity index 100% rename from patches/api/0202-Villager-Restocks-API.patch rename to patches/api/0201-Villager-Restocks-API.patch diff --git a/patches/api/0203-Add-Mob-Goal-API.patch b/patches/api/0202-Add-Mob-Goal-API.patch similarity index 100% rename from patches/api/0203-Add-Mob-Goal-API.patch rename to patches/api/0202-Add-Mob-Goal-API.patch diff --git a/patches/api/0204-Expose-game-version.patch b/patches/api/0203-Expose-game-version.patch similarity index 89% rename from patches/api/0204-Expose-game-version.patch rename to patches/api/0203-Expose-game-version.patch index e7fa5e0de2..47f783d713 100644 --- a/patches/api/0204-Expose-game-version.patch +++ b/patches/api/0203-Expose-game-version.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Expose game version diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java -index f9f9a3f0689e6494bd56f288319930018052c38b..5bbb9e858253d31dc58d1eda1c88c8a84d2fb65a 100644 +index efcbc1e667fab15a55fe44b234dc9713aec895f2..ba49dfc7547da0e1e408c2afc70b1f42a1beb76c 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java @@ -118,6 +118,18 @@ public final class Bukkit { @@ -28,7 +28,7 @@ index f9f9a3f0689e6494bd56f288319930018052c38b..5bbb9e858253d31dc58d1eda1c88c8a8 * Gets a view of all currently logged in players. This {@linkplain * Collections#unmodifiableCollection(Collection) view} is a reused diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java -index e0b79d23f4c16505cf54afb822b2f6c370560931..00904a803418865521c52088bf6fe9db2eb28b31 100644 +index b666d5f03bfb5cc91570688382f541498dd1a1c5..2b21ffb60312cdd2033096b52155610b689f3294 100644 --- a/src/main/java/org/bukkit/Server.java +++ b/src/main/java/org/bukkit/Server.java @@ -97,6 +97,16 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi diff --git a/patches/api/0205-Add-villager-reputation-API.patch b/patches/api/0204-Add-villager-reputation-API.patch similarity index 100% rename from patches/api/0205-Add-villager-reputation-API.patch rename to patches/api/0204-Add-villager-reputation-API.patch diff --git a/patches/api/0206-Spawn-Reason-API.patch b/patches/api/0205-Spawn-Reason-API.patch similarity index 100% rename from patches/api/0206-Spawn-Reason-API.patch rename to patches/api/0205-Spawn-Reason-API.patch diff --git a/patches/api/0207-Potential-bed-API.patch b/patches/api/0206-Potential-bed-API.patch similarity index 100% rename from patches/api/0207-Potential-bed-API.patch rename to patches/api/0206-Potential-bed-API.patch diff --git a/patches/api/0208-Prioritise-own-classes-where-possible.patch b/patches/api/0207-Prioritise-own-classes-where-possible.patch similarity index 100% rename from patches/api/0208-Prioritise-own-classes-where-possible.patch rename to patches/api/0207-Prioritise-own-classes-where-possible.patch diff --git a/patches/api/0209-Provide-a-useful-PluginClassLoader-toString.patch b/patches/api/0208-Provide-a-useful-PluginClassLoader-toString.patch similarity index 100% rename from patches/api/0209-Provide-a-useful-PluginClassLoader-toString.patch rename to patches/api/0208-Provide-a-useful-PluginClassLoader-toString.patch diff --git a/patches/api/0210-Inventory-getHolder-method-without-block-snapshot.patch b/patches/api/0209-Inventory-getHolder-method-without-block-snapshot.patch similarity index 100% rename from patches/api/0210-Inventory-getHolder-method-without-block-snapshot.patch rename to patches/api/0209-Inventory-getHolder-method-without-block-snapshot.patch diff --git a/patches/api/0211-Expose-Arrow-getItemStack.patch b/patches/api/0210-Expose-Arrow-getItemStack.patch similarity index 100% rename from patches/api/0211-Expose-Arrow-getItemStack.patch rename to patches/api/0210-Expose-Arrow-getItemStack.patch diff --git a/patches/api/0212-Add-and-implement-PlayerRecipeBookClickEvent.patch b/patches/api/0211-Add-and-implement-PlayerRecipeBookClickEvent.patch similarity index 100% rename from patches/api/0212-Add-and-implement-PlayerRecipeBookClickEvent.patch rename to patches/api/0211-Add-and-implement-PlayerRecipeBookClickEvent.patch diff --git a/patches/api/0213-Support-components-in-ItemMeta.patch b/patches/api/0212-Support-components-in-ItemMeta.patch similarity index 100% rename from patches/api/0213-Support-components-in-ItemMeta.patch rename to patches/api/0212-Support-components-in-ItemMeta.patch diff --git a/patches/api/0214-added-2-new-TargetReasons-for-1.16-mob-behavior.patch b/patches/api/0213-added-2-new-TargetReasons-for-1.16-mob-behavior.patch similarity index 100% rename from patches/api/0214-added-2-new-TargetReasons-for-1.16-mob-behavior.patch rename to patches/api/0213-added-2-new-TargetReasons-for-1.16-mob-behavior.patch diff --git a/patches/api/0215-Add-entity-liquid-API.patch b/patches/api/0214-Add-entity-liquid-API.patch similarity index 100% rename from patches/api/0215-Add-entity-liquid-API.patch rename to patches/api/0214-Add-entity-liquid-API.patch diff --git a/patches/api/0216-Add-PrepareResultEvent-PrepareGrindstoneEvent.patch b/patches/api/0215-Add-PrepareResultEvent-PrepareGrindstoneEvent.patch similarity index 100% rename from patches/api/0216-Add-PrepareResultEvent-PrepareGrindstoneEvent.patch rename to patches/api/0215-Add-PrepareResultEvent-PrepareGrindstoneEvent.patch diff --git a/patches/api/0217-Allow-delegation-to-vanilla-chunk-gen.patch b/patches/api/0216-Allow-delegation-to-vanilla-chunk-gen.patch similarity index 100% rename from patches/api/0217-Allow-delegation-to-vanilla-chunk-gen.patch rename to patches/api/0216-Allow-delegation-to-vanilla-chunk-gen.patch diff --git a/patches/api/0218-Support-hex-colors-in-getLastColors.patch b/patches/api/0217-Support-hex-colors-in-getLastColors.patch similarity index 100% rename from patches/api/0218-Support-hex-colors-in-getLastColors.patch rename to patches/api/0217-Support-hex-colors-in-getLastColors.patch diff --git a/patches/api/0219-Add-setMaxPlayers-API.patch b/patches/api/0218-Add-setMaxPlayers-API.patch similarity index 88% rename from patches/api/0219-Add-setMaxPlayers-API.patch rename to patches/api/0218-Add-setMaxPlayers-API.patch index a1747e9616..c07c240e07 100644 --- a/patches/api/0219-Add-setMaxPlayers-API.patch +++ b/patches/api/0218-Add-setMaxPlayers-API.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add #setMaxPlayers API diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java -index 191fbd4441b9287dd0ae30e2a636c1f95e382572..c6648e84f762ae4b2d06e2a5cd08ff1f7739f49a 100644 +index a5bad2633506ded8394f7706c0eced9c395c13e8..d64d32fad3063b3337ddde7d1350101c5a4d8519 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java @@ -171,6 +171,17 @@ public final class Bukkit { @@ -27,7 +27,7 @@ index 191fbd4441b9287dd0ae30e2a636c1f95e382572..c6648e84f762ae4b2d06e2a5cd08ff1f * Get the game port that the server runs on. * diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java -index c8798700fbb3885c7522e1170242cba573826358..bc7bfb56bd0291c3ab1d74970057d4b0c52b3075 100644 +index a0695411d3a55babd3cb5f926c3496985ef6e402..b0b6220d16c7889b4c7d81925db864b740d39a37 100644 --- a/src/main/java/org/bukkit/Server.java +++ b/src/main/java/org/bukkit/Server.java @@ -144,6 +144,15 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi diff --git a/patches/api/0220-Add-moon-phase-API.patch b/patches/api/0219-Add-moon-phase-API.patch similarity index 94% rename from patches/api/0220-Add-moon-phase-API.patch rename to patches/api/0219-Add-moon-phase-API.patch index 4d135f999c..e4ddb1559b 100644 --- a/patches/api/0220-Add-moon-phase-API.patch +++ b/patches/api/0219-Add-moon-phase-API.patch @@ -47,7 +47,7 @@ index 0000000000000000000000000000000000000000..df05153397b42930cd53d37b30824c7e + } +} diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java -index 823ac73c8bb30b142a51ed25858b7f3449bf8c83..a805cb95e0695a0729e083ea0995a1ef02b3724f 100644 +index c55c07b33e56706c2ffa710e1bb52c387e87ecb1..35d1c5981c679cb3173b72fdfa0cf3af25cd20d6 100644 --- a/src/main/java/org/bukkit/World.java +++ b/src/main/java/org/bukkit/World.java @@ -68,6 +68,12 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient diff --git a/patches/api/0221-Add-playPickupItemAnimation-to-LivingEntity.patch b/patches/api/0220-Add-playPickupItemAnimation-to-LivingEntity.patch similarity index 100% rename from patches/api/0221-Add-playPickupItemAnimation-to-LivingEntity.patch rename to patches/api/0220-Add-playPickupItemAnimation-to-LivingEntity.patch diff --git a/patches/api/0222-Add-BellRingEvent.patch b/patches/api/0221-Add-BellRingEvent.patch similarity index 100% rename from patches/api/0222-Add-BellRingEvent.patch rename to patches/api/0221-Add-BellRingEvent.patch diff --git a/patches/api/0223-Brand-support.patch b/patches/api/0222-Brand-support.patch similarity index 85% rename from patches/api/0223-Brand-support.patch rename to patches/api/0222-Brand-support.patch index 911dec591d..643b9adcbb 100644 --- a/patches/api/0223-Brand-support.patch +++ b/patches/api/0222-Brand-support.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Brand support diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index 768eca64aba4446fc341b71c12ff538169399f76..95bc3af2b14126caaf2394941ea6cbb60afeac0d 100644 +index 23588bf0642810e775f556e0e7f3945433f9f280..42988e8ea5c7c4caca3b046e74621b846b1ff0af 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java -@@ -2204,6 +2204,16 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM +@@ -2238,6 +2238,16 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM // Paper end } diff --git a/patches/api/0224-Add-more-Evoker-API.patch b/patches/api/0223-Add-more-Evoker-API.patch similarity index 100% rename from patches/api/0224-Add-more-Evoker-API.patch rename to patches/api/0223-Add-more-Evoker-API.patch diff --git a/patches/api/0225-Add-methods-to-get-translation-keys.patch b/patches/api/0224-Add-methods-to-get-translation-keys.patch similarity index 100% rename from patches/api/0225-Add-methods-to-get-translation-keys.patch rename to patches/api/0224-Add-methods-to-get-translation-keys.patch diff --git a/patches/api/0226-Create-HoverEvent-from-ItemStack-Entity.patch b/patches/api/0225-Create-HoverEvent-from-ItemStack-Entity.patch similarity index 100% rename from patches/api/0226-Create-HoverEvent-from-ItemStack-Entity.patch rename to patches/api/0225-Create-HoverEvent-from-ItemStack-Entity.patch diff --git a/patches/api/0227-Add-additional-open-container-api-to-HumanEntity.patch b/patches/api/0226-Add-additional-open-container-api-to-HumanEntity.patch similarity index 100% rename from patches/api/0227-Add-additional-open-container-api-to-HumanEntity.patch rename to patches/api/0226-Add-additional-open-container-api-to-HumanEntity.patch diff --git a/patches/api/0228-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch b/patches/api/0227-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch similarity index 100% rename from patches/api/0228-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch rename to patches/api/0227-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch diff --git a/patches/api/0229-Entity-isTicking.patch b/patches/api/0228-Entity-isTicking.patch similarity index 100% rename from patches/api/0229-Entity-isTicking.patch rename to patches/api/0228-Entity-isTicking.patch diff --git a/patches/api/0230-Clarify-the-Javadocs-for-Entity.getEntitySpawnReason.patch b/patches/api/0229-Clarify-the-Javadocs-for-Entity.getEntitySpawnReason.patch similarity index 100% rename from patches/api/0230-Clarify-the-Javadocs-for-Entity.getEntitySpawnReason.patch rename to patches/api/0229-Clarify-the-Javadocs-for-Entity.getEntitySpawnReason.patch diff --git a/patches/api/0231-Villager-resetOffers.patch b/patches/api/0230-Villager-resetOffers.patch similarity index 100% rename from patches/api/0231-Villager-resetOffers.patch rename to patches/api/0230-Villager-resetOffers.patch diff --git a/patches/api/0232-Player-elytra-boost-API.patch b/patches/api/0231-Player-elytra-boost-API.patch similarity index 88% rename from patches/api/0232-Player-elytra-boost-API.patch rename to patches/api/0231-Player-elytra-boost-API.patch index f95a601b71..b49b46f87d 100644 --- a/patches/api/0232-Player-elytra-boost-API.patch +++ b/patches/api/0231-Player-elytra-boost-API.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Player elytra boost API diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index 95bc3af2b14126caaf2394941ea6cbb60afeac0d..046476fdfcd9ceecb6e0f314228c3f81ad46ec80 100644 +index 42988e8ea5c7c4caca3b046e74621b846b1ff0af..220838894bb24a5cab3f005c8a9f4ba7c438b576 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java -@@ -2076,6 +2076,19 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM +@@ -2110,6 +2110,19 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM */ @NotNull T getClientOption(@NotNull ClientOption option); diff --git a/patches/api/0233-Add-getOfflinePlayerIfCached-String.patch b/patches/api/0232-Add-getOfflinePlayerIfCached-String.patch similarity index 92% rename from patches/api/0233-Add-getOfflinePlayerIfCached-String.patch rename to patches/api/0232-Add-getOfflinePlayerIfCached-String.patch index fef1b0b6b7..196c2d70cf 100644 --- a/patches/api/0233-Add-getOfflinePlayerIfCached-String.patch +++ b/patches/api/0232-Add-getOfflinePlayerIfCached-String.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add getOfflinePlayerIfCached(String) diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java -index e94e8df18b5e2f10de6f4ece5bd55899d4972830..bf6895b6040097f411dcbc24454735af4d585a74 100644 +index d64d32fad3063b3337ddde7d1350101c5a4d8519..9cd632751c7e15c79b65209d69c76e8ae1916deb 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java @@ -1041,6 +1041,27 @@ public final class Bukkit { @@ -37,7 +37,7 @@ index e94e8df18b5e2f10de6f4ece5bd55899d4972830..bf6895b6040097f411dcbc24454735af * Gets the player by the given UUID, regardless if they are offline or * online. diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java -index 63f5cbb8d292fbcefde32375c67ae8eed3f63d9c..3badf646e25b457435d3cba4b64c8a94423f91af 100644 +index b0b6220d16c7889b4c7d81925db864b740d39a37..3e77a6a294afa41e3dd0e5288b4a761d77ee18ff 100644 --- a/src/main/java/org/bukkit/Server.java +++ b/src/main/java/org/bukkit/Server.java @@ -879,6 +879,25 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi diff --git a/patches/api/0234-Add-ignore-discounts-API.patch b/patches/api/0233-Add-ignore-discounts-API.patch similarity index 100% rename from patches/api/0234-Add-ignore-discounts-API.patch rename to patches/api/0233-Add-ignore-discounts-API.patch diff --git a/patches/api/0235-Item-no-age-no-player-pickup.patch b/patches/api/0234-Item-no-age-no-player-pickup.patch similarity index 100% rename from patches/api/0235-Item-no-age-no-player-pickup.patch rename to patches/api/0234-Item-no-age-no-player-pickup.patch diff --git a/patches/api/0236-Beacon-API-custom-effect-ranges.patch b/patches/api/0235-Beacon-API-custom-effect-ranges.patch similarity index 100% rename from patches/api/0236-Beacon-API-custom-effect-ranges.patch rename to patches/api/0235-Beacon-API-custom-effect-ranges.patch diff --git a/patches/api/0237-Add-API-for-quit-reason.patch b/patches/api/0236-Add-API-for-quit-reason.patch similarity index 100% rename from patches/api/0237-Add-API-for-quit-reason.patch rename to patches/api/0236-Add-API-for-quit-reason.patch diff --git a/patches/api/0238-Add-Destroy-Speed-API.patch b/patches/api/0237-Add-Destroy-Speed-API.patch similarity index 100% rename from patches/api/0238-Add-Destroy-Speed-API.patch rename to patches/api/0237-Add-Destroy-Speed-API.patch diff --git a/patches/api/0239-Add-LivingEntity-clearActiveItem.patch b/patches/api/0238-Add-LivingEntity-clearActiveItem.patch similarity index 100% rename from patches/api/0239-Add-LivingEntity-clearActiveItem.patch rename to patches/api/0238-Add-LivingEntity-clearActiveItem.patch diff --git a/patches/api/0240-Add-PlayerItemCooldownEvent.patch b/patches/api/0239-Add-PlayerItemCooldownEvent.patch similarity index 100% rename from patches/api/0240-Add-PlayerItemCooldownEvent.patch rename to patches/api/0239-Add-PlayerItemCooldownEvent.patch diff --git a/patches/api/0241-More-lightning-API.patch b/patches/api/0240-More-lightning-API.patch similarity index 100% rename from patches/api/0241-More-lightning-API.patch rename to patches/api/0240-More-lightning-API.patch diff --git a/patches/api/0242-Add-PlayerShearBlockEvent.patch b/patches/api/0241-Add-PlayerShearBlockEvent.patch similarity index 100% rename from patches/api/0242-Add-PlayerShearBlockEvent.patch rename to patches/api/0241-Add-PlayerShearBlockEvent.patch diff --git a/patches/api/0243-Enable-multi-release-plugin-jars.patch b/patches/api/0242-Enable-multi-release-plugin-jars.patch similarity index 100% rename from patches/api/0243-Enable-multi-release-plugin-jars.patch rename to patches/api/0242-Enable-multi-release-plugin-jars.patch diff --git a/patches/api/0244-Player-Chunk-Load-Unload-Events.patch b/patches/api/0243-Player-Chunk-Load-Unload-Events.patch similarity index 100% rename from patches/api/0244-Player-Chunk-Load-Unload-Events.patch rename to patches/api/0243-Player-Chunk-Load-Unload-Events.patch diff --git a/patches/api/0245-Expose-LivingEntity-hurt-direction.patch b/patches/api/0244-Expose-LivingEntity-hurt-direction.patch similarity index 100% rename from patches/api/0245-Expose-LivingEntity-hurt-direction.patch rename to patches/api/0244-Expose-LivingEntity-hurt-direction.patch diff --git a/patches/api/0246-Add-OBSTRUCTED-reason-to-BedEnterResult.patch b/patches/api/0245-Add-OBSTRUCTED-reason-to-BedEnterResult.patch similarity index 100% rename from patches/api/0246-Add-OBSTRUCTED-reason-to-BedEnterResult.patch rename to patches/api/0245-Add-OBSTRUCTED-reason-to-BedEnterResult.patch diff --git a/patches/api/0247-Added-PlayerTradeEvent.patch b/patches/api/0246-Added-PlayerTradeEvent.patch similarity index 100% rename from patches/api/0247-Added-PlayerTradeEvent.patch rename to patches/api/0246-Added-PlayerTradeEvent.patch diff --git a/patches/api/0248-Add-TargetHitEvent-API.patch b/patches/api/0247-Add-TargetHitEvent-API.patch similarity index 100% rename from patches/api/0248-Add-TargetHitEvent-API.patch rename to patches/api/0247-Add-TargetHitEvent-API.patch diff --git a/patches/api/0249-Additional-Block-Material-API-s.patch b/patches/api/0248-Additional-Block-Material-API-s.patch similarity index 95% rename from patches/api/0249-Additional-Block-Material-API-s.patch rename to patches/api/0248-Additional-Block-Material-API-s.patch index 9187241cdf..6565323d0b 100644 --- a/patches/api/0249-Additional-Block-Material-API-s.patch +++ b/patches/api/0248-Additional-Block-Material-API-s.patch @@ -9,7 +9,7 @@ process to do this in the Bukkit API Adds API for buildable, replaceable, burnable too. diff --git a/src/main/java/org/bukkit/block/Block.java b/src/main/java/org/bukkit/block/Block.java -index 9583c2b91f7f50c9c377d3aac30001f86ad95461..9f5a68cb91ec10721031607c83621cc31db5d7b0 100644 +index cc570b31e817433d6e90990277cce283ee5d449e..492a59b0cc5ca3ea53688c7a734fb8c1a43c29b7 100644 --- a/src/main/java/org/bukkit/block/Block.java +++ b/src/main/java/org/bukkit/block/Block.java @@ -428,6 +428,42 @@ public interface Block extends Metadatable, net.kyori.adventure.translation.Tran diff --git a/patches/api/0250-Add-API-to-get-Material-from-Boats-and-Minecarts.patch b/patches/api/0249-Add-API-to-get-Material-from-Boats-and-Minecarts.patch similarity index 100% rename from patches/api/0250-Add-API-to-get-Material-from-Boats-and-Minecarts.patch rename to patches/api/0249-Add-API-to-get-Material-from-Boats-and-Minecarts.patch diff --git a/patches/api/0251-Add-PlayerFlowerPotManipulateEvent.patch b/patches/api/0250-Add-PlayerFlowerPotManipulateEvent.patch similarity index 100% rename from patches/api/0251-Add-PlayerFlowerPotManipulateEvent.patch rename to patches/api/0250-Add-PlayerFlowerPotManipulateEvent.patch diff --git a/patches/api/0252-Zombie-API-breaking-doors.patch b/patches/api/0251-Zombie-API-breaking-doors.patch similarity index 100% rename from patches/api/0252-Zombie-API-breaking-doors.patch rename to patches/api/0251-Zombie-API-breaking-doors.patch diff --git a/patches/api/0253-Add-EntityLoadCrossbowEvent.patch b/patches/api/0252-Add-EntityLoadCrossbowEvent.patch similarity index 100% rename from patches/api/0253-Add-EntityLoadCrossbowEvent.patch rename to patches/api/0252-Add-EntityLoadCrossbowEvent.patch diff --git a/patches/api/0254-Added-WorldGameRuleChangeEvent.patch b/patches/api/0253-Added-WorldGameRuleChangeEvent.patch similarity index 100% rename from patches/api/0254-Added-WorldGameRuleChangeEvent.patch rename to patches/api/0253-Added-WorldGameRuleChangeEvent.patch diff --git a/patches/api/0255-Added-ServerResourcesReloadedEvent.patch b/patches/api/0254-Added-ServerResourcesReloadedEvent.patch similarity index 100% rename from patches/api/0255-Added-ServerResourcesReloadedEvent.patch rename to patches/api/0254-Added-ServerResourcesReloadedEvent.patch diff --git a/patches/api/0256-Add-BlockFailedDispenseEvent.patch b/patches/api/0255-Add-BlockFailedDispenseEvent.patch similarity index 100% rename from patches/api/0256-Add-BlockFailedDispenseEvent.patch rename to patches/api/0255-Add-BlockFailedDispenseEvent.patch diff --git a/patches/api/0257-Added-PlayerLecternPageChangeEvent.patch b/patches/api/0256-Added-PlayerLecternPageChangeEvent.patch similarity index 100% rename from patches/api/0257-Added-PlayerLecternPageChangeEvent.patch rename to patches/api/0256-Added-PlayerLecternPageChangeEvent.patch diff --git a/patches/api/0258-Added-PlayerLoomPatternSelectEvent.patch b/patches/api/0257-Added-PlayerLoomPatternSelectEvent.patch similarity index 100% rename from patches/api/0258-Added-PlayerLoomPatternSelectEvent.patch rename to patches/api/0257-Added-PlayerLoomPatternSelectEvent.patch diff --git a/patches/api/0259-Better-AnnotationTest-printout.patch b/patches/api/0258-Better-AnnotationTest-printout.patch similarity index 100% rename from patches/api/0259-Better-AnnotationTest-printout.patch rename to patches/api/0258-Better-AnnotationTest-printout.patch diff --git a/patches/api/0260-Add-API-to-get-exact-interaction-point-in-PlayerInte.patch b/patches/api/0259-Add-API-to-get-exact-interaction-point-in-PlayerInte.patch similarity index 100% rename from patches/api/0260-Add-API-to-get-exact-interaction-point-in-PlayerInte.patch rename to patches/api/0259-Add-API-to-get-exact-interaction-point-in-PlayerInte.patch diff --git a/patches/api/0261-Add-sendOpLevel-API.patch b/patches/api/0260-Add-sendOpLevel-API.patch similarity index 87% rename from patches/api/0261-Add-sendOpLevel-API.patch rename to patches/api/0260-Add-sendOpLevel-API.patch index 1d0df9db69..8aa48cc335 100644 --- a/patches/api/0261-Add-sendOpLevel-API.patch +++ b/patches/api/0260-Add-sendOpLevel-API.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add sendOpLevel API diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index 046476fdfcd9ceecb6e0f314228c3f81ad46ec80..bb3c29cc5121232e93ec8d5a5c308b1f85071f0c 100644 +index 220838894bb24a5cab3f005c8a9f4ba7c438b576..6bac2d1828ed0397460753ecc5680730418b54dc 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java -@@ -2089,6 +2089,17 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM +@@ -2123,6 +2123,17 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM */ @Nullable Firework boostElytra(@NotNull ItemStack firework); diff --git a/patches/api/0262-Add-StructureLocateEvent.patch b/patches/api/0261-Add-StructureLocateEvent.patch similarity index 100% rename from patches/api/0262-Add-StructureLocateEvent.patch rename to patches/api/0261-Add-StructureLocateEvent.patch diff --git a/patches/api/0263-Return-chat-component-with-empty-text-instead-of-thr.patch b/patches/api/0262-Return-chat-component-with-empty-text-instead-of-thr.patch similarity index 100% rename from patches/api/0263-Return-chat-component-with-empty-text-instead-of-thr.patch rename to patches/api/0262-Return-chat-component-with-empty-text-instead-of-thr.patch diff --git a/patches/api/0264-Add-BlockPreDispenseEvent.patch b/patches/api/0263-Add-BlockPreDispenseEvent.patch similarity index 100% rename from patches/api/0264-Add-BlockPreDispenseEvent.patch rename to patches/api/0263-Add-BlockPreDispenseEvent.patch diff --git a/patches/api/0265-Added-Vanilla-Entity-Tags.patch b/patches/api/0264-Added-Vanilla-Entity-Tags.patch similarity index 100% rename from patches/api/0265-Added-Vanilla-Entity-Tags.patch rename to patches/api/0264-Added-Vanilla-Entity-Tags.patch diff --git a/patches/api/0266-added-Wither-API.patch b/patches/api/0265-added-Wither-API.patch similarity index 100% rename from patches/api/0266-added-Wither-API.patch rename to patches/api/0265-added-Wither-API.patch diff --git a/patches/api/0267-Added-PlayerChangeBeaconEffectEvent.patch b/patches/api/0266-Added-PlayerChangeBeaconEffectEvent.patch similarity index 100% rename from patches/api/0267-Added-PlayerChangeBeaconEffectEvent.patch rename to patches/api/0266-Added-PlayerChangeBeaconEffectEvent.patch diff --git a/patches/api/0268-Added-PlayerStonecutterRecipeSelectEvent.patch b/patches/api/0267-Added-PlayerStonecutterRecipeSelectEvent.patch similarity index 100% rename from patches/api/0268-Added-PlayerStonecutterRecipeSelectEvent.patch rename to patches/api/0267-Added-PlayerStonecutterRecipeSelectEvent.patch diff --git a/patches/api/0269-Add-dropLeash-variable-to-EntityUnleashEvent.patch b/patches/api/0268-Add-dropLeash-variable-to-EntityUnleashEvent.patch similarity index 100% rename from patches/api/0269-Add-dropLeash-variable-to-EntityUnleashEvent.patch rename to patches/api/0268-Add-dropLeash-variable-to-EntityUnleashEvent.patch diff --git a/patches/api/0270-EntityMoveEvent.patch b/patches/api/0269-EntityMoveEvent.patch similarity index 98% rename from patches/api/0270-EntityMoveEvent.patch rename to patches/api/0269-EntityMoveEvent.patch index 487e39befe..2855c3bdef 100644 --- a/patches/api/0270-EntityMoveEvent.patch +++ b/patches/api/0269-EntityMoveEvent.patch @@ -6,7 +6,7 @@ Subject: [PATCH] EntityMoveEvent diff --git a/src/main/java/io/papermc/paper/event/entity/EntityMoveEvent.java b/src/main/java/io/papermc/paper/event/entity/EntityMoveEvent.java new file mode 100644 -index 0000000000000000000000000000000000000000..9704bfa38cf535a247d7b661e5132ba82c836832 +index 0000000000000000000000000000000000000000..245d56ba7e8e37e3555b606f5e85fc663897f62b --- /dev/null +++ b/src/main/java/io/papermc/paper/event/entity/EntityMoveEvent.java @@ -0,0 +1,143 @@ diff --git a/patches/api/0271-add-DragonEggFormEvent.patch b/patches/api/0270-add-DragonEggFormEvent.patch similarity index 100% rename from patches/api/0271-add-DragonEggFormEvent.patch rename to patches/api/0270-add-DragonEggFormEvent.patch diff --git a/patches/api/0272-Allow-adding-items-to-BlockDropItemEvent.patch b/patches/api/0271-Allow-adding-items-to-BlockDropItemEvent.patch similarity index 100% rename from patches/api/0272-Allow-adding-items-to-BlockDropItemEvent.patch rename to patches/api/0271-Allow-adding-items-to-BlockDropItemEvent.patch diff --git a/patches/api/0273-Add-getMainThreadExecutor-to-BukkitScheduler.patch b/patches/api/0272-Add-getMainThreadExecutor-to-BukkitScheduler.patch similarity index 100% rename from patches/api/0273-Add-getMainThreadExecutor-to-BukkitScheduler.patch rename to patches/api/0272-Add-getMainThreadExecutor-to-BukkitScheduler.patch diff --git a/patches/api/0274-living-entity-allow-attribute-registration.patch b/patches/api/0273-living-entity-allow-attribute-registration.patch similarity index 100% rename from patches/api/0274-living-entity-allow-attribute-registration.patch rename to patches/api/0273-living-entity-allow-attribute-registration.patch diff --git a/patches/api/0275-Add-missing-effects.patch b/patches/api/0274-Add-missing-effects.patch similarity index 100% rename from patches/api/0275-Add-missing-effects.patch rename to patches/api/0274-Add-missing-effects.patch diff --git a/patches/api/0276-Expose-Tracked-Players.patch b/patches/api/0275-Expose-Tracked-Players.patch similarity index 86% rename from patches/api/0276-Expose-Tracked-Players.patch rename to patches/api/0275-Expose-Tracked-Players.patch index da3be8b8fd..1e279be371 100644 --- a/patches/api/0276-Expose-Tracked-Players.patch +++ b/patches/api/0275-Expose-Tracked-Players.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Expose Tracked Players diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index bb3c29cc5121232e93ec8d5a5c308b1f85071f0c..97ace842faa16d02d10181bc4ec3a382bc7e4e65 100644 +index 6bac2d1828ed0397460753ecc5680730418b54dc..782bd1657b487e38ee943857b3d3543ef294c8f3 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java @@ -1,6 +1,7 @@ @@ -16,7 +16,7 @@ index bb3c29cc5121232e93ec8d5a5c308b1f85071f0c..97ace842faa16d02d10181bc4ec3a382 import java.util.UUID; import com.destroystokyo.paper.ClientOption; // Paper import com.destroystokyo.paper.Title; // Paper -@@ -2102,6 +2103,14 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM +@@ -2136,6 +2137,14 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM void sendOpLevel(byte level); // Paper end diff --git a/patches/api/0277-Cache-the-result-of-Material-isBlock.patch b/patches/api/0276-Cache-the-result-of-Material-isBlock.patch similarity index 100% rename from patches/api/0277-Cache-the-result-of-Material-isBlock.patch rename to patches/api/0276-Cache-the-result-of-Material-isBlock.patch diff --git a/patches/api/0278-Add-worldborder-events.patch b/patches/api/0277-Add-worldborder-events.patch similarity index 100% rename from patches/api/0278-Add-worldborder-events.patch rename to patches/api/0277-Add-worldborder-events.patch diff --git a/patches/api/0279-added-PlayerNameEntityEvent.patch b/patches/api/0278-added-PlayerNameEntityEvent.patch similarity index 100% rename from patches/api/0279-added-PlayerNameEntityEvent.patch rename to patches/api/0278-added-PlayerNameEntityEvent.patch diff --git a/patches/api/0280-Add-recipe-to-cook-events.patch b/patches/api/0279-Add-recipe-to-cook-events.patch similarity index 100% rename from patches/api/0280-Add-recipe-to-cook-events.patch rename to patches/api/0279-Add-recipe-to-cook-events.patch diff --git a/patches/api/0281-Add-Block-isValidTool.patch b/patches/api/0280-Add-Block-isValidTool.patch similarity index 90% rename from patches/api/0281-Add-Block-isValidTool.patch rename to patches/api/0280-Add-Block-isValidTool.patch index 1bc49df4af..d3692b5603 100644 --- a/patches/api/0281-Add-Block-isValidTool.patch +++ b/patches/api/0280-Add-Block-isValidTool.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add Block#isValidTool diff --git a/src/main/java/org/bukkit/block/Block.java b/src/main/java/org/bukkit/block/Block.java -index 9f5a68cb91ec10721031607c83621cc31db5d7b0..c521f2bb830242b57c408ba3aa9e63f893df2f07 100644 +index 492a59b0cc5ca3ea53688c7a734fb8c1a43c29b7..ad8bce01ba459a036cd4ebbbe4fc974021924fe2 100644 --- a/src/main/java/org/bukkit/block/Block.java +++ b/src/main/java/org/bukkit/block/Block.java @@ -219,6 +219,15 @@ public interface Block extends Metadatable, net.kyori.adventure.translation.Tran diff --git a/patches/api/0282-Implement-Keyed-on-World.patch b/patches/api/0281-Implement-Keyed-on-World.patch similarity index 98% rename from patches/api/0282-Implement-Keyed-on-World.patch rename to patches/api/0281-Implement-Keyed-on-World.patch index 5aa55bf968..c72cf57187 100644 --- a/patches/api/0282-Implement-Keyed-on-World.patch +++ b/patches/api/0281-Implement-Keyed-on-World.patch @@ -50,7 +50,7 @@ index 3e77a6a294afa41e3dd0e5288b4a761d77ee18ff..14dc56c8e7702e63875f6d2f8aa804a1 * Gets the map from the given item ID. * diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java -index 61403e0d86ef3ab799dfcf490830e515f064bd73..00fc240c2da96d937ade789a1110190d6cd79254 100644 +index 35d1c5981c679cb3173b72fdfa0cf3af25cd20d6..71adcf2f44cfc75d8c86d41651ac7d3f17313bd5 100644 --- a/src/main/java/org/bukkit/World.java +++ b/src/main/java/org/bukkit/World.java @@ -41,7 +41,7 @@ import org.jetbrains.annotations.Nullable; diff --git a/patches/api/0283-fix-Inventory-getContents-null-annotations.patch b/patches/api/0282-fix-Inventory-getContents-null-annotations.patch similarity index 100% rename from patches/api/0283-fix-Inventory-getContents-null-annotations.patch rename to patches/api/0282-fix-Inventory-getContents-null-annotations.patch diff --git a/patches/api/0284-Item-Rarity-API.patch b/patches/api/0283-Item-Rarity-API.patch similarity index 100% rename from patches/api/0284-Item-Rarity-API.patch rename to patches/api/0283-Item-Rarity-API.patch diff --git a/patches/api/0285-Expose-protocol-version.patch b/patches/api/0284-Expose-protocol-version.patch similarity index 100% rename from patches/api/0285-Expose-protocol-version.patch rename to patches/api/0284-Expose-protocol-version.patch diff --git a/patches/api/0286-Allow-for-Component-suggestion-tooltips-in-AsyncTabC.patch b/patches/api/0285-Allow-for-Component-suggestion-tooltips-in-AsyncTabC.patch similarity index 100% rename from patches/api/0286-Allow-for-Component-suggestion-tooltips-in-AsyncTabC.patch rename to patches/api/0285-Allow-for-Component-suggestion-tooltips-in-AsyncTabC.patch diff --git a/patches/api/0287-add-isDeeplySleeping-to-HumanEntity.patch b/patches/api/0286-add-isDeeplySleeping-to-HumanEntity.patch similarity index 100% rename from patches/api/0287-add-isDeeplySleeping-to-HumanEntity.patch rename to patches/api/0286-add-isDeeplySleeping-to-HumanEntity.patch diff --git a/patches/api/0288-add-consumeFuel-to-FurnaceBurnEvent.patch b/patches/api/0287-add-consumeFuel-to-FurnaceBurnEvent.patch similarity index 100% rename from patches/api/0288-add-consumeFuel-to-FurnaceBurnEvent.patch rename to patches/api/0287-add-consumeFuel-to-FurnaceBurnEvent.patch diff --git a/patches/api/0289-add-get-set-drop-chance-to-EntityEquipment.patch b/patches/api/0288-add-get-set-drop-chance-to-EntityEquipment.patch similarity index 100% rename from patches/api/0289-add-get-set-drop-chance-to-EntityEquipment.patch rename to patches/api/0288-add-get-set-drop-chance-to-EntityEquipment.patch diff --git a/patches/api/0290-Added-PlayerDeepSleepEvent.patch b/patches/api/0289-Added-PlayerDeepSleepEvent.patch similarity index 100% rename from patches/api/0290-Added-PlayerDeepSleepEvent.patch rename to patches/api/0289-Added-PlayerDeepSleepEvent.patch diff --git a/patches/api/0291-More-World-API.patch b/patches/api/0290-More-World-API.patch similarity index 97% rename from patches/api/0291-More-World-API.patch rename to patches/api/0290-More-World-API.patch index 41047ddf9b..a3ef5f84ab 100644 --- a/patches/api/0291-More-World-API.patch +++ b/patches/api/0290-More-World-API.patch @@ -5,7 +5,7 @@ Subject: [PATCH] More World API diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java -index 00fc240c2da96d937ade789a1110190d6cd79254..59f11b9f36a7fdf7240daf93ea3f2af99d7ec255 100644 +index 71adcf2f44cfc75d8c86d41651ac7d3f17313bd5..ae7c7a7c879d56361c7608c00fe20ce9b87e35c1 100644 --- a/src/main/java/org/bukkit/World.java +++ b/src/main/java/org/bukkit/World.java @@ -3434,6 +3434,114 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient diff --git a/patches/api/0292-Added-PlayerBedFailEnterEvent.patch b/patches/api/0291-Added-PlayerBedFailEnterEvent.patch similarity index 100% rename from patches/api/0292-Added-PlayerBedFailEnterEvent.patch rename to patches/api/0291-Added-PlayerBedFailEnterEvent.patch diff --git a/patches/api/0293-Introduce-beacon-activation-deactivation-events.patch b/patches/api/0292-Introduce-beacon-activation-deactivation-events.patch similarity index 100% rename from patches/api/0293-Introduce-beacon-activation-deactivation-events.patch rename to patches/api/0292-Introduce-beacon-activation-deactivation-events.patch diff --git a/patches/api/0294-PlayerMoveEvent-Improvements.patch b/patches/api/0293-PlayerMoveEvent-Improvements.patch similarity index 100% rename from patches/api/0294-PlayerMoveEvent-Improvements.patch rename to patches/api/0293-PlayerMoveEvent-Improvements.patch diff --git a/patches/api/0295-add-RespawnFlags-to-PlayerRespawnEvent.patch b/patches/api/0294-add-RespawnFlags-to-PlayerRespawnEvent.patch similarity index 100% rename from patches/api/0295-add-RespawnFlags-to-PlayerRespawnEvent.patch rename to patches/api/0294-add-RespawnFlags-to-PlayerRespawnEvent.patch diff --git a/patches/api/0296-Add-more-WanderingTrader-API.patch b/patches/api/0295-Add-more-WanderingTrader-API.patch similarity index 100% rename from patches/api/0296-Add-more-WanderingTrader-API.patch rename to patches/api/0295-Add-more-WanderingTrader-API.patch diff --git a/patches/api/0297-Add-EntityBlockStorage-clearEntities.patch b/patches/api/0296-Add-EntityBlockStorage-clearEntities.patch similarity index 100% rename from patches/api/0297-Add-EntityBlockStorage-clearEntities.patch rename to patches/api/0296-Add-EntityBlockStorage-clearEntities.patch diff --git a/patches/api/0298-Add-Adventure-message-to-PlayerAdvancementDoneEvent.patch b/patches/api/0297-Add-Adventure-message-to-PlayerAdvancementDoneEvent.patch similarity index 100% rename from patches/api/0298-Add-Adventure-message-to-PlayerAdvancementDoneEvent.patch rename to patches/api/0297-Add-Adventure-message-to-PlayerAdvancementDoneEvent.patch diff --git a/patches/api/0299-Add-raw-address-to-AsyncPlayerPreLoginEvent.patch b/patches/api/0298-Add-raw-address-to-AsyncPlayerPreLoginEvent.patch similarity index 100% rename from patches/api/0299-Add-raw-address-to-AsyncPlayerPreLoginEvent.patch rename to patches/api/0298-Add-raw-address-to-AsyncPlayerPreLoginEvent.patch diff --git a/patches/api/0300-Inventory-close.patch b/patches/api/0299-Inventory-close.patch similarity index 100% rename from patches/api/0300-Inventory-close.patch rename to patches/api/0299-Inventory-close.patch diff --git a/patches/api/0301-Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch b/patches/api/0300-Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch similarity index 100% rename from patches/api/0301-Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch rename to patches/api/0300-Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch diff --git a/patches/api/0302-Add-basic-Datapack-API.patch b/patches/api/0301-Add-basic-Datapack-API.patch similarity index 100% rename from patches/api/0302-Add-basic-Datapack-API.patch rename to patches/api/0301-Add-basic-Datapack-API.patch diff --git a/patches/api/0303-additions-to-PlayerGameModeChangeEvent.patch b/patches/api/0302-additions-to-PlayerGameModeChangeEvent.patch similarity index 100% rename from patches/api/0303-additions-to-PlayerGameModeChangeEvent.patch rename to patches/api/0302-additions-to-PlayerGameModeChangeEvent.patch diff --git a/patches/api/0304-ItemStack-repair-check-API.patch b/patches/api/0303-ItemStack-repair-check-API.patch similarity index 100% rename from patches/api/0304-ItemStack-repair-check-API.patch rename to patches/api/0303-ItemStack-repair-check-API.patch diff --git a/patches/api/0305-More-Enchantment-API.patch b/patches/api/0304-More-Enchantment-API.patch similarity index 100% rename from patches/api/0305-More-Enchantment-API.patch rename to patches/api/0304-More-Enchantment-API.patch diff --git a/patches/api/0306-Add-command-line-option-to-load-extra-plugin-jars-no.patch b/patches/api/0305-Add-command-line-option-to-load-extra-plugin-jars-no.patch similarity index 100% rename from patches/api/0306-Add-command-line-option-to-load-extra-plugin-jars-no.patch rename to patches/api/0305-Add-command-line-option-to-load-extra-plugin-jars-no.patch diff --git a/patches/api/0307-List-all-missing-hard-depends-not-just-first.patch b/patches/api/0306-List-all-missing-hard-depends-not-just-first.patch similarity index 100% rename from patches/api/0307-List-all-missing-hard-depends-not-just-first.patch rename to patches/api/0306-List-all-missing-hard-depends-not-just-first.patch diff --git a/patches/api/0308-Add-Mob-lookAt-API.patch b/patches/api/0307-Add-Mob-lookAt-API.patch similarity index 100% rename from patches/api/0308-Add-Mob-lookAt-API.patch rename to patches/api/0307-Add-Mob-lookAt-API.patch diff --git a/patches/api/0309-ItemStack-editMeta.patch b/patches/api/0308-ItemStack-editMeta.patch similarity index 94% rename from patches/api/0309-ItemStack-editMeta.patch rename to patches/api/0308-ItemStack-editMeta.patch index 6b66f25698..0fd9764156 100644 --- a/patches/api/0309-ItemStack-editMeta.patch +++ b/patches/api/0308-ItemStack-editMeta.patch @@ -5,7 +5,7 @@ Subject: [PATCH] ItemStack#editMeta diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java -index b4d55e0d5e605cb6980b555acd42d16e1e65c196..f3fa8f7f021464aa9d463c14550487cc7d48c873 100644 +index 86bd9f14de5c1ff3d797955be1af56e5efcac884..59a026d80b0a0a4890becf98efdbe5325b2c622a 100644 --- a/src/main/java/org/bukkit/inventory/ItemStack.java +++ b/src/main/java/org/bukkit/inventory/ItemStack.java @@ -543,6 +543,31 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, net.kyor diff --git a/patches/api/0310-Add-EntityInsideBlockEvent.patch b/patches/api/0309-Add-EntityInsideBlockEvent.patch similarity index 100% rename from patches/api/0310-Add-EntityInsideBlockEvent.patch rename to patches/api/0309-Add-EntityInsideBlockEvent.patch diff --git a/patches/api/0311-Attributes-API-for-item-defaults.patch b/patches/api/0310-Attributes-API-for-item-defaults.patch similarity index 100% rename from patches/api/0311-Attributes-API-for-item-defaults.patch rename to patches/api/0310-Attributes-API-for-item-defaults.patch diff --git a/patches/api/0312-Add-cause-to-Weather-ThunderChangeEvents.patch b/patches/api/0311-Add-cause-to-Weather-ThunderChangeEvents.patch similarity index 100% rename from patches/api/0312-Add-cause-to-Weather-ThunderChangeEvents.patch rename to patches/api/0311-Add-cause-to-Weather-ThunderChangeEvents.patch diff --git a/patches/api/0313-More-Lidded-Block-API.patch b/patches/api/0312-More-Lidded-Block-API.patch similarity index 100% rename from patches/api/0313-More-Lidded-Block-API.patch rename to patches/api/0312-More-Lidded-Block-API.patch diff --git a/patches/api/0314-Add-PlayerKickEvent-causes.patch b/patches/api/0313-Add-PlayerKickEvent-causes.patch similarity index 97% rename from patches/api/0314-Add-PlayerKickEvent-causes.patch rename to patches/api/0313-Add-PlayerKickEvent-causes.patch index 4bcd82ded1..6246a0cf13 100644 --- a/patches/api/0314-Add-PlayerKickEvent-causes.patch +++ b/patches/api/0313-Add-PlayerKickEvent-causes.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add PlayerKickEvent causes diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index 97ace842faa16d02d10181bc4ec3a382bc7e4e65..3ec1be36b90dbedb8631135555da4b69110e4791 100644 +index 782bd1657b487e38ee943857b3d3543ef294c8f3..afa245018192a63e5db8bc568ddea2790bf5b8a2 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java @@ -240,6 +240,14 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM diff --git a/patches/api/0315-Add-PufferFishStateChangeEvent.patch b/patches/api/0314-Add-PufferFishStateChangeEvent.patch similarity index 100% rename from patches/api/0315-Add-PufferFishStateChangeEvent.patch rename to patches/api/0314-Add-PufferFishStateChangeEvent.patch diff --git a/patches/api/0316-Add-BellRevealRaiderEvent.patch b/patches/api/0315-Add-BellRevealRaiderEvent.patch similarity index 100% rename from patches/api/0316-Add-BellRevealRaiderEvent.patch rename to patches/api/0315-Add-BellRevealRaiderEvent.patch diff --git a/patches/api/0317-Add-ElderGuardianAppearanceEvent.patch b/patches/api/0316-Add-ElderGuardianAppearanceEvent.patch similarity index 100% rename from patches/api/0317-Add-ElderGuardianAppearanceEvent.patch rename to patches/api/0316-Add-ElderGuardianAppearanceEvent.patch diff --git a/patches/api/0318-Add-more-line-of-sight-methods.patch b/patches/api/0317-Add-more-line-of-sight-methods.patch similarity index 95% rename from patches/api/0318-Add-more-line-of-sight-methods.patch rename to patches/api/0317-Add-more-line-of-sight-methods.patch index 006cdd2d06..b82141bfd9 100644 --- a/patches/api/0318-Add-more-line-of-sight-methods.patch +++ b/patches/api/0317-Add-more-line-of-sight-methods.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add more line of sight methods diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java -index 4834a48100922c6452448d4ef2a75377c7a5afa2..3d06947726ee7072ab8b62722e690f189f3935ab 100644 +index ae7c7a7c879d56361c7608c00fe20ce9b87e35c1..dc497606ebf9903c2d32276ecddda3231457346a 100644 --- a/src/main/java/org/bukkit/World.java +++ b/src/main/java/org/bukkit/World.java @@ -74,6 +74,14 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient diff --git a/patches/api/0319-Add-more-LimitedRegion-API.patch b/patches/api/0318-Add-more-LimitedRegion-API.patch similarity index 100% rename from patches/api/0319-Add-more-LimitedRegion-API.patch rename to patches/api/0318-Add-more-LimitedRegion-API.patch diff --git a/patches/api/0320-Missing-Entity-Behavior-API.patch b/patches/api/0319-Missing-Entity-Behavior-API.patch similarity index 100% rename from patches/api/0320-Missing-Entity-Behavior-API.patch rename to patches/api/0319-Missing-Entity-Behavior-API.patch diff --git a/patches/api/0321-Add-Git-information-to-version-command-on-startup.patch b/patches/api/0320-Add-Git-information-to-version-command-on-startup.patch similarity index 98% rename from patches/api/0321-Add-Git-information-to-version-command-on-startup.patch rename to patches/api/0320-Add-Git-information-to-version-command-on-startup.patch index 47bc1edd81..306558165d 100644 --- a/patches/api/0321-Add-Git-information-to-version-command-on-startup.patch +++ b/patches/api/0320-Add-Git-information-to-version-command-on-startup.patch @@ -104,7 +104,7 @@ index 0000000000000000000000000000000000000000..02ad04c41b9c3ec68b1dcdc22c538340 + } +} diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java -index 071a0013ac4bad6483c1400e8eb7b842fa1b7496..66580ade2cd8b8795b2d79f7958460162c3a17db 100644 +index 76dc042f5fbac39065a97d270666f905ddc59a1c..d36e4bc3c3713407704b865574cba28662f17315 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java @@ -50,6 +50,7 @@ import org.bukkit.util.CachedServerIcon; diff --git a/patches/api/0322-Adds-PlayerArmSwingEvent.patch b/patches/api/0321-Adds-PlayerArmSwingEvent.patch similarity index 100% rename from patches/api/0322-Adds-PlayerArmSwingEvent.patch rename to patches/api/0321-Adds-PlayerArmSwingEvent.patch diff --git a/patches/api/0323-Add-PlayerSignCommandPreprocessEvent.patch b/patches/api/0322-Add-PlayerSignCommandPreprocessEvent.patch similarity index 100% rename from patches/api/0323-Add-PlayerSignCommandPreprocessEvent.patch rename to patches/api/0322-Add-PlayerSignCommandPreprocessEvent.patch diff --git a/patches/api/0324-fix-empty-array-elements-in-command-arguments.patch b/patches/api/0323-fix-empty-array-elements-in-command-arguments.patch similarity index 100% rename from patches/api/0324-fix-empty-array-elements-in-command-arguments.patch rename to patches/api/0323-fix-empty-array-elements-in-command-arguments.patch diff --git a/patches/api/0325-Stinger-API.patch b/patches/api/0324-Stinger-API.patch similarity index 100% rename from patches/api/0325-Stinger-API.patch rename to patches/api/0324-Stinger-API.patch diff --git a/patches/api/0326-Rewrite-LogEvents-to-contain-the-source-jars-in-stac.patch b/patches/api/0325-Rewrite-LogEvents-to-contain-the-source-jars-in-stac.patch similarity index 100% rename from patches/api/0326-Rewrite-LogEvents-to-contain-the-source-jars-in-stac.patch rename to patches/api/0325-Rewrite-LogEvents-to-contain-the-source-jars-in-stac.patch diff --git a/patches/api/0327-Add-PlayerSetSpawnEvent.patch b/patches/api/0326-Add-PlayerSetSpawnEvent.patch similarity index 100% rename from patches/api/0327-Add-PlayerSetSpawnEvent.patch rename to patches/api/0326-Add-PlayerSetSpawnEvent.patch diff --git a/patches/api/0328-Added-EntityDamageItemEvent.patch b/patches/api/0327-Added-EntityDamageItemEvent.patch similarity index 100% rename from patches/api/0328-Added-EntityDamageItemEvent.patch rename to patches/api/0327-Added-EntityDamageItemEvent.patch diff --git a/patches/api/0329-Make-EntityUnleashEvent-cancellable.patch b/patches/api/0328-Make-EntityUnleashEvent-cancellable.patch similarity index 100% rename from patches/api/0329-Make-EntityUnleashEvent-cancellable.patch rename to patches/api/0328-Make-EntityUnleashEvent-cancellable.patch diff --git a/patches/api/0330-Change-EnderEye-target-without-changing-other-things.patch b/patches/api/0329-Change-EnderEye-target-without-changing-other-things.patch similarity index 100% rename from patches/api/0330-Change-EnderEye-target-without-changing-other-things.patch rename to patches/api/0329-Change-EnderEye-target-without-changing-other-things.patch diff --git a/patches/api/0331-Add-BlockBreakBlockEvent.patch b/patches/api/0330-Add-BlockBreakBlockEvent.patch similarity index 100% rename from patches/api/0331-Add-BlockBreakBlockEvent.patch rename to patches/api/0330-Add-BlockBreakBlockEvent.patch diff --git a/patches/api/0332-Add-helpers-for-left-right-hand-to-Action.patch b/patches/api/0331-Add-helpers-for-left-right-hand-to-Action.patch similarity index 100% rename from patches/api/0332-Add-helpers-for-left-right-hand-to-Action.patch rename to patches/api/0331-Add-helpers-for-left-right-hand-to-Action.patch diff --git a/patches/server/0004-Paper-config-files.patch b/patches/server/0004-Paper-config-files.patch index 5c58cd565d..d9b3b560d8 100644 --- a/patches/server/0004-Paper-config-files.patch +++ b/patches/server/0004-Paper-config-files.patch @@ -298,7 +298,7 @@ index 0000000000000000000000000000000000000000..bee2fa2bfbb61209381f24ed6508d3d1 +} diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java new file mode 100644 -index 0000000000000000000000000000000000000000..da4e7ee0914cd23679645ced0060541de69c64ee +index 0000000000000000000000000000000000000000..a632a796a3e5f2b361a521d70581b83a6839fc73 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -0,0 +1,185 @@ @@ -364,8 +364,8 @@ index 0000000000000000000000000000000000000000..da4e7ee0914cd23679645ced0060541d + commands = new HashMap(); + commands.put("paper", new PaperCommand("paper")); + -+ version = getInt("config-version", 21); -+ set("config-version", 21); ++ version = getInt("config-version", 22); ++ set("config-version", 22); + readConfig(PaperConfig.class, null); + } + diff --git a/patches/server/0005-MC-Dev-fixes.patch b/patches/server/0005-MC-Dev-fixes.patch index f4f48505b0..b4af5a0a54 100644 --- a/patches/server/0005-MC-Dev-fixes.patch +++ b/patches/server/0005-MC-Dev-fixes.patch @@ -221,6 +221,19 @@ index a2bab5980a8fd6d98c20aa601ba0ce97044ce996..d7cc7e12d265a47564c3f1d4af27a3ec return i == 0; }); } finally { +diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java +index 75c1c4671fedb425dea20dc4fb0c6cb2304dee83..5a86bc6f552913e2978c61233148db22e3a240f1 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java ++++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java +@@ -30,7 +30,7 @@ public class PoiSection { + private boolean isValid; + + public static Codec codec(Runnable updateListener) { +- return RecordCodecBuilder.create((instance) -> { ++ return RecordCodecBuilder.create((instance) -> { // Paper - decompile fix + return instance.group(RecordCodecBuilder.point(updateListener), Codec.BOOL.optionalFieldOf("Valid", Boolean.valueOf(false)).forGetter((poiSet) -> { + return poiSet.isValid; + }), PoiRecord.codec(updateListener).listOf().fieldOf("Records").forGetter((poiSet) -> { diff --git a/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java b/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java index 38dd114fbfa07a18987cd774ae05060d723247e4..057b92a2948543644618c63abd3f61d1120db4dd 100644 --- a/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java diff --git a/patches/server/0006-MC-Utils.patch b/patches/server/0006-MC-Utils.patch index 9cc9b4c714..ef645b5932 100644 --- a/patches/server/0006-MC-Utils.patch +++ b/patches/server/0006-MC-Utils.patch @@ -856,251 +856,15 @@ index 0000000000000000000000000000000000000000..4a21ec2397f57c7c2ac3659f7de96cda + return this.map.values().iterator(); + } +} -diff --git a/src/main/java/com/destroystokyo/paper/util/math/IntegerUtil.java b/src/main/java/com/destroystokyo/paper/util/math/IntegerUtil.java -new file mode 100644 -index 0000000000000000000000000000000000000000..c3b936f54b3fff418c265639ef223292ccc89356 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/util/math/IntegerUtil.java -@@ -0,0 +1,230 @@ -+package com.destroystokyo.paper.util.math; -+ -+/** -+ * @author Spottedleaf -+ */ -+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 long 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; -+ } -+ -+ // copied from hacker's delight (signed division magic value) -+ // http://www.hackersdelight.org/hdcodetxt/magic.c.txt -+ public static long getDivisorNumbers(final int d) { -+ final int ad = IntegerUtil.branchlessAbs(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 - t%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/com/destroystokyo/paper/util/misc/AreaMap.java b/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java new file mode 100644 -index 0000000000000000000000000000000000000000..c601f04b9c6dff76606763ea6f4a9a89b7e83203 +index 0000000000000000000000000000000000000000..c89f6986eda5a132a948732ea1b6923370685317 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java @@ -0,0 +1,453 @@ +package com.destroystokyo.paper.util.misc; + -+import com.destroystokyo.paper.util.math.IntegerUtil; ++import io.papermc.paper.util.IntegerUtil; +import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; @@ -1553,13 +1317,13 @@ index 0000000000000000000000000000000000000000..c601f04b9c6dff76606763ea6f4a9a89 +} diff --git a/src/main/java/com/destroystokyo/paper/util/misc/DistanceTrackingAreaMap.java b/src/main/java/com/destroystokyo/paper/util/misc/DistanceTrackingAreaMap.java new file mode 100644 -index 0000000000000000000000000000000000000000..9b8cb361767fbcf5f592db32a12186f0bd6373bd +index 0000000000000000000000000000000000000000..eacec5074ab1237986635c6cb19a4d34a89d4d3f --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/util/misc/DistanceTrackingAreaMap.java @@ -0,0 +1,175 @@ +package com.destroystokyo.paper.util.misc; + -+import com.destroystokyo.paper.util.math.IntegerUtil; ++import io.papermc.paper.util.IntegerUtil; +import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; +import net.minecraft.server.MCUtil; +import net.minecraft.world.level.ChunkPos; @@ -2257,6 +2021,2394 @@ index 0000000000000000000000000000000000000000..9df0006c1a283f77c4d01d9fce9062fc + return (other.backingSet & this.backingSet) != 0; + } +} +diff --git a/src/main/java/io/papermc/paper/chunk/SingleThreadChunkRegionManager.java b/src/main/java/io/papermc/paper/chunk/SingleThreadChunkRegionManager.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0b71cf7a462d25c3e4f910e09a37270ce9bc7776 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/chunk/SingleThreadChunkRegionManager.java +@@ -0,0 +1,477 @@ ++package io.papermc.paper.chunk; ++ ++import io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet; ++import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; ++import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet; ++import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; ++import net.minecraft.server.MCUtil; ++import net.minecraft.server.level.ServerLevel; ++import net.minecraft.world.level.ChunkPos; ++import java.util.ArrayList; ++import java.util.Arrays; ++import java.util.Iterator; ++import java.util.List; ++import java.util.function.Supplier; ++ ++public final class SingleThreadChunkRegionManager { ++ ++ protected final int regionSectionMergeRadius; ++ protected final int regionSectionChunkSize; ++ public final int regionChunkShift; // log2(REGION_CHUNK_SIZE) ++ ++ public final ServerLevel world; ++ public final String name; ++ ++ protected final Long2ObjectOpenHashMap regionsBySection = new Long2ObjectOpenHashMap<>(); ++ protected final ReferenceLinkedOpenHashSet needsRecalculation = new ReferenceLinkedOpenHashSet<>(); ++ protected final int minSectionRecalcCount; ++ protected final double maxDeadRegionPercent; ++ protected final Supplier regionDataSupplier; ++ protected final Supplier regionSectionDataSupplier; ++ ++ public SingleThreadChunkRegionManager(final ServerLevel world, final int minSectionRecalcCount, ++ final double maxDeadRegionPercent, final int sectionMergeRadius, ++ final int regionSectionChunkShift, ++ final String name, final Supplier regionDataSupplier, ++ final Supplier regionSectionDataSupplier) { ++ this.regionSectionMergeRadius = sectionMergeRadius; ++ this.regionSectionChunkSize = 1 << regionSectionChunkShift; ++ this.regionChunkShift = regionSectionChunkShift; ++ this.world = world; ++ this.name = name; ++ this.minSectionRecalcCount = Math.max(2, minSectionRecalcCount); ++ this.maxDeadRegionPercent = maxDeadRegionPercent; ++ this.regionDataSupplier = regionDataSupplier; ++ this.regionSectionDataSupplier = regionSectionDataSupplier; ++ } ++ ++ // tested via https://gist.github.com/Spottedleaf/aa7ade3451c37b4cac061fc77074db2f ++ ++ /* ++ protected void check() { ++ ReferenceOpenHashSet> checked = new ReferenceOpenHashSet<>(); ++ ++ for (RegionSection section : this.regionsBySection.values()) { ++ if (!checked.add(section.region)) { ++ section.region.check(); ++ } ++ } ++ for (Region region : this.needsRecalculation) { ++ region.check(); ++ } ++ } ++ */ ++ ++ protected void addToRecalcQueue(final Region region) { ++ this.needsRecalculation.add(region); ++ } ++ ++ protected void removeFromRecalcQueue(final Region region) { ++ this.needsRecalculation.remove(region); ++ } ++ ++ public RegionSection getRegionSection(final int chunkX, final int chunkZ) { ++ return this.regionsBySection.get(MCUtil.getCoordinateKey(chunkX >> this.regionChunkShift, chunkZ >> this.regionChunkShift)); ++ } ++ ++ public Region getRegion(final int chunkX, final int chunkZ) { ++ final RegionSection section = this.regionsBySection.get(MCUtil.getCoordinateKey(chunkX >> regionChunkShift, chunkZ >> regionChunkShift)); ++ return section != null ? section.region : null; ++ } ++ ++ private final List toMerge = new ArrayList<>(); ++ ++ protected RegionSection getOrCreateAndMergeSection(final int sectionX, final int sectionZ, final RegionSection force) { ++ final long sectionKey = MCUtil.getCoordinateKey(sectionX, sectionZ); ++ ++ if (force == null) { ++ RegionSection region = this.regionsBySection.get(sectionKey); ++ if (region != null) { ++ return region; ++ } ++ } ++ ++ int mergeCandidateSectionSize = -1; ++ Region mergeIntoCandidate = null; ++ ++ // find optimal candidate to merge into ++ ++ final int minX = sectionX - this.regionSectionMergeRadius; ++ final int maxX = sectionX + this.regionSectionMergeRadius; ++ final int minZ = sectionZ - this.regionSectionMergeRadius; ++ final int maxZ = sectionZ + this.regionSectionMergeRadius; ++ for (int currX = minX; currX <= maxX; ++currX) { ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ final RegionSection section = this.regionsBySection.get(MCUtil.getCoordinateKey(currX, currZ)); ++ if (section == null) { ++ continue; ++ } ++ final Region region = section.region; ++ if (region.dead) { ++ throw new IllegalStateException("Dead region should not be in live region manager state: " + region); ++ } ++ final int sections = region.sections.size(); ++ ++ if (sections > mergeCandidateSectionSize) { ++ mergeCandidateSectionSize = sections; ++ mergeIntoCandidate = region; ++ } ++ this.toMerge.add(region); ++ } ++ } ++ ++ // merge ++ if (mergeIntoCandidate != null) { ++ for (int i = 0; i < this.toMerge.size(); ++i) { ++ final Region region = this.toMerge.get(i); ++ if (region.dead || mergeIntoCandidate == region) { ++ continue; ++ } ++ region.mergeInto(mergeIntoCandidate); ++ } ++ this.toMerge.clear(); ++ } else { ++ mergeIntoCandidate = new Region(this); ++ } ++ ++ final RegionSection section; ++ if (force == null) { ++ this.regionsBySection.put(sectionKey, section = new RegionSection(sectionKey, this)); ++ } else { ++ final RegionSection existing = this.regionsBySection.putIfAbsent(sectionKey, force); ++ if (existing != null) { ++ throw new IllegalStateException("Attempting to override section '" + existing.toStringWithRegion() + ++ ", with " + force.toStringWithRegion()); ++ } ++ ++ section = force; ++ } ++ ++ mergeIntoCandidate.addRegionSection(section); ++ //mergeIntoCandidate.check(); ++ //this.check(); ++ ++ return section; ++ } ++ ++ public void addChunk(final int chunkX, final int chunkZ) { ++ this.getOrCreateAndMergeSection(chunkX >> this.regionChunkShift, chunkZ >> this.regionChunkShift, null).addChunk(chunkX, chunkZ); ++ } ++ ++ public void removeChunk(final int chunkX, final int chunkZ) { ++ final RegionSection section = this.regionsBySection.get( ++ MCUtil.getCoordinateKey(chunkX >> this.regionChunkShift, chunkZ >> this.regionChunkShift) ++ ); ++ if (section != null) { ++ section.removeChunk(chunkX, chunkZ); ++ } else { ++ throw new IllegalStateException("Cannot remove chunk at (" + chunkX + "," + chunkZ + ") from region state, section does not exist"); ++ } ++ } ++ ++ public void recalculateRegions() { ++ for (int i = 0, len = this.needsRecalculation.size(); i < len; ++i) { ++ final Region region = this.needsRecalculation.removeFirst(); ++ ++ this.recalculateRegion(region); ++ //this.check(); ++ } ++ } ++ ++ protected void recalculateRegion(final Region region) { ++ region.markedForRecalc = false; ++ //region.check(); ++ // clear unused regions ++ for (final Iterator iterator = region.deadSections.iterator(); iterator.hasNext();) { ++ final RegionSection deadSection = iterator.next(); ++ ++ if (deadSection.hasChunks()) { ++ throw new IllegalStateException("Dead section '" + deadSection.toStringWithRegion() + "' is marked dead but has chunks!"); ++ } ++ if (!region.removeRegionSection(deadSection)) { ++ throw new IllegalStateException("Region " + region + " has inconsistent state, it should contain section " + deadSection); ++ } ++ if (!this.regionsBySection.remove(deadSection.regionCoordinate, deadSection)) { ++ throw new IllegalStateException("Cannot remove dead section '" + ++ deadSection.toStringWithRegion() + "' from section state! State at section coordinate: " + ++ this.regionsBySection.get(deadSection.regionCoordinate)); ++ } ++ } ++ region.deadSections.clear(); ++ ++ // implicitly cover cases where size == 0 ++ if (region.sections.size() < this.minSectionRecalcCount) { ++ //region.check(); ++ return; ++ } ++ ++ // run a test to see if we actually need to recalculate ++ // TODO ++ ++ // destroy and rebuild the region ++ region.dead = true; ++ ++ // destroy region state ++ for (final Iterator iterator = region.sections.unsafeIterator(IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); iterator.hasNext();) { ++ final RegionSection aliveSection = iterator.next(); ++ if (!aliveSection.hasChunks()) { ++ throw new IllegalStateException("Alive section '" + aliveSection.toStringWithRegion() + "' has no chunks!"); ++ } ++ if (!this.regionsBySection.remove(aliveSection.regionCoordinate, aliveSection)) { ++ throw new IllegalStateException("Cannot remove alive section '" + ++ aliveSection.toStringWithRegion() + "' from section state! State at section coordinate: " + ++ this.regionsBySection.get(aliveSection.regionCoordinate)); ++ } ++ } ++ ++ // rebuild regions ++ for (final Iterator iterator = region.sections.unsafeIterator(IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); iterator.hasNext();) { ++ final RegionSection aliveSection = iterator.next(); ++ this.getOrCreateAndMergeSection(aliveSection.getSectionX(), aliveSection.getSectionZ(), aliveSection); ++ } ++ } ++ ++ public static final class Region { ++ protected final IteratorSafeOrderedReferenceSet sections = new IteratorSafeOrderedReferenceSet<>(); ++ protected final ReferenceOpenHashSet deadSections = new ReferenceOpenHashSet<>(16, 0.7f); ++ protected boolean dead; ++ protected boolean markedForRecalc; ++ ++ public final SingleThreadChunkRegionManager regionManager; ++ public final RegionData regionData; ++ ++ protected Region(final SingleThreadChunkRegionManager regionManager) { ++ this.regionManager = regionManager; ++ this.regionData = regionManager.regionDataSupplier.get(); ++ } ++ ++ public IteratorSafeOrderedReferenceSet.Iterator getSections() { ++ return this.sections.iterator(IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); ++ } ++ ++ protected final double getDeadSectionPercent() { ++ return (double)this.deadSections.size() / (double)this.sections.size(); ++ } ++ ++ /* ++ protected void check() { ++ if (this.dead) { ++ throw new IllegalStateException("Dead region!"); ++ } ++ for (final Iterator> iterator = this.sections.unsafeIterator(IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); iterator.hasNext();) { ++ final RegionSection section = iterator.next(); ++ if (section.region != this) { ++ throw new IllegalStateException("Region section must point to us!"); ++ } ++ if (this.regionManager.regionsBySection.get(section.regionCoordinate) != section) { ++ throw new IllegalStateException("Region section must match the regionmanager state!"); ++ } ++ } ++ } ++ */ ++ ++ // note: it is not true that the region at this point is not in any region. use the region field on the section ++ // to see if it is currently in another region. ++ protected final boolean addRegionSection(final RegionSection section) { ++ if (!this.sections.add(section)) { ++ return false; ++ } ++ ++ section.sectionData.addToRegion(section, section.region, this); ++ ++ section.region = this; ++ return true; ++ } ++ ++ protected final boolean removeRegionSection(final RegionSection section) { ++ if (!this.sections.remove(section)) { ++ return false; ++ } ++ ++ section.sectionData.removeFromRegion(section, this); ++ ++ return true; ++ } ++ ++ protected void mergeInto(final Region mergeTarget) { ++ if (this == mergeTarget) { ++ throw new IllegalStateException("Cannot merge a region onto itself"); ++ } ++ if (this.dead) { ++ throw new IllegalStateException("Source region is dead! Source " + this + ", target " + mergeTarget); ++ } else if (mergeTarget.dead) { ++ throw new IllegalStateException("Target region is dead! Source " + this + ", target " + mergeTarget); ++ } ++ this.dead = true; ++ if (this.markedForRecalc) { ++ this.regionManager.removeFromRecalcQueue(this); ++ } ++ ++ for (final Iterator iterator = this.sections.unsafeIterator(IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); iterator.hasNext();) { ++ final RegionSection section = iterator.next(); ++ ++ if (!mergeTarget.addRegionSection(section)) { ++ throw new IllegalStateException("Target cannot contain source's sections! Source " + this + ", target " + mergeTarget); ++ } ++ } ++ ++ for (final RegionSection deadSection : this.deadSections) { ++ if (!this.sections.contains(deadSection)) { ++ throw new IllegalStateException("Source region does not even contain its own dead sections! Missing " + deadSection + " from region " + this); ++ } ++ mergeTarget.deadSections.add(deadSection); ++ } ++ //mergeTarget.check(); ++ } ++ ++ protected void markSectionAlive(final RegionSection section) { ++ this.deadSections.remove(section); ++ if (this.markedForRecalc && (this.sections.size() < this.regionManager.minSectionRecalcCount || this.getDeadSectionPercent() < this.regionManager.maxDeadRegionPercent)) { ++ this.regionManager.removeFromRecalcQueue(this); ++ this.markedForRecalc = false; ++ } ++ } ++ ++ protected void markSectionDead(final RegionSection section) { ++ this.deadSections.add(section); ++ if (!this.markedForRecalc && (this.sections.size() >= this.regionManager.minSectionRecalcCount || this.sections.size() == this.deadSections.size()) && this.getDeadSectionPercent() >= this.regionManager.maxDeadRegionPercent) { ++ this.regionManager.addToRecalcQueue(this); ++ this.markedForRecalc = true; ++ } ++ } ++ ++ @Override ++ public String toString() { ++ final StringBuilder ret = new StringBuilder(128); ++ ++ ret.append("Region{"); ++ ret.append("dead=").append(this.dead).append(','); ++ ret.append("markedForRecalc=").append(this.markedForRecalc).append(','); ++ ++ ret.append("sectionCount=").append(this.sections.size()).append(','); ++ ret.append("sections=["); ++ for (final Iterator iterator = this.sections.unsafeIterator(IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); iterator.hasNext();) { ++ final RegionSection section = iterator.next(); ++ ret.append(section); ++ if (iterator.hasNext()) { ++ ret.append(','); ++ } ++ } ++ ret.append(']'); ++ ++ ret.append('}'); ++ return ret.toString(); ++ } ++ } ++ ++ public static final class RegionSection { ++ protected final long regionCoordinate; ++ protected final long[] chunksBitset; ++ protected int chunkCount; ++ protected Region region; ++ ++ public final SingleThreadChunkRegionManager regionManager; ++ public final RegionSectionData sectionData; ++ ++ protected RegionSection(final long regionCoordinate, final SingleThreadChunkRegionManager regionManager) { ++ this.regionCoordinate = regionCoordinate; ++ this.regionManager = regionManager; ++ this.chunksBitset = new long[Math.max(1, regionManager.regionSectionChunkSize * regionManager.regionSectionChunkSize / Long.SIZE)]; ++ this.sectionData = regionManager.regionSectionDataSupplier.get(); ++ } ++ ++ public int getSectionX() { ++ return MCUtil.getCoordinateX(this.regionCoordinate); ++ } ++ ++ public int getSectionZ() { ++ return MCUtil.getCoordinateZ(this.regionCoordinate); ++ } ++ ++ public Region getRegion() { ++ return this.region; ++ } ++ ++ private int getChunkIndex(final int chunkX, final int chunkZ) { ++ return (chunkX & (this.regionManager.regionSectionChunkSize - 1)) | ((chunkZ & (this.regionManager.regionSectionChunkSize - 1)) << this.regionManager.regionChunkShift); ++ } ++ ++ protected boolean hasChunks() { ++ return this.chunkCount != 0; ++ } ++ ++ protected void addChunk(final int chunkX, final int chunkZ) { ++ final int index = this.getChunkIndex(chunkX, chunkZ); ++ final long bitset = this.chunksBitset[index >>> 6]; // index / Long.SIZE ++ final long after = this.chunksBitset[index >>> 6] = bitset | (1L << (index & (Long.SIZE - 1))); ++ if (after == bitset) { ++ throw new IllegalStateException("Cannot add a chunk to a section which already has the chunk! RegionSection: " + this + ", global chunk: " + new ChunkPos(chunkX, chunkZ).toString()); ++ } ++ if (++this.chunkCount != 1) { ++ return; ++ } ++ this.region.markSectionAlive(this); ++ } ++ ++ protected void removeChunk(final int chunkX, final int chunkZ) { ++ final int index = this.getChunkIndex(chunkX, chunkZ); ++ final long before = this.chunksBitset[index >>> 6]; // index / Long.SIZE ++ final long bitset = this.chunksBitset[index >>> 6] = before & ~(1L << (index & (Long.SIZE - 1))); ++ if (before == bitset) { ++ throw new IllegalStateException("Cannot remove a chunk from a section which does not have that chunk! RegionSection: " + this + ", global chunk: " + new ChunkPos(chunkX, chunkZ).toString()); ++ } ++ if (--this.chunkCount != 0) { ++ return; ++ } ++ this.region.markSectionDead(this); ++ } ++ ++ @Override ++ public String toString() { ++ return "RegionSection{" + ++ "regionCoordinate=" + new ChunkPos(this.regionCoordinate).toString() + "," + ++ "chunkCount=" + this.chunkCount + "," + ++ "chunksBitset=" + toString(this.chunksBitset) + "," + ++ "hash=" + this.hashCode() + ++ "}"; ++ } ++ ++ public String toStringWithRegion() { ++ return "RegionSection{" + ++ "regionCoordinate=" + new ChunkPos(this.regionCoordinate).toString() + "," + ++ "chunkCount=" + this.chunkCount + "," + ++ "chunksBitset=" + toString(this.chunksBitset) + "," + ++ "hash=" + this.hashCode() + "," + ++ "region=" + this.region + ++ "}"; ++ } ++ ++ private static String toString(final long[] array) { ++ final StringBuilder ret = new StringBuilder(); ++ for (final long value : array) { ++ // zero pad the hex string ++ final char[] zeros = new char[Long.SIZE / 4]; ++ Arrays.fill(zeros, '0'); ++ final String string = Long.toHexString(value); ++ System.arraycopy(string.toCharArray(), 0, zeros, zeros.length - string.length(), string.length()); ++ ++ ret.append(zeros); ++ } ++ ++ return ret.toString(); ++ } ++ } ++ ++ public static interface RegionData { ++ ++ } ++ ++ public static interface RegionSectionData { ++ ++ public void removeFromRegion(final RegionSection section, final Region from); ++ ++ // removal from the old region is handled via removeFromRegion ++ public void addToRegion(final RegionSection section, final Region oldRegion, final Region newRegion); ++ ++ } ++} +diff --git a/src/main/java/io/papermc/paper/util/CachedLists.java b/src/main/java/io/papermc/paper/util/CachedLists.java +new file mode 100644 +index 0000000000000000000000000000000000000000..be668387f65a633c6ac497fca632a4767a1bf3a2 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/CachedLists.java +@@ -0,0 +1,8 @@ ++package io.papermc.paper.util; ++ ++public final class CachedLists { ++ ++ public static void reset() { ++ ++ } ++} +diff --git a/src/main/java/io/papermc/paper/util/CoordinateUtils.java b/src/main/java/io/papermc/paper/util/CoordinateUtils.java +new file mode 100644 +index 0000000000000000000000000000000000000000..413e4b6da027876dbbe8eb78f2568a440f431547 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/CoordinateUtils.java +@@ -0,0 +1,128 @@ ++package io.papermc.paper.util; ++ ++import net.minecraft.core.BlockPos; ++import net.minecraft.core.SectionPos; ++import net.minecraft.util.Mth; ++import net.minecraft.world.entity.Entity; ++import net.minecraft.world.level.ChunkPos; ++ ++public final class CoordinateUtils { ++ ++ // dx, dz are relative to the target chunk ++ // dx, dz in [-radius, radius] ++ public static int getNeighbourMappedIndex(final int dx, final int dz, final int radius) { ++ return (dx + radius) + (2 * radius + 1)*(dz + radius); ++ } ++ ++ // the chunk keys are compatible with vanilla ++ ++ public static long getChunkKey(final BlockPos pos) { ++ return ((long)(pos.getZ() >> 4) << 32) | ((pos.getX() >> 4) & 0xFFFFFFFFL); ++ } ++ ++ public static long getChunkKey(final Entity entity) { ++ return ((Mth.lfloor(entity.getZ()) >> 4) << 32) | ((Mth.lfloor(entity.getX()) >> 4) & 0xFFFFFFFFL); ++ } ++ ++ public static long getChunkKey(final ChunkPos pos) { ++ return ((long)pos.z << 32) | (pos.x & 0xFFFFFFFFL); ++ } ++ ++ public static long getChunkKey(final SectionPos pos) { ++ return ((long)pos.getZ() << 32) | (pos.getX() & 0xFFFFFFFFL); ++ } ++ ++ public static long getChunkKey(final int x, final int z) { ++ return ((long)z << 32) | (x & 0xFFFFFFFFL); ++ } ++ ++ public static int getChunkX(final long chunkKey) { ++ return (int)chunkKey; ++ } ++ ++ public static int getChunkZ(final long chunkKey) { ++ return (int)(chunkKey >>> 32); ++ } ++ ++ public static int getChunkCoordinate(final double blockCoordinate) { ++ return Mth.floor(blockCoordinate) >> 4; ++ } ++ ++ // the section keys are compatible with vanilla's ++ ++ static final int SECTION_X_BITS = 22; ++ static final long SECTION_X_MASK = (1L << SECTION_X_BITS) - 1; ++ static final int SECTION_Y_BITS = 20; ++ static final long SECTION_Y_MASK = (1L << SECTION_Y_BITS) - 1; ++ static final int SECTION_Z_BITS = 22; ++ static final long SECTION_Z_MASK = (1L << SECTION_Z_BITS) - 1; ++ // format is y,z,x (in order of LSB to MSB) ++ static final int SECTION_Y_SHIFT = 0; ++ static final int SECTION_Z_SHIFT = SECTION_Y_SHIFT + SECTION_Y_BITS; ++ static final int SECTION_X_SHIFT = SECTION_Z_SHIFT + SECTION_X_BITS; ++ static final int SECTION_TO_BLOCK_SHIFT = 4; ++ ++ public static long getChunkSectionKey(final int x, final int y, final int z) { ++ return ((x & SECTION_X_MASK) << SECTION_X_SHIFT) ++ | ((y & SECTION_Y_MASK) << SECTION_Y_SHIFT) ++ | ((z & SECTION_Z_MASK) << SECTION_Z_SHIFT); ++ } ++ ++ public static long getChunkSectionKey(final SectionPos pos) { ++ return ((pos.getX() & SECTION_X_MASK) << SECTION_X_SHIFT) ++ | ((pos.getY() & SECTION_Y_MASK) << SECTION_Y_SHIFT) ++ | ((pos.getZ() & SECTION_Z_MASK) << SECTION_Z_SHIFT); ++ } ++ ++ public static long getChunkSectionKey(final ChunkPos pos, final int y) { ++ return ((pos.x & SECTION_X_MASK) << SECTION_X_SHIFT) ++ | ((y & SECTION_Y_MASK) << SECTION_Y_SHIFT) ++ | ((pos.z & SECTION_Z_MASK) << SECTION_Z_SHIFT); ++ } ++ ++ public static long getChunkSectionKey(final BlockPos pos) { ++ return (((long)pos.getX() << (SECTION_X_SHIFT - SECTION_TO_BLOCK_SHIFT)) & (SECTION_X_MASK << SECTION_X_SHIFT)) | ++ ((pos.getY() >> SECTION_TO_BLOCK_SHIFT) & (SECTION_Y_MASK << SECTION_Y_SHIFT)) | ++ (((long)pos.getZ() << (SECTION_Z_SHIFT - SECTION_TO_BLOCK_SHIFT)) & (SECTION_Z_MASK << SECTION_Z_SHIFT)); ++ } ++ ++ public static long getChunkSectionKey(final Entity entity) { ++ return ((Mth.lfloor(entity.getX()) << (SECTION_X_SHIFT - SECTION_TO_BLOCK_SHIFT)) & (SECTION_X_MASK << SECTION_X_SHIFT)) | ++ ((Mth.lfloor(entity.getY()) >> SECTION_TO_BLOCK_SHIFT) & (SECTION_Y_MASK << SECTION_Y_SHIFT)) | ++ ((Mth.lfloor(entity.getZ()) << (SECTION_Z_SHIFT - SECTION_TO_BLOCK_SHIFT)) & (SECTION_Z_MASK << SECTION_Z_SHIFT)); ++ } ++ ++ public static int getChunkSectionX(final long key) { ++ return (int)(key << (Long.SIZE - (SECTION_X_SHIFT + SECTION_X_BITS)) >> (Long.SIZE - SECTION_X_BITS)); ++ } ++ ++ public static int getChunkSectionY(final long key) { ++ return (int)(key << (Long.SIZE - (SECTION_Y_SHIFT + SECTION_Y_BITS)) >> (Long.SIZE - SECTION_Y_BITS)); ++ } ++ ++ public static int getChunkSectionZ(final long key) { ++ return (int)(key << (Long.SIZE - (SECTION_Z_SHIFT + SECTION_Z_BITS)) >> (Long.SIZE - SECTION_Z_BITS)); ++ } ++ ++ // the block coordinates are not necessarily compatible with vanilla's ++ ++ public static int getBlockCoordinate(final double blockCoordinate) { ++ return Mth.floor(blockCoordinate); ++ } ++ ++ public static long getBlockKey(final int x, final int y, final int z) { ++ return ((long)x & 0x7FFFFFF) | (((long)z & 0x7FFFFFF) << 27) | ((long)y << 54); ++ } ++ ++ public static long getBlockKey(final BlockPos pos) { ++ return ((long)pos.getX() & 0x7FFFFFF) | (((long)pos.getZ() & 0x7FFFFFF) << 27) | ((long)pos.getY() << 54); ++ } ++ ++ public static long getBlockKey(final Entity entity) { ++ return ((long)entity.getX() & 0x7FFFFFF) | (((long)entity.getZ() & 0x7FFFFFF) << 27) | ((long)entity.getY() << 54); ++ } ++ ++ private CoordinateUtils() { ++ throw new RuntimeException(); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/util/IntegerUtil.java b/src/main/java/io/papermc/paper/util/IntegerUtil.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a1bc1d1d0c86217ef18883d281195bc6b27e52ac +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/IntegerUtil.java +@@ -0,0 +1,226 @@ ++package io.papermc.paper.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; ++ } ++ ++ // copied from hacker's delight (signed division magic value) ++ // http://www.hackersdelight.org/hdcodetxt/magic.c.txt ++ public static long getDivisorNumbers(final int d) { ++ final int ad = IntegerUtil.branchlessAbs(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 - t%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/io/papermc/paper/util/IntervalledCounter.java b/src/main/java/io/papermc/paper/util/IntervalledCounter.java +new file mode 100644 +index 0000000000000000000000000000000000000000..059e8c61108cb78a80895cae36f2f8ac644e704c +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/IntervalledCounter.java +@@ -0,0 +1,100 @@ ++package io.papermc.paper.util; ++ ++public final class IntervalledCounter { ++ ++ protected long[] times; ++ protected final long interval; ++ protected long minTime; ++ protected int sum; ++ protected int head; // inclusive ++ protected int tail; // exclusive ++ ++ public IntervalledCounter(final long interval) { ++ this.times = new long[8]; ++ this.interval = interval; ++ } ++ ++ public void updateCurrentTime() { ++ this.updateCurrentTime(System.nanoTime()); ++ } ++ ++ public void updateCurrentTime(final long currentTime) { ++ int sum = this.sum; ++ int head = this.head; ++ final int tail = this.tail; ++ final long minTime = currentTime - this.interval; ++ ++ final int arrayLen = this.times.length; ++ ++ // guard against overflow by using subtraction ++ while (head != tail && this.times[head] - minTime < 0) { ++ head = (head + 1) % arrayLen; ++ --sum; ++ } ++ ++ this.sum = sum; ++ this.head = head; ++ this.minTime = minTime; ++ } ++ ++ public void addTime(final long currTime) { ++ // guard against overflow by using subtraction ++ if (currTime - this.minTime < 0) { ++ return; ++ } ++ int nextTail = (this.tail + 1) % this.times.length; ++ if (nextTail == this.head) { ++ this.resize(); ++ nextTail = (this.tail + 1) % this.times.length; ++ } ++ ++ this.times[this.tail] = currTime; ++ this.tail = nextTail; ++ } ++ ++ public void updateAndAdd(final int count) { ++ final long currTime = System.nanoTime(); ++ this.updateCurrentTime(currTime); ++ for (int i = 0; i < count; ++i) { ++ this.addTime(currTime); ++ } ++ } ++ ++ public void updateAndAdd(final int count, final long currTime) { ++ this.updateCurrentTime(currTime); ++ for (int i = 0; i < count; ++i) { ++ this.addTime(currTime); ++ } ++ } ++ ++ private void resize() { ++ final long[] oldElements = this.times; ++ final long[] newElements = new long[this.times.length * 2]; ++ this.times = newElements; ++ ++ final int head = this.head; ++ final int tail = this.tail; ++ final int size = tail >= head ? (tail - head) : (tail + (oldElements.length - head)); ++ this.head = 0; ++ this.tail = size; ++ ++ if (tail >= head) { ++ System.arraycopy(oldElements, head, newElements, 0, size); ++ } else { ++ System.arraycopy(oldElements, head, newElements, 0, oldElements.length - head); ++ System.arraycopy(oldElements, 0, newElements, oldElements.length - head, tail); ++ } ++ } ++ ++ // returns in units per second ++ public double getRate() { ++ return this.size() / (this.interval * 1.0e-9); ++ } ++ ++ public int size() { ++ final int head = this.head; ++ final int tail = this.tail; ++ ++ return tail >= head ? (tail - head) : (tail + (this.times.length - head)); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/util/WorldUtil.java b/src/main/java/io/papermc/paper/util/WorldUtil.java +new file mode 100644 +index 0000000000000000000000000000000000000000..67bb91fcfb532a919954cd9d7733d09a6c3fec35 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/WorldUtil.java +@@ -0,0 +1,46 @@ ++package io.papermc.paper.util; ++ ++import net.minecraft.world.level.LevelHeightAccessor; ++ ++public final class WorldUtil { ++ ++ // min, max are inclusive ++ ++ public static int getMaxSection(final LevelHeightAccessor world) { ++ return world.getMaxSection() - 1; // getMaxSection() is exclusive ++ } ++ ++ public static int getMinSection(final LevelHeightAccessor world) { ++ return world.getMinSection(); ++ } ++ ++ public static int getMaxLightSection(final LevelHeightAccessor world) { ++ return getMaxSection(world) + 1; ++ } ++ ++ public static int getMinLightSection(final LevelHeightAccessor world) { ++ return getMinSection(world) - 1; ++ } ++ ++ ++ ++ public static int getTotalSections(final LevelHeightAccessor world) { ++ return getMaxSection(world) - getMinSection(world) + 1; ++ } ++ ++ public static int getTotalLightSections(final LevelHeightAccessor world) { ++ return getMaxLightSection(world) - getMinLightSection(world) + 1; ++ } ++ ++ public static int getMinBlockY(final LevelHeightAccessor world) { ++ return getMinSection(world) << 4; ++ } ++ ++ public static int getMaxBlockY(final LevelHeightAccessor world) { ++ return (getMaxSection(world) << 4) | 15; ++ } ++ ++ private WorldUtil() { ++ throw new RuntimeException(); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java b/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0fd814f1d65c111266a2b20f86561839a4cef755 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java +@@ -0,0 +1,334 @@ ++package io.papermc.paper.util.maplist; ++ ++import it.unimi.dsi.fastutil.objects.Reference2IntLinkedOpenHashMap; ++import it.unimi.dsi.fastutil.objects.Reference2IntMap; ++import org.bukkit.Bukkit; ++import java.util.Arrays; ++import java.util.NoSuchElementException; ++ ++public final class IteratorSafeOrderedReferenceSet { ++ ++ public static final int ITERATOR_FLAG_SEE_ADDITIONS = 1 << 0; ++ ++ protected final Reference2IntLinkedOpenHashMap indexMap; ++ protected int firstInvalidIndex = -1; ++ ++ /* list impl */ ++ protected E[] listElements; ++ protected int listSize; ++ ++ protected final double maxFragFactor; ++ ++ protected int iteratorCount; ++ ++ private final boolean threadRestricted; ++ ++ public IteratorSafeOrderedReferenceSet() { ++ this(16, 0.75f, 16, 0.2); ++ } ++ ++ public IteratorSafeOrderedReferenceSet(final boolean threadRestricted) { ++ this(16, 0.75f, 16, 0.2, threadRestricted); ++ } ++ ++ public IteratorSafeOrderedReferenceSet(final int setCapacity, final float setLoadFactor, final int arrayCapacity, ++ final double maxFragFactor) { ++ this(setCapacity, setLoadFactor, arrayCapacity, maxFragFactor, false); ++ } ++ public IteratorSafeOrderedReferenceSet(final int setCapacity, final float setLoadFactor, final int arrayCapacity, ++ final double maxFragFactor, final boolean threadRestricted) { ++ this.indexMap = new Reference2IntLinkedOpenHashMap<>(setCapacity, setLoadFactor); ++ this.indexMap.defaultReturnValue(-1); ++ this.maxFragFactor = maxFragFactor; ++ this.listElements = (E[])new Object[arrayCapacity]; ++ this.threadRestricted = threadRestricted; ++ } ++ ++ /* ++ public void check() { ++ int iterated = 0; ++ ReferenceOpenHashSet check = new ReferenceOpenHashSet<>(); ++ if (this.listElements != null) { ++ for (int i = 0; i < this.listSize; ++i) { ++ Object obj = this.listElements[i]; ++ if (obj != null) { ++ iterated++; ++ if (!check.add((E)obj)) { ++ throw new IllegalStateException("contains duplicate"); ++ } ++ if (!this.contains((E)obj)) { ++ throw new IllegalStateException("desync"); ++ } ++ } ++ } ++ } ++ ++ if (iterated != this.size()) { ++ throw new IllegalStateException("Size is mismatched! Got " + iterated + ", expected " + this.size()); ++ } ++ ++ check.clear(); ++ iterated = 0; ++ for (final java.util.Iterator iterator = this.unsafeIterator(IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); iterator.hasNext();) { ++ final E element = iterator.next(); ++ iterated++; ++ if (!check.add(element)) { ++ throw new IllegalStateException("contains duplicate (iterator is wrong)"); ++ } ++ if (!this.contains(element)) { ++ throw new IllegalStateException("desync (iterator is wrong)"); ++ } ++ } ++ ++ if (iterated != this.size()) { ++ throw new IllegalStateException("Size is mismatched! (iterator is wrong) Got " + iterated + ", expected " + this.size()); ++ } ++ } ++ */ ++ ++ protected final boolean allowSafeIteration() { ++ return !this.threadRestricted || Bukkit.isPrimaryThread(); ++ } ++ ++ protected final double getFragFactor() { ++ return 1.0 - ((double)this.indexMap.size() / (double)this.listSize); ++ } ++ ++ public int createRawIterator() { ++ if (this.allowSafeIteration()) { ++ ++this.iteratorCount; ++ } ++ if (this.indexMap.isEmpty()) { ++ return -1; ++ } else { ++ return this.firstInvalidIndex == 0 ? this.indexMap.getInt(this.indexMap.firstKey()) : 0; ++ } ++ } ++ ++ public int advanceRawIterator(final int index) { ++ final E[] elements = this.listElements; ++ int ret = index + 1; ++ for (int len = this.listSize; ret < len; ++ret) { ++ if (elements[ret] != null) { ++ return ret; ++ } ++ } ++ ++ return -1; ++ } ++ ++ public void finishRawIterator() { ++ if (this.allowSafeIteration() && --this.iteratorCount == 0) { ++ if (this.getFragFactor() >= this.maxFragFactor) { ++ this.defrag(); ++ } ++ } ++ } ++ ++ public boolean remove(final E element) { ++ final int index = this.indexMap.removeInt(element); ++ if (index >= 0) { ++ if (this.firstInvalidIndex < 0 || index < this.firstInvalidIndex) { ++ this.firstInvalidIndex = index; ++ } ++ if (this.listElements[index] != element) { ++ throw new IllegalStateException(); ++ } ++ this.listElements[index] = null; ++ if (this.allowSafeIteration() && this.iteratorCount == 0 && this.getFragFactor() >= this.maxFragFactor) { ++ this.defrag(); ++ } ++ //this.check(); ++ return true; ++ } ++ return false; ++ } ++ ++ public boolean contains(final E element) { ++ return this.indexMap.containsKey(element); ++ } ++ ++ public boolean add(final E element) { ++ final int listSize = this.listSize; ++ ++ final int previous = this.indexMap.putIfAbsent(element, listSize); ++ if (previous != -1) { ++ return false; ++ } ++ ++ if (listSize >= this.listElements.length) { ++ this.listElements = Arrays.copyOf(this.listElements, listSize * 2); ++ } ++ this.listElements[listSize] = element; ++ this.listSize = listSize + 1; ++ ++ //this.check(); ++ return true; ++ } ++ ++ protected void defrag() { ++ if (this.firstInvalidIndex < 0) { ++ return; // nothing to do ++ } ++ ++ if (this.indexMap.isEmpty()) { ++ Arrays.fill(this.listElements, 0, this.listSize, null); ++ this.listSize = 0; ++ this.firstInvalidIndex = -1; ++ //this.check(); ++ return; ++ } ++ ++ final E[] backingArray = this.listElements; ++ ++ int lastValidIndex; ++ java.util.Iterator> iterator; ++ ++ if (this.firstInvalidIndex == 0) { ++ iterator = this.indexMap.reference2IntEntrySet().fastIterator(); ++ lastValidIndex = 0; ++ } else { ++ lastValidIndex = this.firstInvalidIndex; ++ final E key = backingArray[lastValidIndex - 1]; ++ iterator = this.indexMap.reference2IntEntrySet().fastIterator(new Reference2IntMap.Entry() { ++ @Override ++ public int getIntValue() { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public int setValue(int i) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public E getKey() { ++ return key; ++ } ++ }); ++ } ++ ++ while (iterator.hasNext()) { ++ final Reference2IntMap.Entry entry = iterator.next(); ++ ++ final int newIndex = lastValidIndex++; ++ backingArray[newIndex] = entry.getKey(); ++ entry.setValue(newIndex); ++ } ++ ++ // cleanup end ++ Arrays.fill(backingArray, lastValidIndex, this.listSize, null); ++ this.listSize = lastValidIndex; ++ this.firstInvalidIndex = -1; ++ //this.check(); ++ } ++ ++ public E rawGet(final int index) { ++ return this.listElements[index]; ++ } ++ ++ public int size() { ++ // always returns the correct amount - listSize can be different ++ return this.indexMap.size(); ++ } ++ ++ public IteratorSafeOrderedReferenceSet.Iterator iterator() { ++ return this.iterator(0); ++ } ++ ++ public IteratorSafeOrderedReferenceSet.Iterator iterator(final int flags) { ++ if (this.allowSafeIteration()) { ++ ++this.iteratorCount; ++ } ++ return new BaseIterator<>(this, true, (flags & ITERATOR_FLAG_SEE_ADDITIONS) != 0 ? Integer.MAX_VALUE : this.listSize); ++ } ++ ++ public java.util.Iterator unsafeIterator() { ++ return this.unsafeIterator(0); ++ } ++ public java.util.Iterator unsafeIterator(final int flags) { ++ return new BaseIterator<>(this, false, (flags & ITERATOR_FLAG_SEE_ADDITIONS) != 0 ? Integer.MAX_VALUE : this.listSize); ++ } ++ ++ public static interface Iterator extends java.util.Iterator { ++ ++ public void finishedIterating(); ++ ++ } ++ ++ protected static final class BaseIterator implements IteratorSafeOrderedReferenceSet.Iterator { ++ ++ protected final IteratorSafeOrderedReferenceSet set; ++ protected final boolean canFinish; ++ protected final int maxIndex; ++ protected int nextIndex; ++ protected E pendingValue; ++ protected boolean finished; ++ protected E lastReturned; ++ ++ protected BaseIterator(final IteratorSafeOrderedReferenceSet set, final boolean canFinish, final int maxIndex) { ++ this.set = set; ++ this.canFinish = canFinish; ++ this.maxIndex = maxIndex; ++ } ++ ++ @Override ++ public boolean hasNext() { ++ if (this.finished) { ++ return false; ++ } ++ if (this.pendingValue != null) { ++ return true; ++ } ++ ++ final E[] elements = this.set.listElements; ++ int index, len; ++ for (index = this.nextIndex, len = Math.min(this.maxIndex, this.set.listSize); index < len; ++index) { ++ final E element = elements[index]; ++ if (element != null) { ++ this.pendingValue = element; ++ this.nextIndex = index + 1; ++ return true; ++ } ++ } ++ ++ this.nextIndex = index; ++ return false; ++ } ++ ++ @Override ++ public E next() { ++ if (!this.hasNext()) { ++ throw new NoSuchElementException(); ++ } ++ final E ret = this.pendingValue; ++ ++ this.pendingValue = null; ++ this.lastReturned = ret; ++ ++ return ret; ++ } ++ ++ @Override ++ public void remove() { ++ final E lastReturned = this.lastReturned; ++ if (lastReturned == null) { ++ throw new IllegalStateException(); ++ } ++ this.lastReturned = null; ++ this.set.remove(lastReturned); ++ } ++ ++ @Override ++ public void finishedIterating() { ++ if (this.finished || !this.canFinish) { ++ throw new IllegalStateException(); ++ } ++ this.lastReturned = null; ++ this.finished = true; ++ if (this.set.allowSafeIteration()) { ++ this.set.finishRawIterator(); ++ } ++ } ++ } ++} +diff --git a/src/main/java/io/papermc/paper/util/misc/Delayed26WayDistancePropagator3D.java b/src/main/java/io/papermc/paper/util/misc/Delayed26WayDistancePropagator3D.java +new file mode 100644 +index 0000000000000000000000000000000000000000..470402573bc31106d5a63e415b958fb7f9c36aa9 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/misc/Delayed26WayDistancePropagator3D.java +@@ -0,0 +1,297 @@ ++package io.papermc.paper.util.misc; ++ ++import io.papermc.paper.util.CoordinateUtils; ++import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap; ++import it.unimi.dsi.fastutil.longs.LongIterator; ++import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet; ++ ++public final class Delayed26WayDistancePropagator3D { ++ ++ // this map is considered "stale" unless updates are propagated. ++ protected final Delayed8WayDistancePropagator2D.LevelMap levels = new Delayed8WayDistancePropagator2D.LevelMap(8192*2, 0.6f); ++ ++ // this map is never stale ++ protected final Long2ByteOpenHashMap sources = new Long2ByteOpenHashMap(4096, 0.6f); ++ ++ // Generally updates to positions are made close to other updates, so we link to decrease cache misses when ++ // propagating updates ++ protected final LongLinkedOpenHashSet updatedSources = new LongLinkedOpenHashSet(); ++ ++ @FunctionalInterface ++ public static interface LevelChangeCallback { ++ ++ /** ++ * This can be called for intermediate updates. So do not rely on newLevel being close to or ++ * the exact level that is expected after a full propagation has occured. ++ */ ++ public void onLevelUpdate(final long coordinate, final byte oldLevel, final byte newLevel); ++ ++ } ++ ++ protected final LevelChangeCallback changeCallback; ++ ++ public Delayed26WayDistancePropagator3D() { ++ this(null); ++ } ++ ++ public Delayed26WayDistancePropagator3D(final LevelChangeCallback changeCallback) { ++ this.changeCallback = changeCallback; ++ } ++ ++ public int getLevel(final long pos) { ++ return this.levels.get(pos); ++ } ++ ++ public int getLevel(final int x, final int y, final int z) { ++ return this.levels.get(CoordinateUtils.getChunkSectionKey(x, y, z)); ++ } ++ ++ public void setSource(final int x, final int y, final int z, final int level) { ++ this.setSource(CoordinateUtils.getChunkSectionKey(x, y, z), level); ++ } ++ ++ public void setSource(final long coordinate, final int level) { ++ if ((level & 63) != level || level == 0) { ++ throw new IllegalArgumentException("Level must be in (0, 63], not " + level); ++ } ++ ++ final byte byteLevel = (byte)level; ++ final byte oldLevel = this.sources.put(coordinate, byteLevel); ++ ++ if (oldLevel == byteLevel) { ++ return; // nothing to do ++ } ++ ++ // queue to update later ++ this.updatedSources.add(coordinate); ++ } ++ ++ public void removeSource(final int x, final int y, final int z) { ++ this.removeSource(CoordinateUtils.getChunkSectionKey(x, y, z)); ++ } ++ ++ public void removeSource(final long coordinate) { ++ if (this.sources.remove(coordinate) != 0) { ++ this.updatedSources.add(coordinate); ++ } ++ } ++ ++ // queues used for BFS propagating levels ++ protected final Delayed8WayDistancePropagator2D.WorkQueue[] levelIncreaseWorkQueues = new Delayed8WayDistancePropagator2D.WorkQueue[64]; ++ { ++ for (int i = 0; i < this.levelIncreaseWorkQueues.length; ++i) { ++ this.levelIncreaseWorkQueues[i] = new Delayed8WayDistancePropagator2D.WorkQueue(); ++ } ++ } ++ protected final Delayed8WayDistancePropagator2D.WorkQueue[] levelRemoveWorkQueues = new Delayed8WayDistancePropagator2D.WorkQueue[64]; ++ { ++ for (int i = 0; i < this.levelRemoveWorkQueues.length; ++i) { ++ this.levelRemoveWorkQueues[i] = new Delayed8WayDistancePropagator2D.WorkQueue(); ++ } ++ } ++ protected long levelIncreaseWorkQueueBitset; ++ protected long levelRemoveWorkQueueBitset; ++ ++ protected final void addToIncreaseWorkQueue(final long coordinate, final byte level) { ++ final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[level]; ++ queue.queuedCoordinates.enqueue(coordinate); ++ queue.queuedLevels.enqueue(level); ++ ++ this.levelIncreaseWorkQueueBitset |= (1L << level); ++ } ++ ++ protected final void addToIncreaseWorkQueue(final long coordinate, final byte index, final byte level) { ++ final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[index]; ++ queue.queuedCoordinates.enqueue(coordinate); ++ queue.queuedLevels.enqueue(level); ++ ++ this.levelIncreaseWorkQueueBitset |= (1L << index); ++ } ++ ++ protected final void addToRemoveWorkQueue(final long coordinate, final byte level) { ++ final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelRemoveWorkQueues[level]; ++ queue.queuedCoordinates.enqueue(coordinate); ++ queue.queuedLevels.enqueue(level); ++ ++ this.levelRemoveWorkQueueBitset |= (1L << level); ++ } ++ ++ public boolean propagateUpdates() { ++ if (this.updatedSources.isEmpty()) { ++ return false; ++ } ++ ++ boolean ret = false; ++ ++ for (final LongIterator iterator = this.updatedSources.iterator(); iterator.hasNext();) { ++ final long coordinate = iterator.nextLong(); ++ ++ final byte currentLevel = this.levels.get(coordinate); ++ final byte updatedSource = this.sources.get(coordinate); ++ ++ if (currentLevel == updatedSource) { ++ continue; ++ } ++ ret = true; ++ ++ if (updatedSource > currentLevel) { ++ // level increase ++ this.addToIncreaseWorkQueue(coordinate, updatedSource); ++ } else { ++ // level decrease ++ this.addToRemoveWorkQueue(coordinate, currentLevel); ++ // if the current coordinate is a source, then the decrease propagation will detect that and queue ++ // the source propagation ++ } ++ } ++ ++ this.updatedSources.clear(); ++ ++ // propagate source level increases first for performance reasons (in crowded areas hopefully the additions ++ // make the removes remove less) ++ this.propagateIncreases(); ++ ++ // now we propagate the decreases (which will then re-propagate clobbered sources) ++ this.propagateDecreases(); ++ ++ return ret; ++ } ++ ++ protected void propagateIncreases() { ++ for (int queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelIncreaseWorkQueueBitset); ++ this.levelIncreaseWorkQueueBitset != 0L; ++ this.levelIncreaseWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelIncreaseWorkQueueBitset)) { ++ ++ final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[queueIndex]; ++ while (!queue.queuedLevels.isEmpty()) { ++ final long coordinate = queue.queuedCoordinates.removeFirstLong(); ++ byte level = queue.queuedLevels.removeFirstByte(); ++ ++ final boolean neighbourCheck = level < 0; ++ ++ final byte currentLevel; ++ if (neighbourCheck) { ++ level = (byte)-level; ++ currentLevel = this.levels.get(coordinate); ++ } else { ++ currentLevel = this.levels.putIfGreater(coordinate, level); ++ } ++ ++ if (neighbourCheck) { ++ // used when propagating from decrease to indicate that this level needs to check its neighbours ++ // this means the level at coordinate could be equal, but would still need neighbours checked ++ ++ if (currentLevel != level) { ++ // something caused the level to change, which means something propagated to it (which means ++ // us propagating here is redundant), or something removed the level (which means we ++ // cannot propagate further) ++ continue; ++ } ++ } else if (currentLevel >= level) { ++ // something higher/equal propagated ++ continue; ++ } ++ if (this.changeCallback != null) { ++ this.changeCallback.onLevelUpdate(coordinate, currentLevel, level); ++ } ++ ++ if (level == 1) { ++ // can't propagate 0 to neighbours ++ continue; ++ } ++ ++ // propagate to neighbours ++ final byte neighbourLevel = (byte)(level - 1); ++ final int x = CoordinateUtils.getChunkSectionX(coordinate); ++ final int y = CoordinateUtils.getChunkSectionY(coordinate); ++ final int z = CoordinateUtils.getChunkSectionZ(coordinate); ++ ++ for (int dy = -1; dy <= 1; ++dy) { ++ for (int dz = -1; dz <= 1; ++dz) { ++ for (int dx = -1; dx <= 1; ++dx) { ++ if ((dy | dz | dx) == 0) { ++ // already propagated to coordinate ++ continue; ++ } ++ ++ // sure we can check the neighbour level in the map right now and avoid a propagation, ++ // but then we would still have to recheck it when popping the value off of the queue! ++ // so just avoid the double lookup ++ final long neighbourCoordinate = CoordinateUtils.getChunkSectionKey(dx + x, dy + y, dz + z); ++ this.addToIncreaseWorkQueue(neighbourCoordinate, neighbourLevel); ++ } ++ } ++ } ++ } ++ } ++ } ++ ++ protected void propagateDecreases() { ++ for (int queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelRemoveWorkQueueBitset); ++ this.levelRemoveWorkQueueBitset != 0L; ++ this.levelRemoveWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelRemoveWorkQueueBitset)) { ++ ++ final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelRemoveWorkQueues[queueIndex]; ++ while (!queue.queuedLevels.isEmpty()) { ++ final long coordinate = queue.queuedCoordinates.removeFirstLong(); ++ final byte level = queue.queuedLevels.removeFirstByte(); ++ ++ final byte currentLevel = this.levels.removeIfGreaterOrEqual(coordinate, level); ++ if (currentLevel == 0) { ++ // something else removed ++ continue; ++ } ++ ++ if (currentLevel > level) { ++ // something higher propagated here or we hit the propagation of another source ++ // in the second case we need to re-propagate because we could have just clobbered another source's ++ // propagation ++ this.addToIncreaseWorkQueue(coordinate, currentLevel, (byte)-currentLevel); // indicate to the increase code that the level's neighbours need checking ++ continue; ++ } ++ ++ if (this.changeCallback != null) { ++ this.changeCallback.onLevelUpdate(coordinate, currentLevel, (byte)0); ++ } ++ ++ final byte source = this.sources.get(coordinate); ++ if (source != 0) { ++ // must re-propagate source later ++ this.addToIncreaseWorkQueue(coordinate, source); ++ } ++ ++ if (level == 0) { ++ // can't propagate -1 to neighbours ++ // we have to check neighbours for removing 1 just in case the neighbour is 2 ++ continue; ++ } ++ ++ // propagate to neighbours ++ final byte neighbourLevel = (byte)(level - 1); ++ final int x = CoordinateUtils.getChunkSectionX(coordinate); ++ final int y = CoordinateUtils.getChunkSectionY(coordinate); ++ final int z = CoordinateUtils.getChunkSectionZ(coordinate); ++ ++ for (int dy = -1; dy <= 1; ++dy) { ++ for (int dz = -1; dz <= 1; ++dz) { ++ for (int dx = -1; dx <= 1; ++dx) { ++ if ((dy | dz | dx) == 0) { ++ // already propagated to coordinate ++ continue; ++ } ++ ++ // sure we can check the neighbour level in the map right now and avoid a propagation, ++ // but then we would still have to recheck it when popping the value off of the queue! ++ // so just avoid the double lookup ++ final long neighbourCoordinate = CoordinateUtils.getChunkSectionKey(dx + x, dy + y, dz + z); ++ this.addToRemoveWorkQueue(neighbourCoordinate, neighbourLevel); ++ } ++ } ++ } ++ } ++ } ++ ++ // propagate sources we clobbered in the process ++ this.propagateIncreases(); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java b/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java +new file mode 100644 +index 0000000000000000000000000000000000000000..4d3dc8fba51bf5c0dceb06744781d1df2bbf2df6 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java +@@ -0,0 +1,718 @@ ++package io.papermc.paper.util.misc; ++ ++import it.unimi.dsi.fastutil.HashCommon; ++import it.unimi.dsi.fastutil.bytes.ByteArrayFIFOQueue; ++import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap; ++import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue; ++import it.unimi.dsi.fastutil.longs.LongIterator; ++import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet; ++import net.minecraft.server.MCUtil; ++ ++public final class Delayed8WayDistancePropagator2D { ++ ++ // Test ++ /* ++ protected static void test(int x, int z, com.destroystokyo.paper.util.misc.DistanceTrackingAreaMap reference, Delayed8WayDistancePropagator2D test) { ++ int got = test.getLevel(x, z); ++ ++ int expect = 0; ++ Object[] nearest = reference.getObjectsInRange(x, z) == null ? null : reference.getObjectsInRange(x, z).getBackingSet(); ++ if (nearest != null) { ++ for (Object _obj : nearest) { ++ if (_obj instanceof Ticket) { ++ Ticket ticket = (Ticket)_obj; ++ long ticketCoord = reference.getLastCoordinate(ticket); ++ int viewDistance = reference.getLastViewDistance(ticket); ++ int distance = Math.max(com.destroystokyo.paper.util.math.IntegerUtil.branchlessAbs(MCUtil.getCoordinateX(ticketCoord) - x), ++ com.destroystokyo.paper.util.math.IntegerUtil.branchlessAbs(MCUtil.getCoordinateZ(ticketCoord) - z)); ++ int level = viewDistance - distance; ++ if (level > expect) { ++ expect = level; ++ } ++ } ++ } ++ } ++ ++ if (expect != got) { ++ throw new IllegalStateException("Expected " + expect + " at pos (" + x + "," + z + ") but got " + got); ++ } ++ } ++ ++ static class Ticket { ++ ++ int x; ++ int z; ++ ++ final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet empty ++ = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<>(this); ++ ++ } ++ ++ public static void main(final String[] args) { ++ com.destroystokyo.paper.util.misc.DistanceTrackingAreaMap reference = new com.destroystokyo.paper.util.misc.DistanceTrackingAreaMap() { ++ @Override ++ protected com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet getEmptySetFor(Ticket object) { ++ return object.empty; ++ } ++ }; ++ Delayed8WayDistancePropagator2D test = new Delayed8WayDistancePropagator2D(); ++ ++ final int maxDistance = 64; ++ // test origin ++ { ++ Ticket originTicket = new Ticket(); ++ int originDistance = 31; ++ // test single source ++ reference.add(originTicket, 0, 0, originDistance); ++ test.setSource(0, 0, originDistance); test.propagateUpdates(); // set and propagate ++ for (int dx = -originDistance; dx <= originDistance; ++dx) { ++ for (int dz = -originDistance; dz <= originDistance; ++dz) { ++ test(dx, dz, reference, test); ++ } ++ } ++ // test single source decrease ++ reference.update(originTicket, 0, 0, originDistance/2); ++ test.setSource(0, 0, originDistance/2); test.propagateUpdates(); // set and propagate ++ for (int dx = -originDistance; dx <= originDistance; ++dx) { ++ for (int dz = -originDistance; dz <= originDistance; ++dz) { ++ test(dx, dz, reference, test); ++ } ++ } ++ // test source increase ++ originDistance = 2*originDistance; ++ reference.update(originTicket, 0, 0, originDistance); ++ test.setSource(0, 0, originDistance); test.propagateUpdates(); // set and propagate ++ for (int dx = -4*originDistance; dx <= 4*originDistance; ++dx) { ++ for (int dz = -4*originDistance; dz <= 4*originDistance; ++dz) { ++ test(dx, dz, reference, test); ++ } ++ } ++ ++ reference.remove(originTicket); ++ test.removeSource(0, 0); test.propagateUpdates(); ++ } ++ ++ // test multiple sources at origin ++ { ++ int originDistance = 31; ++ java.util.List list = new java.util.ArrayList<>(); ++ for (int i = 0; i < 10; ++i) { ++ Ticket a = new Ticket(); ++ list.add(a); ++ a.x = (i & 1) == 1 ? -i : i; ++ a.z = (i & 1) == 1 ? -i : i; ++ } ++ for (Ticket ticket : list) { ++ reference.add(ticket, ticket.x, ticket.z, originDistance); ++ test.setSource(ticket.x, ticket.z, originDistance); ++ } ++ test.propagateUpdates(); ++ ++ for (int dx = -8*originDistance; dx <= 8*originDistance; ++dx) { ++ for (int dz = -8*originDistance; dz <= 8*originDistance; ++dz) { ++ test(dx, dz, reference, test); ++ } ++ } ++ ++ // test ticket level decrease ++ ++ for (Ticket ticket : list) { ++ reference.update(ticket, ticket.x, ticket.z, originDistance/2); ++ test.setSource(ticket.x, ticket.z, originDistance/2); ++ } ++ test.propagateUpdates(); ++ ++ for (int dx = -8*originDistance; dx <= 8*originDistance; ++dx) { ++ for (int dz = -8*originDistance; dz <= 8*originDistance; ++dz) { ++ test(dx, dz, reference, test); ++ } ++ } ++ ++ // test ticket level increase ++ ++ for (Ticket ticket : list) { ++ reference.update(ticket, ticket.x, ticket.z, originDistance*2); ++ test.setSource(ticket.x, ticket.z, originDistance*2); ++ } ++ test.propagateUpdates(); ++ ++ for (int dx = -16*originDistance; dx <= 16*originDistance; ++dx) { ++ for (int dz = -16*originDistance; dz <= 16*originDistance; ++dz) { ++ test(dx, dz, reference, test); ++ } ++ } ++ ++ // test ticket remove ++ for (int i = 0, len = list.size(); i < len; ++i) { ++ if ((i & 3) != 0) { ++ continue; ++ } ++ Ticket ticket = list.get(i); ++ reference.remove(ticket); ++ test.removeSource(ticket.x, ticket.z); ++ } ++ test.propagateUpdates(); ++ ++ for (int dx = -16*originDistance; dx <= 16*originDistance; ++dx) { ++ for (int dz = -16*originDistance; dz <= 16*originDistance; ++dz) { ++ test(dx, dz, reference, test); ++ } ++ } ++ } ++ ++ // now test at coordinate offsets ++ // test offset ++ { ++ Ticket originTicket = new Ticket(); ++ int originDistance = 31; ++ int offX = 54432; ++ int offZ = -134567; ++ // test single source ++ reference.add(originTicket, offX, offZ, originDistance); ++ test.setSource(offX, offZ, originDistance); test.propagateUpdates(); // set and propagate ++ for (int dx = -originDistance; dx <= originDistance; ++dx) { ++ for (int dz = -originDistance; dz <= originDistance; ++dz) { ++ test(dx + offX, dz + offZ, reference, test); ++ } ++ } ++ // test single source decrease ++ reference.update(originTicket, offX, offZ, originDistance/2); ++ test.setSource(offX, offZ, originDistance/2); test.propagateUpdates(); // set and propagate ++ for (int dx = -originDistance; dx <= originDistance; ++dx) { ++ for (int dz = -originDistance; dz <= originDistance; ++dz) { ++ test(dx + offX, dz + offZ, reference, test); ++ } ++ } ++ // test source increase ++ originDistance = 2*originDistance; ++ reference.update(originTicket, offX, offZ, originDistance); ++ test.setSource(offX, offZ, originDistance); test.propagateUpdates(); // set and propagate ++ for (int dx = -4*originDistance; dx <= 4*originDistance; ++dx) { ++ for (int dz = -4*originDistance; dz <= 4*originDistance; ++dz) { ++ test(dx + offX, dz + offZ, reference, test); ++ } ++ } ++ ++ reference.remove(originTicket); ++ test.removeSource(offX, offZ); test.propagateUpdates(); ++ } ++ ++ // test multiple sources at origin ++ { ++ int originDistance = 31; ++ int offX = 54432; ++ int offZ = -134567; ++ java.util.List list = new java.util.ArrayList<>(); ++ for (int i = 0; i < 10; ++i) { ++ Ticket a = new Ticket(); ++ list.add(a); ++ a.x = offX + ((i & 1) == 1 ? -i : i); ++ a.z = offZ + ((i & 1) == 1 ? -i : i); ++ } ++ for (Ticket ticket : list) { ++ reference.add(ticket, ticket.x, ticket.z, originDistance); ++ test.setSource(ticket.x, ticket.z, originDistance); ++ } ++ test.propagateUpdates(); ++ ++ for (int dx = -8*originDistance; dx <= 8*originDistance; ++dx) { ++ for (int dz = -8*originDistance; dz <= 8*originDistance; ++dz) { ++ test(dx, dz, reference, test); ++ } ++ } ++ ++ // test ticket level decrease ++ ++ for (Ticket ticket : list) { ++ reference.update(ticket, ticket.x, ticket.z, originDistance/2); ++ test.setSource(ticket.x, ticket.z, originDistance/2); ++ } ++ test.propagateUpdates(); ++ ++ for (int dx = -8*originDistance; dx <= 8*originDistance; ++dx) { ++ for (int dz = -8*originDistance; dz <= 8*originDistance; ++dz) { ++ test(dx, dz, reference, test); ++ } ++ } ++ ++ // test ticket level increase ++ ++ for (Ticket ticket : list) { ++ reference.update(ticket, ticket.x, ticket.z, originDistance*2); ++ test.setSource(ticket.x, ticket.z, originDistance*2); ++ } ++ test.propagateUpdates(); ++ ++ for (int dx = -16*originDistance; dx <= 16*originDistance; ++dx) { ++ for (int dz = -16*originDistance; dz <= 16*originDistance; ++dz) { ++ test(dx, dz, reference, test); ++ } ++ } ++ ++ // test ticket remove ++ for (int i = 0, len = list.size(); i < len; ++i) { ++ if ((i & 3) != 0) { ++ continue; ++ } ++ Ticket ticket = list.get(i); ++ reference.remove(ticket); ++ test.removeSource(ticket.x, ticket.z); ++ } ++ test.propagateUpdates(); ++ ++ for (int dx = -16*originDistance; dx <= 16*originDistance; ++dx) { ++ for (int dz = -16*originDistance; dz <= 16*originDistance; ++dz) { ++ test(dx, dz, reference, test); ++ } ++ } ++ } ++ } ++ */ ++ ++ // this map is considered "stale" unless updates are propagated. ++ protected final LevelMap levels = new LevelMap(8192*2, 0.6f); ++ ++ // this map is never stale ++ protected final Long2ByteOpenHashMap sources = new Long2ByteOpenHashMap(4096, 0.6f); ++ ++ // Generally updates to positions are made close to other updates, so we link to decrease cache misses when ++ // propagating updates ++ protected final LongLinkedOpenHashSet updatedSources = new LongLinkedOpenHashSet(); ++ ++ @FunctionalInterface ++ public static interface LevelChangeCallback { ++ ++ /** ++ * This can be called for intermediate updates. So do not rely on newLevel being close to or ++ * the exact level that is expected after a full propagation has occured. ++ */ ++ public void onLevelUpdate(final long coordinate, final byte oldLevel, final byte newLevel); ++ ++ } ++ ++ protected final LevelChangeCallback changeCallback; ++ ++ public Delayed8WayDistancePropagator2D() { ++ this(null); ++ } ++ ++ public Delayed8WayDistancePropagator2D(final LevelChangeCallback changeCallback) { ++ this.changeCallback = changeCallback; ++ } ++ ++ public int getLevel(final long pos) { ++ return this.levels.get(pos); ++ } ++ ++ public int getLevel(final int x, final int z) { ++ return this.levels.get(MCUtil.getCoordinateKey(x, z)); ++ } ++ ++ public void setSource(final int x, final int z, final int level) { ++ this.setSource(MCUtil.getCoordinateKey(x, z), level); ++ } ++ ++ public void setSource(final long coordinate, final int level) { ++ if ((level & 63) != level || level == 0) { ++ throw new IllegalArgumentException("Level must be in (0, 63], not " + level); ++ } ++ ++ final byte byteLevel = (byte)level; ++ final byte oldLevel = this.sources.put(coordinate, byteLevel); ++ ++ if (oldLevel == byteLevel) { ++ return; // nothing to do ++ } ++ ++ // queue to update later ++ this.updatedSources.add(coordinate); ++ } ++ ++ public void removeSource(final int x, final int z) { ++ this.removeSource(MCUtil.getCoordinateKey(x, z)); ++ } ++ ++ public void removeSource(final long coordinate) { ++ if (this.sources.remove(coordinate) != 0) { ++ this.updatedSources.add(coordinate); ++ } ++ } ++ ++ // queues used for BFS propagating levels ++ protected final WorkQueue[] levelIncreaseWorkQueues = new WorkQueue[64]; ++ { ++ for (int i = 0; i < this.levelIncreaseWorkQueues.length; ++i) { ++ this.levelIncreaseWorkQueues[i] = new WorkQueue(); ++ } ++ } ++ protected final WorkQueue[] levelRemoveWorkQueues = new WorkQueue[64]; ++ { ++ for (int i = 0; i < this.levelRemoveWorkQueues.length; ++i) { ++ this.levelRemoveWorkQueues[i] = new WorkQueue(); ++ } ++ } ++ protected long levelIncreaseWorkQueueBitset; ++ protected long levelRemoveWorkQueueBitset; ++ ++ protected final void addToIncreaseWorkQueue(final long coordinate, final byte level) { ++ final WorkQueue queue = this.levelIncreaseWorkQueues[level]; ++ queue.queuedCoordinates.enqueue(coordinate); ++ queue.queuedLevels.enqueue(level); ++ ++ this.levelIncreaseWorkQueueBitset |= (1L << level); ++ } ++ ++ protected final void addToIncreaseWorkQueue(final long coordinate, final byte index, final byte level) { ++ final WorkQueue queue = this.levelIncreaseWorkQueues[index]; ++ queue.queuedCoordinates.enqueue(coordinate); ++ queue.queuedLevels.enqueue(level); ++ ++ this.levelIncreaseWorkQueueBitset |= (1L << index); ++ } ++ ++ protected final void addToRemoveWorkQueue(final long coordinate, final byte level) { ++ final WorkQueue queue = this.levelRemoveWorkQueues[level]; ++ queue.queuedCoordinates.enqueue(coordinate); ++ queue.queuedLevels.enqueue(level); ++ ++ this.levelRemoveWorkQueueBitset |= (1L << level); ++ } ++ ++ public boolean propagateUpdates() { ++ if (this.updatedSources.isEmpty()) { ++ return false; ++ } ++ ++ boolean ret = false; ++ ++ for (final LongIterator iterator = this.updatedSources.iterator(); iterator.hasNext();) { ++ final long coordinate = iterator.nextLong(); ++ ++ final byte currentLevel = this.levels.get(coordinate); ++ final byte updatedSource = this.sources.get(coordinate); ++ ++ if (currentLevel == updatedSource) { ++ continue; ++ } ++ ret = true; ++ ++ if (updatedSource > currentLevel) { ++ // level increase ++ this.addToIncreaseWorkQueue(coordinate, updatedSource); ++ } else { ++ // level decrease ++ this.addToRemoveWorkQueue(coordinate, currentLevel); ++ // if the current coordinate is a source, then the decrease propagation will detect that and queue ++ // the source propagation ++ } ++ } ++ ++ this.updatedSources.clear(); ++ ++ // propagate source level increases first for performance reasons (in crowded areas hopefully the additions ++ // make the removes remove less) ++ this.propagateIncreases(); ++ ++ // now we propagate the decreases (which will then re-propagate clobbered sources) ++ this.propagateDecreases(); ++ ++ return ret; ++ } ++ ++ protected void propagateIncreases() { ++ for (int queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelIncreaseWorkQueueBitset); ++ this.levelIncreaseWorkQueueBitset != 0L; ++ this.levelIncreaseWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelIncreaseWorkQueueBitset)) { ++ ++ final WorkQueue queue = this.levelIncreaseWorkQueues[queueIndex]; ++ while (!queue.queuedLevels.isEmpty()) { ++ final long coordinate = queue.queuedCoordinates.removeFirstLong(); ++ byte level = queue.queuedLevels.removeFirstByte(); ++ ++ final boolean neighbourCheck = level < 0; ++ ++ final byte currentLevel; ++ if (neighbourCheck) { ++ level = (byte)-level; ++ currentLevel = this.levels.get(coordinate); ++ } else { ++ currentLevel = this.levels.putIfGreater(coordinate, level); ++ } ++ ++ if (neighbourCheck) { ++ // used when propagating from decrease to indicate that this level needs to check its neighbours ++ // this means the level at coordinate could be equal, but would still need neighbours checked ++ ++ if (currentLevel != level) { ++ // something caused the level to change, which means something propagated to it (which means ++ // us propagating here is redundant), or something removed the level (which means we ++ // cannot propagate further) ++ continue; ++ } ++ } else if (currentLevel >= level) { ++ // something higher/equal propagated ++ continue; ++ } ++ if (this.changeCallback != null) { ++ this.changeCallback.onLevelUpdate(coordinate, currentLevel, level); ++ } ++ ++ if (level == 1) { ++ // can't propagate 0 to neighbours ++ continue; ++ } ++ ++ // propagate to neighbours ++ final byte neighbourLevel = (byte)(level - 1); ++ final int x = (int)coordinate; ++ final int z = (int)(coordinate >>> 32); ++ ++ for (int dx = -1; dx <= 1; ++dx) { ++ for (int dz = -1; dz <= 1; ++dz) { ++ if ((dx | dz) == 0) { ++ // already propagated to coordinate ++ continue; ++ } ++ ++ // sure we can check the neighbour level in the map right now and avoid a propagation, ++ // but then we would still have to recheck it when popping the value off of the queue! ++ // so just avoid the double lookup ++ final long neighbourCoordinate = MCUtil.getCoordinateKey(x + dx, z + dz); ++ this.addToIncreaseWorkQueue(neighbourCoordinate, neighbourLevel); ++ } ++ } ++ } ++ } ++ } ++ ++ protected void propagateDecreases() { ++ for (int queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelRemoveWorkQueueBitset); ++ this.levelRemoveWorkQueueBitset != 0L; ++ this.levelRemoveWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelRemoveWorkQueueBitset)) { ++ ++ final WorkQueue queue = this.levelRemoveWorkQueues[queueIndex]; ++ while (!queue.queuedLevels.isEmpty()) { ++ final long coordinate = queue.queuedCoordinates.removeFirstLong(); ++ final byte level = queue.queuedLevels.removeFirstByte(); ++ ++ final byte currentLevel = this.levels.removeIfGreaterOrEqual(coordinate, level); ++ if (currentLevel == 0) { ++ // something else removed ++ continue; ++ } ++ ++ if (currentLevel > level) { ++ // something higher propagated here or we hit the propagation of another source ++ // in the second case we need to re-propagate because we could have just clobbered another source's ++ // propagation ++ this.addToIncreaseWorkQueue(coordinate, currentLevel, (byte)-currentLevel); // indicate to the increase code that the level's neighbours need checking ++ continue; ++ } ++ ++ if (this.changeCallback != null) { ++ this.changeCallback.onLevelUpdate(coordinate, currentLevel, (byte)0); ++ } ++ ++ final byte source = this.sources.get(coordinate); ++ if (source != 0) { ++ // must re-propagate source later ++ this.addToIncreaseWorkQueue(coordinate, source); ++ } ++ ++ if (level == 0) { ++ // can't propagate -1 to neighbours ++ // we have to check neighbours for removing 1 just in case the neighbour is 2 ++ continue; ++ } ++ ++ // propagate to neighbours ++ final byte neighbourLevel = (byte)(level - 1); ++ final int x = (int)coordinate; ++ final int z = (int)(coordinate >>> 32); ++ ++ for (int dx = -1; dx <= 1; ++dx) { ++ for (int dz = -1; dz <= 1; ++dz) { ++ if ((dx | dz) == 0) { ++ // already propagated to coordinate ++ continue; ++ } ++ ++ // sure we can check the neighbour level in the map right now and avoid a propagation, ++ // but then we would still have to recheck it when popping the value off of the queue! ++ // so just avoid the double lookup ++ final long neighbourCoordinate = MCUtil.getCoordinateKey(x + dx, z + dz); ++ this.addToRemoveWorkQueue(neighbourCoordinate, neighbourLevel); ++ } ++ } ++ } ++ } ++ ++ // propagate sources we clobbered in the process ++ this.propagateIncreases(); ++ } ++ ++ protected static final class LevelMap extends Long2ByteOpenHashMap { ++ public LevelMap() { ++ super(); ++ } ++ ++ public LevelMap(final int expected, final float loadFactor) { ++ super(expected, loadFactor); ++ } ++ ++ // copied from superclass ++ private int find(final long k) { ++ if (k == 0L) { ++ return this.containsNullKey ? this.n : -(this.n + 1); ++ } else { ++ final long[] key = this.key; ++ long curr; ++ int pos; ++ if ((curr = key[pos = (int)HashCommon.mix(k) & this.mask]) == 0L) { ++ return -(pos + 1); ++ } else if (k == curr) { ++ return pos; ++ } else { ++ while((curr = key[pos = pos + 1 & this.mask]) != 0L) { ++ if (k == curr) { ++ return pos; ++ } ++ } ++ ++ return -(pos + 1); ++ } ++ } ++ } ++ ++ // copied from superclass ++ private void insert(final int pos, final long k, final byte v) { ++ if (pos == this.n) { ++ this.containsNullKey = true; ++ } ++ ++ this.key[pos] = k; ++ this.value[pos] = v; ++ if (this.size++ >= this.maxFill) { ++ this.rehash(HashCommon.arraySize(this.size + 1, this.f)); ++ } ++ } ++ ++ // copied from superclass ++ public byte putIfGreater(final long key, final byte value) { ++ final int pos = this.find(key); ++ if (pos < 0) { ++ if (this.defRetValue < value) { ++ this.insert(-pos - 1, key, value); ++ } ++ return this.defRetValue; ++ } else { ++ final byte curr = this.value[pos]; ++ if (value > curr) { ++ this.value[pos] = value; ++ return curr; ++ } ++ return curr; ++ } ++ } ++ ++ // copied from superclass ++ private void removeEntry(final int pos) { ++ --this.size; ++ this.shiftKeys(pos); ++ if (this.n > this.minN && this.size < this.maxFill / 4 && this.n > 16) { ++ this.rehash(this.n / 2); ++ } ++ } ++ ++ // copied from superclass ++ private void removeNullEntry() { ++ this.containsNullKey = false; ++ --this.size; ++ if (this.n > this.minN && this.size < this.maxFill / 4 && this.n > 16) { ++ this.rehash(this.n / 2); ++ } ++ } ++ ++ // copied from superclass ++ public byte removeIfGreaterOrEqual(final long key, final byte value) { ++ if (key == 0L) { ++ if (!this.containsNullKey) { ++ return this.defRetValue; ++ } ++ final byte current = this.value[this.n]; ++ if (value >= current) { ++ this.removeNullEntry(); ++ return current; ++ } ++ return current; ++ } else { ++ long[] keys = this.key; ++ byte[] values = this.value; ++ long curr; ++ int pos; ++ if ((curr = keys[pos = (int)HashCommon.mix(key) & this.mask]) == 0L) { ++ return this.defRetValue; ++ } else if (key == curr) { ++ final byte current = values[pos]; ++ if (value >= current) { ++ this.removeEntry(pos); ++ return current; ++ } ++ return current; ++ } else { ++ while((curr = keys[pos = pos + 1 & this.mask]) != 0L) { ++ if (key == curr) { ++ final byte current = values[pos]; ++ if (value >= current) { ++ this.removeEntry(pos); ++ return current; ++ } ++ return current; ++ } ++ } ++ ++ return this.defRetValue; ++ } ++ } ++ } ++ } ++ ++ protected static final class WorkQueue { ++ ++ public final NoResizeLongArrayFIFODeque queuedCoordinates = new NoResizeLongArrayFIFODeque(); ++ public final NoResizeByteArrayFIFODeque queuedLevels = new NoResizeByteArrayFIFODeque(); ++ ++ } ++ ++ protected static final class NoResizeLongArrayFIFODeque extends LongArrayFIFOQueue { ++ ++ /** ++ * Assumes non-empty. If empty, undefined behaviour. ++ */ ++ public long removeFirstLong() { ++ // copied from superclass ++ long t = this.array[this.start]; ++ if (++this.start == this.length) { ++ this.start = 0; ++ } ++ ++ return t; ++ } ++ } ++ ++ protected static final class NoResizeByteArrayFIFODeque extends ByteArrayFIFOQueue { ++ ++ /** ++ * Assumes non-empty. If empty, undefined behaviour. ++ */ ++ public byte removeFirstByte() { ++ // copied from superclass ++ byte t = this.array[this.start]; ++ if (++this.start == this.length) { ++ this.start = 0; ++ } ++ ++ return t; ++ } ++ } ++} diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java index 771e4b72589d7117a154ab6917bd4a56d55f19db..65e0ca442980f273d2fe5f131e174cd92f80da20 100644 --- a/src/main/java/net/minecraft/Util.java @@ -2830,10 +4982,18 @@ index 0000000000000000000000000000000000000000..80f8d6ce6dd717d4b37b78539c65b6ac + } +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 6f56df5b0550735e3da122729080125c3dff5d06..dc7779b722cfa5337717b92896c168d839c14946 100644 +index 6f56df5b0550735e3da122729080125c3dff5d06..dab59e1a523ab14f28764b179931a7a54e1bf52e 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -964,6 +964,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function serverFactory) { + AtomicReference atomicreference = new AtomicReference(); +@@ -964,6 +965,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop>) completablefuture).thenApply((either1) -> { // CraftBukkit - decompile error Objects.requireNonNull(chunkStorage); return either1.ifLeft(chunkStorage::packTicks); -@@ -441,11 +457,19 @@ public class ChunkHolder { +@@ -441,12 +457,29 @@ public class ChunkHolder { if (!flag4 && flag5) { this.tickingChunkFuture = chunkStorage.prepareTickingChunk(this); this.scheduleFullChunkPromotion(chunkStorage, this.tickingChunkFuture, executor, ChunkHolder.FullChunkStatus.TICKING); @@ -2967,6 +5155,9 @@ index 6fa6fb852cd5af2009008ac6158251ba5e8cf6fb..24f72050229031898fd9da585ad2ceec + either.ifLeft(chunk -> { + // note: Here is a very good place to add callbacks to logic waiting on this. + ChunkHolder.this.isTickingReady = true; ++ // Paper start - ticking chunk set ++ ChunkHolder.this.chunkMap.level.getChunkSource().tickingChunks.add(chunk); ++ // Paper end - ticking chunk set + }); + }); + // Paper end @@ -2977,9 +5168,16 @@ index 6fa6fb852cd5af2009008ac6158251ba5e8cf6fb..24f72050229031898fd9da585ad2ceec - this.tickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK); + this.tickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK); this.isTickingReady = false; // Paper - cache chunk ticking stage this.tickingChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE; ++ // Paper start - ticking chunk set ++ LevelChunk chunkIfCached = this.getFullChunkUnchecked(); ++ if (chunkIfCached != null) { ++ this.chunkMap.level.getChunkSource().tickingChunks.remove(chunkIfCached); ++ } ++ // Paper end - ticking chunk set } -@@ -459,11 +483,18 @@ public class ChunkHolder { + boolean flag6 = playerchunk_state.isOrAfter(ChunkHolder.FullChunkStatus.ENTITY_TICKING); +@@ -459,12 +492,28 @@ public class ChunkHolder { this.entityTickingChunkFuture = chunkStorage.prepareEntityTickingChunk(this.pos); this.scheduleFullChunkPromotion(chunkStorage, this.entityTickingChunkFuture, executor, ChunkHolder.FullChunkStatus.ENTITY_TICKING); @@ -2987,6 +5185,9 @@ index 6fa6fb852cd5af2009008ac6158251ba5e8cf6fb..24f72050229031898fd9da585ad2ceec + this.entityTickingChunkFuture.thenAccept(either -> { + either.ifLeft(chunk -> { + ChunkHolder.this.isEntityTickingReady = true; ++ // Paper start - entity ticking chunk set ++ ChunkHolder.this.chunkMap.level.getChunkSource().entityTickingChunks.add(chunk); ++ // Paper end - entity ticking chunk set + }); + }); + // Paper end @@ -2997,10 +5198,17 @@ index 6fa6fb852cd5af2009008ac6158251ba5e8cf6fb..24f72050229031898fd9da585ad2ceec - this.entityTickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK); + this.entityTickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK); this.isEntityTickingReady = false; // Paper - cache chunk ticking stage this.entityTickingChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE; ++ // Paper start - entity ticking chunk set ++ LevelChunk chunkIfCached = this.getFullChunkUnchecked(); ++ if (chunkIfCached != null) { ++ this.chunkMap.level.getChunkSource().entityTickingChunks.remove(chunkIfCached); ++ } ++ // Paper end - entity ticking chunk set } + if (!playerchunk_state1.isOrAfter(playerchunk_state)) { diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index e9d2034f0753670c2ce69cc93c7e98e89af65c87..2b62f4664f439808661d559dc99762bfbac09b16 100644 +index e9d2034f0753670c2ce69cc93c7e98e89af65c87..12a9a6fa04867d5695d46bf7973f1542798485be 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java @@ -55,6 +55,7 @@ import net.minecraft.network.protocol.game.ClientboundSetChunkCacheCenterPacket; @@ -3011,7 +5219,7 @@ index e9d2034f0753670c2ce69cc93c7e98e89af65c87..2b62f4664f439808661d559dc99762bf import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.server.network.ServerPlayerConnection; import net.minecraft.util.CsvOutput; -@@ -152,6 +153,26 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -152,6 +153,56 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider }; // CraftBukkit end @@ -3034,11 +5242,52 @@ index e9d2034f0753670c2ce69cc93c7e98e89af65c87..2b62f4664f439808661d559dc99762bf + // Note: players need to be explicitly added to distance maps before they can be updated + } + // Paper end ++ // Paper start ++ public final List regionManagers = new java.util.ArrayList<>(); ++ public final io.papermc.paper.chunk.SingleThreadChunkRegionManager dataRegionManager; ++ ++ public static final class DataRegionData implements io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionData { ++ } ++ ++ public static final class DataRegionSectionData implements io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSectionData { ++ ++ @Override ++ public void removeFromRegion(final io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section, ++ final io.papermc.paper.chunk.SingleThreadChunkRegionManager.Region from) { ++ final DataRegionSectionData sectionData = (DataRegionSectionData)section.sectionData; ++ final DataRegionData fromData = (DataRegionData)from.regionData; ++ } ++ ++ @Override ++ public void addToRegion(final io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section, ++ final io.papermc.paper.chunk.SingleThreadChunkRegionManager.Region oldRegion, ++ final io.papermc.paper.chunk.SingleThreadChunkRegionManager.Region newRegion) { ++ final DataRegionSectionData sectionData = (DataRegionSectionData)section.sectionData; ++ final DataRegionData oldRegionData = oldRegion == null ? null : (DataRegionData)oldRegion.regionData; ++ final DataRegionData newRegionData = (DataRegionData)newRegion.regionData; ++ } ++ } ++ ++ public final ChunkHolder getUnloadingChunkHolder(int chunkX, int chunkZ) { ++ return this.pendingUnloads.get(io.papermc.paper.util.CoordinateUtils.getChunkKey(chunkX, chunkZ)); ++ } ++ // Tuiniy end + public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureManager structureManager, Executor executor, BlockableEventLoop mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier persistentStateManagerFactory, int viewDistance, boolean dsync) { super(new File(session.getDimensionPath(world.dimension()), "region"), dataFixer, dsync); this.visibleChunkMap = this.updatingChunkMap.clone(); -@@ -273,6 +294,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -187,6 +238,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + this.overworldDataStorage = persistentStateManagerFactory; + this.poiManager = new PoiManager(new File(file, "poi"), dataFixer, dsync, world); + this.setViewDistance(viewDistance); ++ // Paper start ++ this.dataRegionManager = new io.papermc.paper.chunk.SingleThreadChunkRegionManager(this.level, 2, (1.0 / 3.0), 1, 6, "Data", DataRegionData::new, DataRegionSectionData::new); ++ this.regionManagers.add(this.dataRegionManager); ++ // Paper end + } + + private static double euclideanDistanceSquared(ChunkPos pos, Entity entity) { +@@ -273,6 +328,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } } @@ -3053,7 +5302,47 @@ index e9d2034f0753670c2ce69cc93c7e98e89af65c87..2b62f4664f439808661d559dc99762bf private CompletableFuture, ChunkHolder.ChunkLoadingFailure>> getChunkRangeFuture(ChunkPos centerChunk, int margin, IntFunction distanceToStatus) { List>> list = Lists.newArrayList(); int j = centerChunk.x; -@@ -962,6 +991,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -363,6 +426,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + holder.setTicketLevel(level); + } else { + holder = new ChunkHolder(new ChunkPos(pos), level, this.level, this.lightEngine, this.queueSorter, this); ++ // Paper start ++ for (int index = 0, len = this.regionManagers.size(); index < len; ++index) { ++ this.regionManagers.get(index).addChunk(holder.pos.x, holder.pos.z); ++ } ++ // Paper end + } + + this.updatingChunkMap.put(pos, holder); +@@ -484,7 +552,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + if (completablefuture1 != completablefuture) { + this.scheduleUnload(pos, holder); + } else { +- if (this.pendingUnloads.remove(pos, holder) && ichunkaccess != null) { ++ // Paper start ++ boolean removed; ++ if ((removed = this.pendingUnloads.remove(pos, holder)) && ichunkaccess != null) { ++ for (int index = 0, len = this.regionManagers.size(); index < len; ++index) { ++ this.regionManagers.get(index).removeChunk(holder.pos.x, holder.pos.z); ++ } ++ // Paper end + if (ichunkaccess instanceof LevelChunk) { + ((LevelChunk) ichunkaccess).setLoaded(false); + } +@@ -499,7 +573,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + this.lightEngine.updateChunkStatus(ichunkaccess.getPos()); + this.lightEngine.tryScheduleUpdate(); + this.progressListener.onStatusChange(ichunkaccess.getPos(), (ChunkStatus) null); +- } ++ } else if (removed) { // Paper start ++ for (int index = 0, len = this.regionManagers.size(); index < len; ++index) { ++ this.regionManagers.get(index).removeChunk(holder.pos.x, holder.pos.z); ++ } ++ } // Paper end + + } + }; +@@ -962,6 +1040,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider if (!flag1) { this.distanceManager.addPlayer(SectionPos.of((Entity) player), player); } @@ -3061,7 +5350,7 @@ index e9d2034f0753670c2ce69cc93c7e98e89af65c87..2b62f4664f439808661d559dc99762bf } else { SectionPos sectionposition = player.getLastSectionPos(); -@@ -969,6 +999,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -969,6 +1048,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider if (!flag2) { this.distanceManager.removePlayer(sectionposition, player); } @@ -3069,7 +5358,7 @@ index e9d2034f0753670c2ce69cc93c7e98e89af65c87..2b62f4664f439808661d559dc99762bf } for (int k = i - this.viewDistance; k <= i + this.viewDistance; ++k) { -@@ -1079,6 +1110,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1079,6 +1159,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } } @@ -3079,18 +5368,21 @@ index e9d2034f0753670c2ce69cc93c7e98e89af65c87..2b62f4664f439808661d559dc99762bf @Override diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 9591f50922343283597bad6d9ac17c175d8ae230..48c876381d75c66f24d59bd2c415dd7de293afee 100644 +index 9591f50922343283597bad6d9ac17c175d8ae230..77d98bfa0ad9fc92a8e794b5a4e00c3e471c123a 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -44,6 +44,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureMana +@@ -44,8 +44,10 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureMana import net.minecraft.world.level.storage.DimensionDataStorage; import net.minecraft.world.level.storage.LevelData; import net.minecraft.world.level.storage.LevelStorageSource; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; // Paper public class ServerChunkCache extends ChunkSource { ++ public static final org.apache.logging.log4j.Logger LOGGER = org.apache.logging.log4j.LogManager.getLogger(); // Paper -@@ -66,6 +67,156 @@ public class ServerChunkCache extends ChunkSource { + public static final List CHUNK_STATUSES = ChunkStatus.getStatusList(); + private final DistanceManager distanceManager; +@@ -66,6 +68,316 @@ public class ServerChunkCache extends ChunkSource { @Nullable @VisibleForDebug private NaturalSpawner.SpawnState lastSpawnState; @@ -3165,7 +5457,7 @@ index 9591f50922343283597bad6d9ac17c175d8ae230..48c876381d75c66f24d59bd2c415dd7d + return (LevelChunk)this.getChunk(x, z, ChunkStatus.FULL, true); + } + -+ private long chunkFutureAwaitCounter; ++ long chunkFutureAwaitCounter; // Paper - private -> package private + + public void getEntityTickingChunkAsync(int x, int z, java.util.function.Consumer onLoad) { + if (Thread.currentThread() != this.mainThread) { @@ -3243,11 +5535,171 @@ index 9591f50922343283597bad6d9ac17c175d8ae230..48c876381d75c66f24d59bd2c415dd7d + } + }, this.mainThreadProcessor); + } ++ // Paper end ++ ++ // Paper start ++ // this will try to avoid chunk neighbours for lighting ++ public final ChunkAccess getFullStatusChunkAt(int chunkX, int chunkZ) { ++ LevelChunk ifLoaded = this.getChunkAtIfLoadedImmediately(chunkX, chunkZ); ++ if (ifLoaded != null) { ++ return ifLoaded; ++ } ++ ++ ChunkAccess empty = this.getChunk(chunkX, chunkZ, ChunkStatus.EMPTY, true); ++ if (empty != null && empty.getStatus().isOrAfter(ChunkStatus.FULL)) { ++ return empty; ++ } ++ return this.getChunk(chunkX, chunkZ, ChunkStatus.FULL, true); ++ } ++ ++ public final ChunkAccess getFullStatusChunkAtIfLoaded(int chunkX, int chunkZ) { ++ LevelChunk ifLoaded = this.getChunkAtIfLoadedImmediately(chunkX, chunkZ); ++ if (ifLoaded != null) { ++ return ifLoaded; ++ } ++ ++ ChunkAccess ret = this.getChunkAtImmediately(chunkX, chunkZ); ++ if (ret != null && ret.getStatus().isOrAfter(ChunkStatus.FULL)) { ++ return ret; ++ } else { ++ return null; ++ } ++ } ++ ++ void getChunkAtAsynchronously(int chunkX, int chunkZ, int ticketLevel, ++ java.util.function.Consumer consumer) { ++ this.getChunkAtAsynchronously(chunkX, chunkZ, ticketLevel, (ChunkHolder chunkHolder) -> { ++ if (ticketLevel <= 33) { ++ return (CompletableFuture)chunkHolder.getFullChunkFuture(); ++ } else { ++ return chunkHolder.getOrScheduleFuture(ChunkHolder.getStatus(ticketLevel), ServerChunkCache.this.chunkMap); ++ } ++ }, consumer); ++ } ++ ++ void getChunkAtAsynchronously(int chunkX, int chunkZ, int ticketLevel, ++ java.util.function.Function>> function, ++ java.util.function.Consumer consumer) { ++ if (Thread.currentThread() != this.mainThread) { ++ throw new IllegalStateException(); ++ } ++ ++ ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); ++ Long identifier = Long.valueOf(this.chunkFutureAwaitCounter++); ++ this.addTicketAtLevel(TicketType.FUTURE_AWAIT, chunkPos, ticketLevel, identifier); ++ this.runDistanceManagerUpdates(); ++ ++ ChunkHolder chunk = this.chunkMap.getUpdatingChunkIfPresent(chunkPos.toLong()); ++ ++ if (chunk == null) { ++ throw new IllegalStateException("Expected playerchunk " + chunkPos + " in world '" + this.level.getWorld().getName() + "'"); ++ } ++ ++ CompletableFuture> future = function.apply(chunk); ++ ++ future.whenCompleteAsync((either, throwable) -> { ++ try { ++ if (throwable != null) { ++ if (throwable instanceof ThreadDeath) { ++ throw (ThreadDeath)throwable; ++ } ++ LOGGER.fatal("Failed to complete future await for chunk " + chunkPos.toString() + " in world '" + ServerChunkCache.this.level.getWorld().getName() + "'", throwable); ++ } else if (either.right().isPresent()) { ++ LOGGER.fatal("Failed to complete future await for chunk " + chunkPos.toString() + " in world '" + ServerChunkCache.this.level.getWorld().getName() + "': " + either.right().get().toString()); ++ } ++ ++ try { ++ if (consumer != null) { ++ consumer.accept(either == null ? null : either.left().orElse(null)); // indicate failure to the callback. ++ } ++ } catch (Throwable thr) { ++ if (thr instanceof ThreadDeath) { ++ throw (ThreadDeath)thr; ++ } ++ LOGGER.fatal("Load callback for future await failed " + chunkPos.toString() + " in world '" + ServerChunkCache.this.level.getWorld().getName() + "'", thr); ++ return; ++ } ++ } finally { ++ // due to odd behaviour with CB unload implementation we need to have these AFTER the load callback. ++ ServerChunkCache.this.addTicketAtLevel(TicketType.UNKNOWN, chunkPos, ticketLevel, chunkPos); ++ ServerChunkCache.this.removeTicketAtLevel(TicketType.FUTURE_AWAIT, chunkPos, ticketLevel, identifier); ++ } ++ }, this.mainThreadProcessor); ++ } ++ ++ void chunkLoadAccept(int chunkX, int chunkZ, ChunkAccess chunk, java.util.function.Consumer consumer) { ++ try { ++ consumer.accept(chunk); ++ } catch (Throwable throwable) { ++ if (throwable instanceof ThreadDeath) { ++ throw (ThreadDeath)throwable; ++ } ++ LOGGER.error("Load callback for chunk " + chunkX + "," + chunkZ + " in world '" + this.level.getWorld().getName() + "' threw an exception", throwable); ++ } ++ } ++ ++ public final void getChunkAtAsynchronously(int chunkX, int chunkZ, ChunkStatus status, boolean gen, boolean allowSubTicketLevel, java.util.function.Consumer onLoad) { ++ // try to fire sync ++ int chunkStatusTicketLevel = 33 + ChunkStatus.getDistance(status); ++ ChunkHolder playerChunk = this.chunkMap.getUpdatingChunkIfPresent(io.papermc.paper.util.CoordinateUtils.getChunkKey(chunkX, chunkZ)); ++ if (playerChunk != null) { ++ ChunkStatus holderStatus = playerChunk.getChunkHolderStatus(); ++ ChunkAccess immediate = playerChunk.getAvailableChunkNow(); ++ if (immediate != null) { ++ if (allowSubTicketLevel ? immediate.getStatus().isOrAfter(status) : (playerChunk.getTicketLevel() <= chunkStatusTicketLevel && holderStatus != null && holderStatus.isOrAfter(status))) { ++ this.chunkLoadAccept(chunkX, chunkZ, immediate, onLoad); ++ return; ++ } else { ++ if (gen || (!allowSubTicketLevel && immediate.getStatus().isOrAfter(status))) { ++ this.getChunkAtAsynchronously(chunkX, chunkZ, chunkStatusTicketLevel, onLoad); ++ return; ++ } else { ++ this.chunkLoadAccept(chunkX, chunkZ, null, onLoad); ++ return; ++ } ++ } ++ } ++ } ++ ++ // need to fire async ++ ++ if (gen && !allowSubTicketLevel) { ++ this.getChunkAtAsynchronously(chunkX, chunkZ, chunkStatusTicketLevel, onLoad); ++ return; ++ } ++ ++ this.getChunkAtAsynchronously(chunkX, chunkZ, net.minecraft.server.MCUtil.getTicketLevelFor(ChunkStatus.EMPTY), (ChunkAccess chunk) -> { ++ if (chunk == null) { ++ throw new IllegalStateException("Chunk cannot be null"); ++ } ++ ++ if (!chunk.getStatus().isOrAfter(status)) { ++ if (gen) { ++ this.getChunkAtAsynchronously(chunkX, chunkZ, chunkStatusTicketLevel, onLoad); ++ return; ++ } else { ++ ServerChunkCache.this.chunkLoadAccept(chunkX, chunkZ, null, onLoad); ++ return; ++ } ++ } else { ++ if (allowSubTicketLevel) { ++ ServerChunkCache.this.chunkLoadAccept(chunkX, chunkZ, chunk, onLoad); ++ return; ++ } else { ++ this.getChunkAtAsynchronously(chunkX, chunkZ, chunkStatusTicketLevel, onLoad); ++ return; ++ } ++ } ++ }); ++ } ++ ++ final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet tickingChunks = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(4096, 0.75f, 4096, 0.15, true); ++ final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet entityTickingChunks = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(4096, 0.75f, 4096, 0.15, true); + // Paper end public ServerChunkCache(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureManager structureManager, Executor workerExecutor, ChunkGenerator chunkGenerator, int viewDistance, boolean flag, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkstatusupdatelistener, Supplier supplier) { this.level = world; -@@ -127,6 +278,49 @@ public class ServerChunkCache extends ChunkSource { +@@ -127,6 +439,49 @@ public class ServerChunkCache extends ChunkSource { this.lastChunk[0] = chunk; } @@ -3297,7 +5749,7 @@ index 9591f50922343283597bad6d9ac17c175d8ae230..48c876381d75c66f24d59bd2c415dd7d @Nullable @Override public ChunkAccess getChunk(int x, int z, ChunkStatus leastStatus, boolean create) { -@@ -453,7 +647,7 @@ public class ServerChunkCache extends ChunkSource { +@@ -453,7 +808,7 @@ public class ServerChunkCache extends ChunkSource { } this.level.getProfiler().popPush("broadcast"); @@ -3307,7 +5759,7 @@ index 9591f50922343283597bad6d9ac17c175d8ae230..48c876381d75c66f24d59bd2c415dd7d Objects.requireNonNull(playerchunk); diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 7cf9c202f126696fc37860d4121fb47456b0435a..f9fde3155944bb44ecb14524c585ed8eb9eac78c 100644 +index 7cf9c202f126696fc37860d4121fb47456b0435a..24b763a7d333be18e3b31b20f9405eb814741f8a 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java @@ -9,6 +9,7 @@ import it.unimi.dsi.fastutil.longs.LongSet; @@ -3318,8 +5770,113 @@ index 7cf9c202f126696fc37860d4121fb47456b0435a..f9fde3155944bb44ecb14524c585ed8e import it.unimi.dsi.fastutil.objects.ObjectIterator; import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +@@ -161,6 +162,7 @@ import org.bukkit.event.server.MapInitializeEvent; + import org.bukkit.event.weather.LightningStrikeEvent; + import org.bukkit.event.world.TimeSkipEvent; + // CraftBukkit end ++import it.unimi.dsi.fastutil.ints.IntArrayList; // Paper + + public class ServerLevel extends Level implements WorldGenLevel { + +@@ -200,6 +202,96 @@ public class ServerLevel extends Level implements WorldGenLevel { + return this.chunkSource.getChunk(x, z, false); + } + ++ // Paper start ++ public final boolean areChunksLoadedForMove(AABB axisalignedbb) { ++ // copied code from collision methods, so that we can guarantee that they wont load chunks (we don't override ++ // ICollisionAccess methods for VoxelShapes) ++ // be more strict too, add a block (dumb plugins in move events?) ++ int minBlockX = Mth.floor(axisalignedbb.minX - 1.0E-7D) - 3; ++ int maxBlockX = Mth.floor(axisalignedbb.maxX + 1.0E-7D) + 3; ++ ++ int minBlockZ = Mth.floor(axisalignedbb.minZ - 1.0E-7D) - 3; ++ int maxBlockZ = Mth.floor(axisalignedbb.maxZ + 1.0E-7D) + 3; ++ ++ int minChunkX = minBlockX >> 4; ++ int maxChunkX = maxBlockX >> 4; ++ ++ int minChunkZ = minBlockZ >> 4; ++ int maxChunkZ = maxBlockZ >> 4; ++ ++ ServerChunkCache chunkProvider = this.getChunkSource(); ++ ++ for (int cx = minChunkX; cx <= maxChunkX; ++cx) { ++ for (int cz = minChunkZ; cz <= maxChunkZ; ++cz) { ++ if (chunkProvider.getChunkAtIfLoadedImmediately(cx, cz) == null) { ++ return false; ++ } ++ } ++ } ++ ++ return true; ++ } ++ ++ public final void loadChunksForMoveAsync(AABB axisalignedbb, double toX, double toZ, ++ java.util.function.Consumer> onLoad) { ++ if (Thread.currentThread() != this.thread) { ++ this.getChunkSource().mainThreadProcessor.execute(() -> { ++ this.loadChunksForMoveAsync(axisalignedbb, toX, toZ, onLoad); ++ }); ++ return; ++ } ++ List ret = new java.util.ArrayList<>(); ++ IntArrayList ticketLevels = new IntArrayList(); ++ ++ int minBlockX = Mth.floor(axisalignedbb.minX - 1.0E-7D) - 3; ++ int maxBlockX = Mth.floor(axisalignedbb.maxX + 1.0E-7D) + 3; ++ ++ int minBlockZ = Mth.floor(axisalignedbb.minZ - 1.0E-7D) - 3; ++ int maxBlockZ = Mth.floor(axisalignedbb.maxZ + 1.0E-7D) + 3; ++ ++ int minChunkX = minBlockX >> 4; ++ int maxChunkX = maxBlockX >> 4; ++ ++ int minChunkZ = minBlockZ >> 4; ++ int maxChunkZ = maxBlockZ >> 4; ++ ++ ServerChunkCache chunkProvider = this.getChunkSource(); ++ ++ int requiredChunks = (maxChunkX - minChunkX + 1) * (maxChunkZ - minChunkZ + 1); ++ int[] loadedChunks = new int[1]; ++ ++ Long holderIdentifier = Long.valueOf(chunkProvider.chunkFutureAwaitCounter++); ++ ++ java.util.function.Consumer consumer = (net.minecraft.world.level.chunk.ChunkAccess chunk) -> { ++ if (chunk != null) { ++ int ticketLevel = Math.max(33, chunkProvider.chunkMap.getUpdatingChunkIfPresent(chunk.getPos().toLong()).getTicketLevel()); ++ ret.add(chunk); ++ ticketLevels.add(ticketLevel); ++ chunkProvider.addTicketAtLevel(TicketType.FUTURE_AWAIT, chunk.getPos(), ticketLevel, holderIdentifier); ++ } ++ if (++loadedChunks[0] == requiredChunks) { ++ try { ++ onLoad.accept(java.util.Collections.unmodifiableList(ret)); ++ } finally { ++ for (int i = 0, len = ret.size(); i < len; ++i) { ++ ChunkPos chunkPos = ret.get(i).getPos(); ++ int ticketLevel = ticketLevels.getInt(i); ++ ++ chunkProvider.addTicketAtLevel(TicketType.UNKNOWN, chunkPos, ticketLevel, chunkPos); ++ chunkProvider.removeTicketAtLevel(TicketType.FUTURE_AWAIT, chunkPos, ticketLevel, holderIdentifier); ++ } ++ } ++ } ++ }; ++ ++ for (int cx = minChunkX; cx <= maxChunkX; ++cx) { ++ for (int cz = minChunkZ; cz <= maxChunkZ; ++cz) { ++ chunkProvider.getChunkAtAsynchronously(cx, cz, net.minecraft.world.level.chunk.ChunkStatus.FULL, true, false, consumer); ++ } ++ } ++ } ++ // Paper end ++ + // 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) { + // Objects.requireNonNull(minecraftserver); // CraftBukkit - decompile error diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 725dcd9dfdf27aff70c5c56277ff29bb6eee612b..b3ce71df7ed67583925b21b59d8f1ccf9ed5beda 100644 +index 6c7c9750d67c914afaf4ecc48facac3189eba75b..89c718e5b975a83b07a114b2b2c6fe31c75cb580 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -229,6 +229,8 @@ public class ServerPlayer extends Player { @@ -3418,6 +5975,22 @@ index a4c5edee297af6d68d518b77f706732b5ccbe4de..7bf4bf5cb2c1b54a7e2733091f48f3a8 @Override public void tell(R runnable) { +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index c57bde6b2042c0a68cb02f43c28cae0070f38111..a5f704edf4d8afdb088f3a4ac8e3957f8851842d 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -298,6 +298,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { + return this.level.hasChunk((int) Math.floor(this.getX()) >> 4, (int) Math.floor(this.getZ()) >> 4); + } + // CraftBukkit end ++ // Paper start ++ public final AABB getBoundingBoxAt(double x, double y, double z) { ++ return this.dimensions.makeBoundingBox(x, y, z); ++ } ++ // Paper end + + public Entity(EntityType type, Level world) { + this.id = Entity.ENTITY_COUNTER.incrementAndGet(); diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java index e01a7a18d026a8b794a575dd2f5dc907c510fda4..2473816c70c05662d75f6b875b46a4e53d7b76fa 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java @@ -3758,6 +6331,58 @@ index 00118cc80ebc31e5fac95c31c07634f0e2904263..138b6792bc6ee26e0b9aaaef7bf58fb2 @Nullable @Override public BlockEntity getBlockEntity(BlockPos pos) { +diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java +index 505731735126e81a4cc768311dce337385e5503f..adf1c04d34383d088a3fc8c06cdc8b55552c354f 100644 +--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java ++++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java +@@ -603,14 +603,14 @@ public abstract class BlockBehaviour { + + public abstract static class BlockStateBase extends StateHolder { + +- private final int lightEmission; +- private final boolean useShapeForLightOcclusion; ++ private final int lightEmission; public final int getEmittedLight() { return this.lightEmission; } // Paper - OBFHELPER ++ private final boolean useShapeForLightOcclusion; public final boolean isTransparentOnSomeFaces() { return this.useShapeForLightOcclusion; } // Paper - OBFHELPER + private final boolean isAir; + private final Material material; + private final MaterialColor materialColor; + public final float destroySpeed; + private final boolean requiresCorrectToolForDrops; +- private final boolean canOcclude; ++ private final boolean canOcclude; public final boolean isOpaque() { return this.canOcclude; } // Paper - OBFHELPER + private final BlockBehaviour.StatePredicate isRedstoneConductor; + private final BlockBehaviour.StatePredicate isSuffocating; + private final BlockBehaviour.StatePredicate isViewBlocking; +@@ -638,10 +638,18 @@ public abstract class BlockBehaviour { + this.emissiveRendering = blockbase_info.emissiveRendering; + } + ++ // Paper start ++ protected boolean shapeExceedsCube = true; ++ public final boolean shapeExceedsCube() { ++ return this.shapeExceedsCube; ++ } ++ // Paper end ++ + public void initCache() { + if (!this.getBlock().hasDynamicShape()) { + this.cache = new BlockBehaviour.BlockStateBase.Cache(this.asState()); + } ++ this.shapeExceedsCube = this.cache == null || this.cache.largeCollisionShape; // Paper - moved from actual method to here + + } + +@@ -673,8 +681,8 @@ public abstract class BlockBehaviour { + return this.getBlock().getOcclusionShape(this.asState(), world, pos); + } + +- public boolean hasLargeCollisionShape() { +- return this.cache == null || this.cache.largeCollisionShape; ++ public final boolean hasLargeCollisionShape() { // Paper ++ return this.shapeExceedsCube; // Paper - moved into shape cache init + } + + public boolean useShapeForLightOcclusion() { 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 0d117a6b319340a0f13a516a6c43501f752bc89a..e9a04017df42312e4e0e7e414c9ccc95c71ddae1 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java @@ -4271,6 +6896,43 @@ index 93d356ef2aa4d66126312fa7f2b49cb7d3b0c835..c5d132dcf70a55de041c0187ff8fb889 @Override public WorldBorder getWorldBorder() { throw new UnsupportedOperationException("Not supported yet."); +diff --git a/src/main/java/org/bukkit/craftbukkit/util/UnsafeList.java b/src/main/java/org/bukkit/craftbukkit/util/UnsafeList.java +index d40c0d8be1b0153d62021b8bcb6e8b37fd0acb4e..e38e57b1f9ef27020de35d7ddcb36a663140f880 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/UnsafeList.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/UnsafeList.java +@@ -119,6 +119,32 @@ public class UnsafeList extends AbstractList implements List, RandomAcc + return this.indexOf(o) >= 0; + } + ++ // Paper start ++ protected transient int maxSize; ++ public void setSize(int size) { ++ if (this.maxSize < this.size) { ++ this.maxSize = this.size; ++ } ++ this.size = size; ++ } ++ ++ public void completeReset() { ++ if (this.data != null) { ++ Arrays.fill(this.data, 0, Math.max(this.size, this.maxSize), null); ++ } ++ this.size = 0; ++ this.maxSize = 0; ++ if (this.iterPool != null) { ++ for (Iterator temp : this.iterPool) { ++ if (temp == null) { ++ continue; ++ } ++ ((Itr)temp).valid = false; ++ } ++ } ++ } ++ // Paper end ++ + @Override + public void clear() { + // Create new array to reset memory usage to initial capacity diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java index acf13887957c899b986a9aabe859bf2405e464ac..58c9ab2f6db97bfbf280efc56f9c9be791604a75 100644 --- a/src/main/java/org/spigotmc/SpigotConfig.java diff --git a/patches/server/0007-Not-implemeneted.patch b/patches/server/0007-Not-implemeneted.patch new file mode 100644 index 0000000000..4c0c7c371b --- /dev/null +++ b/patches/server/0007-Not-implemeneted.patch @@ -0,0 +1,67 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sun, 3 Mar 2019 20:53:18 -0800 +Subject: [PATCH] Not implemeneted + +Currently a placeholder patch. + +diff --git a/src/main/java/io/papermc/paper/util/TickThread.java b/src/main/java/io/papermc/paper/util/TickThread.java +new file mode 100644 +index 0000000000000000000000000000000000000000..728c39bb163b2334da9915441fd46970b16cf2a9 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/TickThread.java +@@ -0,0 +1,41 @@ ++package io.papermc.paper.util; ++ ++import net.minecraft.server.MinecraftServer; ++import org.bukkit.Bukkit; ++ ++public final class TickThread extends Thread { ++ ++ public static final boolean STRICT_THREAD_CHECKS = Boolean.getBoolean("paper.strict-thread-checks"); ++ ++ static { ++ if (STRICT_THREAD_CHECKS) { ++ MinecraftServer.LOGGER.warn("Strict thread checks enabled - performance may suffer"); ++ } ++ } ++ ++ public static void softEnsureTickThread(final String reason) { ++ if (!STRICT_THREAD_CHECKS) { ++ return; ++ } ++ ensureTickThread(reason); ++ } ++ ++ ++ public static void ensureTickThread(final String reason) { ++ if (!Bukkit.isPrimaryThread()) { ++ MinecraftServer.LOGGER.fatal("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable()); ++ throw new IllegalStateException(reason); ++ } ++ } ++ ++ public final int id; /* We don't override getId as the spec requires that it be unique (with respect to all other threads) */ ++ ++ public TickThread(final Runnable run, final String name, final int id) { ++ super(run, name); ++ this.id = id; ++ } ++ ++ public static TickThread getCurrentTickThread() { ++ return (TickThread)Thread.currentThread(); ++ } ++} +diff --git a/src/main/java/org/spigotmc/AsyncCatcher.java b/src/main/java/org/spigotmc/AsyncCatcher.java +index bbf0d9d9c44fe8d7add2f978994ec129420814c7..78669fa035b7537ff7e533cf32aaf2995625424f 100644 +--- a/src/main/java/org/spigotmc/AsyncCatcher.java ++++ b/src/main/java/org/spigotmc/AsyncCatcher.java +@@ -9,7 +9,7 @@ public class AsyncCatcher + + public static void catchOp(String reason) + { +- if ( AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().serverThread ) ++ if ( (AsyncCatcher.enabled || io.papermc.paper.util.TickThread.STRICT_THREAD_CHECKS) && Thread.currentThread() != MinecraftServer.getServer().serverThread ) // Paper + { + throw new IllegalStateException( "Asynchronous " + reason + "!" ); + } diff --git a/patches/server/0007-Paper-Metrics.patch b/patches/server/0008-Paper-Metrics.patch similarity index 99% rename from patches/server/0007-Paper-Metrics.patch rename to patches/server/0008-Paper-Metrics.patch index c94eb31fa6..a209b93e95 100644 --- a/patches/server/0007-Paper-Metrics.patch +++ b/patches/server/0008-Paper-Metrics.patch @@ -690,7 +690,7 @@ index 0000000000000000000000000000000000000000..e3b74dbdf8e14219a56fab939f3174e0 + } +} diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 2c0514892d3993bef57ecf677cf8bb0fbe0216e4..da922f395f0fff0881ead893c900c5b2623f48f0 100644 +index a632a796a3e5f2b361a521d70581b83a6839fc73..644a26bd5ac49e67a04ced0ac07097e22fe064f5 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -42,6 +42,7 @@ public class PaperConfig { @@ -714,7 +714,7 @@ index 2c0514892d3993bef57ecf677cf8bb0fbe0216e4..da922f395f0fff0881ead893c900c5b2 static void readConfig(Class clazz, Object instance) { diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java -index e38b5957b015be3c835ca28a9fe6ee75d7954393..2d226fd06759ed92bf5591259fc86f34f606a3ec 100644 +index 58c9ab2f6db97bfbf280efc56f9c9be791604a75..f8a9d6a394f796634e4663ef4078a4c98447e13c 100644 --- a/src/main/java/org/spigotmc/SpigotConfig.java +++ b/src/main/java/org/spigotmc/SpigotConfig.java @@ -83,6 +83,7 @@ public class SpigotConfig diff --git a/patches/server/0008-Add-MinecraftKey-Information-to-Objects.patch b/patches/server/0009-Add-MinecraftKey-Information-to-Objects.patch similarity index 97% rename from patches/server/0008-Add-MinecraftKey-Information-to-Objects.patch rename to patches/server/0009-Add-MinecraftKey-Information-to-Objects.patch index 6de4887420..76cde33920 100644 --- a/patches/server/0008-Add-MinecraftKey-Information-to-Objects.patch +++ b/patches/server/0009-Add-MinecraftKey-Information-to-Objects.patch @@ -37,7 +37,7 @@ index 0000000000000000000000000000000000000000..d02bd109399d6b32cbbb5e6f9ec7e650 + } +} diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index c57bde6b2042c0a68cb02f43c28cae0070f38111..89ed84d2e3ac8f25785a2d39b4fd37e50497e49e 100644 +index a5f704edf4d8afdb088f3a4ac8e3957f8851842d..264d216429c164798d5e645c044b95892b3b615e 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -146,7 +146,7 @@ import org.bukkit.event.player.PlayerTeleportEvent; @@ -49,7 +49,7 @@ index c57bde6b2042c0a68cb02f43c28cae0070f38111..89ed84d2e3ac8f25785a2d39b4fd37e5 // CraftBukkit start private static final int CURRENT_LEVEL = 2; -@@ -1955,12 +1955,31 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -1960,12 +1960,31 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { return true; } diff --git a/patches/server/0009-Timings-v2.patch b/patches/server/0010-Timings-v2.patch similarity index 97% rename from patches/server/0009-Timings-v2.patch rename to patches/server/0010-Timings-v2.patch index 8aaba9363e..8db7e16161 100644 --- a/patches/server/0009-Timings-v2.patch +++ b/patches/server/0010-Timings-v2.patch @@ -669,7 +669,7 @@ index 0000000000000000000000000000000000000000..0fda52841b5e1643efeda92106124998 + } +} diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 99ad5dfe52534048078a386020c4f3f277020107..997041d4bc1cd6be97a2d403723eded7eda60351 100644 +index 644a26bd5ac49e67a04ced0ac07097e22fe064f5..666d956d0417d02a3c9e78a688edbe66e42cce18 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -14,12 +14,15 @@ import java.util.concurrent.TimeUnit; @@ -772,7 +772,7 @@ index b3a6aeba2363d283f03982cf749f25cfa11a5052..449f1b2f5dca350dc0912e14c8c2bf3e PacketUtils.LOGGER.debug("Ignoring packet due to disconnection: {}", packet); } diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index dc7779b722cfa5337717b92896c168d839c14946..b3707d09bd73c306ea5e87785e6754ede9fc2846 100644 +index dab59e1a523ab14f28764b179931a7a54e1bf52e..5de0b15ee206ad01b1b4522b2d375fae08d2486f 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -182,7 +182,7 @@ import org.bukkit.craftbukkit.generator.CustomWorldChunkManager; @@ -784,7 +784,7 @@ index dc7779b722cfa5337717b92896c168d839c14946..b3707d09bd73c306ea5e87785e6754ed import org.spigotmc.SlackActivityAccountant; // Spigot public abstract class MinecraftServer extends ReentrantBlockableEventLoop implements SnooperPopulator, CommandSource, AutoCloseable { -@@ -910,6 +910,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= 5000000000L) { -@@ -1231,14 +1252,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0 && this.tickCount % this.autosavePeriod == 0) { // CraftBukkit @@ -861,9 +861,9 @@ index dc7779b722cfa5337717b92896c168d839c14946..b3707d09bd73c306ea5e87785e6754ed } this.profiler.push("snooper"); -@@ -1251,6 +1270,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop> scheduleChunkLoad(ChunkPos pos) { return CompletableFuture.supplyAsync(() -> { @@ -1080,7 +1080,7 @@ index 2b62f4664f439808661d559dc99762bfbac09b16..d5067a9a4931e0bf267dbacc925bef80 boolean flag = nbttagcompound.contains("Level", 10) && nbttagcompound.getCompound("Level").contains("Status", 8); if (flag) { -@@ -595,7 +600,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -644,7 +649,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } ChunkMap.LOGGER.error("Chunk file at {} is missing level data, skipping", pos); @@ -1089,7 +1089,7 @@ index 2b62f4664f439808661d559dc99762bfbac09b16..d5067a9a4931e0bf267dbacc925bef80 } catch (ReportedException reportedexception) { Throwable throwable = reportedexception.getCause(); -@@ -709,6 +714,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -758,6 +763,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider ChunkStatus chunkstatus = ChunkHolder.getStatus(playerchunk.getTicketLevel()); return !chunkstatus.isOrAfter(ChunkStatus.FULL) ? ChunkHolder.UNLOADED_CHUNK : either.mapLeft((ichunkaccess) -> { @@ -1097,7 +1097,7 @@ index 2b62f4664f439808661d559dc99762bfbac09b16..d5067a9a4931e0bf267dbacc925bef80 ChunkPos chunkcoordintpair = playerchunk.getPos(); ProtoChunk protochunk = (ProtoChunk) ichunkaccess; LevelChunk chunk; -@@ -732,6 +738,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -781,6 +787,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } return chunk; @@ -1105,7 +1105,7 @@ index 2b62f4664f439808661d559dc99762bfbac09b16..d5067a9a4931e0bf267dbacc925bef80 }); }, (runnable) -> { ProcessorHandle mailbox = this.mainThreadMailbox; -@@ -1189,6 +1196,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1238,6 +1245,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider ChunkMap.TrackedEntity playerchunkmap_entitytracker; ObjectIterator objectiterator; @@ -1113,7 +1113,7 @@ index 2b62f4664f439808661d559dc99762bfbac09b16..d5067a9a4931e0bf267dbacc925bef80 for (objectiterator = this.entityMap.values().iterator(); objectiterator.hasNext(); playerchunkmap_entitytracker.serverEntity.sendChanges()) { playerchunkmap_entitytracker = (ChunkMap.TrackedEntity) objectiterator.next(); -@@ -1206,14 +1214,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1255,14 +1263,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider playerchunkmap_entitytracker.lastSectionPos = sectionposition1; } } @@ -1132,10 +1132,10 @@ index 2b62f4664f439808661d559dc99762bfbac09b16..d5067a9a4931e0bf267dbacc925bef80 } diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 48c876381d75c66f24d59bd2c415dd7de293afee..e11b4be6e6990101ce77b6349ab8c70453e835a5 100644 +index 77d98bfa0ad9fc92a8e794b5a4e00c3e471c123a..3e2a5f83afcc3b9c9fe62748895d489135af03bf 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -346,13 +346,15 @@ public class ServerChunkCache extends ChunkSource { +@@ -507,13 +507,15 @@ public class ServerChunkCache extends ChunkSource { } gameprofilerfiller.incrementCounter("getChunkCacheMiss"); @@ -1153,7 +1153,7 @@ index 48c876381d75c66f24d59bd2c415dd7de293afee..e11b4be6e6990101ce77b6349ab8c704 ichunkaccess = (ChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> { return ichunkaccess1; }, (playerchunk_failure) -> { -@@ -552,7 +554,9 @@ public class ServerChunkCache extends ChunkSource { +@@ -713,7 +715,9 @@ public class ServerChunkCache extends ChunkSource { public void save(boolean flush) { this.runDistanceManagerUpdates(); @@ -1163,7 +1163,7 @@ index 48c876381d75c66f24d59bd2c415dd7de293afee..e11b4be6e6990101ce77b6349ab8c704 } @Override -@@ -590,7 +594,9 @@ public class ServerChunkCache extends ChunkSource { +@@ -751,7 +755,9 @@ public class ServerChunkCache extends ChunkSource { this.runDistanceManagerUpdates(); this.level.timings.doChunkMap.stopTiming(); // Spigot this.level.getProfiler().popPush("chunks"); @@ -1173,7 +1173,7 @@ index 48c876381d75c66f24d59bd2c415dd7de293afee..e11b4be6e6990101ce77b6349ab8c704 this.level.timings.doChunkUnload.startTiming(); // Spigot this.level.getProfiler().popPush("unload"); this.chunkMap.tick(booleansupplier); -@@ -614,14 +620,17 @@ public class ServerChunkCache extends ChunkSource { +@@ -775,14 +781,17 @@ public class ServerChunkCache extends ChunkSource { boolean flag2 = level.ticksPerAnimalSpawns != 0L && worlddata.getGameTime() % level.ticksPerAnimalSpawns == 0L; // CraftBukkit this.level.getProfiler().push("naturalSpawnCount"); @@ -1191,7 +1191,7 @@ index 48c876381d75c66f24d59bd2c415dd7de293afee..e11b4be6e6990101ce77b6349ab8c704 list.forEach((playerchunk) -> { Optional optional = ((Either) playerchunk.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left(); -@@ -635,15 +644,18 @@ public class ServerChunkCache extends ChunkSource { +@@ -796,15 +805,18 @@ public class ServerChunkCache extends ChunkSource { NaturalSpawner.spawnForChunk(this.level, chunk, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag2); } @@ -1212,7 +1212,7 @@ index 48c876381d75c66f24d59bd2c415dd7de293afee..e11b4be6e6990101ce77b6349ab8c704 } this.level.getProfiler().popPush("broadcast"); -@@ -651,15 +663,20 @@ public class ServerChunkCache extends ChunkSource { +@@ -812,15 +824,20 @@ public class ServerChunkCache extends ChunkSource { Optional optional = ((Either) playerchunk.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left(); // CraftBukkit - decompile error Objects.requireNonNull(playerchunk); @@ -1237,7 +1237,7 @@ index 48c876381d75c66f24d59bd2c415dd7de293afee..e11b4be6e6990101ce77b6349ab8c704 private void getFullChunk(long pos, Consumer chunkConsumer) { diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index f9fde3155944bb44ecb14524c585ed8eb9eac78c..e8184b64a0a8807f898deffc913adfd3a54d79fc 100644 +index 24b763a7d333be18e3b31b20f9405eb814741f8a..9d00198c74bae8507df75553c9005ba847fd7c11 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java @@ -1,6 +1,8 @@ @@ -1257,7 +1257,7 @@ index f9fde3155944bb44ecb14524c585ed8eb9eac78c..e8184b64a0a8807f898deffc913adfd3 import org.bukkit.craftbukkit.event.CraftEventFactory; import org.bukkit.craftbukkit.util.WorldUUID; import org.bukkit.event.entity.CreatureSpawnEvent; -@@ -217,13 +218,13 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -308,13 +309,13 @@ public class ServerLevel extends Level implements WorldGenLevel { DefaultedRegistry registryblocks = Registry.BLOCK; Objects.requireNonNull(registryblocks); @@ -1273,7 +1273,7 @@ index f9fde3155944bb44ecb14524c585ed8eb9eac78c..e8184b64a0a8807f898deffc913adfd3 this.navigatingMobs = new ObjectOpenHashSet(); this.blockEvents = new ObjectLinkedOpenHashSet(); this.dragonParts = new Int2ObjectOpenHashMap(); -@@ -430,17 +431,21 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -521,17 +522,21 @@ public class ServerLevel extends Level implements WorldGenLevel { this.updateSkyBrightness(); this.tickTime(); gameprofilerfiller.popPush("tickPending"); @@ -1297,7 +1297,7 @@ index f9fde3155944bb44ecb14524c585ed8eb9eac78c..e8184b64a0a8807f898deffc913adfd3 gameprofilerfiller.popPush("blockEvents"); timings.doSounds.startTiming(); // Spigot this.runBlockEvents(); -@@ -598,6 +603,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -689,6 +694,7 @@ public class ServerLevel extends Level implements WorldGenLevel { } gameprofilerfiller.popPush("tickBlocks"); @@ -1305,7 +1305,7 @@ index f9fde3155944bb44ecb14524c585ed8eb9eac78c..e8184b64a0a8807f898deffc913adfd3 if (randomTickSpeed > 0) { LevelChunkSection[] achunksection = chunk.getSections(); int l = achunksection.length; -@@ -630,6 +636,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -721,6 +727,7 @@ public class ServerLevel extends Level implements WorldGenLevel { } } @@ -1313,7 +1313,7 @@ index f9fde3155944bb44ecb14524c585ed8eb9eac78c..e8184b64a0a8807f898deffc913adfd3 gameprofilerfiller.pop(); } -@@ -755,14 +762,22 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -846,14 +853,22 @@ public class ServerLevel extends Level implements WorldGenLevel { } public void tickNonPassenger(Entity entity) { @@ -1337,7 +1337,7 @@ index f9fde3155944bb44ecb14524c585ed8eb9eac78c..e8184b64a0a8807f898deffc913adfd3 entity.setOldPosAndRot(); ProfilerFiller gameprofilerfiller = this.getProfiler(); -@@ -781,7 +796,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -872,7 +887,7 @@ public class ServerLevel extends Level implements WorldGenLevel { this.tickPassenger(entity, entity1); } @@ -1346,7 +1346,7 @@ index f9fde3155944bb44ecb14524c585ed8eb9eac78c..e8184b64a0a8807f898deffc913adfd3 } -@@ -823,6 +838,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -914,6 +929,7 @@ public class ServerLevel extends Level implements WorldGenLevel { if (!flag1) { org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld())); // CraftBukkit @@ -1354,7 +1354,7 @@ index f9fde3155944bb44ecb14524c585ed8eb9eac78c..e8184b64a0a8807f898deffc913adfd3 if (progressListener != null) { progressListener.progressStartNoAbort(new TranslatableComponent("menu.savingLevel")); } -@@ -832,7 +848,10 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -923,7 +939,10 @@ public class ServerLevel extends Level implements WorldGenLevel { progressListener.progressStage(new TranslatableComponent("menu.savingChunks")); } @@ -1445,7 +1445,7 @@ index 8c4744b3a3ebf73d31f59d1566320031550aa3bb..c731f22390773bcd43d392b86ae5b42b public UserWhiteList getWhiteList() { diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 89ed84d2e3ac8f25785a2d39b4fd37e50497e49e..96f8a886f348c1e18b4b1b97579ea44d43a05a44 100644 +index 264d216429c164798d5e645c044b95892b3b615e..1b8563be119498b8a9199ebd6fd4ad405577e0ee 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -126,7 +126,6 @@ import org.bukkit.craftbukkit.event.CraftPortalEvent; @@ -1464,7 +1464,7 @@ index 89ed84d2e3ac8f25785a2d39b4fd37e50497e49e..96f8a886f348c1e18b4b1b97579ea44d // Spigot start public final org.spigotmc.ActivationRange.ActivationType activationType = org.spigotmc.ActivationRange.initializeEntityActivationType(this); public final boolean defaultActivationState; -@@ -717,7 +715,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -722,7 +720,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n } public void move(MoverType movementType, Vec3 movement) { @@ -1472,7 +1472,7 @@ index 89ed84d2e3ac8f25785a2d39b4fd37e50497e49e..96f8a886f348c1e18b4b1b97579ea44d if (this.noPhysics) { this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z); } else { -@@ -864,7 +861,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -869,7 +866,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n this.level.getProfiler().pop(); } } diff --git a/patches/server/0010-Adventure.patch b/patches/server/0011-Adventure.patch similarity index 99% rename from patches/server/0010-Adventure.patch rename to patches/server/0011-Adventure.patch index 50f176ae1d..890dec2424 100644 --- a/patches/server/0010-Adventure.patch +++ b/patches/server/0011-Adventure.patch @@ -7,7 +7,7 @@ Co-authored-by: zml Co-authored-by: Jake Potrebic diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 997041d4bc1cd6be97a2d403723eded7eda60351..8330f57d57181002dab4599e2c1f477cf9e51784 100644 +index 666d956d0417d02a3c9e78a688edbe66e42cce18..f5155d4b48f17c82b7a637418c40ffcdc6cc6271 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -222,4 +222,13 @@ public class PaperConfig { diff --git a/patches/server/0011-Configurable-cactus-bamboo-and-reed-growth-heights.patch b/patches/server/0012-Configurable-cactus-bamboo-and-reed-growth-heights.patch similarity index 100% rename from patches/server/0011-Configurable-cactus-bamboo-and-reed-growth-heights.patch rename to patches/server/0012-Configurable-cactus-bamboo-and-reed-growth-heights.patch diff --git a/patches/server/0012-Configurable-baby-zombie-movement-speed.patch b/patches/server/0013-Configurable-baby-zombie-movement-speed.patch similarity index 97% rename from patches/server/0012-Configurable-baby-zombie-movement-speed.patch rename to patches/server/0013-Configurable-baby-zombie-movement-speed.patch index 5320c06b10..35c2fb7d6f 100644 --- a/patches/server/0012-Configurable-baby-zombie-movement-speed.patch +++ b/patches/server/0013-Configurable-baby-zombie-movement-speed.patch @@ -25,7 +25,7 @@ index 3618cc017feb60e257a28f67cbddca3f792a9833..796c17e0941922a9716212c6eae91643 + } } diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -index 564e8cb28c094a1bb9ab7d214464d6e6d9788bcf..f8b97aa5819e228f31c7953ee6e5c4e69fe55666 100644 +index 2846a0e71e3e349319fa6483d547eb676ff5d058..30d89a97a396d0037279489b756c4e1a254b8ef9 100644 --- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java +++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java @@ -79,7 +79,7 @@ import org.bukkit.event.entity.EntityTransformEvent; diff --git a/patches/server/0013-Configurable-fishing-time-ranges.patch b/patches/server/0014-Configurable-fishing-time-ranges.patch similarity index 100% rename from patches/server/0013-Configurable-fishing-time-ranges.patch rename to patches/server/0014-Configurable-fishing-time-ranges.patch diff --git a/patches/server/0014-Allow-nerfed-mobs-to-jump-and-take-water-damage.patch b/patches/server/0015-Allow-nerfed-mobs-to-jump-and-take-water-damage.patch similarity index 96% rename from patches/server/0014-Allow-nerfed-mobs-to-jump-and-take-water-damage.patch rename to patches/server/0015-Allow-nerfed-mobs-to-jump-and-take-water-damage.patch index 2ff607b54c..b192c9a1e6 100644 --- a/patches/server/0014-Allow-nerfed-mobs-to-jump-and-take-water-damage.patch +++ b/patches/server/0015-Allow-nerfed-mobs-to-jump-and-take-water-damage.patch @@ -19,10 +19,10 @@ index 78948c42b13194005bdbbbc69c2b7ae0732a78c5..b41e7922dd96c3358eb849ab39982a75 + } } diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 96f8a886f348c1e18b4b1b97579ea44d43a05a44..f1230adeaf399424630412975310cb3e31ed001f 100644 +index 1b8563be119498b8a9199ebd6fd4ad405577e0ee..f903453fd129f6fb5023615088bd84d33052f0dc 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -1265,6 +1265,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -1270,6 +1270,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n return this.isInWater() || this.isInRain(); } diff --git a/patches/server/0015-Add-configurable-despawn-distances-for-living-entiti.patch b/patches/server/0016-Add-configurable-despawn-distances-for-living-entiti.patch similarity index 96% rename from patches/server/0015-Add-configurable-despawn-distances-for-living-entiti.patch rename to patches/server/0016-Add-configurable-despawn-distances-for-living-entiti.patch index 5e1d56d15a..99c167e628 100644 --- a/patches/server/0015-Add-configurable-despawn-distances-for-living-entiti.patch +++ b/patches/server/0016-Add-configurable-despawn-distances-for-living-entiti.patch @@ -30,7 +30,7 @@ index b41e7922dd96c3358eb849ab39982a75736e3476..2f0d582baf0eb2bb477944d0cb1369db + } } diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index 5c01a30ccdcf05f516700fb95a46606f2c4cfc6b..78813a0efcab432c8c8c869da13ea49c93874cbf 100644 +index abaa57d9a4d222753d28801c6ab86b11c71aca6b..c9a9a4c6ba930dedb227c1f1d4ca4bb520983854 100644 --- a/src/main/java/net/minecraft/world/entity/Mob.java +++ b/src/main/java/net/minecraft/world/entity/Mob.java @@ -778,16 +778,16 @@ public abstract class Mob extends LivingEntity { diff --git a/patches/server/0016-Allow-for-toggling-of-spawn-chunks.patch b/patches/server/0017-Allow-for-toggling-of-spawn-chunks.patch similarity index 94% rename from patches/server/0016-Allow-for-toggling-of-spawn-chunks.patch rename to patches/server/0017-Allow-for-toggling-of-spawn-chunks.patch index ac33756a03..9df86932c0 100644 --- a/patches/server/0016-Allow-for-toggling-of-spawn-chunks.patch +++ b/patches/server/0017-Allow-for-toggling-of-spawn-chunks.patch @@ -20,7 +20,7 @@ index 2f0d582baf0eb2bb477944d0cb1369db6ca33956..89e76dd73811fd0f6f8c8e7e5af804d5 + } } diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index e38cd4df0924cce7fee4fd98967990945a62ecf4..a682e7a621b799d0855318788c7f5ad108716256 100644 +index 64bf654a622e2b5e79c45d288c812e32518769c6..339601220b91b9b9881ed031c8709d6bc4e32729 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -237,6 +237,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { diff --git a/patches/server/0017-Drop-falling-block-and-tnt-entities-at-the-specified.patch b/patches/server/0018-Drop-falling-block-and-tnt-entities-at-the-specified.patch similarity index 100% rename from patches/server/0017-Drop-falling-block-and-tnt-entities-at-the-specified.patch rename to patches/server/0018-Drop-falling-block-and-tnt-entities-at-the-specified.patch diff --git a/patches/server/0018-Show-Paper-in-client-crashes-server-lists-and-Mojang.patch b/patches/server/0019-Show-Paper-in-client-crashes-server-lists-and-Mojang.patch similarity index 96% rename from patches/server/0018-Show-Paper-in-client-crashes-server-lists-and-Mojang.patch rename to patches/server/0019-Show-Paper-in-client-crashes-server-lists-and-Mojang.patch index fc597576db..08c6c58470 100644 --- a/patches/server/0018-Show-Paper-in-client-crashes-server-lists-and-Mojang.patch +++ b/patches/server/0019-Show-Paper-in-client-crashes-server-lists-and-Mojang.patch @@ -19,10 +19,10 @@ index 2c53a400611c78236c5a1c1270d27c02e94251bf..a1d5c0f8fe2adb2ee56f3217e089211e if (outputStream != null) { try { diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index b3707d09bd73c306ea5e87785e6754ede9fc2846..cb1caae296d8d27d65f6f63f0f86aee53d363664 100644 +index 5de0b15ee206ad01b1b4522b2d375fae08d2486f..c24c5c39103fd2ae37711b745fef74c44ac18565 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1435,7 +1435,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { return worldserver + " " + worldserver.dimension().location(); diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index c19276ce8a2f4045ca7fe7b7600bd952b8bd5777..b178f93d38a865ae2841d044326370623065b112 100644 +index 5df7fe061fdfd7f1455cf283973be471d878ffb2..c2dc2ec29949074e71d0b4e5244ec71587f01178 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -197,6 +197,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -198,6 +198,7 @@ public class ServerLevel extends Level implements WorldGenLevel { private int tickPosition; public final LevelStorageSource.LevelStorageAccess convertable; public final UUID uuid; diff --git a/patches/server/0071-Entity-AddTo-RemoveFrom-World-Events.patch b/patches/server/0072-Entity-AddTo-RemoveFrom-World-Events.patch similarity index 82% rename from patches/server/0071-Entity-AddTo-RemoveFrom-World-Events.patch rename to patches/server/0072-Entity-AddTo-RemoveFrom-World-Events.patch index 8889ffff2b..8c7af1f4f6 100644 --- a/patches/server/0071-Entity-AddTo-RemoveFrom-World-Events.patch +++ b/patches/server/0072-Entity-AddTo-RemoveFrom-World-Events.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Entity AddTo/RemoveFrom World Events diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index ba964bc8b2d86b86be2d527fdc8d8aa342ac0e5e..4291492dc5cf0c845af30f62e2dcb15826842d12 100644 +index c2dc2ec29949074e71d0b4e5244ec71587f01178..9d6ff9aff140574038b7f619cdfebf47d4b3a14c 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1865,6 +1865,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1956,6 +1956,7 @@ public class ServerLevel extends Level implements WorldGenLevel { entity.setOrigin(entity.getOriginVector().toLocation(getWorld())); } // Paper end @@ -16,7 +16,7 @@ index ba964bc8b2d86b86be2d527fdc8d8aa342ac0e5e..4291492dc5cf0c845af30f62e2dcb158 } public void onTrackingEnd(Entity entity) { -@@ -1929,6 +1930,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -2020,6 +2021,7 @@ public class ServerLevel extends Level implements WorldGenLevel { } entity.valid = false; // CraftBukkit diff --git a/patches/server/0072-Configurable-Chunk-Inhabited-Time.patch b/patches/server/0073-Configurable-Chunk-Inhabited-Time.patch similarity index 100% rename from patches/server/0072-Configurable-Chunk-Inhabited-Time.patch rename to patches/server/0073-Configurable-Chunk-Inhabited-Time.patch diff --git a/patches/server/0073-EntityPathfindEvent.patch b/patches/server/0074-EntityPathfindEvent.patch similarity index 100% rename from patches/server/0073-EntityPathfindEvent.patch rename to patches/server/0074-EntityPathfindEvent.patch diff --git a/patches/server/0074-Sanitise-RegionFileCache-and-make-configurable.patch b/patches/server/0075-Sanitise-RegionFileCache-and-make-configurable.patch similarity index 95% rename from patches/server/0074-Sanitise-RegionFileCache-and-make-configurable.patch rename to patches/server/0075-Sanitise-RegionFileCache-and-make-configurable.patch index 44370fd0cb..b14be3b54c 100644 --- a/patches/server/0074-Sanitise-RegionFileCache-and-make-configurable.patch +++ b/patches/server/0075-Sanitise-RegionFileCache-and-make-configurable.patch @@ -11,7 +11,7 @@ The implementation uses a LinkedHashMap as an LRU cache (modified from HashMap). The maximum size of the RegionFileCache is also made configurable. diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index e3ca633a99d30ebf9dc35fd9236836b577a2b598..78f3e00715a5d3a60c341461a7cd4fa52e2492f0 100644 +index acdbd21947093ed076c4668d3480a50f4e289b8d..a3a4ecabed1ec7511005affe610fc2ec8a87d67a 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -236,4 +236,9 @@ public class PaperConfig { diff --git a/patches/server/0075-Do-not-load-chunks-for-Pathfinding.patch b/patches/server/0076-Do-not-load-chunks-for-Pathfinding.patch similarity index 100% rename from patches/server/0075-Do-not-load-chunks-for-Pathfinding.patch rename to patches/server/0076-Do-not-load-chunks-for-Pathfinding.patch diff --git a/patches/server/0076-Add-PlayerUseUnknownEntityEvent.patch b/patches/server/0077-Add-PlayerUseUnknownEntityEvent.patch similarity index 100% rename from patches/server/0076-Add-PlayerUseUnknownEntityEvent.patch rename to patches/server/0077-Add-PlayerUseUnknownEntityEvent.patch diff --git a/patches/server/0077-Fix-reducedDebugInfo-not-initialized-on-client.patch b/patches/server/0078-Fix-reducedDebugInfo-not-initialized-on-client.patch similarity index 100% rename from patches/server/0077-Fix-reducedDebugInfo-not-initialized-on-client.patch rename to patches/server/0078-Fix-reducedDebugInfo-not-initialized-on-client.patch diff --git a/patches/server/0078-Configurable-Grass-Spread-Tick-Rate.patch b/patches/server/0079-Configurable-Grass-Spread-Tick-Rate.patch similarity index 100% rename from patches/server/0078-Configurable-Grass-Spread-Tick-Rate.patch rename to patches/server/0079-Configurable-Grass-Spread-Tick-Rate.patch diff --git a/patches/server/0079-Fix-Cancelling-BlockPlaceEvent-triggering-physics.patch b/patches/server/0080-Fix-Cancelling-BlockPlaceEvent-triggering-physics.patch similarity index 90% rename from patches/server/0079-Fix-Cancelling-BlockPlaceEvent-triggering-physics.patch rename to patches/server/0080-Fix-Cancelling-BlockPlaceEvent-triggering-physics.patch index e07de933c8..33c82c42ca 100644 --- a/patches/server/0079-Fix-Cancelling-BlockPlaceEvent-triggering-physics.patch +++ b/patches/server/0080-Fix-Cancelling-BlockPlaceEvent-triggering-physics.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Fix Cancelling BlockPlaceEvent triggering physics diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 5c395ffa57372dee3fc29e9bd4b5545211aec29c..22c4c94a4b81c8ca53d717005b6260f0bafbbea4 100644 +index f05dcb3c9f17a0aa991fcbd3b3f58f507059a8cd..c028b74f4a6c5e52be48b042f44fe4b98466382b 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -542,6 +542,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { diff --git a/patches/server/0080-Optimize-DataBits.patch b/patches/server/0081-Optimize-DataBits.patch similarity index 100% rename from patches/server/0080-Optimize-DataBits.patch rename to patches/server/0081-Optimize-DataBits.patch diff --git a/patches/server/0081-Option-to-use-vanilla-per-world-scoreboard-coloring-.patch b/patches/server/0082-Option-to-use-vanilla-per-world-scoreboard-coloring-.patch similarity index 100% rename from patches/server/0081-Option-to-use-vanilla-per-world-scoreboard-coloring-.patch rename to patches/server/0082-Option-to-use-vanilla-per-world-scoreboard-coloring-.patch diff --git a/patches/server/0082-Workaround-for-setting-passengers-on-players.patch b/patches/server/0083-Workaround-for-setting-passengers-on-players.patch similarity index 100% rename from patches/server/0082-Workaround-for-setting-passengers-on-players.patch rename to patches/server/0083-Workaround-for-setting-passengers-on-players.patch diff --git a/patches/server/0083-Configurable-Player-Collision.patch b/patches/server/0084-Configurable-Player-Collision.patch similarity index 96% rename from patches/server/0083-Configurable-Player-Collision.patch rename to patches/server/0084-Configurable-Player-Collision.patch index 74d98f7997..70992f500b 100644 --- a/patches/server/0083-Configurable-Player-Collision.patch +++ b/patches/server/0084-Configurable-Player-Collision.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Configurable Player Collision diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 78f3e00715a5d3a60c341461a7cd4fa52e2492f0..2413ed16de148ea63667dca7ab3ee89fb714cf84 100644 +index a3a4ecabed1ec7511005affe610fc2ec8a87d67a..1d2680ad57c2a44838157fbf8601b14fda03ce17 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -241,4 +241,9 @@ public class PaperConfig { @@ -32,7 +32,7 @@ index 8885220e4813b34627b42523834bbec995d8950d..4c9660176e783999301565790b8cf6f4 buf.writeComponent(this.playerPrefix); buf.writeComponent(this.playerSuffix); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 597b8d4c79714484e7c8be6e4f90294a96259e4e..5ef6d6cafbd39cf15189bc65cc4dea1f99ef37a0 100644 +index 1faf63b6de560b23e3ca3b8358a4fd1ff9204bd4..7b947c0d5bd63957df6348ea2ade79f9dc3e59fa 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -161,6 +161,7 @@ import net.minecraft.world.level.storage.loot.LootTables; @@ -43,7 +43,7 @@ index 597b8d4c79714484e7c8be6e4f90294a96259e4e..5ef6d6cafbd39cf15189bc65cc4dea1f import org.apache.commons.lang3.Validate; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -@@ -614,6 +615,20 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { -@@ -2250,7 +2252,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop processQueue = new java.util.concurrent.ConcurrentLinkedQueue(); public int autosavePeriod; -@@ -363,7 +364,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange; chunkRange = (chunkRange > 8) ? 8 : chunkRange; @@ -36,10 +36,10 @@ index 5ce8ac377b0d2b05dd90baa67f420945cc419609..919a489a5c7b338659c62ae67fc0a6ce }); } diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index e11b4be6e6990101ce77b6349ab8c70453e835a5..a778eff06a24a665874a315704f00dc5996420c9 100644 +index 3e2a5f83afcc3b9c9fe62748895d489135af03bf..dd61048b3898ef3ea5a2bd2f95e9bd3fc11dc298 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -630,6 +630,15 @@ public class ServerChunkCache extends ChunkSource { +@@ -791,6 +791,15 @@ public class ServerChunkCache extends ChunkSource { List list = Lists.newArrayList(this.chunkMap.getChunks()); Collections.shuffle(list); @@ -56,7 +56,7 @@ index e11b4be6e6990101ce77b6349ab8c70453e835a5..a778eff06a24a665874a315704f00dc5 list.forEach((playerchunk) -> { Optional optional = ((Either) playerchunk.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left(); diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 9761078ba0c984eec303e96d1ee64fbce659075c..ca9662d92db176270ec5490ecc51208b666bc594 100644 +index e5bafb77d26a95fab4b0f7a419fc09f885821c0d..3a3f04cc3aaaebc1df8eace740ca29203bd6d871 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -1,5 +1,6 @@ diff --git a/patches/server/0174-PreCreatureSpawnEvent.patch b/patches/server/0175-PreCreatureSpawnEvent.patch similarity index 100% rename from patches/server/0174-PreCreatureSpawnEvent.patch rename to patches/server/0175-PreCreatureSpawnEvent.patch diff --git a/patches/server/0175-Add-setPlayerProfile-API-for-Skulls.patch b/patches/server/0176-Add-setPlayerProfile-API-for-Skulls.patch similarity index 100% rename from patches/server/0175-Add-setPlayerProfile-API-for-Skulls.patch rename to patches/server/0176-Add-setPlayerProfile-API-for-Skulls.patch diff --git a/patches/server/0176-Fill-Profile-Property-Events.patch b/patches/server/0177-Fill-Profile-Property-Events.patch similarity index 100% rename from patches/server/0176-Fill-Profile-Property-Events.patch rename to patches/server/0177-Fill-Profile-Property-Events.patch diff --git a/patches/server/0177-PlayerAdvancementCriterionGrantEvent.patch b/patches/server/0178-PlayerAdvancementCriterionGrantEvent.patch similarity index 100% rename from patches/server/0177-PlayerAdvancementCriterionGrantEvent.patch rename to patches/server/0178-PlayerAdvancementCriterionGrantEvent.patch diff --git a/patches/server/0178-Add-ArmorStand-Item-Meta.patch b/patches/server/0179-Add-ArmorStand-Item-Meta.patch similarity index 100% rename from patches/server/0178-Add-ArmorStand-Item-Meta.patch rename to patches/server/0179-Add-ArmorStand-Item-Meta.patch diff --git a/patches/server/0179-Extend-Player-Interact-cancellation.patch b/patches/server/0180-Extend-Player-Interact-cancellation.patch similarity index 100% rename from patches/server/0179-Extend-Player-Interact-cancellation.patch rename to patches/server/0180-Extend-Player-Interact-cancellation.patch diff --git a/patches/server/0180-Tameable-getOwnerUniqueId-API.patch b/patches/server/0181-Tameable-getOwnerUniqueId-API.patch similarity index 100% rename from patches/server/0180-Tameable-getOwnerUniqueId-API.patch rename to patches/server/0181-Tameable-getOwnerUniqueId-API.patch diff --git a/patches/server/0181-Toggleable-player-crits-helps-mitigate-hacked-client.patch b/patches/server/0182-Toggleable-player-crits-helps-mitigate-hacked-client.patch similarity index 100% rename from patches/server/0181-Toggleable-player-crits-helps-mitigate-hacked-client.patch rename to patches/server/0182-Toggleable-player-crits-helps-mitigate-hacked-client.patch diff --git a/patches/server/0182-Disable-Explicit-Network-Manager-Flushing.patch b/patches/server/0183-Disable-Explicit-Network-Manager-Flushing.patch similarity index 100% rename from patches/server/0182-Disable-Explicit-Network-Manager-Flushing.patch rename to patches/server/0183-Disable-Explicit-Network-Manager-Flushing.patch diff --git a/patches/server/0183-Implement-extended-PaperServerListPingEvent.patch b/patches/server/0184-Implement-extended-PaperServerListPingEvent.patch similarity index 98% rename from patches/server/0183-Implement-extended-PaperServerListPingEvent.patch rename to patches/server/0184-Implement-extended-PaperServerListPingEvent.patch index e106b17676..575b063b89 100644 --- a/patches/server/0183-Implement-extended-PaperServerListPingEvent.patch +++ b/patches/server/0184-Implement-extended-PaperServerListPingEvent.patch @@ -190,7 +190,7 @@ index 67455a5ba75c9b816213e44d6872c5ddf8e27e98..23efad80934930beadf15e65781551d4 public ClientboundStatusResponsePacket(ServerStatus metadata) { diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index d7ec805b7922b19e4d67288bb4eb4fc10af5166f..cc90827ca284c058f6f209cf7c9cc6ca8b8be29d 100644 +index ce5fab6878a65b7ad4fd6aead5b77a9724d3dd63..74e780212773e0d3cce19cb9c33cdcaa4a708f14 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -2,6 +2,9 @@ package net.minecraft.server; @@ -203,7 +203,7 @@ index d7ec805b7922b19e4d67288bb4eb4fc10af5166f..cc90827ca284c058f6f209cf7c9cc6ca import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -@@ -1328,7 +1331,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= 5000000000L) { this.lastServerStatus = i; this.status.setPlayers(new ServerStatus.Players(this.getMaxPlayers(), this.getPlayerCount())); diff --git a/patches/server/0184-Improved-Async-Task-Scheduler.patch b/patches/server/0185-Improved-Async-Task-Scheduler.patch similarity index 100% rename from patches/server/0184-Improved-Async-Task-Scheduler.patch rename to patches/server/0185-Improved-Async-Task-Scheduler.patch diff --git a/patches/server/0185-Ability-to-change-PlayerProfile-in-AsyncPreLoginEven.patch b/patches/server/0186-Ability-to-change-PlayerProfile-in-AsyncPreLoginEven.patch similarity index 100% rename from patches/server/0185-Ability-to-change-PlayerProfile-in-AsyncPreLoginEven.patch rename to patches/server/0186-Ability-to-change-PlayerProfile-in-AsyncPreLoginEven.patch diff --git a/patches/server/0186-Player.setPlayerProfile-API.patch b/patches/server/0187-Player.setPlayerProfile-API.patch similarity index 100% rename from patches/server/0186-Player.setPlayerProfile-API.patch rename to patches/server/0187-Player.setPlayerProfile-API.patch diff --git a/patches/server/0187-getPlayerUniqueId-API.patch b/patches/server/0188-getPlayerUniqueId-API.patch similarity index 100% rename from patches/server/0187-getPlayerUniqueId-API.patch rename to patches/server/0188-getPlayerUniqueId-API.patch diff --git a/patches/server/0188-Upstream-config-migrations.patch b/patches/server/0189-Upstream-config-migrations.patch similarity index 94% rename from patches/server/0188-Upstream-config-migrations.patch rename to patches/server/0189-Upstream-config-migrations.patch index da735f8266..0c85e12c08 100644 --- a/patches/server/0188-Upstream-config-migrations.patch +++ b/patches/server/0189-Upstream-config-migrations.patch @@ -7,7 +7,7 @@ This patch contains config migrations for when upstream adds options which Paper already had. diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 8007220fa0646414c9cec66161aca2394e62dde3..6b952280399833eec54a72e87d916e65ae4c7339 100644 +index 5bb6f09d138f981329378edab707f8275cdfc5a0..3d5fd6be97bef7ec47893a85cae771f4a4451092 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -288,4 +288,22 @@ public class PaperConfig { diff --git a/patches/server/0189-Make-legacy-ping-handler-more-reliable.patch b/patches/server/0190-Make-legacy-ping-handler-more-reliable.patch similarity index 100% rename from patches/server/0189-Make-legacy-ping-handler-more-reliable.patch rename to patches/server/0190-Make-legacy-ping-handler-more-reliable.patch diff --git a/patches/server/0190-Call-PaperServerListPingEvent-for-legacy-pings.patch b/patches/server/0191-Call-PaperServerListPingEvent-for-legacy-pings.patch similarity index 100% rename from patches/server/0190-Call-PaperServerListPingEvent-for-legacy-pings.patch rename to patches/server/0191-Call-PaperServerListPingEvent-for-legacy-pings.patch diff --git a/patches/server/0191-Flag-to-disable-the-channel-limit.patch b/patches/server/0192-Flag-to-disable-the-channel-limit.patch similarity index 100% rename from patches/server/0191-Flag-to-disable-the-channel-limit.patch rename to patches/server/0192-Flag-to-disable-the-channel-limit.patch diff --git a/patches/server/0192-Add-method-to-open-already-placed-sign.patch b/patches/server/0193-Add-method-to-open-already-placed-sign.patch similarity index 100% rename from patches/server/0192-Add-method-to-open-already-placed-sign.patch rename to patches/server/0193-Add-method-to-open-already-placed-sign.patch diff --git a/patches/server/0193-Configurable-sprint-interruption-on-attack.patch b/patches/server/0194-Configurable-sprint-interruption-on-attack.patch similarity index 100% rename from patches/server/0193-Configurable-sprint-interruption-on-attack.patch rename to patches/server/0194-Configurable-sprint-interruption-on-attack.patch diff --git a/patches/server/0194-Fix-exploit-that-allowed-colored-signs-to-be-created.patch b/patches/server/0195-Fix-exploit-that-allowed-colored-signs-to-be-created.patch similarity index 100% rename from patches/server/0194-Fix-exploit-that-allowed-colored-signs-to-be-created.patch rename to patches/server/0195-Fix-exploit-that-allowed-colored-signs-to-be-created.patch diff --git a/patches/server/0195-EndermanEscapeEvent.patch b/patches/server/0196-EndermanEscapeEvent.patch similarity index 100% rename from patches/server/0195-EndermanEscapeEvent.patch rename to patches/server/0196-EndermanEscapeEvent.patch diff --git a/patches/server/0196-Enderman.teleportRandomly.patch b/patches/server/0197-Enderman.teleportRandomly.patch similarity index 100% rename from patches/server/0196-Enderman.teleportRandomly.patch rename to patches/server/0197-Enderman.teleportRandomly.patch diff --git a/patches/server/0197-Block-Enderpearl-Travel-Exploit.patch b/patches/server/0198-Block-Enderpearl-Travel-Exploit.patch similarity index 100% rename from patches/server/0197-Block-Enderpearl-Travel-Exploit.patch rename to patches/server/0198-Block-Enderpearl-Travel-Exploit.patch diff --git a/patches/server/0198-Expand-World.spawnParticle-API-and-add-Builder.patch b/patches/server/0199-Expand-World.spawnParticle-API-and-add-Builder.patch similarity index 96% rename from patches/server/0198-Expand-World.spawnParticle-API-and-add-Builder.patch rename to patches/server/0199-Expand-World.spawnParticle-API-and-add-Builder.patch index f7cdfaadfc..16e50b1508 100644 --- a/patches/server/0198-Expand-World.spawnParticle-API-and-add-Builder.patch +++ b/patches/server/0199-Expand-World.spawnParticle-API-and-add-Builder.patch @@ -10,10 +10,10 @@ Adds an option to control the force mode of the particle. This adds a new Builder API which is much friendlier to use. diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 2aecd5ce8e5db8c0fc54bc0c86759baaaa4bd615..22c6d606256708ff9142569e745e58b608da62a6 100644 +index 26c25b103f08a2e66179d1ed8f450778aa2e539a..86a5a4456be65b94b0c27a0355c3844cbd296a20 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1242,12 +1242,17 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1333,12 +1333,17 @@ public class ServerLevel extends Level implements WorldGenLevel { } public int sendParticles(ServerPlayer sender, T t0, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6, boolean force) { diff --git a/patches/server/0199-Prevent-Frosted-Ice-from-loading-holding-chunks.patch b/patches/server/0200-Prevent-Frosted-Ice-from-loading-holding-chunks.patch similarity index 100% rename from patches/server/0199-Prevent-Frosted-Ice-from-loading-holding-chunks.patch rename to patches/server/0200-Prevent-Frosted-Ice-from-loading-holding-chunks.patch diff --git a/patches/server/0200-EndermanAttackPlayerEvent.patch b/patches/server/0201-EndermanAttackPlayerEvent.patch similarity index 100% rename from patches/server/0200-EndermanAttackPlayerEvent.patch rename to patches/server/0201-EndermanAttackPlayerEvent.patch diff --git a/patches/server/0201-WitchConsumePotionEvent.patch b/patches/server/0202-WitchConsumePotionEvent.patch similarity index 100% rename from patches/server/0201-WitchConsumePotionEvent.patch rename to patches/server/0202-WitchConsumePotionEvent.patch diff --git a/patches/server/0202-WitchThrowPotionEvent.patch b/patches/server/0203-WitchThrowPotionEvent.patch similarity index 100% rename from patches/server/0202-WitchThrowPotionEvent.patch rename to patches/server/0203-WitchThrowPotionEvent.patch diff --git a/patches/server/0203-Allow-spawning-Item-entities-with-World.spawnEntity.patch b/patches/server/0204-Allow-spawning-Item-entities-with-World.spawnEntity.patch similarity index 100% rename from patches/server/0203-Allow-spawning-Item-entities-with-World.spawnEntity.patch rename to patches/server/0204-Allow-spawning-Item-entities-with-World.spawnEntity.patch diff --git a/patches/server/0204-WitchReadyPotionEvent.patch b/patches/server/0205-WitchReadyPotionEvent.patch similarity index 100% rename from patches/server/0204-WitchReadyPotionEvent.patch rename to patches/server/0205-WitchReadyPotionEvent.patch diff --git a/patches/server/0205-ItemStack-getMaxItemUseDuration.patch b/patches/server/0206-ItemStack-getMaxItemUseDuration.patch similarity index 100% rename from patches/server/0205-ItemStack-getMaxItemUseDuration.patch rename to patches/server/0206-ItemStack-getMaxItemUseDuration.patch diff --git a/patches/server/0206-Implement-EntityTeleportEndGatewayEvent.patch b/patches/server/0207-Implement-EntityTeleportEndGatewayEvent.patch similarity index 100% rename from patches/server/0206-Implement-EntityTeleportEndGatewayEvent.patch rename to patches/server/0207-Implement-EntityTeleportEndGatewayEvent.patch diff --git a/patches/server/0207-Unset-Ignited-flag-on-cancel-of-Explosion-Event.patch b/patches/server/0208-Unset-Ignited-flag-on-cancel-of-Explosion-Event.patch similarity index 100% rename from patches/server/0207-Unset-Ignited-flag-on-cancel-of-Explosion-Event.patch rename to patches/server/0208-Unset-Ignited-flag-on-cancel-of-Explosion-Event.patch diff --git a/patches/server/0208-Fix-CraftEntity-hashCode.patch b/patches/server/0209-Fix-CraftEntity-hashCode.patch similarity index 100% rename from patches/server/0208-Fix-CraftEntity-hashCode.patch rename to patches/server/0209-Fix-CraftEntity-hashCode.patch diff --git a/patches/server/0209-Configurable-Alternative-LootPool-Luck-Formula.patch b/patches/server/0210-Configurable-Alternative-LootPool-Luck-Formula.patch similarity index 97% rename from patches/server/0209-Configurable-Alternative-LootPool-Luck-Formula.patch rename to patches/server/0210-Configurable-Alternative-LootPool-Luck-Formula.patch index 6f3738939b..a97a392734 100644 --- a/patches/server/0209-Configurable-Alternative-LootPool-Luck-Formula.patch +++ b/patches/server/0210-Configurable-Alternative-LootPool-Luck-Formula.patch @@ -36,7 +36,7 @@ This change will result in some major changes to fishing formulas. I would love to see this change in Vanilla, so Mojang please pull :) diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 6b952280399833eec54a72e87d916e65ae4c7339..b42272b574e31d695ee31bd3fb76a4b906713442 100644 +index 3d5fd6be97bef7ec47893a85cae771f4a4451092..ce4ab03a40fac6a840f4c4b78da0a96bb6fb6823 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -306,4 +306,12 @@ public class PaperConfig { diff --git a/patches/server/0210-Print-Error-details-when-failing-to-save-player-data.patch b/patches/server/0211-Print-Error-details-when-failing-to-save-player-data.patch similarity index 100% rename from patches/server/0210-Print-Error-details-when-failing-to-save-player-data.patch rename to patches/server/0211-Print-Error-details-when-failing-to-save-player-data.patch diff --git a/patches/server/0211-Make-shield-blocking-delay-configurable.patch b/patches/server/0212-Make-shield-blocking-delay-configurable.patch similarity index 100% rename from patches/server/0211-Make-shield-blocking-delay-configurable.patch rename to patches/server/0212-Make-shield-blocking-delay-configurable.patch diff --git a/patches/server/0212-Improve-EntityShootBowEvent.patch b/patches/server/0213-Improve-EntityShootBowEvent.patch similarity index 100% rename from patches/server/0212-Improve-EntityShootBowEvent.patch rename to patches/server/0213-Improve-EntityShootBowEvent.patch diff --git a/patches/server/0213-PlayerReadyArrowEvent.patch b/patches/server/0214-PlayerReadyArrowEvent.patch similarity index 100% rename from patches/server/0213-PlayerReadyArrowEvent.patch rename to patches/server/0214-PlayerReadyArrowEvent.patch diff --git a/patches/server/0214-Implement-EntityKnockbackByEntityEvent.patch b/patches/server/0215-Implement-EntityKnockbackByEntityEvent.patch similarity index 98% rename from patches/server/0214-Implement-EntityKnockbackByEntityEvent.patch rename to patches/server/0215-Implement-EntityKnockbackByEntityEvent.patch index 8fc62b441e..cef1f3db37 100644 --- a/patches/server/0214-Implement-EntityKnockbackByEntityEvent.patch +++ b/patches/server/0215-Implement-EntityKnockbackByEntityEvent.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Implement EntityKnockbackByEntityEvent This event is called when an entity receives knockback by another entity. diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 0a954a83e29f2ffc622675f671416bdadc1d3f6a..91a0699ba30fa1e9c8172e07f034ea1fabee9d11 100644 +index 3be95347badafc35990f5fbb1c0be77b8702bc64..bfec0ae49c74ec5f6762140199cbfaf9e87a047c 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java @@ -1437,7 +1437,7 @@ public abstract class LivingEntity extends Entity { diff --git a/patches/server/0215-Expand-Explosions-API.patch b/patches/server/0216-Expand-Explosions-API.patch similarity index 100% rename from patches/server/0215-Expand-Explosions-API.patch rename to patches/server/0216-Expand-Explosions-API.patch diff --git a/patches/server/0216-LivingEntity-Hand-Raised-Item-Use-API.patch b/patches/server/0217-LivingEntity-Hand-Raised-Item-Use-API.patch similarity index 100% rename from patches/server/0216-LivingEntity-Hand-Raised-Item-Use-API.patch rename to patches/server/0217-LivingEntity-Hand-Raised-Item-Use-API.patch diff --git a/patches/server/0217-RangedEntity-API.patch b/patches/server/0218-RangedEntity-API.patch similarity index 100% rename from patches/server/0217-RangedEntity-API.patch rename to patches/server/0218-RangedEntity-API.patch diff --git a/patches/server/0218-Add-config-to-disable-ender-dragon-legacy-check.patch b/patches/server/0219-Add-config-to-disable-ender-dragon-legacy-check.patch similarity index 100% rename from patches/server/0218-Add-config-to-disable-ender-dragon-legacy-check.patch rename to patches/server/0219-Add-config-to-disable-ender-dragon-legacy-check.patch diff --git a/patches/server/0219-Implement-World.getEntity-UUID-API.patch b/patches/server/0220-Implement-World.getEntity-UUID-API.patch similarity index 100% rename from patches/server/0219-Implement-World.getEntity-UUID-API.patch rename to patches/server/0220-Implement-World.getEntity-UUID-API.patch diff --git a/patches/server/0220-InventoryCloseEvent-Reason-API.patch b/patches/server/0221-InventoryCloseEvent-Reason-API.patch similarity index 97% rename from patches/server/0220-InventoryCloseEvent-Reason-API.patch rename to patches/server/0221-InventoryCloseEvent-Reason-API.patch index 793fb65362..f87f35da41 100644 --- a/patches/server/0220-InventoryCloseEvent-Reason-API.patch +++ b/patches/server/0221-InventoryCloseEvent-Reason-API.patch @@ -7,10 +7,10 @@ Allows you to determine why an inventory was closed, enabling plugin developers to "confirm" things based on if it was player triggered close or not. diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index d06fa20dd605e9ce0e41a4d69ffeec98bceb3a63..19f8e74f292e83f7438683efddbaa4930f1a7c48 100644 +index 86a5a4456be65b94b0c27a0355c3844cbd296a20..c4aa14e81151ada49e61f4dd25267f7eb10450a6 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1016,7 +1016,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1107,7 +1107,7 @@ public class ServerLevel extends Level implements WorldGenLevel { for (net.minecraft.world.level.block.entity.BlockEntity tileentity : chunk.getBlockEntities().values()) { if (tileentity instanceof net.minecraft.world.Container) { for (org.bukkit.entity.HumanEntity h : Lists.newArrayList(((net.minecraft.world.Container) tileentity).getViewers())) { @@ -19,7 +19,7 @@ index d06fa20dd605e9ce0e41a4d69ffeec98bceb3a63..19f8e74f292e83f7438683efddbaa493 } } } -@@ -1902,7 +1902,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1993,7 +1993,7 @@ public class ServerLevel extends Level implements WorldGenLevel { // Spigot Start if (entity.getBukkitEntity() instanceof org.bukkit.inventory.InventoryHolder) { for (org.bukkit.entity.HumanEntity h : Lists.newArrayList(((org.bukkit.inventory.InventoryHolder) entity.getBukkitEntity()).getInventory().getViewers())) { @@ -29,7 +29,7 @@ index d06fa20dd605e9ce0e41a4d69ffeec98bceb3a63..19f8e74f292e83f7438683efddbaa493 } // Spigot End diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index ca9662d92db176270ec5490ecc51208b666bc594..740ee29affa30ffd7a7e2b612cae4a1124f9bfb4 100644 +index 3a3f04cc3aaaebc1df8eace740ca29203bd6d871..2ecbe2c21fd9beaf4ce4f1b3531acb1036f3dfaa 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -598,7 +598,7 @@ public class ServerPlayer extends Player { diff --git a/patches/server/0221-Vex-get-setSummoner-API.patch b/patches/server/0222-Vex-get-setSummoner-API.patch similarity index 100% rename from patches/server/0221-Vex-get-setSummoner-API.patch rename to patches/server/0222-Vex-get-setSummoner-API.patch diff --git a/patches/server/0222-Refresh-player-inventory-when-cancelling-PlayerInter.patch b/patches/server/0223-Refresh-player-inventory-when-cancelling-PlayerInter.patch similarity index 100% rename from patches/server/0222-Refresh-player-inventory-when-cancelling-PlayerInter.patch rename to patches/server/0223-Refresh-player-inventory-when-cancelling-PlayerInter.patch diff --git a/patches/server/0223-Avoid-item-merge-if-stack-size-above-max-stack-size.patch b/patches/server/0224-Avoid-item-merge-if-stack-size-above-max-stack-size.patch similarity index 100% rename from patches/server/0223-Avoid-item-merge-if-stack-size-above-max-stack-size.patch rename to patches/server/0224-Avoid-item-merge-if-stack-size-above-max-stack-size.patch diff --git a/patches/server/0224-Use-AsyncAppender-to-keep-logging-IO-off-main-thread.patch b/patches/server/0225-Use-AsyncAppender-to-keep-logging-IO-off-main-thread.patch similarity index 94% rename from patches/server/0224-Use-AsyncAppender-to-keep-logging-IO-off-main-thread.patch rename to patches/server/0225-Use-AsyncAppender-to-keep-logging-IO-off-main-thread.patch index cd9332ede9..0f5e9b6bcf 100644 --- a/patches/server/0224-Use-AsyncAppender-to-keep-logging-IO-off-main-thread.patch +++ b/patches/server/0225-Use-AsyncAppender-to-keep-logging-IO-off-main-thread.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Use AsyncAppender to keep logging IO off main thread diff --git a/build.gradle.kts b/build.gradle.kts -index 34a0d6b54a15c8aa0c706541316c5d448e3d94b9..76a8db5ff623b0a3b83a0861f642da67ad2be3aa 100644 +index 8738c5bf1de3a145723ba44fd4a41e944113752b..5543c8e30c707d7cee5fd0579dd3d3dd7c26a13e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -38,6 +38,7 @@ dependencies { diff --git a/patches/server/0225-add-more-information-to-Entity.toString.patch b/patches/server/0226-add-more-information-to-Entity.toString.patch similarity index 89% rename from patches/server/0225-add-more-information-to-Entity.toString.patch rename to patches/server/0226-add-more-information-to-Entity.toString.patch index ef9bca954c..31553ec0f3 100644 --- a/patches/server/0225-add-more-information-to-Entity.toString.patch +++ b/patches/server/0226-add-more-information-to-Entity.toString.patch @@ -6,10 +6,10 @@ Subject: [PATCH] add more information to Entity.toString() UUID, ticks lived, valid, dead diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 06ee6000f53da50e7883a68208bdb5f839632f94..1d9b91e6c7180e4a7db03a01c7ec5f799f5ab983 100644 +index 3b2334e9ba44205a4e0ec12045eab4fad91bb15a..2a6954e3c9ff82d68647bf8f4c4803184fab0bbe 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -2810,7 +2810,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -2815,7 +2815,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n } public String toString() { diff --git a/patches/server/0226-Add-CraftMagicNumbers.isSupportedApiVersion.patch b/patches/server/0227-Add-CraftMagicNumbers.isSupportedApiVersion.patch similarity index 100% rename from patches/server/0226-Add-CraftMagicNumbers.isSupportedApiVersion.patch rename to patches/server/0227-Add-CraftMagicNumbers.isSupportedApiVersion.patch diff --git a/patches/server/0227-EnderDragon-Events.patch b/patches/server/0228-EnderDragon-Events.patch similarity index 100% rename from patches/server/0227-EnderDragon-Events.patch rename to patches/server/0228-EnderDragon-Events.patch diff --git a/patches/server/0228-PlayerElytraBoostEvent.patch b/patches/server/0229-PlayerElytraBoostEvent.patch similarity index 100% rename from patches/server/0228-PlayerElytraBoostEvent.patch rename to patches/server/0229-PlayerElytraBoostEvent.patch diff --git a/patches/server/0229-PlayerLaunchProjectileEvent.patch b/patches/server/0230-PlayerLaunchProjectileEvent.patch similarity index 100% rename from patches/server/0229-PlayerLaunchProjectileEvent.patch rename to patches/server/0230-PlayerLaunchProjectileEvent.patch diff --git a/patches/server/0230-Improve-BlockPosition-inlining.patch b/patches/server/0231-Improve-BlockPosition-inlining.patch similarity index 100% rename from patches/server/0230-Improve-BlockPosition-inlining.patch rename to patches/server/0231-Improve-BlockPosition-inlining.patch diff --git a/patches/server/0231-Optimize-IntIdentityHashBiMiap-nextId.patch b/patches/server/0232-Optimize-IntIdentityHashBiMiap-nextId.patch similarity index 100% rename from patches/server/0231-Optimize-IntIdentityHashBiMiap-nextId.patch rename to patches/server/0232-Optimize-IntIdentityHashBiMiap-nextId.patch diff --git a/patches/server/0232-Option-to-prevent-armor-stands-from-doing-entity-loo.patch b/patches/server/0233-Option-to-prevent-armor-stands-from-doing-entity-loo.patch similarity index 96% rename from patches/server/0232-Option-to-prevent-armor-stands-from-doing-entity-loo.patch rename to patches/server/0233-Option-to-prevent-armor-stands-from-doing-entity-loo.patch index e24e15c8fe..c0e3be8af9 100644 --- a/patches/server/0232-Option-to-prevent-armor-stands-from-doing-entity-loo.patch +++ b/patches/server/0233-Option-to-prevent-armor-stands-from-doing-entity-loo.patch @@ -31,7 +31,7 @@ index 6d717d3852afb3a3a4bef30c68980c402bdfefff..b47b1215e685c453c3496439bb350a91 for (int i = 0; i < list.size(); ++i) { diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index e83ad85f2350da4a01fd0c848a821d556c7ea9b2..a84c8e135511eed9db5895bdf7fc68b3952a5521 100644 +index f627e328e506cb7985c2cb0cc4046f173f39f6fc..d49ce298219dd2144ca1357ab9f158455187c985 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -772,6 +772,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable { diff --git a/patches/server/0233-Vanished-players-don-t-have-rights.patch b/patches/server/0234-Vanished-players-don-t-have-rights.patch similarity index 98% rename from patches/server/0233-Vanished-players-don-t-have-rights.patch rename to patches/server/0234-Vanished-players-don-t-have-rights.patch index f883c5e96c..3bd9160c33 100644 --- a/patches/server/0233-Vanished-players-don-t-have-rights.patch +++ b/patches/server/0234-Vanished-players-don-t-have-rights.patch @@ -38,7 +38,7 @@ index b65736c8395c04b29f97d460a8559c2e44ed3a4f..8e6df16568c0dab482e10ad1b38920d7 BlockCanBuildEvent event = new BlockCanBuildEvent(CraftBlock.at(context.getLevel(), context.getClickedPos()), player, CraftBlockData.fromData(state), defaultReturn); diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index a84c8e135511eed9db5895bdf7fc68b3952a5521..1fef077a6d5efc8bdc171b5c6e2a49129f8589ce 100644 +index d49ce298219dd2144ca1357ab9f158455187c985..17f596e6059334114ce3ee17fbde1ce3d14c5ca1 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -71,6 +71,10 @@ import net.minecraft.world.level.saveddata.maps.MapItemSavedData; diff --git a/patches/server/0234-Allow-disabling-armour-stand-ticking.patch b/patches/server/0235-Allow-disabling-armour-stand-ticking.patch similarity index 100% rename from patches/server/0234-Allow-disabling-armour-stand-ticking.patch rename to patches/server/0235-Allow-disabling-armour-stand-ticking.patch diff --git a/patches/server/0235-SkeletonHorse-Additions.patch b/patches/server/0236-SkeletonHorse-Additions.patch similarity index 100% rename from patches/server/0235-SkeletonHorse-Additions.patch rename to patches/server/0236-SkeletonHorse-Additions.patch diff --git a/patches/server/0236-Don-t-call-getItemMeta-on-hasItemMeta.patch b/patches/server/0237-Don-t-call-getItemMeta-on-hasItemMeta.patch similarity index 100% rename from patches/server/0236-Don-t-call-getItemMeta-on-hasItemMeta.patch rename to patches/server/0237-Don-t-call-getItemMeta-on-hasItemMeta.patch diff --git a/patches/server/0237-Implement-Expanded-ArmorStand-API.patch b/patches/server/0238-Implement-Expanded-ArmorStand-API.patch similarity index 100% rename from patches/server/0237-Implement-Expanded-ArmorStand-API.patch rename to patches/server/0238-Implement-Expanded-ArmorStand-API.patch diff --git a/patches/server/0238-AnvilDamageEvent.patch b/patches/server/0239-AnvilDamageEvent.patch similarity index 94% rename from patches/server/0238-AnvilDamageEvent.patch rename to patches/server/0239-AnvilDamageEvent.patch index c07725a645..f180555c22 100644 --- a/patches/server/0238-AnvilDamageEvent.patch +++ b/patches/server/0239-AnvilDamageEvent.patch @@ -5,7 +5,7 @@ Subject: [PATCH] AnvilDamageEvent diff --git a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -index 2e501ea5f2dc2f13bff9531dcc71e6b7d9b53864..0d1e828debc87c6ddc569dfa8f85be9918365dc1 100644 +index 6acd18521bde95183bf3910328559a2a5829c03d..2ed4930648411ccd52fc970b069e57eba9bceef8 100644 --- a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java +++ b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java @@ -90,6 +90,16 @@ public class AnvilMenu extends ItemCombinerMenu { diff --git a/patches/server/0239-Add-hand-to-bucket-events.patch b/patches/server/0240-Add-hand-to-bucket-events.patch similarity index 98% rename from patches/server/0239-Add-hand-to-bucket-events.patch rename to patches/server/0240-Add-hand-to-bucket-events.patch index e33bef787f..327422a96e 100644 --- a/patches/server/0239-Add-hand-to-bucket-events.patch +++ b/patches/server/0240-Add-hand-to-bucket-events.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add hand to bucket events diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index c4bb49f8007169f62deeef19ccce2b907d961374..68e0d76aed7e993c35ac2e73dda6ed251a817d39 100644 +index c4aa14e81151ada49e61f4dd25267f7eb10450a6..dd2710f2a093fcca9f80edf41866459d63bac94c 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1374,15 +1374,17 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1465,15 +1465,17 @@ public class ServerLevel extends Level implements WorldGenLevel { this.getServer().getPlayerList().broadcastAll(new ClientboundSetDefaultSpawnPositionPacket(pos, angle)); } diff --git a/patches/server/0240-Add-TNTPrimeEvent.patch b/patches/server/0241-Add-TNTPrimeEvent.patch similarity index 100% rename from patches/server/0240-Add-TNTPrimeEvent.patch rename to patches/server/0241-Add-TNTPrimeEvent.patch diff --git a/patches/server/0241-Break-up-and-make-tab-spam-limits-configurable.patch b/patches/server/0242-Break-up-and-make-tab-spam-limits-configurable.patch similarity index 98% rename from patches/server/0241-Break-up-and-make-tab-spam-limits-configurable.patch rename to patches/server/0242-Break-up-and-make-tab-spam-limits-configurable.patch index 5d8e303e29..6fb0983f77 100644 --- a/patches/server/0241-Break-up-and-make-tab-spam-limits-configurable.patch +++ b/patches/server/0242-Break-up-and-make-tab-spam-limits-configurable.patch @@ -22,7 +22,7 @@ to take the burden of this into their own hand without having to rely on plugins doing unsafe things. diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index b42272b574e31d695ee31bd3fb76a4b906713442..7e4ac2699eca8aebba675f3ffdac3a4dad422e4c 100644 +index ce4ab03a40fac6a840f4c4b78da0a96bb6fb6823..a6d27dcdf954bc2682aba1d9efab42d2d626f8da 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -314,4 +314,18 @@ public class PaperConfig { diff --git a/patches/server/0242-MC-135506-Experience-should-save-as-Integers.patch b/patches/server/0243-MC-135506-Experience-should-save-as-Integers.patch similarity index 100% rename from patches/server/0242-MC-135506-Experience-should-save-as-Integers.patch rename to patches/server/0243-MC-135506-Experience-should-save-as-Integers.patch diff --git a/patches/server/0243-Fix-client-rendering-skulls-from-same-user.patch b/patches/server/0244-Fix-client-rendering-skulls-from-same-user.patch similarity index 100% rename from patches/server/0243-Fix-client-rendering-skulls-from-same-user.patch rename to patches/server/0244-Fix-client-rendering-skulls-from-same-user.patch diff --git a/patches/server/0244-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch b/patches/server/0245-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch similarity index 91% rename from patches/server/0244-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch rename to patches/server/0245-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch index d8e690b058..10683d35b6 100644 --- a/patches/server/0244-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch +++ b/patches/server/0245-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch @@ -8,10 +8,10 @@ Add -Ddebug.entities=true to your JVM flags to gain more information 1.17: Needs to be reworked for new entity storage system diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 919a489a5c7b338659c62ae67fc0a6ceee9dcdf9..db4dac607cf24d3d2cd407255c60678ae4be1a1b 100644 +index 272840d1f463276944cf4aa941f4451895fa8594..ab3a6aaa7d9df119af219a0c7cd83a763b8c830c 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -1157,6 +1157,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1206,6 +1206,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } else { ChunkMap.TrackedEntity playerchunkmap_entitytracker = new ChunkMap.TrackedEntity(entity, i, j, entitytypes.trackDeltas()); @@ -19,7 +19,7 @@ index 919a489a5c7b338659c62ae67fc0a6ceee9dcdf9..db4dac607cf24d3d2cd407255c60678a this.entityMap.put(entity.getId(), playerchunkmap_entitytracker); playerchunkmap_entitytracker.updatePlayers(this.level.players()); if (entity instanceof ServerPlayer) { -@@ -1199,7 +1200,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1248,7 +1249,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider if (playerchunkmap_entitytracker1 != null) { playerchunkmap_entitytracker1.broadcastRemoved(); } @@ -29,10 +29,10 @@ index 919a489a5c7b338659c62ae67fc0a6ceee9dcdf9..db4dac607cf24d3d2cd407255c60678a protected void tick() { diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 4e0e5d3e0b91d4b7be4eaa6fe252287d90bc010e..a67905f4cc6ade36b17eeb6c77d00f4837c42c86 100644 +index dd2710f2a093fcca9f80edf41866459d63bac94c..1d95dae881fd89056e2ad86a8f33aecf90cf8a3b 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -198,6 +198,9 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -199,6 +199,9 @@ public class ServerLevel extends Level implements WorldGenLevel { public final LevelStorageSource.LevelStorageAccess convertable; public final UUID uuid; public boolean hasPhysicsEvent = true; // Paper @@ -42,7 +42,7 @@ index 4e0e5d3e0b91d4b7be4eaa6fe252287d90bc010e..a67905f4cc6ade36b17eeb6c77d00f48 @Override public LevelChunk getChunkIfLoaded(int x, int z) { // Paper - this was added in world too but keeping here for NMS ABI return this.chunkSource.getChunk(x, z, false); -@@ -979,7 +982,28 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1070,7 +1073,28 @@ public class ServerLevel extends Level implements WorldGenLevel { // CraftBukkit start private boolean addEntity0(Entity entity, CreatureSpawnEvent.SpawnReason spawnReason) { org.spigotmc.AsyncCatcher.catchOp("entity add"); // Spigot @@ -72,7 +72,7 @@ index 4e0e5d3e0b91d4b7be4eaa6fe252287d90bc010e..a67905f4cc6ade36b17eeb6c77d00f48 return false; } else { diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 7ce7660bcd6cdc23d6d06c9009a96a08e637ccbd..103a0eb2580384d50eda74da83fbc64f5e6dd7cd 100644 +index 2a6954e3c9ff82d68647bf8f4c4803184fab0bbe..b4f2b969ad30be38c0a9e3f8efd2a57c3e0f7df0 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -171,6 +171,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n @@ -85,7 +85,7 @@ index 7ce7660bcd6cdc23d6d06c9009a96a08e637ccbd..103a0eb2580384d50eda74da83fbc64f if (this.bukkitEntity == null) { this.bukkitEntity = CraftEntity.getEntity(this.level.getCraftServer(), this); diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index ac46dac8be79953720fab6485caf677f2c3ad87b..de4768e0c1d3665fa22ec1d32596283e778b3961 100644 +index 96c685ffbf6058b69f0c573a1255a9e8596365bf..e0458f1340b6f42fecab18d09538414a53708b14 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -141,6 +141,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { diff --git a/patches/server/0245-Add-Early-Warning-Feature-to-WatchDog.patch b/patches/server/0246-Add-Early-Warning-Feature-to-WatchDog.patch similarity index 96% rename from patches/server/0245-Add-Early-Warning-Feature-to-WatchDog.patch rename to patches/server/0246-Add-Early-Warning-Feature-to-WatchDog.patch index c973f0f614..62fc937b8c 100644 --- a/patches/server/0245-Add-Early-Warning-Feature-to-WatchDog.patch +++ b/patches/server/0246-Add-Early-Warning-Feature-to-WatchDog.patch @@ -9,7 +9,7 @@ thread dumps at an interval until the point of crash. This will help diagnose what was going on in that time before the crash. diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 7e4ac2699eca8aebba675f3ffdac3a4dad422e4c..96366bfb03e1bc57a10154c13341a80ce6f0f4c4 100644 +index a6d27dcdf954bc2682aba1d9efab42d2d626f8da..dd81701230133442186524e9cd65d70232b824f3 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -25,6 +25,7 @@ import org.bukkit.configuration.file.YamlConfiguration; @@ -36,10 +36,10 @@ index 7e4ac2699eca8aebba675f3ffdac3a4dad422e4c..96366bfb03e1bc57a10154c13341a80c public static int tabSpamLimit = 500; private static void tabSpamLimiters() { diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index cc90827ca284c058f6f209cf7c9cc6ca8b8be29d..f043bc485e2813706bce53f10775d295bb2dd5e7 100644 +index 74e780212773e0d3cce19cb9c33cdcaa4a708f14..cc4a94669c5f0dee99b6d8cb89650cf0c701e9ab 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1099,6 +1099,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { return true; }); @@ -2365,7 +2369,7 @@ index 5ec46f8f39b36333c33b179db91c17d79337e2e1..fffeb068a432c0cf1d7d52ff7c4dca60 } else { this.visibleChunkMap.values().stream().filter(ChunkHolder::wasAccessibleSinceLastSave).forEach((playerchunk) -> { ChunkAccess ichunkaccess = (ChunkAccess) playerchunk.getChunkToSave().getNow(null); // CraftBukkit - decompile error -@@ -459,16 +461,20 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -498,16 +500,20 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } @@ -2387,7 +2391,7 @@ index 5ec46f8f39b36333c33b179db91c17d79337e2e1..fffeb068a432c0cf1d7d52ff7c4dca60 } gameprofilerfiller.pop(); -@@ -489,12 +495,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -528,12 +534,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider if (playerchunk != null) { this.pendingUnloads.put(j, playerchunk); this.modified = true; @@ -2402,7 +2406,7 @@ index 5ec46f8f39b36333c33b179db91c17d79337e2e1..fffeb068a432c0cf1d7d52ff7c4dca60 } } activityAccountant.endActivity(); // Spigot -@@ -508,6 +515,46 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -547,6 +554,46 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } @@ -2449,7 +2453,7 @@ index 5ec46f8f39b36333c33b179db91c17d79337e2e1..fffeb068a432c0cf1d7d52ff7c4dca60 private void scheduleUnload(long pos, ChunkHolder holder) { CompletableFuture completablefuture = holder.getChunkToSave(); Consumer consumer = (ichunkaccess) -> { // CraftBukkit - decompile error -@@ -521,7 +568,16 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -566,7 +613,16 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider ((LevelChunk) ichunkaccess).setLoaded(false); } @@ -2467,7 +2471,7 @@ index 5ec46f8f39b36333c33b179db91c17d79337e2e1..fffeb068a432c0cf1d7d52ff7c4dca60 if (this.entitiesInLevel.remove(pos) && ichunkaccess instanceof LevelChunk) { LevelChunk chunk = (LevelChunk) ichunkaccess; -@@ -582,19 +638,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -631,19 +687,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } private CompletableFuture> scheduleChunkLoad(ChunkPos pos) { @@ -2484,23 +2488,23 @@ index 5ec46f8f39b36333c33b179db91c17d79337e2e1..fffeb068a432c0cf1d7d52ff7c4dca60 + if (ioThrowable != null) { + com.destroystokyo.paper.util.SneakyThrow.sneaky(ioThrowable); + } -+ + +- if (nbttagcompound != null) {try (Timing ignored2 = this.level.timings.chunkLoadLevelTimer.startTimingIfSync()) { // Paper start - timings +- boolean flag = nbttagcompound.contains("Level", 10) && nbttagcompound.getCompound("Level").contains("Status", 8); + this.getVillagePlace().loadInData(pos, chunkHolder.poiData); + chunkHolder.tasks.forEach(Runnable::run); + // Paper end -- if (nbttagcompound != null) {try (Timing ignored2 = this.level.timings.chunkLoadLevelTimer.startTimingIfSync()) { // Paper start - timings -- boolean flag = nbttagcompound.contains("Level", 10) && nbttagcompound.getCompound("Level").contains("Status", 8); -+ if (chunkHolder.protoChunk != null) {try (Timing ignored2 = this.level.timings.chunkLoadLevelTimer.startTimingIfSync()) { // Paper start - timings // Paper - chunk is created async - - if (flag) { - ProtoChunk protochunk = ChunkSerializer.read(this.level, this.structureManager, this.poiManager, pos, nbttagcompound); ++ if (chunkHolder.protoChunk != null) {try (Timing ignored2 = this.level.timings.chunkLoadLevelTimer.startTimingIfSync()) { // Paper start - timings // Paper - chunk is created async ++ + if (true) { + ProtoChunk protochunk = chunkHolder.protoChunk; this.markPosition(pos, protochunk.getStatus().getChunkType()); return Either.left(protochunk); -@@ -617,7 +677,32 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -666,7 +726,32 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.markPositionReplaceable(pos); return Either.left(new ProtoChunk(pos, UpgradeData.EMPTY, this.level)); @@ -2534,7 +2538,7 @@ index 5ec46f8f39b36333c33b179db91c17d79337e2e1..fffeb068a432c0cf1d7d52ff7c4dca60 } private void markPositionReplaceable(ChunkPos chunkcoordintpair) { -@@ -799,6 +884,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -848,6 +933,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public boolean save(ChunkAccess chunk) { @@ -2542,7 +2546,7 @@ index 5ec46f8f39b36333c33b179db91c17d79337e2e1..fffeb068a432c0cf1d7d52ff7c4dca60 this.poiManager.flush(chunk.getPos()); if (!chunk.isUnsaved()) { return false; -@@ -810,7 +896,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -859,7 +945,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider ChunkStatus chunkstatus = chunk.getStatus(); if (chunkstatus.getChunkType() != ChunkStatus.ChunkType.LEVELCHUNK) { @@ -2551,7 +2555,7 @@ index 5ec46f8f39b36333c33b179db91c17d79337e2e1..fffeb068a432c0cf1d7d52ff7c4dca60 return false; } -@@ -820,9 +906,16 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -869,9 +955,16 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } this.level.getProfiler().incrementCounter("chunkSave"); @@ -2560,9 +2564,9 @@ index 5ec46f8f39b36333c33b179db91c17d79337e2e1..fffeb068a432c0cf1d7d52ff7c4dca60 + try (co.aikar.timings.Timing ignored1 = this.level.timings.chunkSaveDataSerialization.startTiming()) { // Paper + nbttagcompound = ChunkSerializer.write(this.level, chunk); + } // Paper ++ - this.write(chunkcoordintpair, nbttagcompound); -+ + // Paper start - async chunk io + com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.scheduleSave(this.level, chunkcoordintpair.x, chunkcoordintpair.z, + null, nbttagcompound, com.destroystokyo.paper.io.PrioritizedTaskQueue.NORMAL_PRIORITY); @@ -2570,7 +2574,7 @@ index 5ec46f8f39b36333c33b179db91c17d79337e2e1..fffeb068a432c0cf1d7d52ff7c4dca60 this.markPosition(chunkcoordintpair, chunkstatus.getChunkType()); return true; } catch (Exception exception) { -@@ -831,6 +924,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -880,6 +973,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider return false; } } @@ -2578,7 +2582,7 @@ index 5ec46f8f39b36333c33b179db91c17d79337e2e1..fffeb068a432c0cf1d7d52ff7c4dca60 } private boolean isExistingChunkFull(ChunkPos chunkcoordintpair) { -@@ -958,6 +1052,35 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1007,6 +1101,35 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } } @@ -2614,7 +2618,7 @@ index 5ec46f8f39b36333c33b179db91c17d79337e2e1..fffeb068a432c0cf1d7d52ff7c4dca60 @Nullable private CompoundTag readChunk(ChunkPos pos) throws IOException { CompoundTag nbttagcompound = this.read(pos); -@@ -1311,6 +1434,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1360,6 +1483,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } @@ -2623,10 +2627,10 @@ index 5ec46f8f39b36333c33b179db91c17d79337e2e1..fffeb068a432c0cf1d7d52ff7c4dca60 return this.poiManager; } diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index a778eff06a24a665874a315704f00dc5996420c9..13313635e994f848edbe3e3fe607a21f8baa1790 100644 +index dd61048b3898ef3ea5a2bd2f95e9bd3fc11dc298..e12a794ace00f2b440e4cb1c197df018e7e27063 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -320,10 +320,128 @@ public class ServerChunkCache extends ChunkSource { +@@ -481,10 +481,128 @@ public class ServerChunkCache extends ChunkSource { return ret; } // Paper end @@ -2755,7 +2759,7 @@ index a778eff06a24a665874a315704f00dc5996420c9..13313635e994f848edbe3e3fe607a21f if (Thread.currentThread() != this.mainThread) { return (ChunkAccess) CompletableFuture.supplyAsync(() -> { return this.getChunk(x, z, leastStatus, create); -@@ -346,13 +464,18 @@ public class ServerChunkCache extends ChunkSource { +@@ -507,13 +625,18 @@ public class ServerChunkCache extends ChunkSource { } gameprofilerfiller.incrementCounter("getChunkCacheMiss"); @@ -2775,7 +2779,7 @@ index a778eff06a24a665874a315704f00dc5996420c9..13313635e994f848edbe3e3fe607a21f this.level.timings.syncChunkLoad.stopTiming(); // Paper } // Paper ichunkaccess = (ChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> { -@@ -439,6 +562,11 @@ public class ServerChunkCache extends ChunkSource { +@@ -600,6 +723,11 @@ public class ServerChunkCache extends ChunkSource { } private CompletableFuture> getChunkFutureMainThread(int i, int j, ChunkStatus chunkstatus, boolean flag) { @@ -2787,7 +2791,7 @@ index a778eff06a24a665874a315704f00dc5996420c9..13313635e994f848edbe3e3fe607a21f ChunkPos chunkcoordintpair = new ChunkPos(i, j); long k = chunkcoordintpair.toLong(); int l = 33 + ChunkStatus.getDistance(chunkstatus); -@@ -836,11 +964,12 @@ public class ServerChunkCache extends ChunkSource { +@@ -997,11 +1125,12 @@ public class ServerChunkCache extends ChunkSource { // CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task public boolean pollTask() { try { @@ -2802,13 +2806,14 @@ index a778eff06a24a665874a315704f00dc5996420c9..13313635e994f848edbe3e3fe607a21f } finally { chunkMap.callbackExecutor.run(); diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 22402247d82cfe722254289f0905f5e01e8c800d..6c4411d621ff60ed44fba00f14ef4c1735ed56f7 100644 +index 1d95dae881fd89056e2ad86a8f33aecf90cf8a3b..769753441eea00296d818b7a741551fd3034cb6a 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -206,6 +206,79 @@ public class ServerLevel extends Level implements WorldGenLevel { - return this.chunkSource.getChunk(x, z, false); +@@ -295,6 +295,78 @@ public class ServerLevel extends Level implements WorldGenLevel { + } + } } - ++ + // Paper start - Asynchronous IO + public final com.destroystokyo.paper.io.PaperFileIOThread.ChunkDataController poiDataController = new com.destroystokyo.paper.io.PaperFileIOThread.ChunkDataController() { + @Override @@ -2880,12 +2885,10 @@ index 22402247d82cfe722254289f0905f5e01e8c800d..6c4411d621ff60ed44fba00f14ef4c17 + } + }; + public final com.destroystokyo.paper.io.chunk.ChunkTaskManager asyncChunkTaskManager; -+ // Paper end -+ + // Paper end + // 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) { - // Objects.requireNonNull(minecraftserver); // CraftBukkit - decompile error -@@ -277,6 +350,8 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -368,6 +440,8 @@ public class ServerLevel extends Level implements WorldGenLevel { this.sleepStatus = new SleepStatus(); this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit @@ -3658,7 +3661,7 @@ index d99d019df02f8defb6c5c9082b8c650231c20775..5d0e56a2c8fb6f801643928f8b75e06c // Spigot start @Override diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -index 8b776445716e483e040be165c18118593a63d694..3980197b7e876ffea27c183121a8e58409ecc4a7 100644 +index 8b776445716e483e040be165c18118593a63d694..f1fdf5fdaf3a590e8a011db0c5c741be4bfefc9c 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -15,6 +15,7 @@ import net.minecraft.network.chat.Component; @@ -3669,29 +3672,38 @@ index 8b776445716e483e040be165c18118593a63d694..3980197b7e876ffea27c183121a8e584 import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.AreaEffectCloud; import net.minecraft.world.entity.Entity; -@@ -518,6 +519,28 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { +@@ -518,6 +519,37 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { this.entity.setYHeadRot(yaw); } -+ @Override// Paper start -+ public java.util.concurrent.CompletableFuture teleportAsync(Location loc, @javax.annotation.Nonnull org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) { -+ net.minecraft.server.level.ChunkMap playerChunkMap = ((CraftWorld) loc.getWorld()).getHandle().getChunkSource().chunkMap; -+ java.util.concurrent.CompletableFuture future = new java.util.concurrent.CompletableFuture<>(); ++ // Paper start ++ @Override ++ public java.util.concurrent.CompletableFuture teleportAsync(Location location, TeleportCause cause) { ++ Preconditions.checkArgument(location != null, "location"); ++ location.checkFinite(); ++ Location locationClone = location.clone(); // clone so we don't need to worry about mutations after this call. + -+ loc.getWorld().getChunkAtAsyncUrgently(loc).thenCompose(chunk -> { -+ net.minecraft.world.level.ChunkPos pair = new net.minecraft.world.level.ChunkPos(chunk.getX(), chunk.getZ()); -+ ((CraftWorld) loc.getWorld()).getHandle().getChunkSource().addTicketAtLevel(TicketType.POST_TELEPORT, pair, 31, 0); -+ net.minecraft.server.level.ChunkHolder updatingChunk = playerChunkMap.getUpdatingChunkIfPresent(pair.toLong()); -+ if (updatingChunk != null) { -+ return updatingChunk.getEntityTickingChunkFuture(); -+ } else { -+ return java.util.concurrent.CompletableFuture.completedFuture(com.mojang.datafixers.util.Either.left(((org.bukkit.craftbukkit.CraftChunk)chunk).getHandle())); ++ net.minecraft.server.level.ServerLevel world = ((CraftWorld)locationClone.getWorld()).getHandle(); ++ java.util.concurrent.CompletableFuture ret = new java.util.concurrent.CompletableFuture<>(); ++ ++ world.loadChunksForMoveAsync(getHandle().getBoundingBoxAt(locationClone.getX(), locationClone.getY(), locationClone.getZ()), location.getX(), location.getZ(), (list) -> { ++ net.minecraft.server.level.ServerChunkCache chunkProviderServer = world.getChunkSource(); ++ for (net.minecraft.world.level.chunk.ChunkAccess chunk : list) { ++ chunkProviderServer.addTicketAtLevel(net.minecraft.server.level.TicketType.POST_TELEPORT, chunk.getPos(), 33, CraftEntity.this.getEntityId()); + } -+ }).thenAccept((chunk) -> future.complete(teleport(loc, cause))).exceptionally(ex -> { -+ future.completeExceptionally(ex); -+ return null; ++ net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(() -> { ++ try { ++ ret.complete(CraftEntity.this.teleport(locationClone, cause) ? Boolean.TRUE : Boolean.FALSE); ++ } catch (Throwable throwable) { ++ if (throwable instanceof ThreadDeath) { ++ throw (ThreadDeath)throwable; ++ } ++ ret.completeExceptionally(throwable); ++ } ++ }); + }); -+ return future; ++ ++ return ret; + } + // Paper end + diff --git a/patches/server/0261-Add-ray-tracing-methods-to-LivingEntity.patch b/patches/server/0262-Add-ray-tracing-methods-to-LivingEntity.patch similarity index 100% rename from patches/server/0261-Add-ray-tracing-methods-to-LivingEntity.patch rename to patches/server/0262-Add-ray-tracing-methods-to-LivingEntity.patch diff --git a/patches/server/0262-Expose-attack-cooldown-methods-for-Player.patch b/patches/server/0263-Expose-attack-cooldown-methods-for-Player.patch similarity index 100% rename from patches/server/0262-Expose-attack-cooldown-methods-for-Player.patch rename to patches/server/0263-Expose-attack-cooldown-methods-for-Player.patch diff --git a/patches/server/0263-Improve-death-events.patch b/patches/server/0264-Improve-death-events.patch similarity index 99% rename from patches/server/0263-Improve-death-events.patch rename to patches/server/0264-Improve-death-events.patch index 77021d76b3..f437721854 100644 --- a/patches/server/0263-Improve-death-events.patch +++ b/patches/server/0264-Improve-death-events.patch @@ -19,7 +19,7 @@ maybe more (please check patch overrides for drops for more): - players, armor stands, foxes, chested donkeys/llamas diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 740ee29affa30ffd7a7e2b612cae4a1124f9bfb4..a0ae4dc83e6ff43239a7d4bbd6f8e4197d6290d3 100644 +index 2ecbe2c21fd9beaf4ce4f1b3531acb1036f3dfaa..d5670af087fbcc9ebc18d4779373487b704cf810 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -219,6 +219,10 @@ public class ServerPlayer extends Player { diff --git a/patches/server/0264-Allow-chests-to-be-placed-with-NBT-data.patch b/patches/server/0265-Allow-chests-to-be-placed-with-NBT-data.patch similarity index 100% rename from patches/server/0264-Allow-chests-to-be-placed-with-NBT-data.patch rename to patches/server/0265-Allow-chests-to-be-placed-with-NBT-data.patch diff --git a/patches/server/0265-Mob-Pathfinding-API.patch b/patches/server/0266-Mob-Pathfinding-API.patch similarity index 100% rename from patches/server/0265-Mob-Pathfinding-API.patch rename to patches/server/0266-Mob-Pathfinding-API.patch diff --git a/patches/server/0266-Prevent-chunk-loading-from-Fluid-Flowing.patch b/patches/server/0267-Prevent-chunk-loading-from-Fluid-Flowing.patch similarity index 100% rename from patches/server/0266-Prevent-chunk-loading-from-Fluid-Flowing.patch rename to patches/server/0267-Prevent-chunk-loading-from-Fluid-Flowing.patch diff --git a/patches/server/0267-Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch b/patches/server/0268-Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch similarity index 100% rename from patches/server/0267-Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch rename to patches/server/0268-Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch diff --git a/patches/server/0268-Prevent-Mob-AI-Rules-from-Loading-Chunks.patch b/patches/server/0269-Prevent-Mob-AI-Rules-from-Loading-Chunks.patch similarity index 100% rename from patches/server/0268-Prevent-Mob-AI-Rules-from-Loading-Chunks.patch rename to patches/server/0269-Prevent-Mob-AI-Rules-from-Loading-Chunks.patch diff --git a/patches/server/0269-Prevent-mob-spawning-from-loading-generating-chunks.patch b/patches/server/0270-Prevent-mob-spawning-from-loading-generating-chunks.patch similarity index 100% rename from patches/server/0269-Prevent-mob-spawning-from-loading-generating-chunks.patch rename to patches/server/0270-Prevent-mob-spawning-from-loading-generating-chunks.patch diff --git a/patches/server/0270-Implement-furnace-cook-speed-multiplier-API.patch b/patches/server/0271-Implement-furnace-cook-speed-multiplier-API.patch similarity index 100% rename from patches/server/0270-Implement-furnace-cook-speed-multiplier-API.patch rename to patches/server/0271-Implement-furnace-cook-speed-multiplier-API.patch diff --git a/patches/server/0271-Catch-JsonParseException-in-Entity-and-TE-names.patch b/patches/server/0272-Catch-JsonParseException-in-Entity-and-TE-names.patch similarity index 100% rename from patches/server/0271-Catch-JsonParseException-in-Entity-and-TE-names.patch rename to patches/server/0272-Catch-JsonParseException-in-Entity-and-TE-names.patch diff --git a/patches/server/0272-Honor-EntityAgeable.ageLock.patch b/patches/server/0273-Honor-EntityAgeable.ageLock.patch similarity index 100% rename from patches/server/0272-Honor-EntityAgeable.ageLock.patch rename to patches/server/0273-Honor-EntityAgeable.ageLock.patch diff --git a/patches/server/0273-Configurable-connection-throttle-kick-message.patch b/patches/server/0274-Configurable-connection-throttle-kick-message.patch similarity index 96% rename from patches/server/0273-Configurable-connection-throttle-kick-message.patch rename to patches/server/0274-Configurable-connection-throttle-kick-message.patch index 91ba48f48a..c33f8377e5 100644 --- a/patches/server/0273-Configurable-connection-throttle-kick-message.patch +++ b/patches/server/0274-Configurable-connection-throttle-kick-message.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Configurable connection throttle kick message diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 8042efc858c6d134ce1071465479f8de11db64b4..c0b6e398839136ef57fdf37d34c91a4db538b92d 100644 +index 8a1f581bd7ef8a20329813dedb5bb952e32d41ae..69ad59f0faf1e9a1134d0a460b49569f670055f0 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -291,6 +291,11 @@ public class PaperConfig { diff --git a/patches/server/0274-Hook-into-CB-plugin-rewrites.patch b/patches/server/0275-Hook-into-CB-plugin-rewrites.patch similarity index 100% rename from patches/server/0274-Hook-into-CB-plugin-rewrites.patch rename to patches/server/0275-Hook-into-CB-plugin-rewrites.patch diff --git a/patches/server/0275-Add-sun-related-API.patch b/patches/server/0276-Add-sun-related-API.patch similarity index 100% rename from patches/server/0275-Add-sun-related-API.patch rename to patches/server/0276-Add-sun-related-API.patch diff --git a/patches/server/0276-Add-LivingEntity-getTargetEntity.patch b/patches/server/0277-Add-LivingEntity-getTargetEntity.patch similarity index 100% rename from patches/server/0276-Add-LivingEntity-getTargetEntity.patch rename to patches/server/0277-Add-LivingEntity-getTargetEntity.patch diff --git a/patches/server/0277-Turtle-API.patch b/patches/server/0278-Turtle-API.patch similarity index 100% rename from patches/server/0277-Turtle-API.patch rename to patches/server/0278-Turtle-API.patch diff --git a/patches/server/0278-MC-50319-Check-other-worlds-for-shooter-of-projectil.patch b/patches/server/0279-MC-50319-Check-other-worlds-for-shooter-of-projectil.patch similarity index 100% rename from patches/server/0278-MC-50319-Check-other-worlds-for-shooter-of-projectil.patch rename to patches/server/0279-MC-50319-Check-other-worlds-for-shooter-of-projectil.patch diff --git a/patches/server/0279-Call-player-spectator-target-events-and-improve-impl.patch b/patches/server/0280-Call-player-spectator-target-events-and-improve-impl.patch similarity index 97% rename from patches/server/0279-Call-player-spectator-target-events-and-improve-impl.patch rename to patches/server/0280-Call-player-spectator-target-events-and-improve-impl.patch index eced650577..f67c4ec80e 100644 --- a/patches/server/0279-Call-player-spectator-target-events-and-improve-impl.patch +++ b/patches/server/0280-Call-player-spectator-target-events-and-improve-impl.patch @@ -19,7 +19,7 @@ spectate the target entity. Co-authored-by: Spottedleaf diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index a0ae4dc83e6ff43239a7d4bbd6f8e4197d6290d3..95939ed686ac052ca338520f2b2169b717a6127c 100644 +index d5670af087fbcc9ebc18d4779373487b704cf810..529d4ec405c76ed2fce5d147d41251513d5293f2 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -1824,14 +1824,58 @@ public class ServerPlayer extends Player { diff --git a/patches/server/0280-Add-Velocity-IP-Forwarding-Support.patch b/patches/server/0281-Add-Velocity-IP-Forwarding-Support.patch similarity index 98% rename from patches/server/0280-Add-Velocity-IP-Forwarding-Support.patch rename to patches/server/0281-Add-Velocity-IP-Forwarding-Support.patch index f1e0c02c97..3b68027100 100644 --- a/patches/server/0280-Add-Velocity-IP-Forwarding-Support.patch +++ b/patches/server/0281-Add-Velocity-IP-Forwarding-Support.patch @@ -14,7 +14,7 @@ forwarding, and is integrated into the Minecraft login process by using the 1.13 login plugin message packet. diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 4d0ebcf6b870eb67e04cea4b1a652c946ef4a2ef..cdec195a7aab9195c07ebfbba4f25b27a319ca6e 100644 +index 69ad59f0faf1e9a1134d0a460b49569f670055f0..4b4fbd8747740111cc2e25f0c4d29a29926a3a1b 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -9,6 +9,7 @@ import java.io.IOException; @@ -225,7 +225,7 @@ index 39bdda56aaa5503efc15207261634127b462c3e7..3fd913f3e963cf2da849a52364356e3b } diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index f18a48b83e848a166a7d99f1937493df25d3e11d..a2acf9c1969fd3d365276421d63aae6617a52dc4 100644 +index d62f1650a0879e46e6c619c0f7387e0c438430ec..d64bcc0aafb918e82e881f6503ce8311e78a3f6f 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -694,7 +694,7 @@ public final class CraftServer implements Server { diff --git a/patches/server/0281-Add-more-Witch-API.patch b/patches/server/0282-Add-more-Witch-API.patch similarity index 100% rename from patches/server/0281-Add-more-Witch-API.patch rename to patches/server/0282-Add-more-Witch-API.patch diff --git a/patches/server/0282-Check-Drowned-for-Villager-Aggression-Config.patch b/patches/server/0283-Check-Drowned-for-Villager-Aggression-Config.patch similarity index 100% rename from patches/server/0282-Check-Drowned-for-Villager-Aggression-Config.patch rename to patches/server/0283-Check-Drowned-for-Villager-Aggression-Config.patch diff --git a/patches/server/0283-Here-s-Johnny.patch b/patches/server/0284-Here-s-Johnny.patch similarity index 100% rename from patches/server/0283-Here-s-Johnny.patch rename to patches/server/0284-Here-s-Johnny.patch diff --git a/patches/server/0284-Add-option-to-prevent-players-from-moving-into-unloa.patch b/patches/server/0285-Add-option-to-prevent-players-from-moving-into-unloa.patch similarity index 100% rename from patches/server/0284-Add-option-to-prevent-players-from-moving-into-unloa.patch rename to patches/server/0285-Add-option-to-prevent-players-from-moving-into-unloa.patch diff --git a/patches/server/0285-Reset-players-airTicks-on-respawn.patch b/patches/server/0286-Reset-players-airTicks-on-respawn.patch similarity index 89% rename from patches/server/0285-Reset-players-airTicks-on-respawn.patch rename to patches/server/0286-Reset-players-airTicks-on-respawn.patch index b60f2ed447..fa71da12e7 100644 --- a/patches/server/0285-Reset-players-airTicks-on-respawn.patch +++ b/patches/server/0286-Reset-players-airTicks-on-respawn.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Reset players airTicks on respawn diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 95939ed686ac052ca338520f2b2169b717a6127c..1fb1755706bac5633094ca7f17744bd46a9e87ee 100644 +index 529d4ec405c76ed2fce5d147d41251513d5293f2..0dd1762782837e905fbc405d95ef6e1dcd9b39b4 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -2223,6 +2223,7 @@ public class ServerPlayer extends Player { diff --git a/patches/server/0286-Don-t-sleep-after-profile-lookups-if-not-needed.patch b/patches/server/0287-Don-t-sleep-after-profile-lookups-if-not-needed.patch similarity index 100% rename from patches/server/0286-Don-t-sleep-after-profile-lookups-if-not-needed.patch rename to patches/server/0287-Don-t-sleep-after-profile-lookups-if-not-needed.patch diff --git a/patches/server/0287-Improve-Server-Thread-Pool-and-Thread-Priorities.patch b/patches/server/0288-Improve-Server-Thread-Pool-and-Thread-Priorities.patch similarity index 96% rename from patches/server/0287-Improve-Server-Thread-Pool-and-Thread-Priorities.patch rename to patches/server/0288-Improve-Server-Thread-Pool-and-Thread-Priorities.patch index 187dcb9ec2..d8652d173f 100644 --- a/patches/server/0287-Improve-Server-Thread-Pool-and-Thread-Priorities.patch +++ b/patches/server/0288-Improve-Server-Thread-Pool-and-Thread-Priorities.patch @@ -67,10 +67,10 @@ index 65e0ca442980f273d2fe5f131e174cd92f80da20..81f4f26a6b83079d36acd1fd86dede0e return executorService; } diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index a727b0305b2dfb92c763ea1b31d85769868cfed2..8b81f618598a4a95e4cb19c3e65b7e6d03459530 100644 +index 7210ad3c4d83b883590c7659925b8b3f56a057e5..75769e37d803f35e5bd484c713c1bd9437babf4f 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -316,6 +316,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop> getFutureIfPresentUnchecked(ChunkStatus leastStatus) { diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index fffeb068a432c0cf1d7d52ff7c4dca60ff6a9664..a3155ea07a204d0d03bb61113fb76a96d7deb826 100644 +index f5800e2b7657de374daa54d68f1adab1873b5516..6d0092c8a71d775ffbe03374199a4be28f418058 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java @@ -84,6 +84,7 @@ import net.minecraft.world.level.chunk.ProtoChunk; @@ -43,7 +43,7 @@ index fffeb068a432c0cf1d7d52ff7c4dca60ff6a9664..a3155ea07a204d0d03bb61113fb76a96 import net.minecraft.world.level.entity.ChunkStatusUpdateListener; import net.minecraft.world.level.levelgen.structure.StructureStart; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager; -@@ -1082,12 +1083,61 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1131,12 +1132,61 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider // Paper end @Nullable @@ -108,10 +108,10 @@ index fffeb068a432c0cf1d7d52ff7c4dca60ff6a9664..a3155ea07a204d0d03bb61113fb76a96 // Spigot start return this.isOutsideOfRange(chunkPos, false); diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 13313635e994f848edbe3e3fe607a21f8baa1790..d5ec4a19869e04877869e3bb53c98a7e5943c931 100644 +index e12a794ace00f2b440e4cb1c197df018e7e27063..ddd535ad7d2b051e0d05320e4491bad46df8ffbe 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -321,6 +321,7 @@ public class ServerChunkCache extends ChunkSource { +@@ -482,6 +482,7 @@ public class ServerChunkCache extends ChunkSource { } // Paper end // Paper start - async chunk io diff --git a/patches/server/0337-Show-blockstate-location-if-we-failed-to-read-it.patch b/patches/server/0338-Show-blockstate-location-if-we-failed-to-read-it.patch similarity index 94% rename from patches/server/0337-Show-blockstate-location-if-we-failed-to-read-it.patch rename to patches/server/0338-Show-blockstate-location-if-we-failed-to-read-it.patch index 5f4a5fc797..3740ac2aea 100644 --- a/patches/server/0337-Show-blockstate-location-if-we-failed-to-read-it.patch +++ b/patches/server/0338-Show-blockstate-location-if-we-failed-to-read-it.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Show blockstate location if we failed to read it diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java -index 0981211043496b6a0b64eecd5109d434a6c51e93..5af5904f126f3e69f22b957849bfaa3b5a3b4cee 100644 +index 204a61767d5cacc962094b9ecc37f457987cbde7..890881fad11549fe35d16f25e3f1f2b2ee527d02 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java @@ -19,6 +19,8 @@ public class CraftBlockEntityState extends CraftBlockStat diff --git a/patches/server/0338-Only-count-Natural-Spawned-mobs-towards-natural-spaw.patch b/patches/server/0339-Only-count-Natural-Spawned-mobs-towards-natural-spaw.patch similarity index 100% rename from patches/server/0338-Only-count-Natural-Spawned-mobs-towards-natural-spaw.patch rename to patches/server/0339-Only-count-Natural-Spawned-mobs-towards-natural-spaw.patch diff --git a/patches/server/0339-Configurable-projectile-relative-velocity.patch b/patches/server/0340-Configurable-projectile-relative-velocity.patch similarity index 100% rename from patches/server/0339-Configurable-projectile-relative-velocity.patch rename to patches/server/0340-Configurable-projectile-relative-velocity.patch diff --git a/patches/server/0340-offset-item-frame-ticking.patch b/patches/server/0341-offset-item-frame-ticking.patch similarity index 100% rename from patches/server/0340-offset-item-frame-ticking.patch rename to patches/server/0341-offset-item-frame-ticking.patch diff --git a/patches/server/0341-Do-less-work-if-we-have-a-custom-Bukkit-generator.patch b/patches/server/0342-Do-less-work-if-we-have-a-custom-Bukkit-generator.patch similarity index 91% rename from patches/server/0341-Do-less-work-if-we-have-a-custom-Bukkit-generator.patch rename to patches/server/0342-Do-less-work-if-we-have-a-custom-Bukkit-generator.patch index 310c3acc10..33ad3c0b03 100644 --- a/patches/server/0341-Do-less-work-if-we-have-a-custom-Bukkit-generator.patch +++ b/patches/server/0342-Do-less-work-if-we-have-a-custom-Bukkit-generator.patch @@ -7,10 +7,10 @@ If the Bukkit generator already has a spawn, use it immediately instead of spending time generating one that we won't use diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 76795ba9ee83b9461b60fb607b419344122be57e..fad88282b3b865cfb0a9a308d9c93cd8dc53b0d4 100644 +index e029064acc58332914eef0524ad991a45d368c93..1fa7e8890987f8f98af51e7e4cc03596f2207678 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -689,12 +689,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper diff --git a/patches/server/0354-PlayerDeathEvent-shouldDropExperience.patch b/patches/server/0355-PlayerDeathEvent-shouldDropExperience.patch similarity index 91% rename from patches/server/0354-PlayerDeathEvent-shouldDropExperience.patch rename to patches/server/0355-PlayerDeathEvent-shouldDropExperience.patch index 838f34461a..0fbe5a06de 100644 --- a/patches/server/0354-PlayerDeathEvent-shouldDropExperience.patch +++ b/patches/server/0355-PlayerDeathEvent-shouldDropExperience.patch @@ -5,7 +5,7 @@ Subject: [PATCH] PlayerDeathEvent#shouldDropExperience diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 8eac8e0898ffe621024daf3a1250e9c932cf0fcf..cd5394a0f1b46946b4f1575412c01bfcd86e782f 100644 +index 91267d3e8a76251d2e590222bebe1f1c428e4533..2a7b0d5de81665a5b899ab85cd1fa30be29f9b3e 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -847,7 +847,7 @@ public class ServerPlayer extends Player { diff --git a/patches/server/0355-Prevent-bees-loading-chunks-checking-hive-position.patch b/patches/server/0356-Prevent-bees-loading-chunks-checking-hive-position.patch similarity index 100% rename from patches/server/0355-Prevent-bees-loading-chunks-checking-hive-position.patch rename to patches/server/0356-Prevent-bees-loading-chunks-checking-hive-position.patch diff --git a/patches/server/0356-Don-t-load-Chunks-from-Hoppers-and-other-things.patch b/patches/server/0357-Don-t-load-Chunks-from-Hoppers-and-other-things.patch similarity index 100% rename from patches/server/0356-Don-t-load-Chunks-from-Hoppers-and-other-things.patch rename to patches/server/0357-Don-t-load-Chunks-from-Hoppers-and-other-things.patch diff --git a/patches/server/0357-Guard-against-serializing-mismatching-chunk-coordina.patch b/patches/server/0358-Guard-against-serializing-mismatching-chunk-coordina.patch similarity index 100% rename from patches/server/0357-Guard-against-serializing-mismatching-chunk-coordina.patch rename to patches/server/0358-Guard-against-serializing-mismatching-chunk-coordina.patch diff --git a/patches/server/0358-Optimise-IEntityAccess-getPlayerByUUID.patch b/patches/server/0359-Optimise-IEntityAccess-getPlayerByUUID.patch similarity index 90% rename from patches/server/0358-Optimise-IEntityAccess-getPlayerByUUID.patch rename to patches/server/0359-Optimise-IEntityAccess-getPlayerByUUID.patch index 56e1e0349d..d6d629d088 100644 --- a/patches/server/0358-Optimise-IEntityAccess-getPlayerByUUID.patch +++ b/patches/server/0359-Optimise-IEntityAccess-getPlayerByUUID.patch @@ -6,10 +6,10 @@ Subject: [PATCH] Optimise IEntityAccess#getPlayerByUUID Use the world entity map instead of iterating over all players diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 3f96d2a8bb11727ec7e197410f20aa535f19b9d4..993617e87c71320fe51adf24bef691aa978d42e3 100644 +index b3d54c90e87347cb7c709059c090371a40c7b047..fa5aa491c3ed6bf50b7f34c9f32a0b146e6c0aef 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -280,6 +280,14 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -370,6 +370,14 @@ public class ServerLevel extends Level implements WorldGenLevel { public final com.destroystokyo.paper.io.chunk.ChunkTaskManager asyncChunkTaskManager; // Paper end diff --git a/patches/server/0359-Fix-items-not-falling-correctly.patch b/patches/server/0360-Fix-items-not-falling-correctly.patch similarity index 100% rename from patches/server/0359-Fix-items-not-falling-correctly.patch rename to patches/server/0360-Fix-items-not-falling-correctly.patch diff --git a/patches/server/0360-Lag-compensate-eating.patch b/patches/server/0361-Lag-compensate-eating.patch similarity index 97% rename from patches/server/0360-Lag-compensate-eating.patch rename to patches/server/0361-Lag-compensate-eating.patch index 0bc92d3729..9cbc79c12e 100644 --- a/patches/server/0360-Lag-compensate-eating.patch +++ b/patches/server/0361-Lag-compensate-eating.patch @@ -7,7 +7,7 @@ When the server is lagging, players will wait longer when eating. Change to also use a time check instead if it passes. diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index b9b01966cdaf01bfe222c89c8e5d7afc690ab415..560236c33d2afe289a534c8cd1d1df413eb5d78d 100644 +index 8e07212a0e943ed698e5c7dc32ce17ffe90cd38e..9f108e1327f09d5c4876b096b50c62866c2fd6d4 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java @@ -3498,6 +3498,11 @@ public abstract class LivingEntity extends Entity { diff --git a/patches/server/0361-Optimize-call-to-getFluid-for-explosions.patch b/patches/server/0362-Optimize-call-to-getFluid-for-explosions.patch similarity index 100% rename from patches/server/0361-Optimize-call-to-getFluid-for-explosions.patch rename to patches/server/0362-Optimize-call-to-getFluid-for-explosions.patch diff --git a/patches/server/0362-Fix-last-firework-in-stack-not-having-effects-when-d.patch b/patches/server/0363-Fix-last-firework-in-stack-not-having-effects-when-d.patch similarity index 100% rename from patches/server/0362-Fix-last-firework-in-stack-not-having-effects-when-d.patch rename to patches/server/0363-Fix-last-firework-in-stack-not-having-effects-when-d.patch diff --git a/patches/server/0363-Add-effect-to-block-break-naturally.patch b/patches/server/0364-Add-effect-to-block-break-naturally.patch similarity index 100% rename from patches/server/0363-Add-effect-to-block-break-naturally.patch rename to patches/server/0364-Add-effect-to-block-break-naturally.patch diff --git a/patches/server/0364-Entity-Activation-Range-2.0.patch b/patches/server/0365-Entity-Activation-Range-2.0.patch similarity index 98% rename from patches/server/0364-Entity-Activation-Range-2.0.patch rename to patches/server/0365-Entity-Activation-Range-2.0.patch index 5b53b9c32d..dc20e3b19a 100644 --- a/patches/server/0364-Entity-Activation-Range-2.0.patch +++ b/patches/server/0365-Entity-Activation-Range-2.0.patch @@ -14,7 +14,7 @@ Adds flying monsters to control ghast and phantoms Adds villagers as separate config diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index fbd79aa102328e2738199c1da5a88e0a9ca76ce6..6ef50417e73709fbf50e93ad41638c03360afc87 100644 +index fa5aa491c3ed6bf50b7f34c9f32a0b146e6c0aef..8a1df5b26a6ba0e2fa13a9974cf41384a8e6d4fe 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java @@ -2,7 +2,6 @@ package net.minecraft.server.level; @@ -41,7 +41,7 @@ index fbd79aa102328e2738199c1da5a88e0a9ca76ce6..6ef50417e73709fbf50e93ad41638c03 import net.minecraft.world.level.entity.EntityPersistentStorage; import net.minecraft.world.level.entity.EntityTickList; import net.minecraft.world.level.entity.EntityTypeTest; -@@ -853,17 +850,17 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -943,17 +940,17 @@ public class ServerLevel extends Level implements WorldGenLevel { ++TimingHistory.entityTicks; // Paper - timings // Spigot start co.aikar.timings.Timing timer; // Paper @@ -63,7 +63,7 @@ index fbd79aa102328e2738199c1da5a88e0a9ca76ce6..6ef50417e73709fbf50e93ad41638c03 try { // Paper end - timings entity.setOldPosAndRot(); -@@ -874,9 +871,13 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -964,9 +961,13 @@ public class ServerLevel extends Level implements WorldGenLevel { return Registry.ENTITY_TYPE.getKey(entity.getType()).toString(); }); gameprofilerfiller.incrementCounter("tickNonPassenger"); @@ -77,7 +77,7 @@ index fbd79aa102328e2738199c1da5a88e0a9ca76ce6..6ef50417e73709fbf50e93ad41638c03 Iterator iterator = entity.getPassengers().iterator(); while (iterator.hasNext()) { -@@ -884,13 +885,18 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -974,13 +975,18 @@ public class ServerLevel extends Level implements WorldGenLevel { this.tickPassenger(entity, entity1); } @@ -97,7 +97,7 @@ index fbd79aa102328e2738199c1da5a88e0a9ca76ce6..6ef50417e73709fbf50e93ad41638c03 passenger.setOldPosAndRot(); ++passenger.tickCount; ProfilerFiller gameprofilerfiller = this.getProfiler(); -@@ -899,8 +905,17 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -989,8 +995,17 @@ public class ServerLevel extends Level implements WorldGenLevel { return Registry.ENTITY_TYPE.getKey(passenger.getType()).toString(); }); gameprofilerfiller.incrementCounter("tickPassenger"); @@ -115,7 +115,7 @@ index fbd79aa102328e2738199c1da5a88e0a9ca76ce6..6ef50417e73709fbf50e93ad41638c03 gameprofilerfiller.pop(); Iterator iterator = passenger.getPassengers().iterator(); -@@ -910,6 +925,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1000,6 +1015,7 @@ public class ServerLevel extends Level implements WorldGenLevel { this.tickPassenger(passenger, entity2); } @@ -124,7 +124,7 @@ index fbd79aa102328e2738199c1da5a88e0a9ca76ce6..6ef50417e73709fbf50e93ad41638c03 } else { passenger.stopRiding(); diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 508e83183bafacac0fa7469ae50123ed26898120..67d149bbe80bd86ab8e62b584aac48a7382c6bb7 100644 +index 8e30a0d42d422fea6bda77d16d5eae8bab01224d..3268187f77b78a04191628b06dc7693b08b8d642 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -327,6 +327,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n @@ -136,7 +136,7 @@ index 508e83183bafacac0fa7469ae50123ed26898120..67d149bbe80bd86ab8e62b584aac48a7 public boolean spawnedViaMobSpawner; // Paper - Yes this name is similar to above, upstream took the better one protected int numCollisions = 0; // Paper public void inactiveTick() { } -@@ -768,6 +770,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -773,6 +775,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n } else { this.wasOnFire = this.isOnFire(); if (movementType == MoverType.PISTON) { @@ -145,7 +145,7 @@ index 508e83183bafacac0fa7469ae50123ed26898120..67d149bbe80bd86ab8e62b584aac48a7 movement = this.limitPistonMovement(movement); if (movement.equals(Vec3.ZERO)) { return; -@@ -780,6 +784,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -785,6 +789,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n this.stuckSpeedMultiplier = Vec3.ZERO; this.setDeltaMovement(Vec3.ZERO); } diff --git a/patches/server/0365-Increase-Light-Queue-Size.patch b/patches/server/0366-Increase-Light-Queue-Size.patch similarity index 93% rename from patches/server/0365-Increase-Light-Queue-Size.patch rename to patches/server/0366-Increase-Light-Queue-Size.patch index 7737d11ff1..74cd419bc9 100644 --- a/patches/server/0365-Increase-Light-Queue-Size.patch +++ b/patches/server/0366-Increase-Light-Queue-Size.patch @@ -29,10 +29,10 @@ index bf704993d0abd50dba91682a7fbb575e3696be62..a91a7d8f56a068b18d50a8b987b71510 } diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index cc764bb6d7460fd7fc649dd0b7ec61329866dc70..ce5cb696dfa2ff9a07ab9e7547ba54920e58f53a 100644 +index 02766d82e48ea4e3adaa7a5b3957fe6b4c94d38a..273774ad46b993212a0cd4cfa81f0e02807c442e 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -843,7 +843,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop (max 5) to automatically fix all light data in the chunks. diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java -index 43067ef5733a29145b3e2ce386999ba518042dd7..c6254f39f98b821f75a21c0ecea457f73247385f 100644 +index 005361c38b02713fb823d0be40954400d59f0c4d..3091c100eaf5a86ba270ef0d96de1852a2a0ac9e 100644 --- a/src/main/java/com/destroystokyo/paper/PaperCommand.java +++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java @@ -10,7 +10,8 @@ import net.minecraft.server.MinecraftServer; @@ -134,7 +134,7 @@ index 43067ef5733a29145b3e2ce386999ba518042dd7..c6254f39f98b821f75a21c0ecea457f7 + } } diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 1273ef4f7f0dff15f8630c0ca3dd1fffcae6acdf..6ddb24ae627b9bfdfd82b19b1a746f32bb8d0532 100644 +index 68997cc0b534c46c33c0f137738fbaca5569445b..63d52f6f0c0097654ddab05fea4bd809dc612a9b 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java @@ -128,6 +128,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -150,7 +150,7 @@ index 1273ef4f7f0dff15f8630c0ca3dd1fffcae6acdf..6ddb24ae627b9bfdfd82b19b1a746f32 public final ChunkProgressListener progressListener; private final ChunkStatusUpdateListener chunkStatusListener; public final ChunkMap.ChunkDistanceManager distanceManager; -@@ -206,11 +212,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -236,11 +242,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.progressListener = worldGenerationProgressListener; this.chunkStatusListener = chunkStatusChangeListener; diff --git a/patches/server/0367-Anti-Xray.patch b/patches/server/0368-Anti-Xray.patch similarity index 99% rename from patches/server/0367-Anti-Xray.patch rename to patches/server/0368-Anti-Xray.patch index 7c202a5e53..b5dcbedfc0 100644 --- a/patches/server/0367-Anti-Xray.patch +++ b/patches/server/0368-Anti-Xray.patch @@ -1091,10 +1091,10 @@ index c28879f32b004f36ff746ea2274f91ddd9501e71..60d72e488bc77cd913328be400ca374a } diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 4f435e462069e73e68dce74b02a233553266dd90..e38e40fb9327e27e2344f76e0975b12d26bb8884 100644 +index 63d52f6f0c0097654ddab05fea4bd809dc612a9b..292fb6d71ca8bd63ad04d87d04f13b7f4f6f98fc 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -1485,7 +1485,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1534,7 +1534,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider public void playerLoadedChunk(ServerPlayer player, Packet[] packets, LevelChunk chunk) { if (packets[0] == null) { @@ -1104,10 +1104,10 @@ index 4f435e462069e73e68dce74b02a233553266dd90..e38e40fb9327e27e2344f76e0975b12d } diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 6ef50417e73709fbf50e93ad41638c03360afc87..275f292cc6b2f6576367ccc8603555ccebda1955 100644 +index 8a1df5b26a6ba0e2fa13a9974cf41384a8e6d4fe..0df81cbddf2e7f164861b95cf572f9d6d3f031ca 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -288,7 +288,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -378,7 +378,7 @@ public class ServerLevel extends Level implements WorldGenLevel { // 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) { // Objects.requireNonNull(minecraftserver); // CraftBukkit - decompile error diff --git a/patches/server/0368-No-Tick-view-distance-implementation.patch b/patches/server/0369-No-Tick-view-distance-implementation.patch similarity index 97% rename from patches/server/0368-No-Tick-view-distance-implementation.patch rename to patches/server/0369-No-Tick-view-distance-implementation.patch index f711182841..0a04a58c46 100644 --- a/patches/server/0368-No-Tick-view-distance-implementation.patch +++ b/patches/server/0369-No-Tick-view-distance-implementation.patch @@ -53,7 +53,7 @@ index 5c1421620e02b7d4ab4171ebff41076c7ee8cd55..4bc418f45c6f16a9a1c78b6688625268 worldData.addProperty("keep-spawn-loaded-range", world.paperConfig.keepLoadedRange); worldData.addProperty("visible-chunk-count", visibleChunks.size()); diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java -index 2720f4a51ffc8c9748932be55cebd5072cd4ffb2..1dd1b9afaee38fdc994ad0a069bd63b02eedf55c 100644 +index 1ea32090783ac5b885b95ae2009dd61de3063ae0..8d3cdd288eacc91f7c9a624f601284e5cd2a36cc 100644 --- a/src/main/java/net/minecraft/server/level/ChunkHolder.java +++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java @@ -75,6 +75,17 @@ public class ChunkHolder { @@ -145,7 +145,7 @@ index 2720f4a51ffc8c9748932be55cebd5072cd4ffb2..1dd1b9afaee38fdc994ad0a069bd63b0 public CompletableFuture> getOrScheduleFuture(ChunkStatus targetStatus, ChunkMap chunkStorage) { diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index e38e40fb9327e27e2344f76e0975b12d26bb8884..16d956e74711aea7b877329911be031b94dab02b 100644 +index 292fb6d71ca8bd63ad04d87d04f13b7f4f6f98fc..d2080cfe58f7e241c9612ad0e500240dcb341878 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java @@ -127,7 +127,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -225,11 +225,11 @@ index e38e40fb9327e27e2344f76e0975b12d26bb8884..16d956e74711aea7b877329911be031b + // Paper end - no-tick view distance } // Paper end - -@@ -223,6 +270,45 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - this.overworldDataStorage = persistentStateManagerFactory; - this.poiManager = new PoiManager(new File(file, "poi"), dataFixer, dsync, world); - this.setViewDistance(viewDistance); + // Paper start +@@ -257,6 +304,45 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + this.dataRegionManager = new io.papermc.paper.chunk.SingleThreadChunkRegionManager(this.level, 2, (1.0 / 3.0), 1, 6, "Data", DataRegionData::new, DataRegionSectionData::new); + this.regionManagers.add(this.dataRegionManager); + // Paper end + // Paper start - no-tick view distance + this.setNoTickViewDistance(this.level.paperConfig.noTickViewDistance); + this.playerViewDistanceTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets, @@ -272,7 +272,7 @@ index e38e40fb9327e27e2344f76e0975b12d26bb8884..16d956e74711aea7b877329911be031b } private static double euclideanDistanceSquared(ChunkPos pos, Entity entity) { -@@ -905,14 +991,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -954,14 +1040,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider completablefuture1.thenAcceptAsync((either) -> { either.ifLeft((chunk) -> { this.tickingGenerated.getAndIncrement(); @@ -289,7 +289,7 @@ index e38e40fb9327e27e2344f76e0975b12d26bb8884..16d956e74711aea7b877329911be031b }); return completablefuture1; } -@@ -1005,27 +1087,34 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1054,27 +1136,34 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void setViewDistance(int watchDistance) { @@ -337,7 +337,7 @@ index e38e40fb9327e27e2344f76e0975b12d26bb8884..16d956e74711aea7b877329911be031b } } -@@ -1037,7 +1126,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1086,7 +1175,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider ChunkHolder playerchunk = this.getVisibleChunkIfPresent(pos.toLong()); if (playerchunk != null) { @@ -346,7 +346,7 @@ index e38e40fb9327e27e2344f76e0975b12d26bb8884..16d956e74711aea7b877329911be031b if (chunk != null) { this.playerLoadedChunk(player, packets, chunk); -@@ -1133,7 +1222,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1182,7 +1271,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider // Paper end @Nullable @@ -355,7 +355,7 @@ index e38e40fb9327e27e2344f76e0975b12d26bb8884..16d956e74711aea7b877329911be031b CompoundTag nbttagcompound = this.read(pos); // Paper start - Cache chunk status on disk if (nbttagcompound == null) { -@@ -1244,13 +1333,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1293,13 +1382,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.removePlayerFromDistanceMaps(player); // Paper - distance maps } @@ -370,7 +370,7 @@ index e38e40fb9327e27e2344f76e0975b12d26bb8884..16d956e74711aea7b877329911be031b } -@@ -1258,7 +1341,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1307,7 +1390,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider SectionPos sectionposition = SectionPos.of((Entity) player); player.setLastSectionPos(sectionposition); @@ -379,7 +379,7 @@ index e38e40fb9327e27e2344f76e0975b12d26bb8884..16d956e74711aea7b877329911be031b return sectionposition; } -@@ -1313,6 +1396,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1362,6 +1445,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider int k1; int l1; @@ -387,7 +387,7 @@ index e38e40fb9327e27e2344f76e0975b12d26bb8884..16d956e74711aea7b877329911be031b if (Math.abs(i1 - i) <= this.viewDistance * 2 && Math.abs(j1 - j) <= this.viewDistance * 2) { k1 = Math.min(i, i1) - this.viewDistance; l1 = Math.min(j, j1) - this.viewDistance; -@@ -1351,6 +1435,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1400,6 +1484,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } } } @@ -395,7 +395,7 @@ index e38e40fb9327e27e2344f76e0975b12d26bb8884..16d956e74711aea7b877329911be031b this.updateMaps(player); // Paper - distance maps -@@ -1358,11 +1443,46 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1407,11 +1492,46 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @Override public Stream getPlayers(ChunkPos chunkPos, boolean onlyOnWatchDistanceEdge) { @@ -447,7 +447,7 @@ index e38e40fb9327e27e2344f76e0975b12d26bb8884..16d956e74711aea7b877329911be031b } public void addEntity(Entity entity) { -@@ -1483,6 +1603,47 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1532,6 +1652,47 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } diff --git a/patches/server/0369-Implement-alternative-item-despawn-rate.patch b/patches/server/0370-Implement-alternative-item-despawn-rate.patch similarity index 98% rename from patches/server/0369-Implement-alternative-item-despawn-rate.patch rename to patches/server/0370-Implement-alternative-item-despawn-rate.patch index 3d27779acc..d3508c85d1 100644 --- a/patches/server/0369-Implement-alternative-item-despawn-rate.patch +++ b/patches/server/0370-Implement-alternative-item-despawn-rate.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Implement alternative item-despawn-rate diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 45e30c0d78b7625a6a55e6d7d60a823b674b75db..31f192773fe5159ed2109f0d367e6b7287ffd186 100644 +index fd1064b08e61c0547cad092e009635ab708b8962..4e7fbe5367c6cf91d54545e7829ec74e9accba8a 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -492,6 +492,54 @@ public class PaperWorldConfig { diff --git a/patches/server/0370-Tracking-Range-Improvements.patch b/patches/server/0371-Tracking-Range-Improvements.patch similarity index 95% rename from patches/server/0370-Tracking-Range-Improvements.patch rename to patches/server/0371-Tracking-Range-Improvements.patch index 5708b9a941..0e8cfe86e8 100644 --- a/patches/server/0370-Tracking-Range-Improvements.patch +++ b/patches/server/0371-Tracking-Range-Improvements.patch @@ -8,10 +8,10 @@ Sets tracking range of watermobs to animals instead of misc and simplifies code Also ignores Enderdragon, defaulting it to Mojang's setting diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 16d956e74711aea7b877329911be031b94dab02b..65bdab2f19bcb03d67a92c0b60d73fc25f3ec5b8 100644 +index d2080cfe58f7e241c9612ad0e500240dcb341878..48ca60925b15d7e320cf07467bd0cf9ddee2a036 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -1836,6 +1836,7 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially +@@ -1885,6 +1885,7 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially while (iterator.hasNext()) { Entity entity = (Entity) iterator.next(); int j = entity.getType().clientTrackingRange() * 16; diff --git a/patches/server/0371-Fix-items-vanishing-through-end-portal.patch b/patches/server/0372-Fix-items-vanishing-through-end-portal.patch similarity index 89% rename from patches/server/0371-Fix-items-vanishing-through-end-portal.patch rename to patches/server/0372-Fix-items-vanishing-through-end-portal.patch index 227e6757db..a1e07862bf 100644 --- a/patches/server/0371-Fix-items-vanishing-through-end-portal.patch +++ b/patches/server/0372-Fix-items-vanishing-through-end-portal.patch @@ -13,10 +13,10 @@ Quickly loading the exact world spawn chunk before searching the heightmap resolves the issue without having to load all spawn chunks. diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 67d149bbe80bd86ab8e62b584aac48a7382c6bb7..635607e8fb58b7e9a5eca7d43ad028cf8815f67b 100644 +index 3268187f77b78a04191628b06dc7693b08b8d642..eec286fd4607c1d9a68c9063535bb630f92b6f4a 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -3010,6 +3010,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -3015,6 +3015,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n BlockPos blockposition1; if (flag1) { diff --git a/patches/server/0372-implement-optional-per-player-mob-spawns.patch b/patches/server/0373-implement-optional-per-player-mob-spawns.patch similarity index 96% rename from patches/server/0372-implement-optional-per-player-mob-spawns.patch rename to patches/server/0373-implement-optional-per-player-mob-spawns.patch index d65990f364..bdeee62f94 100644 --- a/patches/server/0372-implement-optional-per-player-mob-spawns.patch +++ b/patches/server/0373-implement-optional-per-player-mob-spawns.patch @@ -25,17 +25,20 @@ index fe79c0add4f7cb18d487c5bb9415c40c5b551ea2..8d9ddad1879e7616d980ca70de8aecac poiUnload = Timings.ofSafe(name + "Chunk unload - POI"); chunkUnload = Timings.ofSafe(name + "Chunk unload - Chunk"); diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 4e7fbe5367c6cf91d54545e7829ec74e9accba8a..7dc740d392becc622e98a5a83be332adf6fcc4e8 100644 +index 4e7fbe5367c6cf91d54545e7829ec74e9accba8a..62c8a097295e48ba87d102fca17d07d1fdbe2218 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -@@ -574,5 +574,10 @@ public class PaperWorldConfig { +@@ -574,5 +574,13 @@ public class PaperWorldConfig { Bukkit.getLogger().warning("You have enabled permission-based Anti-Xray checking - depending on your permission plugin, this may cause performance issues"); } } + + public boolean perPlayerMobSpawns = false; + private void perPlayerMobSpawns() { -+ perPlayerMobSpawns = getBoolean("per-player-mob-spawns", false); ++ if (PaperConfig.version < 22) { ++ set("per-player-mob-spawns", Boolean.TRUE); ++ } ++ perPlayerMobSpawns = getBoolean("per-player-mob-spawns", true); + } } @@ -545,7 +548,7 @@ index 0000000000000000000000000000000000000000..11de56afaf059b00fa5bec293516bcdc + } +} diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index a10f33a301e40484c7e253c6235c93c54918bf6c..5a8b919cd3a2aa3c12ae16cf970f7ce36cdeb4aa 100644 +index 48ca60925b15d7e320cf07467bd0cf9ddee2a036..46b23a1e543c55b71a7837af41df8cf89d72c22b 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java @@ -145,6 +145,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -556,16 +559,11 @@ index a10f33a301e40484c7e253c6235c93c54918bf6c..5a8b919cd3a2aa3c12ae16cf970f7ce3 // CraftBukkit start - recursion-safe executor for Chunk loadCallback() and unloadCallback() public final CallbackExecutor callbackExecutor = new CallbackExecutor(); -@@ -270,6 +271,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - this.overworldDataStorage = persistentStateManagerFactory; - this.poiManager = new PoiManager(new File(file, "poi"), dataFixer, dsync, world); - this.setViewDistance(viewDistance); -+ this.playerMobDistanceMap = this.level.paperConfig.perPlayerMobSpawns ? new com.destroystokyo.paper.util.PlayerMobDistanceMap() : null; // Paper - // Paper start - no-tick view distance - this.setNoTickViewDistance(this.level.paperConfig.noTickViewDistance); - this.playerViewDistanceTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets, -@@ -311,6 +313,25 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -343,8 +344,28 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + ChunkMap.this.updateChunkTracking(player, new ChunkPos(rangeX, rangeZ), null, true, false); // unloaded, loaded + }); // Paper end - no-tick view distance ++ this.playerMobDistanceMap = this.level.paperConfig.perPlayerMobSpawns ? new com.destroystokyo.paper.util.PlayerMobDistanceMap() : null; // Paper } + // Paper start @@ -591,10 +589,10 @@ index a10f33a301e40484c7e253c6235c93c54918bf6c..5a8b919cd3a2aa3c12ae16cf970f7ce3 double d0 = (double) SectionPos.sectionToBlockCoord(pos.x, 8); double d1 = (double) SectionPos.sectionToBlockCoord(pos.z, 8); diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index d5ec4a19869e04877869e3bb53c98a7e5943c931..8763b20019ad0d3beae3825d126c4ccab1e1252d 100644 +index ddd535ad7d2b051e0d05320e4491bad46df8ffbe..cb273030b70f1d41110b90effe3d542ab930fe39 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -751,7 +751,22 @@ public class ServerChunkCache extends ChunkSource { +@@ -912,7 +912,22 @@ public class ServerChunkCache extends ChunkSource { this.level.getProfiler().push("naturalSpawnCount"); this.level.timings.countNaturalMobs.startTiming(); // Paper - timings int l = this.distanceManager.getNaturalSpawnChunkCount(); @@ -619,7 +617,7 @@ index d5ec4a19869e04877869e3bb53c98a7e5943c931..8763b20019ad0d3beae3825d126c4cca this.lastSpawnState = spawnercreature_d; diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index d234f08782b1a0c50714b8911d048699dc7741d5..d581945ff25f8a7a5e00599e9a939f2c0728b601 100644 +index 7efaf53b4df9d4e7e88020ce4ca41c9222fa8064..5b642969f61d4f62f3f40447252c622806d2a9c6 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -224,6 +224,11 @@ public class ServerPlayer extends Player { diff --git a/patches/server/0373-Avoid-hopper-searches-if-there-are-no-items.patch b/patches/server/0374-Avoid-hopper-searches-if-there-are-no-items.patch similarity index 98% rename from patches/server/0373-Avoid-hopper-searches-if-there-are-no-items.patch rename to patches/server/0374-Avoid-hopper-searches-if-there-are-no-items.patch index 1178cde9fe..b5caa444b7 100644 --- a/patches/server/0373-Avoid-hopper-searches-if-there-are-no-items.patch +++ b/patches/server/0374-Avoid-hopper-searches-if-there-are-no-items.patch @@ -14,7 +14,7 @@ And since minecart hoppers are used _very_ rarely near we can avoid alot of sear Combined, this adds up a lot. diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index fa8cf70d7d24a82757326ff8f7f240cff3dcbe73..fd23c7913b7a426443515e14be0ac9814a5e934e 100644 +index a49fdaa7b7a0ed82e4a1ebab0a4fb086e86b8e36..4104a793bda81ab26f86b5e1eef6e6722b74731a 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -994,7 +994,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { diff --git a/patches/server/0374-Bees-get-gravity-in-void.-Fixes-MC-167279.patch b/patches/server/0375-Bees-get-gravity-in-void.-Fixes-MC-167279.patch similarity index 100% rename from patches/server/0374-Bees-get-gravity-in-void.-Fixes-MC-167279.patch rename to patches/server/0375-Bees-get-gravity-in-void.-Fixes-MC-167279.patch diff --git a/patches/server/0375-Optimise-getChunkAt-calls-for-loaded-chunks.patch b/patches/server/0376-Optimise-getChunkAt-calls-for-loaded-chunks.patch similarity index 92% rename from patches/server/0375-Optimise-getChunkAt-calls-for-loaded-chunks.patch rename to patches/server/0376-Optimise-getChunkAt-calls-for-loaded-chunks.patch index b40945557e..4b23b40952 100644 --- a/patches/server/0375-Optimise-getChunkAt-calls-for-loaded-chunks.patch +++ b/patches/server/0376-Optimise-getChunkAt-calls-for-loaded-chunks.patch @@ -7,10 +7,10 @@ bypass the need to get a player chunk, then get the either, then unwrap it... diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 8763b20019ad0d3beae3825d126c4ccab1e1252d..072ef1efde7bc9dd9343310996339be9c5f70f6b 100644 +index cb273030b70f1d41110b90effe3d542ab930fe39..374678fbcdedc9845e45ecbc91723540392d3d22 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -448,6 +448,12 @@ public class ServerChunkCache extends ChunkSource { +@@ -609,6 +609,12 @@ public class ServerChunkCache extends ChunkSource { return this.getChunk(x, z, leastStatus, create); }, this.mainThreadProcessor).join(); } else { @@ -23,7 +23,7 @@ index 8763b20019ad0d3beae3825d126c4ccab1e1252d..072ef1efde7bc9dd9343310996339be9 ProfilerFiller gameprofilerfiller = this.level.getProfiler(); gameprofilerfiller.incrementCounter("getChunk"); -@@ -499,39 +505,7 @@ public class ServerChunkCache extends ChunkSource { +@@ -660,39 +666,7 @@ public class ServerChunkCache extends ChunkSource { if (Thread.currentThread() != this.mainThread) { return null; } else { diff --git a/patches/server/0376-Add-debug-for-sync-chunk-loads.patch b/patches/server/0377-Add-debug-for-sync-chunk-loads.patch similarity index 97% rename from patches/server/0376-Add-debug-for-sync-chunk-loads.patch rename to patches/server/0377-Add-debug-for-sync-chunk-loads.patch index 0acf59d5bd..19ad0ed137 100644 --- a/patches/server/0376-Add-debug-for-sync-chunk-loads.patch +++ b/patches/server/0377-Add-debug-for-sync-chunk-loads.patch @@ -305,10 +305,10 @@ index 0000000000000000000000000000000000000000..0bb4aaa546939b67a5d22865190f3047 + } +} diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 072ef1efde7bc9dd9343310996339be9c5f70f6b..19544c9e6393cba361c9cf00d5767a06686426bf 100644 +index 374678fbcdedc9845e45ecbc91723540392d3d22..472146bea17f323397df1d3ed1aa31fc91a8741c 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -480,6 +480,7 @@ public class ServerChunkCache extends ChunkSource { +@@ -641,6 +641,7 @@ public class ServerChunkCache extends ChunkSource { this.level.asyncChunkTaskManager.raisePriority(x1, z1, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY); com.destroystokyo.paper.io.chunk.ChunkTaskManager.pushChunkWait(this.level, x1, z1); // Paper end @@ -317,10 +317,10 @@ index 072ef1efde7bc9dd9343310996339be9c5f70f6b..19544c9e6393cba361c9cf00d5767a06 chunkproviderserver_a.managedBlock(completablefuture::isDone); com.destroystokyo.paper.io.chunk.ChunkTaskManager.popChunkWait(); // Paper - async chunk debug diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index afb62f6d7d0c4dc80e9ceea6c6fe420f551b40b6..54008a97cc88ac431c3517c19160903408ee9255 100644 +index 0df81cbddf2e7f164861b95cf572f9d6d3f031ca..207bcd82065e4c1832e4d7814635017210bd9d4d 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -276,6 +276,12 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -366,6 +366,12 @@ public class ServerLevel extends Level implements WorldGenLevel { }; public final com.destroystokyo.paper.io.chunk.ChunkTaskManager asyncChunkTaskManager; // Paper end diff --git a/patches/server/0377-Allow-overriding-the-java-version-check.patch b/patches/server/0378-Allow-overriding-the-java-version-check.patch similarity index 100% rename from patches/server/0377-Allow-overriding-the-java-version-check.patch rename to patches/server/0378-Allow-overriding-the-java-version-check.patch diff --git a/patches/server/0378-Add-ThrownEggHatchEvent.patch b/patches/server/0379-Add-ThrownEggHatchEvent.patch similarity index 100% rename from patches/server/0378-Add-ThrownEggHatchEvent.patch rename to patches/server/0379-Add-ThrownEggHatchEvent.patch diff --git a/patches/server/0379-Entity-Jump-API.patch b/patches/server/0380-Entity-Jump-API.patch similarity index 100% rename from patches/server/0379-Entity-Jump-API.patch rename to patches/server/0380-Entity-Jump-API.patch diff --git a/patches/server/0380-Add-option-to-nerf-pigmen-from-nether-portals.patch b/patches/server/0381-Add-option-to-nerf-pigmen-from-nether-portals.patch similarity index 91% rename from patches/server/0380-Add-option-to-nerf-pigmen-from-nether-portals.patch rename to patches/server/0381-Add-option-to-nerf-pigmen-from-nether-portals.patch index 8a23addf4a..201478488b 100644 --- a/patches/server/0380-Add-option-to-nerf-pigmen-from-nether-portals.patch +++ b/patches/server/0381-Add-option-to-nerf-pigmen-from-nether-portals.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add option to nerf pigmen from nether portals diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 7dc740d392becc622e98a5a83be332adf6fcc4e8..9e1263804f1a07400a21e5ce705c24b2b771219c 100644 +index 62c8a097295e48ba87d102fca17d07d1fdbe2218..aa8f61df4b186fd72db94a9ad6f0abbf2da8febc 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -482,6 +482,11 @@ public class PaperWorldConfig { @@ -21,7 +21,7 @@ index 7dc740d392becc622e98a5a83be332adf6fcc4e8..9e1263804f1a07400a21e5ce705c24b2 private void lightQueueSize() { lightQueueSize = getInt("light-queue-size", lightQueueSize); diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 635607e8fb58b7e9a5eca7d43ad028cf8815f67b..9e692c816bb8173dba7314866e3321bfa24e577e 100644 +index eec286fd4607c1d9a68c9063535bb630f92b6f4a..f8c611a6a55dd56e5231834c9481c61727b628e9 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -330,6 +330,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n @@ -32,7 +32,7 @@ index 635607e8fb58b7e9a5eca7d43ad028cf8815f67b..9e692c816bb8173dba7314866e3321bf protected int numCollisions = 0; // Paper public void inactiveTick() { } // Spigot end -@@ -1887,6 +1888,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -1892,6 +1893,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n if (spawnedViaMobSpawner) { nbt.putBoolean("Paper.FromMobSpawner", true); } @@ -42,7 +42,7 @@ index 635607e8fb58b7e9a5eca7d43ad028cf8815f67b..9e692c816bb8173dba7314866e3321bf // Paper end return nbt; } catch (Throwable throwable) { -@@ -2028,6 +2032,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -2033,6 +2037,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n } spawnedViaMobSpawner = nbt.getBoolean("Paper.FromMobSpawner"); // Restore entity's from mob spawner status diff --git a/patches/server/0381-Make-the-GUI-graph-fancier.patch b/patches/server/0382-Make-the-GUI-graph-fancier.patch similarity index 100% rename from patches/server/0381-Make-the-GUI-graph-fancier.patch rename to patches/server/0382-Make-the-GUI-graph-fancier.patch diff --git a/patches/server/0382-add-hand-to-BlockMultiPlaceEvent.patch b/patches/server/0383-add-hand-to-BlockMultiPlaceEvent.patch similarity index 93% rename from patches/server/0382-add-hand-to-BlockMultiPlaceEvent.patch rename to patches/server/0383-add-hand-to-BlockMultiPlaceEvent.patch index f755016165..6d7a30c5c5 100644 --- a/patches/server/0382-add-hand-to-BlockMultiPlaceEvent.patch +++ b/patches/server/0383-add-hand-to-BlockMultiPlaceEvent.patch @@ -5,7 +5,7 @@ Subject: [PATCH] add hand to BlockMultiPlaceEvent diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java -index 253075002cffb67095d010a6a4c67d9efff9d5ea..8e63b18f455e57ba4ccd4a73ee9be1cb14857b61 100644 +index 736e308dff475623fd44370f3ea76e51b582d1dd..0cdcb8adf6879c08c88615023446acaf2282fbce 100644 --- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -339,13 +339,18 @@ public class CraftEventFactory { diff --git a/patches/server/0383-Prevent-teleporting-dead-entities.patch b/patches/server/0384-Prevent-teleporting-dead-entities.patch similarity index 100% rename from patches/server/0383-Prevent-teleporting-dead-entities.patch rename to patches/server/0384-Prevent-teleporting-dead-entities.patch diff --git a/patches/server/0384-Validate-tripwire-hook-placement-before-update.patch b/patches/server/0385-Validate-tripwire-hook-placement-before-update.patch similarity index 100% rename from patches/server/0384-Validate-tripwire-hook-placement-before-update.patch rename to patches/server/0385-Validate-tripwire-hook-placement-before-update.patch diff --git a/patches/server/0385-Add-option-to-allow-iron-golems-to-spawn-in-air.patch b/patches/server/0386-Add-option-to-allow-iron-golems-to-spawn-in-air.patch similarity index 95% rename from patches/server/0385-Add-option-to-allow-iron-golems-to-spawn-in-air.patch rename to patches/server/0386-Add-option-to-allow-iron-golems-to-spawn-in-air.patch index 2a6844e61f..f920e433b4 100644 --- a/patches/server/0385-Add-option-to-allow-iron-golems-to-spawn-in-air.patch +++ b/patches/server/0386-Add-option-to-allow-iron-golems-to-spawn-in-air.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add option to allow iron golems to spawn in air diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 3d14a7dbcc6bc46141596a7e04f790bfe8f560c6..9fb1ea2e363de88c48530341fd11541ebdec8831 100644 +index aa8f61df4b186fd72db94a9ad6f0abbf2da8febc..b497f25f7589a04ec85b5373854e34098aa9fe3c 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -382,6 +382,11 @@ public class PaperWorldConfig { diff --git a/patches/server/0386-Configurable-chance-of-villager-zombie-infection.patch b/patches/server/0387-Configurable-chance-of-villager-zombie-infection.patch similarity index 96% rename from patches/server/0386-Configurable-chance-of-villager-zombie-infection.patch rename to patches/server/0387-Configurable-chance-of-villager-zombie-infection.patch index 79b1ec31af..a49f65cea2 100644 --- a/patches/server/0386-Configurable-chance-of-villager-zombie-infection.patch +++ b/patches/server/0387-Configurable-chance-of-villager-zombie-infection.patch @@ -8,7 +8,7 @@ This allows you to solve an issue in vanilla behavior where: * On normal difficulty they will have a 50% of getting infected or dying. diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 9fb1ea2e363de88c48530341fd11541ebdec8831..9dc8a9e49cac89e236d9fa0c053b70bf10ed6f6e 100644 +index b497f25f7589a04ec85b5373854e34098aa9fe3c..a5704de638b5834e45d634db394a60e20fc934aa 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -492,6 +492,11 @@ public class PaperWorldConfig { diff --git a/patches/server/0387-Optimise-Chunk-getFluid.patch b/patches/server/0388-Optimise-Chunk-getFluid.patch similarity index 100% rename from patches/server/0387-Optimise-Chunk-getFluid.patch rename to patches/server/0388-Optimise-Chunk-getFluid.patch diff --git a/patches/server/0388-Optimise-TickListServer-by-rewriting-it.patch b/patches/server/0389-Optimise-TickListServer-by-rewriting-it.patch similarity index 97% rename from patches/server/0388-Optimise-TickListServer-by-rewriting-it.patch rename to patches/server/0389-Optimise-TickListServer-by-rewriting-it.patch index 3b408dc1af..2b93cff853 100644 --- a/patches/server/0388-Optimise-TickListServer-by-rewriting-it.patch +++ b/patches/server/0389-Optimise-TickListServer-by-rewriting-it.patch @@ -42,7 +42,7 @@ sets the excessive tick delay to the specified ticks (defaults to 60 * 20 ticks, aka 60 seconds) diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 40818f1e90dd3aca77e164365370782828f765f4..2fcb923b8aa0e891de9f86c4c55a4af8969c04cd 100644 +index 34e82edf683a33f140f3b4580b3245f29a775fcd..86e16e39a9a996669989d990b76f69116bcee300 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -372,6 +372,13 @@ public class PaperConfig { @@ -889,7 +889,7 @@ index 0000000000000000000000000000000000000000..118988c39e58f28e8a2851792b9c014f + } +} diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java -index 1dd1b9afaee38fdc994ad0a069bd63b02eedf55c..bbb94e8a5e3585701849e025b534a69a6e67949f 100644 +index 8d3cdd288eacc91f7c9a624f601284e5cd2a36cc..a3ba56f437fe9e4dac59370463052341eb9b7524 100644 --- a/src/main/java/net/minecraft/server/level/ChunkHolder.java +++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java @@ -86,6 +86,19 @@ public class ChunkHolder { @@ -912,11 +912,10 @@ index 1dd1b9afaee38fdc994ad0a069bd63b02eedf55c..bbb94e8a5e3585701849e025b534a69a public ChunkHolder(ChunkPos pos, int level, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) { this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size()); -@@ -538,6 +551,12 @@ public class ChunkHolder { - either.ifLeft(chunk -> { - // note: Here is a very good place to add callbacks to logic waiting on this. - ChunkHolder.this.isTickingReady = true; -+ +@@ -541,6 +554,11 @@ public class ChunkHolder { + // Paper start - ticking chunk set + ChunkHolder.this.chunkMap.level.getChunkSource().tickingChunks.add(chunk); + // Paper end - ticking chunk set + // Paper start - rewrite ticklistserver + if (ChunkHolder.this.chunkMap.level.entityManager.areEntitiesLoaded(this.pos.longKey)) { + ChunkHolder.this.chunkMap.level.onChunkSetTicking(ChunkHolder.this.pos.x, ChunkHolder.this.pos.z); @@ -926,10 +925,10 @@ index 1dd1b9afaee38fdc994ad0a069bd63b02eedf55c..bbb94e8a5e3585701849e025b534a69a }); // Paper end diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 19544c9e6393cba361c9cf00d5767a06686426bf..12bf78c993d2360c37b143b51c425fc5fdf33647 100644 +index 472146bea17f323397df1d3ed1aa31fc91a8741c..c7ecb8fb1cbc1433986fbd03c68b4dbe5a2835c7 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -217,6 +217,18 @@ public class ServerChunkCache extends ChunkSource { +@@ -218,6 +218,18 @@ public class ServerChunkCache extends ChunkSource { }, this.mainThreadProcessor); } // Paper end @@ -946,13 +945,13 @@ index 19544c9e6393cba361c9cf00d5767a06686426bf..12bf78c993d2360c37b143b51c425fc5 + } + // Paper end - rewrite ticklistserver - public ServerChunkCache(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureManager structureManager, Executor workerExecutor, ChunkGenerator chunkGenerator, int viewDistance, boolean flag, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkstatusupdatelistener, Supplier supplier) { - this.level = world; + // Paper start + // this will try to avoid chunk neighbours for lighting diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index a09415592d1b5b1aab4c4bc0471148f0afa80d8b..5c5fb2abd29718650bd82b0b58ab966c97fda8ae 100644 +index 207bcd82065e4c1832e4d7814635017210bd9d4d..f3fa6b366fb749b8dfcfc2343d934da0647b861b 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -291,6 +291,15 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -381,6 +381,15 @@ public class ServerLevel extends Level implements WorldGenLevel { } // Paper end @@ -968,7 +967,7 @@ index a09415592d1b5b1aab4c4bc0471148f0afa80d8b..5c5fb2abd29718650bd82b0b58ab966c // 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) { // Objects.requireNonNull(minecraftserver); // CraftBukkit - decompile error -@@ -307,13 +316,19 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -397,13 +406,19 @@ public class ServerLevel extends Level implements WorldGenLevel { DefaultedRegistry registryblocks = Registry.BLOCK; Objects.requireNonNull(registryblocks); @@ -989,7 +988,7 @@ index a09415592d1b5b1aab4c4bc0471148f0afa80d8b..5c5fb2abd29718650bd82b0b58ab966c this.navigatingMobs = new ObjectOpenHashSet(); this.blockEvents = new ObjectLinkedOpenHashSet(); this.dragonParts = new Int2ObjectOpenHashMap(); -@@ -599,7 +614,9 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -689,7 +704,9 @@ public class ServerLevel extends Level implements WorldGenLevel { if (this.tickTime) { long i = this.levelData.getGameTime() + 1L; diff --git a/patches/server/0389-Pillager-patrol-spawn-settings-and-per-player-option.patch b/patches/server/0390-Pillager-patrol-spawn-settings-and-per-player-option.patch similarity index 97% rename from patches/server/0389-Pillager-patrol-spawn-settings-and-per-player-option.patch rename to patches/server/0390-Pillager-patrol-spawn-settings-and-per-player-option.patch index 705a6c5c21..2e21d6a939 100644 --- a/patches/server/0389-Pillager-patrol-spawn-settings-and-per-player-option.patch +++ b/patches/server/0390-Pillager-patrol-spawn-settings-and-per-player-option.patch @@ -10,7 +10,7 @@ When not per player it will use the Vanilla mechanic of one delay per world and the world age for the start day. diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 9dc8a9e49cac89e236d9fa0c053b70bf10ed6f6e..2d038185846ae34bc301ab93d881022a05ee458b 100644 +index a5704de638b5834e45d634db394a60e20fc934aa..59fe44f53adde4576f95e5150aca02ccc71dcc0b 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -469,10 +469,21 @@ public class PaperWorldConfig { @@ -36,7 +36,7 @@ index 9dc8a9e49cac89e236d9fa0c053b70bf10ed6f6e..2d038185846ae34bc301ab93d881022a private void entitiesTargetWithFollowRange() { entitiesTargetWithFollowRange = getBoolean("entities-target-with-follow-range", entitiesTargetWithFollowRange); diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index fd0734cc9a7b1d918f635048878227f8b18944e1..cb837ea04660a7745451ac4d4f09518ee423cc4a 100644 +index 5b642969f61d4f62f3f40447252c622806d2a9c6..31a2cbcb103174053f7f22c7d53c69cb8a8221c7 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -220,6 +220,7 @@ public class ServerPlayer extends Player { diff --git a/patches/server/0390-Remote-Connections-shouldn-t-hold-up-shutdown.patch b/patches/server/0391-Remote-Connections-shouldn-t-hold-up-shutdown.patch similarity index 100% rename from patches/server/0390-Remote-Connections-shouldn-t-hold-up-shutdown.patch rename to patches/server/0391-Remote-Connections-shouldn-t-hold-up-shutdown.patch diff --git a/patches/server/0391-Do-not-allow-bees-to-load-chunks-for-beehives.patch b/patches/server/0392-Do-not-allow-bees-to-load-chunks-for-beehives.patch similarity index 100% rename from patches/server/0391-Do-not-allow-bees-to-load-chunks-for-beehives.patch rename to patches/server/0392-Do-not-allow-bees-to-load-chunks-for-beehives.patch diff --git a/patches/server/0392-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch b/patches/server/0393-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch similarity index 87% rename from patches/server/0392-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch rename to patches/server/0393-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch index 6413969958..c7f9abbe1c 100644 --- a/patches/server/0392-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch +++ b/patches/server/0393-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch @@ -7,10 +7,10 @@ Suspected case would be around the technique used in .stopRiding Stack will identify any causer of this and warn instead of crashing. diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index b2d88e0423d93fdb45dc8ca7d53c98069ade749e..2b291296821dc6d6a8437bd977eeba517cdb5003 100644 +index 46b23a1e543c55b71a7837af41df8cf89d72c22b..147ee80242758417464a7cdf02e746758b54f6d7 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -1508,6 +1508,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1557,6 +1557,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider public void addEntity(Entity entity) { org.spigotmc.AsyncCatcher.catchOp("entity track"); // Spigot @@ -26,10 +26,10 @@ index b2d88e0423d93fdb45dc8ca7d53c98069ade749e..2b291296821dc6d6a8437bd977eeba51 EntityType entitytypes = entity.getType(); int i = entitytypes.clientTrackingRange() * 16; diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 4e654eda235d05ba96bacc6d9c14ce6428e60d07..3d2aa5c3832a2d3ed42303edde2d0d8528c4570a 100644 +index f3fa6b366fb749b8dfcfc2343d934da0647b861b..f3ba3c430a713fdef7e941b991ea8497de2e6a05 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -2061,7 +2061,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -2151,7 +2151,7 @@ public class ServerLevel extends Level implements WorldGenLevel { public void onTrackingStart(Entity entity) { org.spigotmc.AsyncCatcher.catchOp("entity register"); // Spigot @@ -38,7 +38,7 @@ index 4e654eda235d05ba96bacc6d9c14ce6428e60d07..3d2aa5c3832a2d3ed42303edde2d0d85 if (entity instanceof ServerPlayer) { ServerLevel.this.players.add((ServerPlayer) entity); ServerLevel.this.updateSleepingPlayerList(); -@@ -2083,6 +2083,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -2173,6 +2173,7 @@ public class ServerLevel extends Level implements WorldGenLevel { } entity.valid = true; // CraftBukkit diff --git a/patches/server/0393-Optimize-Collision-to-not-load-chunks.patch b/patches/server/0394-Optimize-Collision-to-not-load-chunks.patch similarity index 98% rename from patches/server/0393-Optimize-Collision-to-not-load-chunks.patch rename to patches/server/0394-Optimize-Collision-to-not-load-chunks.patch index 82a6710f34..b60f60983a 100644 --- a/patches/server/0393-Optimize-Collision-to-not-load-chunks.patch +++ b/patches/server/0394-Optimize-Collision-to-not-load-chunks.patch @@ -26,7 +26,7 @@ index 10eb562d2089dc20c9ec33956c3e2f98084de748..b828d6b2ce20a058acbabba5594e743d entityplayer1.setPos(entityplayer1.getX(), entityplayer1.getY() + 1.0D, entityplayer1.getZ()); } diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 7ce969911723d34c0027c063c184fa392d09fc12..c3f5a6b688873fa416408dd50935ba10d9cc594c 100644 +index f8c611a6a55dd56e5231834c9481c61727b628e9..1164fc5915f0121b697ea10fac73919597902026 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -172,6 +172,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n diff --git a/patches/server/0394-Don-t-tick-dead-players.patch b/patches/server/0395-Don-t-tick-dead-players.patch similarity index 91% rename from patches/server/0394-Don-t-tick-dead-players.patch rename to patches/server/0395-Don-t-tick-dead-players.patch index 410e19363d..950cdd1195 100644 --- a/patches/server/0394-Don-t-tick-dead-players.patch +++ b/patches/server/0395-Don-t-tick-dead-players.patch @@ -7,7 +7,7 @@ Causes sync chunk loads and who knows what all else. This is safe because Spectators are skipped in unloaded chunks too in vanilla. diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index cb837ea04660a7745451ac4d4f09518ee423cc4a..766079c82f2e1189425b3a92a7ea75913233471b 100644 +index 31a2cbcb103174053f7f22c7d53c69cb8a8221c7..3854d1b5acb6d877c2b64f274031574c890822a9 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -639,7 +639,7 @@ public class ServerPlayer extends Player { diff --git a/patches/server/0395-Dead-Player-s-shouldn-t-be-able-to-move.patch b/patches/server/0396-Dead-Player-s-shouldn-t-be-able-to-move.patch similarity index 100% rename from patches/server/0395-Dead-Player-s-shouldn-t-be-able-to-move.patch rename to patches/server/0396-Dead-Player-s-shouldn-t-be-able-to-move.patch diff --git a/patches/server/0396-Optimize-PlayerChunkMap-memory-use-for-visibleChunks.patch b/patches/server/0396-Optimize-PlayerChunkMap-memory-use-for-visibleChunks.patch deleted file mode 100644 index 73fb032aa6..0000000000 --- a/patches/server/0396-Optimize-PlayerChunkMap-memory-use-for-visibleChunks.patch +++ /dev/null @@ -1,299 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Wed, 8 Apr 2020 03:06:30 -0400 -Subject: [PATCH] Optimize PlayerChunkMap memory use for visibleChunks - -No longer clones visible chunks which is causing massive memory -allocation issues, likely the source of Humongous Objects on large servers. - -Instead we just synchronize, clear and rebuild, reusing the same object buffers -as before with only 2 small objects created (FastIterator/MapEntry) - -This should result in siginificant memory use reduction and improved GC behavior. - -diff --git a/src/main/java/com/destroystokyo/paper/util/map/Long2ObjectLinkedOpenHashMapFastCopy.java b/src/main/java/com/destroystokyo/paper/util/map/Long2ObjectLinkedOpenHashMapFastCopy.java -new file mode 100644 -index 0000000000000000000000000000000000000000..f6ff4d8132a95895680f5bc81f8f873e78f0bbdb ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/util/map/Long2ObjectLinkedOpenHashMapFastCopy.java -@@ -0,0 +1,39 @@ -+package com.destroystokyo.paper.util.map; -+ -+import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; -+ -+public class Long2ObjectLinkedOpenHashMapFastCopy extends Long2ObjectLinkedOpenHashMap { -+ -+ public void copyFrom(Long2ObjectLinkedOpenHashMapFastCopy map) { -+ if (key.length != map.key.length) { -+ key = null; -+ key = new long[map.key.length]; -+ } -+ if (value.length != map.value.length) { -+ value = null; -+ //noinspection unchecked -+ value = (V[]) new Object[map.value.length]; -+ } -+ if (link.length != map.link.length) { -+ link = null; -+ link = new long[map.link.length]; -+ } -+ System.arraycopy(map.key, 0, this.key, 0, map.key.length); -+ System.arraycopy(map.value, 0, this.value, 0, map.value.length); -+ System.arraycopy(map.link, 0, this.link, 0, map.link.length); -+ this.size = map.size; -+ this.mask = map.mask; -+ this.first = map.first; -+ this.last = map.last; -+ this.n = map.n; -+ this.maxFill = map.maxFill; -+ this.containsNullKey = map.containsNullKey; -+ } -+ -+ @Override -+ public Long2ObjectLinkedOpenHashMapFastCopy clone() { -+ Long2ObjectLinkedOpenHashMapFastCopy clone = (Long2ObjectLinkedOpenHashMapFastCopy) super.clone(); -+ clone.copyFrom(this); -+ return clone; -+ } -+} -diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java -index 4bc418f45c6f16a9a1c78b6688625268870a96ad..2d5b8e35d52b0dfd075af81a3a936d8a21053b31 100644 ---- a/src/main/java/net/minecraft/server/MCUtil.java -+++ b/src/main/java/net/minecraft/server/MCUtil.java -@@ -614,7 +614,7 @@ public final class MCUtil { - - ServerLevel world = ((org.bukkit.craftbukkit.CraftWorld)bukkitWorld).getHandle(); - ChunkMap chunkMap = world.getChunkSource().chunkMap; -- Long2ObjectLinkedOpenHashMap visibleChunks = chunkMap.visibleChunkMap; -+ Long2ObjectLinkedOpenHashMap visibleChunks = chunkMap.getVisibleChunks(); - DistanceManager chunkMapDistance = chunkMap.distanceManager; - List allChunks = new ArrayList<>(visibleChunks.values()); - List players = world.players; -diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 22517debf77123b2d02f2127eba4b3700040c97d..5e828201871086994f7037a50a0d12aecb0c0ece 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -112,9 +112,36 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - private static final int MIN_VIEW_DISTANCE = 3; - public static final int MAX_VIEW_DISTANCE = 33; - public static final int MAX_CHUNK_DISTANCE = 33 + ChunkStatus.maxDistance(); -+ // Paper start - faster copying -+ public final Long2ObjectLinkedOpenHashMap updatingChunkMap = new com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy<>(); // Paper - faster copying -+ public final Long2ObjectLinkedOpenHashMap visibleChunkMap = new ProtectedVisibleChunksMap(); // Paper - faster copying -+ -+ private class ProtectedVisibleChunksMap extends com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy { -+ @Override -+ public ChunkHolder put(long k, ChunkHolder playerChunk) { -+ throw new UnsupportedOperationException("Updating visible Chunks"); -+ } -+ -+ @Override -+ public ChunkHolder remove(long k) { -+ throw new UnsupportedOperationException("Removing visible Chunks"); -+ } -+ -+ @Override -+ public ChunkHolder get(long k) { -+ return ChunkMap.this.getVisibleChunkIfPresent(k); -+ } -+ -+ public ChunkHolder safeGet(long k) { -+ return super.get(k); -+ } -+ } -+ // Paper end -+ public final com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy pendingVisibleChunks = new com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy(); // Paper - this is used if the visible chunks is updated while iterating only -+ public transient com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy visibleChunksClone; // Paper - used for async access of visible chunks, clone and cache only when needed - public static final int FORCED_TICKET_LEVEL = 31; -- public final Long2ObjectLinkedOpenHashMap updatingChunkMap = new Long2ObjectLinkedOpenHashMap(); -- public volatile Long2ObjectLinkedOpenHashMap visibleChunkMap; -+ // public final Long2ObjectLinkedOpenHashMap updatingChunkMap = new Long2ObjectLinkedOpenHashMap(); // Paper - moved up -+ // public volatile Long2ObjectLinkedOpenHashMap visibleChunkMap; // Paper - moved up - private final Long2ObjectLinkedOpenHashMap pendingUnloads; - public final LongSet entitiesInLevel; - public final ServerLevel level; -@@ -237,7 +264,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - - public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureManager structureManager, Executor executor, BlockableEventLoop mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier persistentStateManagerFactory, int viewDistance, boolean dsync) { - super(new File(session.getDimensionPath(world.dimension()), "region"), dataFixer, dsync); -- this.visibleChunkMap = this.updatingChunkMap.clone(); -+ //this.visibleChunks = this.updatingChunks.clone(); // Paper - no more cloning - this.pendingUnloads = new Long2ObjectLinkedOpenHashMap(); - this.entitiesInLevel = new LongOpenHashSet(); - this.toDrop = new LongOpenHashSet(); -@@ -378,9 +405,52 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - return (ChunkHolder) this.updatingChunkMap.get(pos); - } - -+ // Paper start - remove cloning of visible chunks unless accessed as a collection async -+ private static final boolean DEBUG_ASYNC_VISIBLE_CHUNKS = Boolean.getBoolean("paper.debug-async-visible-chunks"); -+ private boolean isIterating = false; -+ private boolean hasPendingVisibleUpdate = false; -+ public void forEachVisibleChunk(java.util.function.Consumer consumer) { -+ org.spigotmc.AsyncCatcher.catchOp("forEachVisibleChunk"); -+ boolean prev = isIterating; -+ isIterating = true; -+ try { -+ for (ChunkHolder value : this.visibleChunkMap.values()) { -+ consumer.accept(value); -+ } -+ } finally { -+ this.isIterating = prev; -+ if (!this.isIterating && this.hasPendingVisibleUpdate) { -+ ((ProtectedVisibleChunksMap)this.visibleChunkMap).copyFrom(this.pendingVisibleChunks); -+ this.pendingVisibleChunks.clear(); -+ this.hasPendingVisibleUpdate = false; -+ } -+ } -+ } -+ public Long2ObjectLinkedOpenHashMap getVisibleChunks() { -+ if (Thread.currentThread() == this.level.thread) { -+ return this.visibleChunkMap; -+ } else { -+ synchronized (this.visibleChunkMap) { -+ if (DEBUG_ASYNC_VISIBLE_CHUNKS) new Throwable("Async getVisibleChunks").printStackTrace(); -+ if (this.visibleChunksClone == null) { -+ this.visibleChunksClone = this.hasPendingVisibleUpdate ? this.pendingVisibleChunks.clone() : ((ProtectedVisibleChunksMap)this.visibleChunkMap).clone(); -+ } -+ return this.visibleChunksClone; -+ } -+ } -+ } -+ // Paper end -+ - @Nullable - public ChunkHolder getVisibleChunkIfPresent(long pos) { -- return (ChunkHolder) this.visibleChunkMap.get(pos); -+ // Paper start - mt safe get -+ if (Thread.currentThread() != this.level.thread) { -+ synchronized (this.visibleChunkMap) { -+ return (ChunkHolder) (this.hasPendingVisibleUpdate ? this.pendingVisibleChunks.get(pos) : ((ProtectedVisibleChunksMap)this.visibleChunkMap).safeGet(pos)); -+ } -+ } -+ return (ChunkHolder) (this.hasPendingVisibleUpdate ? this.pendingVisibleChunks.get(pos) : ((ProtectedVisibleChunksMap)this.visibleChunkMap).safeGet(pos)); -+ // Paper end - } - - protected IntSupplier getChunkQueueLevel(long pos) { -@@ -537,8 +607,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - - protected void saveAllChunks(boolean flush) { -+ Long2ObjectLinkedOpenHashMap visibleChunks = this.getVisibleChunks(); // Paper remove clone of visible Chunks unless saving off main thread (watchdog kill) - if (flush) { -- List list = (List) this.visibleChunkMap.values().stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).collect(Collectors.toList()); -+ List list = (List) visibleChunks.values().stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).collect(Collectors.toList()); // Paper - remove cloning of visible chunks - MutableBoolean mutableboolean = new MutableBoolean(); - - do { -@@ -568,7 +639,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - this.level.asyncChunkTaskManager.flush(); // Paper - flush to preserve behavior compat with pre-async behaviour - // this.i(); // Paper - nuke IOWorker - } else { -- this.visibleChunkMap.values().stream().filter(ChunkHolder::wasAccessibleSinceLastSave).forEach((playerchunk) -> { -+ visibleChunks.values().stream().filter(ChunkHolder::wasAccessibleSinceLastSave).forEach((playerchunk) -> { - ChunkAccess ichunkaccess = (ChunkAccess) playerchunk.getChunkToSave().getNow(null); // CraftBukkit - decompile error - - if (ichunkaccess instanceof ImposterProtoChunk || ichunkaccess instanceof LevelChunk) { -@@ -726,7 +797,20 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - if (!this.modified) { - return false; - } else { -- this.visibleChunkMap = this.updatingChunkMap.clone(); -+ // Paper start - stop cloning visibleChunks -+ synchronized (this.visibleChunkMap) { -+ if (isIterating) { -+ hasPendingVisibleUpdate = true; -+ this.pendingVisibleChunks.copyFrom((com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy)this.updatingChunkMap); -+ } else { -+ hasPendingVisibleUpdate = false; -+ this.pendingVisibleChunks.clear(); -+ ((ProtectedVisibleChunksMap)this.visibleChunkMap).copyFrom((com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy)this.updatingChunkMap); -+ this.visibleChunksClone = null; -+ } -+ } -+ // Paper end -+ - this.modified = false; - return true; - } -@@ -1173,12 +1257,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - - protected Iterable getChunks() { -- return Iterables.unmodifiableIterable(this.visibleChunkMap.values()); -+ return Iterables.unmodifiableIterable(this.getVisibleChunks().values()); // Paper - } - - void dumpChunks(Writer writer) throws IOException { - CsvOutput csvwriter = CsvOutput.builder().addColumn("x").addColumn("z").addColumn("level").addColumn("in_memory").addColumn("status").addColumn("full_status").addColumn("accessible_ready").addColumn("ticking_ready").addColumn("entity_ticking_ready").addColumn("ticket").addColumn("spawning").addColumn("block_entity_count").build(writer); -- ObjectBidirectionalIterator objectbidirectionaliterator = this.visibleChunkMap.long2ObjectEntrySet().iterator(); -+ ObjectBidirectionalIterator objectbidirectionaliterator = this.getVisibleChunks().long2ObjectEntrySet().iterator(); // Paper - - while (objectbidirectionaliterator.hasNext()) { - Entry entry = (Entry) objectbidirectionaliterator.next(); -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 12bf78c993d2360c37b143b51c425fc5fdf33647..4efefcea2dcf895c4847269bc258cc338f21f230 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -758,7 +758,7 @@ public class ServerChunkCache extends ChunkSource { - - this.lastSpawnState = spawnercreature_d; - this.level.getProfiler().pop(); -- List list = Lists.newArrayList(this.chunkMap.getChunks()); -+ List list = Lists.newArrayList(this.chunkMap.visibleChunkMap.values()); // Paper - - Collections.shuffle(list); - //Paper start - call player naturally spawn event -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index d347f8f3bfe7ed0492dd0cd593837ee2246527aa..059b901e79cf0e2d152375453d36437a43c4f244 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -146,6 +146,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { - - @Override - public int getTileEntityCount() { -+ return net.minecraft.server.MCUtil.ensureMain(() -> { - // We don't use the full world tile entity list, so we must iterate chunks - Long2ObjectLinkedOpenHashMap chunks = world.getChunkSource().chunkMap.visibleChunkMap; - int size = 0; -@@ -157,6 +158,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { - size += chunk.blockEntities.size(); - } - return size; -+ }); - } - - @Override -@@ -166,6 +168,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { - - @Override - public int getChunkCount() { -+ return net.minecraft.server.MCUtil.ensureMain(() -> { - int ret = 0; - - for (ChunkHolder chunkHolder : world.getChunkSource().chunkMap.visibleChunkMap.values()) { -@@ -174,7 +177,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { - } - } - -- return ret; -+ return ret; }); - } - - @Override -@@ -302,6 +305,14 @@ public class CraftWorld extends CraftRegionAccessor implements World { - - @Override - public Chunk[] getLoadedChunks() { -+ // Paper start -+ if (Thread.currentThread() != world.getLevel().thread) { -+ synchronized (world.getChunkSource().chunkMap.visibleChunkMap) { -+ Long2ObjectLinkedOpenHashMap chunks = world.getChunkSource().chunkMap.visibleChunkMap; -+ return chunks.values().stream().map(ChunkHolder::getFullChunk).filter(Objects::nonNull).map(net.minecraft.world.level.chunk.LevelChunk::getBukkitChunk).toArray(Chunk[]::new); -+ } -+ } -+ // Paper end - Long2ObjectLinkedOpenHashMap chunks = this.world.getChunkSource().chunkMap.visibleChunkMap; - return chunks.values().stream().map(ChunkHolder::getFullChunk).filter(Objects::nonNull).map(net.minecraft.world.level.chunk.LevelChunk::getBukkitChunk).toArray(Chunk[]::new); - } diff --git a/patches/server/0398-Don-t-move-existing-players-to-world-spawn.patch b/patches/server/0397-Don-t-move-existing-players-to-world-spawn.patch similarity index 96% rename from patches/server/0398-Don-t-move-existing-players-to-world-spawn.patch rename to patches/server/0397-Don-t-move-existing-players-to-world-spawn.patch index d53afc5ffc..75f987036b 100644 --- a/patches/server/0398-Don-t-move-existing-players-to-world-spawn.patch +++ b/patches/server/0397-Don-t-move-existing-players-to-world-spawn.patch @@ -10,7 +10,7 @@ larger than the keep loaded range. By skipping this, we avoid potential for a large spike on server start. diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 766079c82f2e1189425b3a92a7ea75913233471b..14f5f6b99c51c51b1c8a7d54e58627ae71caf134 100644 +index 3854d1b5acb6d877c2b64f274031574c890822a9..5559daa44369304c5c7bdccb35d27a84cb72f9b3 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -310,7 +310,7 @@ public class ServerPlayer extends Player { diff --git a/patches/server/0397-Mid-Tick-Chunk-Tasks-Speed-up-processing-of-chunk-lo.patch b/patches/server/0397-Mid-Tick-Chunk-Tasks-Speed-up-processing-of-chunk-lo.patch deleted file mode 100644 index 9b2295e1b2..0000000000 --- a/patches/server/0397-Mid-Tick-Chunk-Tasks-Speed-up-processing-of-chunk-lo.patch +++ /dev/null @@ -1,268 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Thu, 9 Apr 2020 00:09:26 -0400 -Subject: [PATCH] Mid Tick Chunk Tasks - Speed up processing of chunk loads and - generation - -Credit to Spotted for the idea - -A lot of the new chunk system requires constant back and forth the main thread -to handle priority scheduling and ensuring conflicting tasks do not run at the -same time. - -The issue is, these queues are only checked at either: - -A) Sync Chunk Loads -B) End of Tick while sleeping - -This results in generating chunks sitting waiting for a full tick to -complete before it will even start the next unit of work to do. - -Additionally, this also delays loading of chunks until this same timing. - -We will now periodically poll the chunk task queues throughout the tick, -looking for work to do. -We do this in a fair method that considers all worlds, not just the one being -ticked, so that each world can get 1 task procesed each before the next pass. - -In a view distance of 15, chunk loading performance was visually faster on the client. - -Flying at high speed in spectator mode was able to keep up with chunk loading (as long as they are already generated) - -diff --git a/src/main/java/co/aikar/timings/MinecraftTimings.java b/src/main/java/co/aikar/timings/MinecraftTimings.java -index b47b7dce26805badd422c1867733ff4bfd00e9f4..b9cdbf8acccfd6b207a0116f068168f3b8c8e17d 100644 ---- a/src/main/java/co/aikar/timings/MinecraftTimings.java -+++ b/src/main/java/co/aikar/timings/MinecraftTimings.java -@@ -16,6 +16,7 @@ import java.util.Map; - public final class MinecraftTimings { - - public static final Timing serverOversleep = Timings.ofSafe("Server Oversleep"); -+ public static final Timing midTickChunkTasks = Timings.ofSafe("Mid Tick Chunk Tasks"); - public static final Timing playerListTimer = Timings.ofSafe("Player List"); - public static final Timing commandFunctionsTimer = Timings.ofSafe("Command Functions"); - public static final Timing connectionTimer = Timings.ofSafe("Connection Handler"); -diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 2fcb923b8aa0e891de9f86c4c55a4af8969c04cd..04b402084d847ba75b8282b25054d7df8a705faa 100644 ---- a/src/main/java/com/destroystokyo/paper/PaperConfig.java -+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java -@@ -428,4 +428,9 @@ public class PaperConfig { - log("Async Chunks: Enabled - Chunks will be loaded much faster, without lag."); - } - } -+ -+ public static int midTickChunkTasks = 1000; -+ private static void midTickChunkTasks() { -+ midTickChunkTasks = getInt("settings.chunk-tasks-per-tick", midTickChunkTasks); -+ } - } -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index ce5cb696dfa2ff9a07ab9e7547ba54920e58f53a..3c299bae9095ddc3732f1817c2d52f8d8a6987db 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1135,6 +1135,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { -+ midTickLoadChunks(); // will only do loads since we are still considered !canSleepForTick - return !this.canOversleep(); - }); - isOversleep = false;MinecraftTimings.serverOversleep.stopTiming(); -@@ -1396,13 +1415,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { - Optional optional = ((Either) playerchunk.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left(); - -@@ -782,6 +785,7 @@ public class ServerChunkCache extends ChunkSource { - chunk.setInhabitedTime(chunk.getInhabitedTime() + j); - if (flag1 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunk.getPos()) && !this.chunkMap.isOutsideOfRange(chunkcoordintpair, true)) { // Spigot - NaturalSpawner.spawnForChunk(this.level, chunk, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag2); -+ if (chunksTicked[0]++ % 10 == 0) this.level.getServer().midTickLoadChunks(); // Paper - } - - // this.level.timings.doTickTiles.startTiming(); // Spigot // Paper -@@ -799,7 +803,7 @@ public class ServerChunkCache extends ChunkSource { - } - - this.level.getProfiler().popPush("broadcast"); -- this.chunkMap.getChunks().forEach((playerchunk) -> { // Paper - no... just no... -+ this.chunkMap.forEachVisibleChunk((playerchunk) -> { // Paper - safe iterator incase chunk loads, also no wrapping - Optional optional = ((Either) playerchunk.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left(); // CraftBukkit - decompile error - - Objects.requireNonNull(playerchunk); -@@ -963,6 +967,41 @@ public class ServerChunkCache extends ChunkSource { - super.doRunTask(task); - } - -+ // Paper start -+ private long lastMidTickChunkTask = 0; -+ public boolean pollChunkLoadTasks() { -+ if (com.destroystokyo.paper.io.chunk.ChunkTaskManager.pollChunkWaitQueue() || ServerChunkCache.this.level.asyncChunkTaskManager.pollNextChunkTask()) { -+ try { -+ ServerChunkCache.this.runDistanceManagerUpdates(); -+ } finally { -+ // from below: process pending Chunk loadCallback() and unloadCallback() after each run task -+ chunkMap.callbackExecutor.run(); -+ } -+ return true; -+ } -+ return false; -+ } -+ public void midTickLoadChunks() { -+ net.minecraft.server.MinecraftServer server = ServerChunkCache.this.level.getServer(); -+ // always try to load chunks, restrain generation/other updates only. don't count these towards tick count -+ //noinspection StatementWithEmptyBody -+ while (pollChunkLoadTasks()) {} -+ -+ if (System.nanoTime() - lastMidTickChunkTask < 200000) { -+ return; -+ } -+ -+ for (;server.midTickChunksTasksRan < com.destroystokyo.paper.PaperConfig.midTickChunkTasks && server.haveTime();) { -+ if (this.pollTask()) { -+ server.midTickChunksTasksRan++; -+ lastMidTickChunkTask = System.nanoTime(); -+ } else { -+ break; -+ } -+ } -+ } -+ // Paper end -+ - @Override - // CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task - public boolean pollTask() { -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 428d03aff76b8e62fb9daa02442b7ea829bc9b14..021915680f6aa054585527dc0caf18b65795a1b7 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -544,6 +544,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - } - timings.scheduledBlocks.stopTiming(); // Paper - -+ this.getServer().midTickLoadChunks(); // Paper - gameprofilerfiller.popPush("raid"); - this.timings.raids.startTiming(); // Paper - timings - this.raids.tick(); -@@ -556,6 +557,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - timings.doSounds.startTiming(); // Spigot - this.runBlockEvents(); - timings.doSounds.stopTiming(); // Spigot -+ this.getServer().midTickLoadChunks(); // Paper - this.handlingTick = false; - gameprofilerfiller.pop(); - boolean flag3 = true || !this.players.isEmpty() || !this.getForcedChunks().isEmpty(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players -@@ -602,10 +604,12 @@ public class ServerLevel extends Level implements WorldGenLevel { - timings.entityTick.stopTiming(); // Spigot - timings.tickEntities.stopTiming(); // Spigot - gameprofilerfiller.pop(); -+ this.getServer().midTickLoadChunks(); // Paper - this.tickBlockEntities(); - } - - gameprofilerfiller.push("entityManagement"); -+ this.getServer().midTickLoadChunks(); // Paper - this.entityManager.tick(); - gameprofilerfiller.pop(); - } diff --git a/patches/server/0399-Add-tick-times-API-and-mspt-command.patch b/patches/server/0398-Add-tick-times-API-and-mspt-command.patch similarity index 93% rename from patches/server/0399-Add-tick-times-API-and-mspt-command.patch rename to patches/server/0398-Add-tick-times-API-and-mspt-command.patch index b3e5da888e..599d20cb6d 100644 --- a/patches/server/0399-Add-tick-times-API-and-mspt-command.patch +++ b/patches/server/0398-Add-tick-times-API-and-mspt-command.patch @@ -75,7 +75,7 @@ index 0000000000000000000000000000000000000000..d0211d4f39f9d6af1d751ac66342b42c + } +} diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 04b402084d847ba75b8282b25054d7df8a705faa..586d78f9d194b9f82b446f456a3f5a1871981577 100644 +index 86e16e39a9a996669989d990b76f69116bcee300..f1034cfb63ea37c22e67b5d4a18214774f208de2 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -69,6 +69,7 @@ public class PaperConfig { @@ -84,10 +84,10 @@ index 04b402084d847ba75b8282b25054d7df8a705faa..586d78f9d194b9f82b446f456a3f5a18 commands.put("paper", new PaperCommand("paper")); + commands.put("mspt", new MSPTCommand("mspt")); - version = getInt("config-version", 21); - set("config-version", 21); + version = getInt("config-version", 22); + set("config-version", 22); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 3c299bae9095ddc3732f1817c2d52f8d8a6987db..43e0c5d29981ccc6a20933afdb33e8b89d8be6fb 100644 +index 273774ad46b993212a0cd4cfa81f0e02807c442e..c4f52d4511476f8fd904e4eb790b07d36cfdb145 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -248,6 +248,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function serverFactory) { AtomicReference atomicreference = new AtomicReference(); Thread thread = new Thread(() -> { -@@ -931,6 +934,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop pooledLinkedPlayerHashSets = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets<>(); // Paper start - no-tick view distance -@@ -1091,7 +1092,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1056,7 +1057,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider return Either.left(chunk); }); }, (runnable) -> { @@ -46,10 +46,10 @@ index b5459099b368ff6c8e62d254196350c2189e52a9..e7b29f632ee7a2fa77a191b25256134d completablefuture1.thenAcceptAsync((either) -> { diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 0765eefbba823570149f076a0d4d6aba1aa4e863..5009a782d80d5c358acb5a412d63a567fd7db3ab 100644 +index c7ecb8fb1cbc1433986fbd03c68b4dbe5a2835c7..abd814f397a90fcaaf98a76ed83b5f9f76f2948e 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -1014,6 +1014,7 @@ public class ServerChunkCache extends ChunkSource { +@@ -1136,6 +1136,7 @@ public class ServerChunkCache extends ChunkSource { return super.pollTask() || execChunkTask; // Paper } } finally { diff --git a/patches/server/0415-Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch b/patches/server/0414-Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch similarity index 100% rename from patches/server/0415-Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch rename to patches/server/0414-Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch diff --git a/patches/server/0416-Broadcast-join-message-to-console.patch b/patches/server/0415-Broadcast-join-message-to-console.patch similarity index 100% rename from patches/server/0416-Broadcast-join-message-to-console.patch rename to patches/server/0415-Broadcast-join-message-to-console.patch diff --git a/patches/server/0417-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch b/patches/server/0416-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch similarity index 95% rename from patches/server/0417-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch rename to patches/server/0416-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch index d3da3904f5..77abcfad41 100644 --- a/patches/server/0417-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch +++ b/patches/server/0416-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch @@ -28,10 +28,10 @@ receives a deterministic result, and should no longer require 1 tick delays anymore. diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index aa9846c7d6b8499e01bf0ffeece6a940e0879328..2b4c35d9bd186fd7c3650a7ad791cd67fb64e635 100644 +index 205d6b9d822d3d28c9e8c3f5a511384ce82adc99..22c691f3bfbfc5f8e7420c14de36a353b31fc123 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -1601,6 +1601,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1566,6 +1566,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider .printStackTrace(); return; } @@ -40,7 +40,7 @@ index aa9846c7d6b8499e01bf0ffeece6a940e0879328..2b4c35d9bd186fd7c3650a7ad791cd67 if (!(entity instanceof EnderDragonPart)) { EntityType entitytypes = entity.getType(); diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index a58424219a2aed13494b174632acee36a9b800d3..e81aeb4811064a8868f29e854635b3ba62509eea 100644 +index fdd6f08d2bf3610417e42ae3504b1a40d864a2b6..9303a574624d9829fdd6b18e07331a99d5745eed 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -243,6 +243,7 @@ public class ServerPlayer extends Player { diff --git a/patches/server/0418-Load-Chunks-for-Login-Asynchronously.patch b/patches/server/0417-Load-Chunks-for-Login-Asynchronously.patch similarity index 99% rename from patches/server/0418-Load-Chunks-for-Login-Asynchronously.patch rename to patches/server/0417-Load-Chunks-for-Login-Asynchronously.patch index 32a74ed76b..42104b540f 100644 --- a/patches/server/0418-Load-Chunks-for-Login-Asynchronously.patch +++ b/patches/server/0417-Load-Chunks-for-Login-Asynchronously.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Load Chunks for Login Asynchronously diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 73dc7394a7209a8cd1eec40115cb6e7828f1dbd6..30f8f4a8314c849a66143a545c1af3440965c6aa 100644 +index 9303a574624d9829fdd6b18e07331a99d5745eed..e98ae3630576ae67e24460f90e577b34f246a150 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -178,6 +178,7 @@ public class ServerPlayer extends Player { diff --git a/patches/server/0419-Move-player-to-spawn-point-if-spawn-in-unloaded-worl.patch b/patches/server/0418-Move-player-to-spawn-point-if-spawn-in-unloaded-worl.patch similarity index 89% rename from patches/server/0419-Move-player-to-spawn-point-if-spawn-in-unloaded-worl.patch rename to patches/server/0418-Move-player-to-spawn-point-if-spawn-in-unloaded-worl.patch index 176afca770..59e186206b 100644 --- a/patches/server/0419-Move-player-to-spawn-point-if-spawn-in-unloaded-worl.patch +++ b/patches/server/0418-Move-player-to-spawn-point-if-spawn-in-unloaded-worl.patch @@ -7,10 +7,10 @@ The code following this has better support for null worlds to move them back to the world spawn. diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index b331d83b2d92af2e1c5641765ba41d6d62a50a3b..e629e40285d09ffca71db3215941f302704463a3 100644 +index 1164fc5915f0121b697ea10fac73919597902026..5802b1721a0d8e1c40d87be099d4f1b1c8a921d7 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -2005,9 +2005,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -2010,9 +2010,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n bworld = server.getWorld(worldName); } diff --git a/patches/server/0420-Add-PlayerAttackEntityCooldownResetEvent.patch b/patches/server/0419-Add-PlayerAttackEntityCooldownResetEvent.patch similarity index 95% rename from patches/server/0420-Add-PlayerAttackEntityCooldownResetEvent.patch rename to patches/server/0419-Add-PlayerAttackEntityCooldownResetEvent.patch index a6104ebe3c..ced1f6e5af 100644 --- a/patches/server/0420-Add-PlayerAttackEntityCooldownResetEvent.patch +++ b/patches/server/0419-Add-PlayerAttackEntityCooldownResetEvent.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add PlayerAttackEntityCooldownResetEvent diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 056a66e0bfe85d57e5c08473f6e30dc2b02224e5..b6a36706846a3106f7e747ce761aa2064a934d05 100644 +index a377d73bb2b2c292aeaefa2d60be27366713fd7c..44df201abb930b455d9de8f36260ec5e4f20f5c2 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java @@ -2040,7 +2040,16 @@ public abstract class LivingEntity extends Entity { diff --git a/patches/server/0421-Don-t-fire-BlockFade-on-worldgen-threads.patch b/patches/server/0420-Don-t-fire-BlockFade-on-worldgen-threads.patch similarity index 100% rename from patches/server/0421-Don-t-fire-BlockFade-on-worldgen-threads.patch rename to patches/server/0420-Don-t-fire-BlockFade-on-worldgen-threads.patch diff --git a/patches/server/0422-Add-phantom-creative-and-insomniac-controls.patch b/patches/server/0421-Add-phantom-creative-and-insomniac-controls.patch similarity index 97% rename from patches/server/0422-Add-phantom-creative-and-insomniac-controls.patch rename to patches/server/0421-Add-phantom-creative-and-insomniac-controls.patch index 7f0b9aa901..81375e7659 100644 --- a/patches/server/0422-Add-phantom-creative-and-insomniac-controls.patch +++ b/patches/server/0421-Add-phantom-creative-and-insomniac-controls.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add phantom creative and insomniac controls diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 3eed64b9562257b7a9fdfd51137a9ddf2cc0a84a..91b5267dcb24646c29ea1ff0b50f3b369f984244 100644 +index 59fe44f53adde4576f95e5150aca02ccc71dcc0b..aafa9e7eb9cc0762c4a43f06f7cf9833ce397817 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -513,6 +513,13 @@ public class PaperWorldConfig { diff --git a/patches/server/0423-Fix-numerous-item-duplication-issues-and-teleport-is.patch b/patches/server/0422-Fix-numerous-item-duplication-issues-and-teleport-is.patch similarity index 94% rename from patches/server/0423-Fix-numerous-item-duplication-issues-and-teleport-is.patch rename to patches/server/0422-Fix-numerous-item-duplication-issues-and-teleport-is.patch index c30f021136..6900ea89f8 100644 --- a/patches/server/0423-Fix-numerous-item-duplication-issues-and-teleport-is.patch +++ b/patches/server/0422-Fix-numerous-item-duplication-issues-and-teleport-is.patch @@ -16,10 +16,10 @@ So even if something NEW comes up, it would be impossible to drop the same item twice because the source was destroyed. diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index e629e40285d09ffca71db3215941f302704463a3..143b6a5b9f3d481a693ed0b9ae96aef8b9d517d5 100644 +index 5802b1721a0d8e1c40d87be099d4f1b1c8a921d7..fbcbe0443f843d04738938f10b433bfe1631fe78 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -2154,11 +2154,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -2159,11 +2159,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n } else { // CraftBukkit start - Capture drops for death event if (this instanceof net.minecraft.world.entity.LivingEntity && !((net.minecraft.world.entity.LivingEntity) this).forceDrops) { @@ -34,7 +34,7 @@ index e629e40285d09ffca71db3215941f302704463a3..143b6a5b9f3d481a693ed0b9ae96aef8 entityitem.setDefaultPickUpDelay(); // CraftBukkit start -@@ -2900,6 +2901,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -2905,6 +2906,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n @Nullable public Entity teleportTo(ServerLevel worldserver, BlockPos location) { // CraftBukkit end @@ -47,7 +47,7 @@ index e629e40285d09ffca71db3215941f302704463a3..143b6a5b9f3d481a693ed0b9ae96aef8 if (this.level instanceof ServerLevel && !this.isRemoved()) { this.level.getProfiler().push("changeDimension"); // CraftBukkit start -@@ -2920,6 +2927,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -2925,6 +2932,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n // CraftBukkit end this.level.getProfiler().popPush("reloading"); @@ -59,7 +59,7 @@ index e629e40285d09ffca71db3215941f302704463a3..143b6a5b9f3d481a693ed0b9ae96aef8 Entity entity = this.getType().create((Level) worldserver); if (entity != null) { -@@ -2933,10 +2945,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -2938,10 +2950,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n // CraftBukkit start - Forward the CraftEntity to the new entity this.getBukkitEntity().setHandle(entity); entity.bukkitEntity = this.getBukkitEntity(); @@ -70,7 +70,7 @@ index e629e40285d09ffca71db3215941f302704463a3..143b6a5b9f3d481a693ed0b9ae96aef8 // CraftBukkit end } -@@ -3061,7 +3069,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -3066,7 +3074,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n } public boolean canChangeDimensions() { diff --git a/patches/server/0424-Implement-Brigadier-Mojang-API.patch b/patches/server/0423-Implement-Brigadier-Mojang-API.patch similarity index 100% rename from patches/server/0424-Implement-Brigadier-Mojang-API.patch rename to patches/server/0423-Implement-Brigadier-Mojang-API.patch diff --git a/patches/server/0425-Villager-Restocks-API.patch b/patches/server/0424-Villager-Restocks-API.patch similarity index 100% rename from patches/server/0425-Villager-Restocks-API.patch rename to patches/server/0424-Villager-Restocks-API.patch diff --git a/patches/server/0426-Validate-PickItem-Packet-and-kick-for-invalid.patch b/patches/server/0425-Validate-PickItem-Packet-and-kick-for-invalid.patch similarity index 100% rename from patches/server/0426-Validate-PickItem-Packet-and-kick-for-invalid.patch rename to patches/server/0425-Validate-PickItem-Packet-and-kick-for-invalid.patch diff --git a/patches/server/0427-Expose-game-version.patch b/patches/server/0426-Expose-game-version.patch similarity index 89% rename from patches/server/0427-Expose-game-version.patch rename to patches/server/0426-Expose-game-version.patch index 451b6002f8..b21d071a9d 100644 --- a/patches/server/0427-Expose-game-version.patch +++ b/patches/server/0426-Expose-game-version.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Expose game version diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index d1661f7f527765a847a057902acad31954896557..b499675f6d94203924cdaebcb2b06db20fe0ed14 100644 +index bb1eaf69b9b32e81f5fd32add9cb37a2e92181d2..72640c2dd945a22e8e562ee6901c9f70ae786816 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -532,6 +532,13 @@ public final class CraftServer implements Server { diff --git a/patches/server/0428-Optimize-Voxel-Shape-Merging.patch b/patches/server/0427-Optimize-Voxel-Shape-Merging.patch similarity index 100% rename from patches/server/0428-Optimize-Voxel-Shape-Merging.patch rename to patches/server/0427-Optimize-Voxel-Shape-Merging.patch diff --git a/patches/server/0429-Set-cap-on-JDK-per-thread-native-byte-buffer-cache.patch b/patches/server/0428-Set-cap-on-JDK-per-thread-native-byte-buffer-cache.patch similarity index 100% rename from patches/server/0429-Set-cap-on-JDK-per-thread-native-byte-buffer-cache.patch rename to patches/server/0428-Set-cap-on-JDK-per-thread-native-byte-buffer-cache.patch diff --git a/patches/server/0430-Implement-Mob-Goal-API.patch b/patches/server/0429-Implement-Mob-Goal-API.patch similarity index 100% rename from patches/server/0430-Implement-Mob-Goal-API.patch rename to patches/server/0429-Implement-Mob-Goal-API.patch diff --git a/patches/server/0431-Use-distance-map-to-optimise-entity-tracker.patch b/patches/server/0430-Use-distance-map-to-optimise-entity-tracker.patch similarity index 88% rename from patches/server/0431-Use-distance-map-to-optimise-entity-tracker.patch rename to patches/server/0430-Use-distance-map-to-optimise-entity-tracker.patch index 788577d525..1f5ff7a02c 100644 --- a/patches/server/0431-Use-distance-map-to-optimise-entity-tracker.patch +++ b/patches/server/0430-Use-distance-map-to-optimise-entity-tracker.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Use distance map to optimise entity tracker Use the distance map to find candidate players for tracking. diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 5505094b43635c0388dc83515635a9ab00d9eb7a..2ee704a0851f9fd0408b54bee9fe2700421abc7a 100644 +index 22c691f3bfbfc5f8e7420c14de36a353b31fc123..5f8399cc2ba5114d11298808528b18f044c650ce 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java @@ -63,6 +63,7 @@ import net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket; @@ -17,7 +17,7 @@ index 5505094b43635c0388dc83515635a9ab00d9eb7a..2ee704a0851f9fd0408b54bee9fe2700 import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.server.network.ServerPlayerConnection; import net.minecraft.util.CsvOutput; -@@ -214,11 +215,33 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -187,11 +188,36 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerViewDistanceTickMap; public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerViewDistanceNoTickMap; // Paper end - no-tick view distance @@ -30,6 +30,9 @@ index 5505094b43635c0388dc83515635a9ab00d9eb7a..2ee704a0851f9fd0408b54bee9fe2700 + static final org.spigotmc.TrackingRange.TrackingRangeType[] TRACKING_RANGE_TYPES = org.spigotmc.TrackingRange.TrackingRangeType.values(); + public final com.destroystokyo.paper.util.misc.PlayerAreaMap[] playerEntityTrackerTrackMaps; + final int[] entityTrackerTrackRanges; ++ public final int getEntityTrackerRange(final int ordinal) { ++ return this.entityTrackerTrackRanges[ordinal]; ++ } + + private int convertSpigotRangeToVanilla(final int vanilla) { + return MinecraftServer.getServer().getScaledTrackingDistance(vanilla); @@ -51,7 +54,7 @@ index 5505094b43635c0388dc83515635a9ab00d9eb7a..2ee704a0851f9fd0408b54bee9fe2700 // Paper start - no-tick view distance int effectiveTickViewDistance = this.getEffectiveViewDistance(); int effectiveNoTickViewDistance = Math.max(this.getEffectiveNoTickViewDistance(), effectiveTickViewDistance); -@@ -235,7 +258,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -208,7 +234,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } void removePlayerFromDistanceMaps(ServerPlayer player) { @@ -64,7 +67,7 @@ index 5505094b43635c0388dc83515635a9ab00d9eb7a..2ee704a0851f9fd0408b54bee9fe2700 // Paper start - no-tick view distance this.playerViewDistanceBroadcastMap.remove(player); this.playerViewDistanceTickMap.remove(player); -@@ -247,6 +274,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -220,6 +250,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider 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 @@ -79,9 +82,9 @@ index 5505094b43635c0388dc83515635a9ab00d9eb7a..2ee704a0851f9fd0408b54bee9fe2700 // Paper start - no-tick view distance int effectiveTickViewDistance = this.getEffectiveViewDistance(); int effectiveNoTickViewDistance = Math.max(this.getEffectiveNoTickViewDistance(), effectiveTickViewDistance); -@@ -300,6 +335,45 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - this.poiManager = new PoiManager(new File(file, "poi"), dataFixer, dsync, world); - this.setViewDistance(viewDistance); +@@ -346,6 +384,45 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + }); + // Paper end - no-tick view distance this.playerMobDistanceMap = this.level.paperConfig.perPlayerMobSpawns ? new com.destroystokyo.paper.util.PlayerMobDistanceMap() : null; // Paper + // Paper start - use distance map to optimise entity tracker + this.playerEntityTrackerTrackMaps = new com.destroystokyo.paper.util.misc.PlayerAreaMap[TRACKING_RANGE_TYPES.length]; @@ -122,10 +125,10 @@ index 5505094b43635c0388dc83515635a9ab00d9eb7a..2ee704a0851f9fd0408b54bee9fe2700 + this.playerEntityTrackerTrackMaps[ordinal] = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets); + } + // Paper end - use distance map to optimise entity tracker - // Paper start - no-tick view distance - this.setNoTickViewDistance(this.level.paperConfig.noTickViewDistance); - this.playerViewDistanceTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets, -@@ -1453,17 +1527,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + } + + // Paper start +@@ -1418,17 +1495,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void move(ServerPlayer player) { @@ -144,7 +147,7 @@ index 5505094b43635c0388dc83515635a9ab00d9eb7a..2ee704a0851f9fd0408b54bee9fe2700 int i = SectionPos.blockToSectionCoord(player.getBlockX()); int j = SectionPos.blockToSectionCoord(player.getBlockZ()); -@@ -1618,7 +1682,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1583,7 +1650,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider entity.tracker = playerchunkmap_entitytracker; // Paper - Fast access to tracker this.entityMap.put(entity.getId(), playerchunkmap_entitytracker); @@ -153,7 +156,7 @@ index 5505094b43635c0388dc83515635a9ab00d9eb7a..2ee704a0851f9fd0408b54bee9fe2700 if (entity instanceof ServerPlayer) { ServerPlayer entityplayer = (ServerPlayer) entity; -@@ -1662,7 +1726,37 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1627,7 +1694,37 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider entity.tracker = null; // Paper - We're no longer tracked } @@ -191,7 +194,7 @@ index 5505094b43635c0388dc83515635a9ab00d9eb7a..2ee704a0851f9fd0408b54bee9fe2700 List list = Lists.newArrayList(); List list1 = this.level.players(); -@@ -1770,23 +1864,31 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially +@@ -1735,23 +1832,31 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially DebugPackets.sendPoiPacketsForChunk(this.level, chunk.getPos()); List list = Lists.newArrayList(); List list1 = Lists.newArrayList(); @@ -235,7 +238,7 @@ index 5505094b43635c0388dc83515635a9ab00d9eb7a..2ee704a0851f9fd0408b54bee9fe2700 Iterator iterator; Entity entity1; -@@ -1869,6 +1971,42 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially +@@ -1834,6 +1939,42 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially this.lastSectionPos = SectionPos.of(entity); } @@ -278,17 +281,8 @@ index 5505094b43635c0388dc83515635a9ab00d9eb7a..2ee704a0851f9fd0408b54bee9fe2700 public boolean equals(Object object) { return object instanceof ChunkMap.TrackedEntity ? ((ChunkMap.TrackedEntity) object).entity.getId() == this.entity.getId() : false; } -@@ -1954,7 +2092,7 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially - int j = entity.getType().clientTrackingRange() * 16; - j = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, j); // Paper - -- if (j > i) { -+ if (j < i) { // Paper - we need the lowest range thanks to the fact that our tracker doesn't account for passenger logic - i = j; - } - } diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 143b6a5b9f3d481a693ed0b9ae96aef8b9d517d5..35107dceb201817597e84da47e4a7506ac9336b2 100644 +index fbcbe0443f843d04738938f10b433bfe1631fe78..ee461f8da2663acc45896a9ae418c2f2129e3319 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -50,6 +50,7 @@ import net.minecraft.network.syncher.EntityDataSerializers; @@ -299,9 +293,9 @@ index 143b6a5b9f3d481a693ed0b9ae96aef8b9d517d5..35107dceb201817597e84da47e4a7506 import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; -@@ -345,6 +346,21 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -350,6 +351,39 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n } - // CraftBukkit end + // Paper end + // Paper start - optimise entity tracking + final org.spigotmc.TrackingRange.TrackingRangeType trackingRangeType = org.spigotmc.TrackingRange.getTrackingRangeType(this); @@ -313,8 +307,26 @@ index 143b6a5b9f3d481a693ed0b9ae96aef8b9d517d5..35107dceb201817597e84da47e4a7506 + } + + public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet getPlayersInTrackRange() { -+ return ((ServerLevel)this.level).getChunkSource().chunkMap.playerEntityTrackerTrackMaps[this.trackingRangeType.ordinal()] -+ .getObjectsInRange(MCUtil.getCoordinateKey(this)); ++ // determine highest range of passengers ++ if (this.passengers.isEmpty()) { ++ return ((ServerLevel)this.level).getChunkSource().chunkMap.playerEntityTrackerTrackMaps[this.trackingRangeType.ordinal()] ++ .getObjectsInRange(MCUtil.getCoordinateKey(this)); ++ } ++ Iterable passengers = this.getIndirectPassengers(); ++ net.minecraft.server.level.ChunkMap chunkMap = ((ServerLevel)this.level).getChunkSource().chunkMap; ++ org.spigotmc.TrackingRange.TrackingRangeType type = this.trackingRangeType; ++ int range = chunkMap.getEntityTrackerRange(type.ordinal()); ++ ++ for (Entity passenger : passengers) { ++ org.spigotmc.TrackingRange.TrackingRangeType passengerType = passenger.trackingRangeType; ++ int passengerRange = chunkMap.getEntityTrackerRange(passengerType.ordinal()); ++ if (passengerRange > range) { ++ type = passengerType; ++ range = passengerRange; ++ } ++ } ++ ++ return chunkMap.playerEntityTrackerTrackMaps[type.ordinal()].getObjectsInRange(MCUtil.getCoordinateKey(this)); + } + // Paper end - optimise entity tracking + diff --git a/patches/server/0432-Optimize-isOutsideRange-to-use-distance-maps.patch b/patches/server/0431-Optimize-isOutsideRange-to-use-distance-maps.patch similarity index 93% rename from patches/server/0432-Optimize-isOutsideRange-to-use-distance-maps.patch rename to patches/server/0431-Optimize-isOutsideRange-to-use-distance-maps.patch index 5f76e69dfc..c968489460 100644 --- a/patches/server/0432-Optimize-isOutsideRange-to-use-distance-maps.patch +++ b/patches/server/0431-Optimize-isOutsideRange-to-use-distance-maps.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Optimize isOutsideRange to use distance maps Use a distance map to find the players in range quickly diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java -index bbb94e8a5e3585701849e025b534a69a6e67949f..7223c6daf6f0eb959a5cab701096324a34b9c88a 100644 +index a3ba56f437fe9e4dac59370463052341eb9b7524..c6c4d2fd3205dfcaff9e0f1a6b92b74903275991 100644 --- a/src/main/java/net/minecraft/server/level/ChunkHolder.java +++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java @@ -100,6 +100,18 @@ public class ChunkHolder { @@ -37,10 +37,10 @@ index bbb94e8a5e3585701849e025b534a69a6e67949f..7223c6daf6f0eb959a5cab701096324a // 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 04ece67574bb95f575edc6f5337a3492e7260102..1e944b2fdfd382bfcc7f94f9a8daf1782931110b 100644 +index 5f8399cc2ba5114d11298808528b18f044c650ce..af8b6138127d033a92fa03f0bc4ba7ef61afc296 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -229,6 +229,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -205,6 +205,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider return MinecraftServer.getServer().getScaledTrackingDistance(vanilla); } // Paper end - use distance map to optimise tracker @@ -58,7 +58,7 @@ index 04ece67574bb95f575edc6f5337a3492e7260102..1e944b2fdfd382bfcc7f94f9a8daf178 void addPlayerToDistanceMaps(ServerPlayer player) { int chunkX = MCUtil.getChunkCoordinate(player.getX()); -@@ -242,6 +253,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -218,6 +229,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider trackMap.add(player, chunkX, chunkZ, Math.min(trackRange, this.getEffectiveViewDistance())); } // Paper end - use distance map to optimise entity tracker @@ -71,7 +71,7 @@ index 04ece67574bb95f575edc6f5337a3492e7260102..1e944b2fdfd382bfcc7f94f9a8daf178 // Paper start - no-tick view distance int effectiveTickViewDistance = this.getEffectiveViewDistance(); int effectiveNoTickViewDistance = Math.max(this.getEffectiveNoTickViewDistance(), effectiveTickViewDistance); -@@ -263,6 +280,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -239,6 +256,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.playerEntityTrackerTrackMaps[i].remove(player); } // Paper end - use distance map to optimise tracker @@ -82,7 +82,7 @@ index 04ece67574bb95f575edc6f5337a3492e7260102..1e944b2fdfd382bfcc7f94f9a8daf178 // Paper start - no-tick view distance this.playerViewDistanceBroadcastMap.remove(player); this.playerViewDistanceTickMap.remove(player); -@@ -282,6 +303,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -258,6 +279,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider trackMap.update(player, chunkX, chunkZ, Math.min(trackRange, this.getEffectiveViewDistance())); } // Paper end - use distance map to optimise entity tracker @@ -92,7 +92,7 @@ index 04ece67574bb95f575edc6f5337a3492e7260102..1e944b2fdfd382bfcc7f94f9a8daf178 // Paper start - no-tick view distance int effectiveTickViewDistance = this.getEffectiveViewDistance(); int effectiveNoTickViewDistance = Math.max(this.getEffectiveNoTickViewDistance(), effectiveTickViewDistance); -@@ -330,7 +354,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -336,7 +360,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.mainThreadMailbox = this.queueSorter.getProcessor(mailbox, false); this.mailboxLight = this.queueSorter.getProcessor(lightthreaded, false);// Paper this.lightEngine = new ThreadedLevelLightEngine(chunkProvider, this, this.level.dimensionType().hasSkyLight(), threadedmailbox1, this.queueSorter.getProcessor(threadedmailbox1, false)); @@ -101,7 +101,7 @@ index 04ece67574bb95f575edc6f5337a3492e7260102..1e944b2fdfd382bfcc7f94f9a8daf178 this.overworldDataStorage = persistentStateManagerFactory; this.poiManager = new PoiManager(new File(file, "poi"), dataFixer, dsync, world); this.setViewDistance(viewDistance); -@@ -374,6 +398,38 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -423,6 +447,38 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.playerEntityTrackerTrackMaps[ordinal] = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets); } // Paper end - use distance map to optimise entity tracker @@ -137,10 +137,10 @@ index 04ece67574bb95f575edc6f5337a3492e7260102..1e944b2fdfd382bfcc7f94f9a8daf178 + } + }); + // Paper end - optimise PlayerChunkMap#isOutsideRange - // Paper start - no-tick view distance - this.setNoTickViewDistance(this.level.paperConfig.noTickViewDistance); - this.playerViewDistanceTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets, -@@ -643,6 +699,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + } + + // Paper start +@@ -610,6 +666,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } else { if (holder != null) { holder.setTicketLevel(level); @@ -148,7 +148,7 @@ index 04ece67574bb95f575edc6f5337a3492e7260102..1e944b2fdfd382bfcc7f94f9a8daf178 } if (holder != null) { -@@ -1463,29 +1520,50 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1431,29 +1488,50 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider return this.isOutsideOfRange(chunkPos, false); } @@ -156,11 +156,6 @@ index 04ece67574bb95f575edc6f5337a3492e7260102..1e944b2fdfd382bfcc7f94f9a8daf178 - int chunkRange = level.spigotConfig.mobSpawnRange; - chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange; - chunkRange = (chunkRange > 8) ? 8 : chunkRange; -- -- final int finalChunkRange = chunkRange; // Paper for lambda below -- //double blockRange = (reducedRange) ? Math.pow(chunkRange << 4, 2) : 16384.0D; // Paper - use from event -- // Spigot end -- long i = chunkcoordintpair.toLong(); + // Paper start - optimise isOutsideOfRange + final boolean isOutsideOfRange(ChunkPos chunkcoordintpair, boolean reducedRange) { + return this.isOutsideOfRange(this.getUpdatingChunkIfPresent(chunkcoordintpair.toLong()), chunkcoordintpair, reducedRange); @@ -174,6 +169,11 @@ index 04ece67574bb95f575edc6f5337a3492e7260102..1e944b2fdfd382bfcc7f94f9a8daf178 + } + Object[] backingSet = playersInRange.getBackingSet(); +- final int finalChunkRange = chunkRange; // Paper for lambda below +- //double blockRange = (reducedRange) ? Math.pow(chunkRange << 4, 2) : 16384.0D; // Paper - use from event +- // Spigot end +- long i = chunkcoordintpair.toLong(); +- - return !this.distanceManager.hasPlayersNearby(i) ? true : this.playerMap.getPlayers(i).noneMatch((entityplayer) -> { - // Paper start - add PlayerNaturallySpawnCreaturesEvent - com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event; @@ -291,10 +291,10 @@ index b49d380ef088aed3204ec71abc437c348ef004fa..577b391dcba1db712c1e2c83296e1c87 public String getDebugStatus() { diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 5009a782d80d5c358acb5a412d63a567fd7db3ab..d02eaf281b752f5b1442d06471b80ce28361f31a 100644 +index abd814f397a90fcaaf98a76ed83b5f9f76f2948e..0ce86c72cb829b816ec7bfb8f0d19396e1b12eb6 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -733,6 +733,37 @@ public class ServerChunkCache extends ChunkSource { +@@ -892,6 +892,37 @@ public class ServerChunkCache extends ChunkSource { boolean flag1 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit if (!flag) { @@ -332,8 +332,8 @@ index 5009a782d80d5c358acb5a412d63a567fd7db3ab..d02eaf281b752f5b1442d06471b80ce2 this.level.getProfiler().push("pollingChunks"); int k = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING); boolean flag2 = level.ticksPerAnimalSpawns != 0L && worlddata.getGameTime() % level.ticksPerAnimalSpawns == 0L; // CraftBukkit -@@ -763,15 +794,7 @@ public class ServerChunkCache extends ChunkSource { - List list = Lists.newArrayList(this.chunkMap.visibleChunkMap.values()); // Paper +@@ -922,15 +953,7 @@ public class ServerChunkCache extends ChunkSource { + List list = Lists.newArrayList(this.chunkMap.getChunks()); Collections.shuffle(list); - //Paper start - call player naturally spawn event @@ -347,9 +347,9 @@ index 5009a782d80d5c358acb5a412d63a567fd7db3ab..d02eaf281b752f5b1442d06471b80ce2 - // Paper end + // Paper - moved natural spawn event up this.level.timings.chunkTicks.startTiming(); // Paper - final int[] chunksTicked = {0}; // Paper list.forEach((playerchunk) -> { -@@ -781,9 +804,9 @@ public class ServerChunkCache extends ChunkSource { + Optional optional = ((Either) playerchunk.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left(); +@@ -939,9 +962,9 @@ public class ServerChunkCache extends ChunkSource { LevelChunk chunk = (LevelChunk) optional.get(); ChunkPos chunkcoordintpair = chunk.getPos(); @@ -359,10 +359,10 @@ index 5009a782d80d5c358acb5a412d63a567fd7db3ab..d02eaf281b752f5b1442d06471b80ce2 - if (flag1 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunk.getPos()) && !this.chunkMap.isOutsideOfRange(chunkcoordintpair, true)) { // Spigot + if (flag1 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunk.getPos()) && !this.chunkMap.isOutsideOfRange(playerchunk, chunkcoordintpair, true)) { // Spigot // Paper - optimise isOutsideOfRange NaturalSpawner.spawnForChunk(this.level, chunk, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag2); - if (chunksTicked[0]++ % 10 == 0) this.level.getServer().midTickLoadChunks(); // Paper } + diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 30f8f4a8314c849a66143a545c1af3440965c6aa..cf09bd17b9d2be04f79edef6debdd815b5f7f86c 100644 +index e98ae3630576ae67e24460f90e577b34f246a150..009b6fe428db1a6e69403d8df32325f78e6b4461 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -250,6 +250,7 @@ public class ServerPlayer extends Player { diff --git a/patches/server/0433-Add-villager-reputation-API.patch b/patches/server/0432-Add-villager-reputation-API.patch similarity index 100% rename from patches/server/0433-Add-villager-reputation-API.patch rename to patches/server/0432-Add-villager-reputation-API.patch diff --git a/patches/server/0434-Option-for-maximum-exp-value-when-merging-orbs.patch b/patches/server/0433-Option-for-maximum-exp-value-when-merging-orbs.patch similarity index 97% rename from patches/server/0434-Option-for-maximum-exp-value-when-merging-orbs.patch rename to patches/server/0433-Option-for-maximum-exp-value-when-merging-orbs.patch index 6a8dac61d3..2cb3cfd3f2 100644 --- a/patches/server/0434-Option-for-maximum-exp-value-when-merging-orbs.patch +++ b/patches/server/0433-Option-for-maximum-exp-value-when-merging-orbs.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Option for maximum exp value when merging orbs diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 91b5267dcb24646c29ea1ff0b50f3b369f984244..beaf99e1e6fb9f850f4119017a4dfc50fa374ace 100644 +index aafa9e7eb9cc0762c4a43f06f7cf9833ce397817..9feb88e7aa282fde1048f2e25457b917ef4de27c 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -356,6 +356,12 @@ public class PaperWorldConfig { diff --git a/patches/server/0435-ExperienceOrbMergeEvent.patch b/patches/server/0434-ExperienceOrbMergeEvent.patch similarity index 100% rename from patches/server/0435-ExperienceOrbMergeEvent.patch rename to patches/server/0434-ExperienceOrbMergeEvent.patch diff --git a/patches/server/0436-Fix-PotionEffect-ignores-icon-flag.patch b/patches/server/0435-Fix-PotionEffect-ignores-icon-flag.patch similarity index 100% rename from patches/server/0436-Fix-PotionEffect-ignores-icon-flag.patch rename to patches/server/0435-Fix-PotionEffect-ignores-icon-flag.patch diff --git a/patches/server/0437-Optimize-brigadier-child-sorting-performance.patch b/patches/server/0436-Optimize-brigadier-child-sorting-performance.patch similarity index 100% rename from patches/server/0437-Optimize-brigadier-child-sorting-performance.patch rename to patches/server/0436-Optimize-brigadier-child-sorting-performance.patch diff --git a/patches/server/0438-Potential-bed-API.patch b/patches/server/0437-Potential-bed-API.patch similarity index 100% rename from patches/server/0438-Potential-bed-API.patch rename to patches/server/0437-Potential-bed-API.patch diff --git a/patches/server/0439-Wait-for-Async-Tasks-during-shutdown.patch b/patches/server/0438-Wait-for-Async-Tasks-during-shutdown.patch similarity index 91% rename from patches/server/0439-Wait-for-Async-Tasks-during-shutdown.patch rename to patches/server/0438-Wait-for-Async-Tasks-during-shutdown.patch index 7381b537cd..6d140320d0 100644 --- a/patches/server/0439-Wait-for-Async-Tasks-during-shutdown.patch +++ b/patches/server/0438-Wait-for-Async-Tasks-during-shutdown.patch @@ -10,10 +10,10 @@ Adds a 5 second grace period for any async tasks to finish and warns if any are still running after that delay just as reload does. diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index dad662d1b252cfb77ff6646deb88514e3b563ea3..a5ef695a57e44a32da415320f6ee9ff5233c6ca4 100644 +index 2e8892211dd3eb4cac27bcb3b302a25d833e2626..8e6938b76ff62f81f868ca0f6badce081263a300 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -968,6 +968,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop implements A diff --git a/patches/server/0447-Fix-Non-Full-Status-Chunk-NBT-Memory-Leak.patch b/patches/server/0446-Fix-Non-Full-Status-Chunk-NBT-Memory-Leak.patch similarity index 100% rename from patches/server/0447-Fix-Non-Full-Status-Chunk-NBT-Memory-Leak.patch rename to patches/server/0446-Fix-Non-Full-Status-Chunk-NBT-Memory-Leak.patch diff --git a/patches/server/0448-Optimize-sending-packets-to-nearby-locations-sounds-.patch b/patches/server/0447-Optimize-sending-packets-to-nearby-locations-sounds-.patch similarity index 100% rename from patches/server/0448-Optimize-sending-packets-to-nearby-locations-sounds-.patch rename to patches/server/0447-Optimize-sending-packets-to-nearby-locations-sounds-.patch diff --git a/patches/server/0449-Fix-villager-trading-demand-MC-163962.patch b/patches/server/0448-Fix-villager-trading-demand-MC-163962.patch similarity index 100% rename from patches/server/0449-Fix-villager-trading-demand-MC-163962.patch rename to patches/server/0448-Fix-villager-trading-demand-MC-163962.patch diff --git a/patches/server/0450-Maps-shouldn-t-load-chunks.patch b/patches/server/0449-Maps-shouldn-t-load-chunks.patch similarity index 100% rename from patches/server/0450-Maps-shouldn-t-load-chunks.patch rename to patches/server/0449-Maps-shouldn-t-load-chunks.patch diff --git a/patches/server/0451-Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch b/patches/server/0450-Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch similarity index 100% rename from patches/server/0451-Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch rename to patches/server/0450-Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch diff --git a/patches/server/0452-Delay-Chunk-Unloads-based-on-Player-Movement.patch b/patches/server/0451-Delay-Chunk-Unloads-based-on-Player-Movement.patch similarity index 98% rename from patches/server/0452-Delay-Chunk-Unloads-based-on-Player-Movement.patch rename to patches/server/0451-Delay-Chunk-Unloads-based-on-Player-Movement.patch index c73894a9fe..824f01875e 100644 --- a/patches/server/0452-Delay-Chunk-Unloads-based-on-Player-Movement.patch +++ b/patches/server/0451-Delay-Chunk-Unloads-based-on-Player-Movement.patch @@ -17,7 +17,7 @@ This allows servers with smaller worlds who do less long distance exploring to s wasting cpu cycles on saving/unloading/reloading chunks repeatedly. diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index beaf99e1e6fb9f850f4119017a4dfc50fa374ace..e1f6190a8ea8606404060b3d2ebc5ceea6665a6e 100644 +index 9feb88e7aa282fde1048f2e25457b917ef4de27c..5638cfdf2da5cdc93ef8e868279062a892ecac5c 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -531,6 +531,15 @@ public class PaperWorldConfig { diff --git a/patches/server/0453-Optimize-Bit-Operations-by-inlining.patch b/patches/server/0452-Optimize-Bit-Operations-by-inlining.patch similarity index 100% rename from patches/server/0453-Optimize-Bit-Operations-by-inlining.patch rename to patches/server/0452-Optimize-Bit-Operations-by-inlining.patch diff --git a/patches/server/0454-Add-Plugin-Tickets-to-API-Chunk-Methods.patch b/patches/server/0453-Add-Plugin-Tickets-to-API-Chunk-Methods.patch similarity index 92% rename from patches/server/0454-Add-Plugin-Tickets-to-API-Chunk-Methods.patch rename to patches/server/0453-Add-Plugin-Tickets-to-API-Chunk-Methods.patch index a34f19724c..cfdfa3a4f8 100644 --- a/patches/server/0454-Add-Plugin-Tickets-to-API-Chunk-Methods.patch +++ b/patches/server/0453-Add-Plugin-Tickets-to-API-Chunk-Methods.patch @@ -22,7 +22,7 @@ wants it to collect even faster, they can restore that setting back to 1 instead Not adding it to .getType() though to keep behavior consistent with vanilla for performance reasons. diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 7a0950a22009a0ae606e74da8026a224817d5037..65fcf1300022b66c87b00637775dbed02b2c734e 100644 +index 5aeca3ab0c28b4d8b174d1376dc76cc1704fd169..c5fdc37664b748d3daa8d29c57121fe5a2bf7e07 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -360,7 +360,7 @@ public final class CraftServer implements Server { @@ -44,10 +44,10 @@ index 7a0950a22009a0ae606e74da8026a224817d5037..65fcf1300022b66c87b00637775dbed0 this.printSaveWarning = false; console.autosavePeriod = this.configuration.getInt("ticks-per.autosave"); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 059b901e79cf0e2d152375453d36437a43c4f244..496bb91da1138fb6a4e004da1eab43e588c82592 100644 +index d347f8f3bfe7ed0492dd0cd593837ee2246527aa..44f13b8bce4c6b9b3a74b2d9d868104feb3c24bf 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -265,8 +265,21 @@ public class CraftWorld extends CraftRegionAccessor implements World { +@@ -262,8 +262,21 @@ public class CraftWorld extends CraftRegionAccessor implements World { @Override public Chunk getChunkAt(int x, int z) { @@ -70,7 +70,7 @@ index 059b901e79cf0e2d152375453d36437a43c4f244..496bb91da1138fb6a4e004da1eab43e5 @Override public Chunk getChunkAt(Block block) { -@@ -341,7 +354,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { +@@ -330,7 +343,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { public boolean unloadChunkRequest(int x, int z) { org.spigotmc.AsyncCatcher.catchOp("chunk unload"); // Spigot if (this.isChunkLoaded(x, z)) { @@ -79,7 +79,7 @@ index 059b901e79cf0e2d152375453d36437a43c4f244..496bb91da1138fb6a4e004da1eab43e5 } return true; -@@ -418,9 +431,12 @@ public class CraftWorld extends CraftRegionAccessor implements World { +@@ -407,9 +420,12 @@ public class CraftWorld extends CraftRegionAccessor implements World { org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot // Paper start - Optimize this method ChunkPos chunkPos = new ChunkPos(x, z); @@ -93,7 +93,7 @@ index 059b901e79cf0e2d152375453d36437a43c4f244..496bb91da1138fb6a4e004da1eab43e5 if (immediate == null) { immediate = world.getChunkSource().chunkMap.getUnloadingChunk(x, z); } -@@ -428,7 +444,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { +@@ -417,7 +433,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { if (!(immediate instanceof ImposterProtoChunk) && !(immediate instanceof net.minecraft.world.level.chunk.LevelChunk)) { return false; // not full status } @@ -102,7 +102,7 @@ index 059b901e79cf0e2d152375453d36437a43c4f244..496bb91da1138fb6a4e004da1eab43e5 world.getChunk(x, z); // make sure we're at ticket level 32 or lower return true; } -@@ -454,7 +470,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { +@@ -443,7 +459,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { // we do this so we do not re-read the chunk data on disk } @@ -111,7 +111,7 @@ index 059b901e79cf0e2d152375453d36437a43c4f244..496bb91da1138fb6a4e004da1eab43e5 world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true); return true; // Paper end -@@ -1898,6 +1914,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { +@@ -1887,6 +1903,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { return this.world.getChunkSource().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> { net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk) either.left().orElse(null); diff --git a/patches/server/0455-incremental-chunk-saving.patch b/patches/server/0454-incremental-chunk-saving.patch similarity index 92% rename from patches/server/0455-incremental-chunk-saving.patch rename to patches/server/0454-incremental-chunk-saving.patch index 1a8dd091eb..b99a05ff2e 100644 --- a/patches/server/0455-incremental-chunk-saving.patch +++ b/patches/server/0454-incremental-chunk-saving.patch @@ -5,7 +5,7 @@ Subject: [PATCH] incremental chunk saving diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index e1f6190a8ea8606404060b3d2ebc5ceea6665a6e..acb4fe32835bca485cfb2ec509370c9f596d6a0f 100644 +index 5638cfdf2da5cdc93ef8e868279062a892ecac5c..7ac92d856d67046a6928e8cd21a57760d4b4954c 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -44,6 +44,21 @@ public class PaperWorldConfig { @@ -31,7 +31,7 @@ index e1f6190a8ea8606404060b3d2ebc5ceea6665a6e..acb4fe32835bca485cfb2ec509370c9f config.addDefault("world-settings.default." + path, def); return config.getBoolean("world-settings." + worldName + "." + path, config.getBoolean("world-settings.default." + path)); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index a5ef695a57e44a32da415320f6ee9ff5233c6ca4..e477ccf2f17ffad0a3720f7ef7ccfe23b91bff3a 100644 +index 8e6938b76ff62f81f868ca0f6badce081263a300..5beed1f7a96df3dfcee236077c34a56c57d78c51 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -298,6 +298,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 100) { // Spigot diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java -index 7223c6daf6f0eb959a5cab701096324a34b9c88a..8245e07f6ecfd9dd997c8525b52c6eadd392e6f1 100644 +index c6c4d2fd3205dfcaff9e0f1a6b92b74903275991..fe8e6d66665dc8c38e74c0b264cf507f99f83091 100644 --- a/src/main/java/net/minecraft/server/level/ChunkHolder.java +++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java @@ -111,6 +111,8 @@ public class ChunkHolder { @@ -104,7 +104,7 @@ index 7223c6daf6f0eb959a5cab701096324a34b9c88a..8245e07f6ecfd9dd997c8525b52c6ead if (!flag2 && flag3) { int expectCreateCount = ++this.fullChunkCreateCount; // Paper this.fullChunkFuture = chunkStorage.prepareAccessibleChunk(this); -@@ -647,9 +661,33 @@ public class ChunkHolder { +@@ -664,9 +678,33 @@ public class ChunkHolder { } public void refreshAccessibility() { @@ -139,7 +139,7 @@ index 7223c6daf6f0eb959a5cab701096324a34b9c88a..8245e07f6ecfd9dd997c8525b52c6ead for (int i = 0; i < this.futures.length(); ++i) { CompletableFuture> completablefuture = (CompletableFuture) this.futures.get(i); diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 58a05168e88272fd4d54c9c7855c41063c53cf8c..12f9f512dc8b9e094dab2caec7b6ddaf2a378ee9 100644 +index 97fbb5dd30fd28bdc3df3ab023b9098dcabc95d4..2651c8161ee54a9085d43eceaeb6d16e8b693b25 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java @@ -97,6 +97,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureMana @@ -150,7 +150,7 @@ index 58a05168e88272fd4d54c9c7855c41063c53cf8c..12f9f512dc8b9e094dab2caec7b6ddaf import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -@@ -738,6 +739,64 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -710,6 +711,64 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } @@ -213,9 +213,9 @@ index 58a05168e88272fd4d54c9c7855c41063c53cf8c..12f9f512dc8b9e094dab2caec7b6ddaf + // Paper end + protected void saveAllChunks(boolean flush) { - Long2ObjectLinkedOpenHashMap visibleChunks = this.getVisibleChunks(); // Paper remove clone of visible Chunks unless saving off main thread (watchdog kill) if (flush) { -@@ -876,6 +935,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + List list = (List) this.visibleChunkMap.values().stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).collect(Collectors.toList()); +@@ -847,6 +906,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider asyncSaveData, chunk); chunk.setUnsaved(false); @@ -223,7 +223,7 @@ index 58a05168e88272fd4d54c9c7855c41063c53cf8c..12f9f512dc8b9e094dab2caec7b6ddaf } // Paper end -@@ -907,6 +967,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -884,6 +944,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.level.unload(chunk); } @@ -231,7 +231,7 @@ index 58a05168e88272fd4d54c9c7855c41063c53cf8c..12f9f512dc8b9e094dab2caec7b6ddaf this.lightEngine.updateChunkStatus(ichunkaccess.getPos()); this.lightEngine.tryScheduleUpdate(); -@@ -1260,6 +1321,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1228,6 +1289,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider if (!chunk.isUnsaved()) { return false; } else { @@ -240,10 +240,10 @@ index 58a05168e88272fd4d54c9c7855c41063c53cf8c..12f9f512dc8b9e094dab2caec7b6ddaf ChunkPos chunkcoordintpair = chunk.getPos(); diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index d02eaf281b752f5b1442d06471b80ce28361f31a..6d947343b6ff2f86a23ea669e371d5ecd9e903c0 100644 +index 0ce86c72cb829b816ec7bfb8f0d19396e1b12eb6..e5317a994cb9b30293ad54b8fc537f703ef994dc 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -675,6 +675,15 @@ public class ServerChunkCache extends ChunkSource { +@@ -836,6 +836,15 @@ public class ServerChunkCache extends ChunkSource { } // Paper - Timings } @@ -260,10 +260,10 @@ index d02eaf281b752f5b1442d06471b80ce28361f31a..6d947343b6ff2f86a23ea669e371d5ec public void close() throws IOException { // CraftBukkit start diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index e3b9f7b92e4f3e8b64e2b13bdf2b3fcee12c7cda..2653a55d3c947577ea153f3060d9b98acdf31c07 100644 +index 35ba86f0104b39903804de86dc5dff81050003bc..86d060691405401fe8ae8ae8ef59322ee841b196 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -964,6 +964,37 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1050,6 +1050,37 @@ public class ServerLevel extends Level implements WorldGenLevel { return !this.server.isUnderSpawnProtection(this, pos, player) && this.getWorldBorder().isWithinBounds(pos); } diff --git a/patches/server/0456-Fix-missing-chunks-due-to-integer-overflow.patch b/patches/server/0455-Fix-missing-chunks-due-to-integer-overflow.patch similarity index 100% rename from patches/server/0456-Fix-missing-chunks-due-to-integer-overflow.patch rename to patches/server/0455-Fix-missing-chunks-due-to-integer-overflow.patch diff --git a/patches/server/0457-Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch b/patches/server/0456-Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch similarity index 100% rename from patches/server/0457-Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch rename to patches/server/0456-Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch diff --git a/patches/server/0458-Fix-piston-physics-inconsistency-MC-188840.patch b/patches/server/0457-Fix-piston-physics-inconsistency-MC-188840.patch similarity index 96% rename from patches/server/0458-Fix-piston-physics-inconsistency-MC-188840.patch rename to patches/server/0457-Fix-piston-physics-inconsistency-MC-188840.patch index 5a28411a9e..a4347dfe63 100644 --- a/patches/server/0458-Fix-piston-physics-inconsistency-MC-188840.patch +++ b/patches/server/0457-Fix-piston-physics-inconsistency-MC-188840.patch @@ -32,19 +32,21 @@ This patch fixes https://bugs.mojang.com/browse/MC-188840 This patch also fixes rail duping and carpet duping. diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 99103cb1f86fdf1eee848037b71c3b73666c2e86..de8b9be11b996f9d1eff30fa037cfa1823eff21b 100644 +index 203834e28309387f04314b97da078ae1e0ed0638..9a17483c4c44e5022fccfe42845e4060c1703119 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java -@@ -452,4 +452,10 @@ public class PaperConfig { +@@ -450,4 +450,12 @@ public class PaperConfig { + private static void consoleHasAllPermissions() { consoleHasAllPermissions = getBoolean("settings.console-has-all-permissions", consoleHasAllPermissions); } - ++ + public static boolean allowPistonDuplication; + private static void allowPistonDuplication() { + config.set("settings.unsupported-settings.allow-piston-duplication-readme", "This setting controls if player should be able to use TNT duplication, but this also allows duplicating carpet, rails and potentially other items"); + allowPistonDuplication = getBoolean("settings.unsupported-settings.allow-piston-duplication", config.getBoolean("settings.unsupported-settings.allow-tnt-duplication", false)); + set("settings.unsupported-settings.allow-tnt-duplication", null); + } ++ } diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java index 4eac07022a7d896ee8921afa6d35cba7f0c89941..dd50a29d47a62df8cdfd69358218d1559b6794ef 100644 diff --git a/patches/server/0459-Fix-sand-duping.patch b/patches/server/0458-Fix-sand-duping.patch similarity index 100% rename from patches/server/0459-Fix-sand-duping.patch rename to patches/server/0458-Fix-sand-duping.patch diff --git a/patches/server/0460-Prevent-position-desync-in-playerconnection-causing-.patch b/patches/server/0459-Prevent-position-desync-in-playerconnection-causing-.patch similarity index 100% rename from patches/server/0460-Prevent-position-desync-in-playerconnection-causing-.patch rename to patches/server/0459-Prevent-position-desync-in-playerconnection-causing-.patch diff --git a/patches/server/0461-Inventory-getHolder-method-without-block-snapshot.patch b/patches/server/0460-Inventory-getHolder-method-without-block-snapshot.patch similarity index 100% rename from patches/server/0461-Inventory-getHolder-method-without-block-snapshot.patch rename to patches/server/0460-Inventory-getHolder-method-without-block-snapshot.patch diff --git a/patches/server/0462-Expose-Arrow-getItemStack.patch b/patches/server/0461-Expose-Arrow-getItemStack.patch similarity index 100% rename from patches/server/0462-Expose-Arrow-getItemStack.patch rename to patches/server/0461-Expose-Arrow-getItemStack.patch diff --git a/patches/server/0463-Add-and-implement-PlayerRecipeBookClickEvent.patch b/patches/server/0462-Add-and-implement-PlayerRecipeBookClickEvent.patch similarity index 100% rename from patches/server/0463-Add-and-implement-PlayerRecipeBookClickEvent.patch rename to patches/server/0462-Add-and-implement-PlayerRecipeBookClickEvent.patch diff --git a/patches/server/0464-Hide-sync-chunk-writes-behind-flag.patch b/patches/server/0463-Hide-sync-chunk-writes-behind-flag.patch similarity index 100% rename from patches/server/0464-Hide-sync-chunk-writes-behind-flag.patch rename to patches/server/0463-Hide-sync-chunk-writes-behind-flag.patch diff --git a/patches/server/0465-Add-permission-for-command-blocks.patch b/patches/server/0464-Add-permission-for-command-blocks.patch similarity index 100% rename from patches/server/0465-Add-permission-for-command-blocks.patch rename to patches/server/0464-Add-permission-for-command-blocks.patch diff --git a/patches/server/0466-Ensure-Entity-AABB-s-are-never-invalid.patch b/patches/server/0465-Ensure-Entity-AABB-s-are-never-invalid.patch similarity index 86% rename from patches/server/0466-Ensure-Entity-AABB-s-are-never-invalid.patch rename to patches/server/0465-Ensure-Entity-AABB-s-are-never-invalid.patch index 2baea7a931..8d20d86499 100644 --- a/patches/server/0466-Ensure-Entity-AABB-s-are-never-invalid.patch +++ b/patches/server/0465-Ensure-Entity-AABB-s-are-never-invalid.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Ensure Entity AABB's are never invalid diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 35107dceb201817597e84da47e4a7506ac9336b2..160c5d395807a96a733e9ad2209d4f57337af76e 100644 +index ee461f8da2663acc45896a9ae418c2f2129e3319..890502222191ce1ab5a598bf040fc462fc915e31 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -561,8 +561,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -584,8 +584,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n } public void setPos(double x, double y, double z) { @@ -19,7 +19,7 @@ index 35107dceb201817597e84da47e4a7506ac9336b2..160c5d395807a96a733e9ad2209d4f57 } protected AABB makeBoundingBox() { -@@ -3743,6 +3743,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -3766,6 +3766,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n } public final void setPosRaw(double x, double y, double z) { @@ -31,7 +31,7 @@ index 35107dceb201817597e84da47e4a7506ac9336b2..160c5d395807a96a733e9ad2209d4f57 if (this.position.x != x || this.position.y != y || this.position.z != z) { this.position = new Vec3(x, y, z); int i = Mth.floor(x); -@@ -3761,6 +3766,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -3784,6 +3789,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n } } diff --git a/patches/server/0467-Optimize-WorldBorder-collision-checks-and-air.patch b/patches/server/0466-Optimize-WorldBorder-collision-checks-and-air.patch similarity index 95% rename from patches/server/0467-Optimize-WorldBorder-collision-checks-and-air.patch rename to patches/server/0466-Optimize-WorldBorder-collision-checks-and-air.patch index 059933d95d..e138e957ad 100644 --- a/patches/server/0467-Optimize-WorldBorder-collision-checks-and-air.patch +++ b/patches/server/0466-Optimize-WorldBorder-collision-checks-and-air.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Optimize WorldBorder collision checks and air diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 160c5d395807a96a733e9ad2209d4f57337af76e..dbaa2d9a067bf03f177ac57ee1f0eb156c779314 100644 +index 890502222191ce1ab5a598bf040fc462fc915e31..19a6b2c686e6421624282d0536dfdd3320da4ec6 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -1047,7 +1047,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -1070,7 +1070,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n AABB axisalignedbb = this.getBoundingBox(); CollisionContext voxelshapecollision = CollisionContext.of(this); VoxelShape voxelshape = this.level.getWorldBorder().getCollisionShape(); diff --git a/patches/server/0468-Fix-Per-World-Difficulty-Remembering-Difficulty.patch b/patches/server/0467-Fix-Per-World-Difficulty-Remembering-Difficulty.patch similarity index 96% rename from patches/server/0468-Fix-Per-World-Difficulty-Remembering-Difficulty.patch rename to patches/server/0467-Fix-Per-World-Difficulty-Remembering-Difficulty.patch index bf265539ae..3a1e56c645 100644 --- a/patches/server/0468-Fix-Per-World-Difficulty-Remembering-Difficulty.patch +++ b/patches/server/0467-Fix-Per-World-Difficulty-Remembering-Difficulty.patch @@ -8,10 +8,10 @@ makes it so that the server keeps the last difficulty used instead of restoring the server.properties every single load. diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index e477ccf2f17ffad0a3720f7ef7ccfe23b91bff3a..01394284fca2f56c1808e75f70a64c4cc2a196ea 100644 +index 5beed1f7a96df3dfcee236077c34a56c57d78c51..c5f7510056cf4f0889d5470b1f1a444555c6ed75 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1738,11 +1738,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop playersInMobSpawnRange; -@@ -470,12 +601,25 @@ public class ChunkHolder { +@@ -470,12 +601,18 @@ public class ChunkHolder { }); } + // Paper start + private boolean loadCallbackScheduled = false; + private boolean unloadCallbackScheduled = false; -+ static void ensureTickThread(final String reason) { -+ // TODO REMOVE -+ if (!org.bukkit.Bukkit.isPrimaryThread()) { -+ MinecraftServer.LOGGER.fatal("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable()); -+ throw new IllegalStateException(reason); -+ } -+ } + // Paper end + private void demoteFullChunk(ChunkMap playerchunkmap, ChunkHolder.FullChunkStatus playerchunk_state) { @@ -259,15 +252,15 @@ index 8245e07f6ecfd9dd997c8525b52c6eadd392e6f1..75fbcc3d9b3aca2738df0c7ce8c1a0b1 } protected void updateFutures(ChunkMap chunkStorage, Executor executor) { -+ ensureTickThread("Async ticket level update"); // Paper ++ io.papermc.paper.util.TickThread.ensureTickThread("Async ticket level update"); // Paper ChunkStatus chunkstatus = ChunkHolder.getStatus(this.oldTicketLevel); ChunkStatus chunkstatus1 = ChunkHolder.getStatus(this.ticketLevel); boolean flag = this.oldTicketLevel <= ChunkMap.MAX_CHUNK_DISTANCE; -@@ -486,9 +630,22 @@ public class ChunkHolder { +@@ -486,9 +623,22 @@ public class ChunkHolder { // ChunkUnloadEvent: Called before the chunk is unloaded: isChunkLoaded is still true and chunk can still be modified by plugins. if (playerchunk_state.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && !playerchunk_state1.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { this.getFutureIfPresentUnchecked(ChunkStatus.FULL).thenAccept((either) -> { -+ ensureTickThread("Async full status chunk future completion"); // Paper ++ io.papermc.paper.util.TickThread.ensureTickThread("Async full status chunk future completion"); // Paper LevelChunk chunk = (LevelChunk)either.left().orElse(null); - if (chunk != null) { + if (chunk != null && chunk.wasLoadCallbackInvoked() && ChunkHolder.this.ticketLevel > 33) { // Paper - only invoke unload if load was called @@ -287,11 +280,11 @@ index 8245e07f6ecfd9dd997c8525b52c6eadd392e6f1..75fbcc3d9b3aca2738df0c7ce8c1a0b1 // Minecraft will apply the chunks tick lists to the world once the chunk got loaded, and then store the tick // lists again inside the chunk once the chunk becomes inaccessible and set the chunk's needsSaving flag. // These actions may however happen deferred, so we manually set the needsSaving flag already here. -@@ -545,12 +702,14 @@ public class ChunkHolder { +@@ -545,12 +695,14 @@ public class ChunkHolder { this.scheduleFullChunkPromotion(chunkStorage, this.fullChunkFuture, executor, ChunkHolder.FullChunkStatus.BORDER); // Paper start - cache ticking ready status this.fullChunkFuture.thenAccept(either -> { -+ ensureTickThread("Async full chunk future completion"); // Paper ++ io.papermc.paper.util.TickThread.ensureTickThread("Async full chunk future completion"); // Paper final Optional left = either.left(); if (left.isPresent() && ChunkHolder.this.fullChunkCreateCount == expectCreateCount) { // note: Here is a very good place to add callbacks to logic waiting on this. @@ -302,23 +295,23 @@ index 8245e07f6ecfd9dd997c8525b52c6eadd392e6f1..75fbcc3d9b3aca2738df0c7ce8c1a0b1 } }); this.updateChunkToSave(this.fullChunkFuture, "full"); -@@ -575,6 +734,7 @@ public class ChunkHolder { +@@ -575,6 +727,7 @@ public class ChunkHolder { this.scheduleFullChunkPromotion(chunkStorage, this.tickingChunkFuture, executor, ChunkHolder.FullChunkStatus.TICKING); // Paper start - cache ticking ready status this.tickingChunkFuture.thenAccept(either -> { -+ ensureTickThread("Async full chunk future completion"); // Paper ++ io.papermc.paper.util.TickThread.ensureTickThread("Async full chunk future completion"); // Paper either.ifLeft(chunk -> { // note: Here is a very good place to add callbacks to logic waiting on this. ChunkHolder.this.isTickingReady = true; -@@ -607,6 +767,7 @@ public class ChunkHolder { +@@ -615,6 +768,7 @@ public class ChunkHolder { this.scheduleFullChunkPromotion(chunkStorage, this.entityTickingChunkFuture, executor, ChunkHolder.FullChunkStatus.ENTITY_TICKING); // Paper start - cache ticking ready status this.entityTickingChunkFuture.thenAccept(either -> { -+ ensureTickThread("Async full chunk future completion"); // Paper ++ io.papermc.paper.util.TickThread.ensureTickThread("Async full chunk future completion"); // Paper either.ifLeft(chunk -> { ChunkHolder.this.isEntityTickingReady = true; - }); -@@ -624,16 +785,44 @@ public class ChunkHolder { + // Paper start - entity ticking chunk set +@@ -641,16 +795,45 @@ public class ChunkHolder { this.demoteFullChunk(chunkStorage, playerchunk_state1); } @@ -337,6 +330,7 @@ index 8245e07f6ecfd9dd997c8525b52c6eadd392e6f1..75fbcc3d9b3aca2738df0c7ce8c1a0b1 + ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY; + } + chunkMap.level.asyncChunkTaskManager.raisePriority(pos.x, pos.z, ioPriority); ++ chunkMap.level.getChunkSource().getLightEngine().queue.changePriority(pos.toLong(), this.queueLevel, priority); // Paper // Restore this in chunk priority later? + } + if (currRequestedPriority != newRequestedPriority) { + this.onLevelChange.onLevelChange(this.pos, () -> this.queueLevel, priority, p -> this.queueLevel = p); // use preferred priority @@ -349,7 +343,7 @@ index 8245e07f6ecfd9dd997c8525b52c6eadd392e6f1..75fbcc3d9b3aca2738df0c7ce8c1a0b1 // ChunkLoadEvent: Called after the chunk is loaded: isChunkLoaded returns true and chunk is ready to be modified by plugins. if (!playerchunk_state.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && playerchunk_state1.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { this.getFutureIfPresentUnchecked(ChunkStatus.FULL).thenAccept((either) -> { -+ ensureTickThread("Async full status chunk future completion"); // Paper ++ io.papermc.paper.util.TickThread.ensureTickThread("Async full status chunk future completion"); // Paper LevelChunk chunk = (LevelChunk)either.left().orElse(null); - if (chunk != null) { + if (chunk != null && ChunkHolder.this.oldTicketLevel <= 33 && !chunk.wasLoadCallbackInvoked()) { // Paper - ensure ticket level is set to loaded before calling, as now this can complete with ticket level > 33 @@ -367,10 +361,10 @@ index 8245e07f6ecfd9dd997c8525b52c6eadd392e6f1..75fbcc3d9b3aca2738df0c7ce8c1a0b1 } }).exceptionally((throwable) -> { diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 12f9f512dc8b9e094dab2caec7b6ddaf2a378ee9..11b743acfad319f536798204d525cda0813e8987 100644 +index 2651c8161ee54a9085d43eceaeb6d16e8b693b25..6fd20a7ebca33f852933af6dbacd4065c1f4e30b 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -149,6 +149,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -122,6 +122,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider public final ServerLevel level; private final ThreadedLevelLightEngine lightEngine; private final BlockableEventLoop mainThreadExecutor; @@ -378,7 +372,7 @@ index 12f9f512dc8b9e094dab2caec7b6ddaf2a378ee9..11b743acfad319f536798204d525cda0 public final ChunkGenerator generator; public final Supplier overworldDataStorage; private final PoiManager poiManager; -@@ -341,6 +342,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -347,6 +348,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.level = world; this.generator = chunkGenerator; this.mainThreadExecutor = mainThreadExecutor; @@ -394,32 +388,8 @@ index 12f9f512dc8b9e094dab2caec7b6ddaf2a378ee9..11b743acfad319f536798204d525cda0 ProcessorMailbox threadedmailbox = ProcessorMailbox.create(executor, "worldgen"); Objects.requireNonNull(mainThreadExecutor); -@@ -436,6 +446,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - this.playerViewDistanceTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets, - (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, - com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { -+ checkHighPriorityChunks(player); - if (newState.size() != 1) { - return; - } -@@ -454,7 +465,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - ChunkPos chunkPos = new ChunkPos(rangeX, rangeZ); - ChunkMap.this.level.getChunkSource().removeTicketAtLevel(TicketType.PLAYER, chunkPos, 31, chunkPos); // entity ticking level, TODO check on update -- }); -+ // Paper start -+ ChunkMap.this.level.getChunkSource().clearPriorityTickets(chunkPos); -+ }, -+ (player, prevPos, newPos) -> { -+ player.lastHighPriorityChecked = -1; // reset and recheck -+ checkHighPriorityChunks(player); -+ }); -+ // Paper end - this.playerViewDistanceNoTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets); - this.playerViewDistanceBroadcastMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets, - (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, -@@ -472,6 +490,116 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - // Paper end - no-tick view distance +@@ -482,6 +492,116 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + // Paper end - optimise PlayerChunkMap#isOutsideRange } + // Paper start - Chunk Prioritization @@ -535,7 +505,7 @@ index 12f9f512dc8b9e094dab2caec7b6ddaf2a378ee9..11b743acfad319f536798204d525cda0 // Paper start public void updatePlayerMobTypeMap(Entity entity) { if (!this.level.paperConfig.perPlayerMobSpawns) { -@@ -630,6 +758,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -597,6 +717,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider List>> list = Lists.newArrayList(); int j = centerChunk.x; int k = centerChunk.z; @@ -543,7 +513,7 @@ index 12f9f512dc8b9e094dab2caec7b6ddaf2a378ee9..11b743acfad319f536798204d525cda0 for (int l = -margin; l <= margin; ++l) { for (int i1 = -margin; i1 <= margin; ++i1) { -@@ -648,6 +777,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -615,6 +736,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider ChunkStatus chunkstatus = (ChunkStatus) distanceToStatus.apply(j1); CompletableFuture> completablefuture = playerchunk.getOrScheduleFuture(chunkstatus, this); @@ -558,7 +528,7 @@ index 12f9f512dc8b9e094dab2caec7b6ddaf2a378ee9..11b743acfad319f536798204d525cda0 list.add(completablefuture); } -@@ -1016,11 +1153,19 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -984,11 +1113,19 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider if (requiredStatus == ChunkStatus.EMPTY) { return this.scheduleChunkLoad(chunkcoordintpair); } else { @@ -579,7 +549,7 @@ index 12f9f512dc8b9e094dab2caec7b6ddaf2a378ee9..11b743acfad319f536798204d525cda0 if (optional.isPresent() && ((ChunkAccess) optional.get()).getStatus().isOrAfter(requiredStatus)) { CompletableFuture> completablefuture = requiredStatus.load(this.level, this.structureManager, this.lightEngine, (ichunkaccess) -> { -@@ -1032,6 +1177,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1000,6 +1137,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } else { return this.scheduleChunkGeneration(holder, requiredStatus); } @@ -587,7 +557,7 @@ index 12f9f512dc8b9e094dab2caec7b6ddaf2a378ee9..11b743acfad319f536798204d525cda0 } } -@@ -1091,14 +1237,24 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1059,14 +1197,24 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider }; CompletableFuture chunkSaveFuture = this.level.asyncChunkTaskManager.getChunkSaveFuture(pos.x, pos.z); @@ -617,7 +587,7 @@ index 12f9f512dc8b9e094dab2caec7b6ddaf2a378ee9..11b743acfad319f536798204d525cda0 return ret; // Paper end } -@@ -1147,7 +1303,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1115,7 +1263,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.releaseLightTicket(chunkcoordintpair); return CompletableFuture.completedFuture(Either.right(playerchunk_failure)); }); @@ -629,7 +599,7 @@ index 12f9f512dc8b9e094dab2caec7b6ddaf2a378ee9..11b743acfad319f536798204d525cda0 } protected void releaseLightTicket(ChunkPos pos) { -@@ -1230,7 +1389,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1198,7 +1349,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider long i = playerchunk.getPos().toLong(); Objects.requireNonNull(playerchunk); @@ -1021,10 +991,10 @@ index d94241bcca4f2fd5e464a860bd356af504dc68b7..1cc4e0a1f3d8235ef88b48e01ca8b78a } } diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 6d947343b6ff2f86a23ea669e371d5ecd9e903c0..d6f0c82b503a0fa73f6589fc4c331d27fbe27638 100644 +index e5317a994cb9b30293ad54b8fc537f703ef994dc..eb6a802ea12a19a058fb7e23b7418b8b426b4ca0 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -449,6 +449,26 @@ public class ServerChunkCache extends ChunkSource { +@@ -610,6 +610,26 @@ public class ServerChunkCache extends ChunkSource { public void removeTicketAtLevel(TicketType ticketType, ChunkPos chunkPos, int ticketLevel, T identifier) { this.distanceManager.removeTicketAtLevel(ticketType, chunkPos, ticketLevel, identifier); } @@ -1051,7 +1021,7 @@ index 6d947343b6ff2f86a23ea669e371d5ecd9e903c0..d6f0c82b503a0fa73f6589fc4c331d27 // Paper end - async chunk io @Nullable -@@ -489,6 +509,8 @@ public class ServerChunkCache extends ChunkSource { +@@ -650,6 +670,8 @@ public class ServerChunkCache extends ChunkSource { Objects.requireNonNull(completablefuture); if (!completablefuture.isDone()) { // Paper // Paper start - async chunk io/loading @@ -1060,7 +1030,7 @@ index 6d947343b6ff2f86a23ea669e371d5ecd9e903c0..d6f0c82b503a0fa73f6589fc4c331d27 this.level.asyncChunkTaskManager.raisePriority(x1, z1, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY); com.destroystokyo.paper.io.chunk.ChunkTaskManager.pushChunkWait(this.level, x1, z1); // Paper end -@@ -497,6 +519,8 @@ public class ServerChunkCache extends ChunkSource { +@@ -658,6 +680,8 @@ public class ServerChunkCache extends ChunkSource { chunkproviderserver_a.managedBlock(completablefuture::isDone); com.destroystokyo.paper.io.chunk.ChunkTaskManager.popChunkWait(); // Paper - async chunk debug this.level.timings.syncChunkLoad.stopTiming(); // Paper @@ -1069,7 +1039,7 @@ index 6d947343b6ff2f86a23ea669e371d5ecd9e903c0..d6f0c82b503a0fa73f6589fc4c331d27 } // Paper ichunkaccess = (ChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> { return ichunkaccess1; -@@ -570,10 +594,12 @@ public class ServerChunkCache extends ChunkSource { +@@ -731,10 +755,12 @@ public class ServerChunkCache extends ChunkSource { if (flag && !currentlyUnloading) { // CraftBukkit end this.distanceManager.addTicket(TicketType.UNKNOWN, chunkcoordintpair, l, chunkcoordintpair); @@ -1082,7 +1052,7 @@ index 6d947343b6ff2f86a23ea669e371d5ecd9e903c0..d6f0c82b503a0fa73f6589fc4c331d27 this.runDistanceManagerUpdates(); playerchunk = this.getVisibleChunkIfPresent(k); gameprofilerfiller.pop(); -@@ -582,8 +608,13 @@ public class ServerChunkCache extends ChunkSource { +@@ -743,8 +769,13 @@ public class ServerChunkCache extends ChunkSource { } } } @@ -1098,7 +1068,7 @@ index 6d947343b6ff2f86a23ea669e371d5ecd9e903c0..d6f0c82b503a0fa73f6589fc4c331d27 } private boolean chunkAbsent(@Nullable ChunkHolder holder, int maxLevel) { -@@ -635,6 +666,7 @@ public class ServerChunkCache extends ChunkSource { +@@ -796,6 +827,7 @@ public class ServerChunkCache extends ChunkSource { } public boolean runDistanceManagerUpdates() { @@ -1155,6 +1125,279 @@ index 009b6fe428db1a6e69403d8df32325f78e6b4461..1da049b171e26ac422fa0e56d8772577 for (int i = 0; i < this.getInventory().getContainerSize(); ++i) { ItemStack itemstack = this.getInventory().getItem(i); +diff --git a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java +index 2c4aa4b66d83b6e7a104479860b7982629c63c3b..833b6b2193cf08e123aabb344f2283730aed1bcd 100644 +--- a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java ++++ b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java +@@ -1,6 +1,7 @@ + package net.minecraft.server.level; + + import com.mojang.datafixers.util.Pair; ++import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; // Paper + import it.unimi.dsi.fastutil.objects.ObjectArrayList; + import it.unimi.dsi.fastutil.objects.ObjectList; + import it.unimi.dsi.fastutil.objects.ObjectListIterator; +@@ -16,6 +17,7 @@ import net.minecraft.util.thread.ProcessorMailbox; + import net.minecraft.world.level.ChunkPos; + import net.minecraft.world.level.LightLayer; + import net.minecraft.world.level.chunk.ChunkAccess; ++import net.minecraft.world.level.chunk.ChunkStatus; + import net.minecraft.world.level.chunk.DataLayer; + import net.minecraft.world.level.chunk.LevelChunkSection; + import net.minecraft.world.level.chunk.LightChunkGetter; +@@ -26,15 +28,140 @@ import org.apache.logging.log4j.Logger; + public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCloseable { + private static final Logger LOGGER = LogManager.getLogger(); + private final ProcessorMailbox taskMailbox; +- private final ObjectList> lightTasks = new ObjectArrayList<>(); +- private final ChunkMap chunkMap; ++ // Paper start ++ private static final int MAX_PRIORITIES = ChunkMap.MAX_CHUNK_DISTANCE + 2; ++ ++ static class ChunkLightQueue { ++ public boolean shouldFastUpdate; ++ java.util.ArrayDeque pre = new java.util.ArrayDeque(); ++ java.util.ArrayDeque post = new java.util.ArrayDeque(); ++ ++ ChunkLightQueue(long chunk) {} ++ } ++ ++ static class PendingLightTask { ++ long chunkId; ++ IntSupplier priority; ++ Runnable pre; ++ Runnable post; ++ boolean fastUpdate; ++ ++ public PendingLightTask(long chunkId, IntSupplier priority, Runnable pre, Runnable post, boolean fastUpdate) { ++ this.chunkId = chunkId; ++ this.priority = priority; ++ this.pre = pre; ++ this.post = post; ++ this.fastUpdate = fastUpdate; ++ } ++ } ++ ++ ++ // Retain the chunks priority level for queued light tasks ++ class LightQueue { ++ private int size = 0; ++ private final Long2ObjectLinkedOpenHashMap[] buckets = new Long2ObjectLinkedOpenHashMap[MAX_PRIORITIES]; ++ private final java.util.concurrent.ConcurrentLinkedQueue pendingTasks = new java.util.concurrent.ConcurrentLinkedQueue<>(); ++ private final java.util.concurrent.ConcurrentLinkedQueue priorityChanges = new java.util.concurrent.ConcurrentLinkedQueue<>(); ++ ++ private LightQueue() { ++ for (int i = 0; i < buckets.length; i++) { ++ buckets[i] = new Long2ObjectLinkedOpenHashMap<>(); ++ } ++ } ++ ++ public void changePriority(long pair, int currentPriority, int priority) { ++ this.priorityChanges.add(() -> { ++ ChunkLightQueue remove = this.buckets[currentPriority].remove(pair); ++ if (remove != null) { ++ ChunkLightQueue existing = this.buckets[Math.max(1, priority)].put(pair, remove); ++ if (existing != null) { ++ remove.pre.addAll(existing.pre); ++ remove.post.addAll(existing.post); ++ } ++ } ++ }); ++ } ++ ++ public final void addChunk(long chunkId, IntSupplier priority, Runnable pre, Runnable post) { ++ pendingTasks.add(new PendingLightTask(chunkId, priority, pre, post, true)); ++ tryScheduleUpdate(); ++ } ++ ++ public final void add(long chunkId, IntSupplier priority, ThreadedLevelLightEngine.TaskType type, Runnable run) { ++ pendingTasks.add(new PendingLightTask(chunkId, priority, type == TaskType.PRE_UPDATE ? run : null, type == TaskType.POST_UPDATE ? run : null, false)); ++ } ++ public final void add(PendingLightTask update) { ++ int priority = update.priority.getAsInt(); ++ ChunkLightQueue lightQueue = this.buckets[priority].computeIfAbsent(update.chunkId, ChunkLightQueue::new); ++ ++ if (update.pre != null) { ++ this.size++; ++ lightQueue.pre.add(update.pre); ++ } ++ if (update.post != null) { ++ this.size++; ++ lightQueue.post.add(update.post); ++ } ++ if (update.fastUpdate) { ++ lightQueue.shouldFastUpdate = true; ++ } ++ } ++ ++ public final boolean isEmpty() { ++ return this.size == 0 && this.pendingTasks.isEmpty(); ++ } ++ ++ public final int size() { ++ return this.size; ++ } ++ ++ public boolean poll(java.util.List pre, java.util.List post) { ++ PendingLightTask pending; ++ while ((pending = pendingTasks.poll()) != null) { ++ add(pending); ++ } ++ Runnable run; ++ while ((run = priorityChanges.poll()) != null) { ++ run.run(); ++ } ++ boolean hasWork = false; ++ Long2ObjectLinkedOpenHashMap[] buckets = this.buckets; ++ int priority = 0; ++ while (priority < MAX_PRIORITIES && !isEmpty()) { ++ Long2ObjectLinkedOpenHashMap bucket = buckets[priority]; ++ if (bucket.isEmpty()) { ++ priority++; ++ if (hasWork) { ++ return true; ++ } else { ++ continue; ++ } ++ } ++ ChunkLightQueue queue = bucket.removeFirst(); ++ this.size -= queue.pre.size() + queue.post.size(); ++ pre.addAll(queue.pre); ++ post.addAll(queue.post); ++ queue.pre.clear(); ++ queue.post.clear(); ++ hasWork = true; ++ if (queue.shouldFastUpdate) { ++ return true; ++ } ++ } ++ return hasWork; ++ } ++ } ++ ++ final LightQueue queue = new LightQueue(); ++ // Paper end ++ private final ChunkMap chunkMap; private final ChunkMap playerChunkMap; // Paper + private final ProcessorHandle> sorterMailbox; + private volatile int taskPerBatch = 5; + private final AtomicBoolean scheduled = new AtomicBoolean(); + + public ThreadedLevelLightEngine(LightChunkGetter chunkProvider, ChunkMap chunkStorage, boolean hasBlockLight, ProcessorMailbox processor, ProcessorHandle> executor) { + super(chunkProvider, true, hasBlockLight); +- this.chunkMap = chunkStorage; ++ this.chunkMap = chunkStorage; this.playerChunkMap = chunkMap; // Paper + this.sorterMailbox = executor; + this.taskMailbox = processor; + } +@@ -120,13 +247,9 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl + } + + private void addTask(int x, int z, IntSupplier completedLevelSupplier, ThreadedLevelLightEngine.TaskType stage, Runnable task) { +- this.sorterMailbox.tell(ChunkTaskPriorityQueueSorter.message(() -> { +- this.lightTasks.add(Pair.of(stage, task)); +- if (this.lightTasks.size() >= this.taskPerBatch) { +- this.runUpdate(); +- } +- +- }, ChunkPos.asLong(x, z), completedLevelSupplier)); ++ // Paper start - replace method ++ this.queue.add(ChunkPos.asLong(x, z), completedLevelSupplier, stage, task); ++ // Paper end + } + + @Override +@@ -142,8 +265,14 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl + + public CompletableFuture lightChunk(ChunkAccess chunk, boolean excludeBlocks) { + ChunkPos chunkPos = chunk.getPos(); +- chunk.setLightCorrect(false); +- this.addTask(chunkPos.x, chunkPos.z, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> { ++ // Paper start ++ //ichunkaccess.b(false); // Don't need to disable this ++ long pair = chunkPos.toLong(); ++ CompletableFuture future = new CompletableFuture<>(); ++ IntSupplier prioritySupplier = playerChunkMap.getChunkQueueLevel(pair); ++ boolean[] skippedPre = {false}; ++ this.queue.addChunk(pair, prioritySupplier, Util.name(() -> { ++ // Paper end + LevelChunkSection[] levelChunkSections = chunk.getSections(); + + for(int i = 0; i < chunk.getSectionsCount(); ++i) { +@@ -163,51 +292,45 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl + + }, () -> { + return "lightChunk " + chunkPos + " " + excludeBlocks; +- })); +- return CompletableFuture.supplyAsync(() -> { ++ // Paper start - merge the 2 together ++ }), () -> { ++ this.chunkMap.releaseLightTicket(chunkPos); // Paper - moved from below, we want to call this even when returning early ++ if (skippedPre[0]) return; // Paper - future's already complete + chunk.setLightCorrect(true); + super.retainData(chunkPos, false); +- this.chunkMap.releaseLightTicket(chunkPos); +- return chunk; +- }, (runnable) -> { +- this.addTask(chunkPos.x, chunkPos.z, ThreadedLevelLightEngine.TaskType.POST_UPDATE, runnable); ++ //this.chunkMap.releaseLightTicket(chunkPos); // Paper - moved up ++ future.complete(chunk); + }); ++ return future; ++ // Paper end + } + + public void tryScheduleUpdate() { +- if ((!this.lightTasks.isEmpty() || super.hasLightWork()) && this.scheduled.compareAndSet(false, true)) { ++ if ((!this.queue.isEmpty() || super.hasLightWork()) && this.scheduled.compareAndSet(false, true)) { // Paper + this.taskMailbox.tell(() -> { + this.runUpdate(); + this.scheduled.set(false); ++ tryScheduleUpdate(); // Paper - if we still have work to do, do it! + }); + } + + } + ++ // Paper start - replace impl ++ private final java.util.List pre = new java.util.ArrayList<>(); ++ private final java.util.List post = new java.util.ArrayList<>(); + private void runUpdate() { +- int i = Math.min(this.lightTasks.size(), this.taskPerBatch); +- ObjectListIterator> objectListIterator = this.lightTasks.iterator(); +- +- int j; +- for(j = 0; objectListIterator.hasNext() && j < i; ++j) { +- Pair pair = objectListIterator.next(); +- if (pair.getFirst() == ThreadedLevelLightEngine.TaskType.PRE_UPDATE) { +- pair.getSecond().run(); +- } ++ if (queue.poll(pre, post)) { ++ pre.forEach(Runnable::run); ++ pre.clear(); ++ super.runUpdates(Integer.MAX_VALUE, true, true); ++ post.forEach(Runnable::run); ++ post.clear(); ++ } else { ++ // might have level updates to go still ++ super.runUpdates(Integer.MAX_VALUE, true, true); + } +- +- objectListIterator.back(j); +- super.runUpdates(Integer.MAX_VALUE, true, true); +- +- for(int var5 = 0; objectListIterator.hasNext() && var5 < i; ++var5) { +- Pair pair2 = objectListIterator.next(); +- if (pair2.getFirst() == ThreadedLevelLightEngine.TaskType.POST_UPDATE) { +- pair2.getSecond().run(); +- } +- +- objectListIterator.remove(); +- } +- ++ // Paper end + } + + public void setTaskPerBatch(int taskBatchSize) { diff --git a/src/main/java/net/minecraft/server/level/Ticket.java b/src/main/java/net/minecraft/server/level/Ticket.java index f1128f0d4a9a0241ac6c9bc18dd13b431c616bb1..2b2b7851d5f68bcdb41d58bcc64740ba58bf1ef4 100644 --- a/src/main/java/net/minecraft/server/level/Ticket.java @@ -1224,7 +1467,7 @@ index e0c9857a922d8a3f421d7df0f056b82c4775390e..fefd3e4b7d9b2ec77dd2bc26298c3732 entityplayer1.setPos(entityplayer1.getX(), entityplayer1.getY() + 1.0D, entityplayer1.getZ()); } diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 73fc8ebd2ec207db2efc545f6bb304d53880b78f..e1b6a390105232500d7146cc456bf30912934904 100644 +index 3e1f34ed8c3b4bd4f6c3624332a5f2be62e196fe..8df955a0aa6cd0c7d10619fb63abd16af4754be8 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -223,7 +223,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n @@ -1266,10 +1509,10 @@ index a84b75a53a0324fab9aeb9b80bf74eb0a84ecd2e..d580f0375cce5e995c024f1b0cd4843b org.bukkit.event.world.ChunkUnloadEvent unloadEvent = new org.bukkit.event.world.ChunkUnloadEvent(this.bukkitChunk, this.isUnsaved()); server.getPluginManager().callEvent(unloadEvent); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 496bb91da1138fb6a4e004da1eab43e588c82592..ff1cb5f679f57988eb30b77fbcc5de2699a90dca 100644 +index 44f13b8bce4c6b9b3a74b2d9d868104feb3c24bf..4b70f3ad3a300dc099c15c70ba846e043d728e11 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -1912,6 +1912,12 @@ public class CraftWorld extends CraftRegionAccessor implements World { +@@ -1901,6 +1901,12 @@ public class CraftWorld extends CraftRegionAccessor implements World { return future; } diff --git a/patches/server/0488-Improve-Chunk-Status-Transition-Speed.patch b/patches/server/0487-Improve-Chunk-Status-Transition-Speed.patch similarity index 92% rename from patches/server/0488-Improve-Chunk-Status-Transition-Speed.patch rename to patches/server/0487-Improve-Chunk-Status-Transition-Speed.patch index 6007510277..2244d1b5b9 100644 --- a/patches/server/0488-Improve-Chunk-Status-Transition-Speed.patch +++ b/patches/server/0487-Improve-Chunk-Status-Transition-Speed.patch @@ -36,7 +36,7 @@ scenario / path: Previously would have hopped to SERVER around 12+ times there extra. diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java -index 75fbcc3d9b3aca2738df0c7ce8c1a0b157ff375a..0c6d513b6bcbd518fe4beee7acd7b96ce801c78e 100644 +index 4550614bf0026677ae4389d64d4c2bfd7161ba02..e91552020b96d039c5516ddd8e85eadb8ab89c3c 100644 --- a/src/main/java/net/minecraft/server/level/ChunkHolder.java +++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java @@ -244,6 +244,13 @@ public class ChunkHolder { @@ -54,10 +54,10 @@ index 75fbcc3d9b3aca2738df0c7ce8c1a0b157ff375a..0c6d513b6bcbd518fe4beee7acd7b96c public ChunkHolder(ChunkPos pos, int level, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) { this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size()); diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 11b743acfad319f536798204d525cda0813e8987..1de84204656d60e4dc91cc37661cb7c1573a22d2 100644 +index 6fd20a7ebca33f852933af6dbacd4065c1f4e30b..91364805047c055a8755b7a2900988c750589ff7 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -827,7 +827,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -786,7 +786,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider return either.mapLeft((list) -> { return (LevelChunk) list.get(list.size() / 2); }); @@ -66,7 +66,7 @@ index 11b743acfad319f536798204d525cda0813e8987..1de84204656d60e4dc91cc37661cb7c1 } @Nullable -@@ -1277,6 +1277,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1237,6 +1237,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider return "chunkGenerate " + requiredStatus.getName(); }); Executor executor = (runnable) -> { diff --git a/patches/server/0489-Optimize-NetworkManager-Exception-Handling.patch b/patches/server/0488-Optimize-NetworkManager-Exception-Handling.patch similarity index 100% rename from patches/server/0489-Optimize-NetworkManager-Exception-Handling.patch rename to patches/server/0488-Optimize-NetworkManager-Exception-Handling.patch diff --git a/patches/server/0490-Optimize-the-advancement-data-player-iteration-to-be.patch b/patches/server/0489-Optimize-the-advancement-data-player-iteration-to-be.patch similarity index 100% rename from patches/server/0490-Optimize-the-advancement-data-player-iteration-to-be.patch rename to patches/server/0489-Optimize-the-advancement-data-player-iteration-to-be.patch diff --git a/patches/server/0491-Fix-arrows-never-despawning-MC-125757.patch b/patches/server/0490-Fix-arrows-never-despawning-MC-125757.patch similarity index 100% rename from patches/server/0491-Fix-arrows-never-despawning-MC-125757.patch rename to patches/server/0490-Fix-arrows-never-despawning-MC-125757.patch diff --git a/patches/server/0492-Thread-Safe-Vanilla-Command-permission-checking.patch b/patches/server/0491-Thread-Safe-Vanilla-Command-permission-checking.patch similarity index 97% rename from patches/server/0492-Thread-Safe-Vanilla-Command-permission-checking.patch rename to patches/server/0491-Thread-Safe-Vanilla-Command-permission-checking.patch index e20621ed87..1506252fa5 100644 --- a/patches/server/0492-Thread-Safe-Vanilla-Command-permission-checking.patch +++ b/patches/server/0491-Thread-Safe-Vanilla-Command-permission-checking.patch @@ -9,7 +9,7 @@ to race conditions. Plus, .canUse we want to be safe for async anyways. diff --git a/src/main/java/com/mojang/brigadier/tree/CommandNode.java b/src/main/java/com/mojang/brigadier/tree/CommandNode.java -index aa3a1795850a419f624f14bd7c4daab0020779d0..e91d7dd7517c556d12541098878de9be6b0663ba 100644 +index 74af1b6158394d66fe470f9b7db741871f2bb534..30274979d8dafc7c0a374c3e6b1d1b5dbd6cfe4c 100644 --- a/src/main/java/com/mojang/brigadier/tree/CommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/CommandNode.java @@ -74,10 +74,10 @@ public abstract class CommandNode implements Comparable> { diff --git a/patches/server/0493-Move-range-check-for-block-placing-up.patch b/patches/server/0492-Move-range-check-for-block-placing-up.patch similarity index 100% rename from patches/server/0493-Move-range-check-for-block-placing-up.patch rename to patches/server/0492-Move-range-check-for-block-placing-up.patch diff --git a/patches/server/0495-Fix-SPIGOT-5989.patch b/patches/server/0493-Fix-SPIGOT-5989.patch similarity index 97% rename from patches/server/0495-Fix-SPIGOT-5989.patch rename to patches/server/0493-Fix-SPIGOT-5989.patch index f2057aefc6..286bb10bf2 100644 --- a/patches/server/0495-Fix-SPIGOT-5989.patch +++ b/patches/server/0493-Fix-SPIGOT-5989.patch @@ -10,7 +10,7 @@ This fixes that by checking if the modified spawn location is still at a respawn anchor. diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 4918f89401cdf6a85d8b71f3e3a6608239a388bd..83df832b1fe5be1c1114ce3ae10c1985bf564a0c 100644 +index fefd3e4b7d9b2ec77dd2bc26298c3732d651df32..476a3a317779fdecbced48caa85f6835c54eb018 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java @@ -79,6 +79,7 @@ import net.minecraft.world.level.GameRules; diff --git a/patches/server/0496-Fix-SPIGOT-5824-Bukkit-world-container-is-not-used.patch b/patches/server/0494-Fix-SPIGOT-5824-Bukkit-world-container-is-not-used.patch similarity index 100% rename from patches/server/0496-Fix-SPIGOT-5824-Bukkit-world-container-is-not-used.patch rename to patches/server/0494-Fix-SPIGOT-5824-Bukkit-world-container-is-not-used.patch diff --git a/patches/server/0494-Optimize-Light-Engine.patch b/patches/server/0494-Optimize-Light-Engine.patch deleted file mode 100644 index a424b1c7da..0000000000 --- a/patches/server/0494-Optimize-Light-Engine.patch +++ /dev/null @@ -1,1302 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Thu, 4 Jun 2020 22:43:29 -0400 -Subject: [PATCH] Optimize Light Engine - -Massive update to light to improve performance and chunk loading/generation. - -1) Massive bit packing/unpacking optimizations and inlining. - A lot of performance has to do with constant packing and unpacking of bits. - We now inline a most bit operations, and re-use base x/y/z bits in many places. - This helps with cpu level processing to just do all the math at once instead - of having to jump in and out of function calls. - - This much logic also is likely over the JVM Inline limit for JIT too. -2) Applied a few of JellySquid's Phosphor mod optimizations such as - - ensuring we don't notify neighbor chunks when neighbor chunk doesn't need to be notified - - reduce hasLight checks in initializing light, and prob some more, they are tagged JellySquid where phosphor influence was used. -3) Optimize hot path accesses to getting updating chunk to have less branching -4) Optimize getBlock accesses to have less branching, and less unpacking -5) Have a separate urgent bucket for chunk light tasks. These tasks will always cut in line over non blocking light tasks. -6) Retain chunk priority while light tasks are enqueued. So if a task comes in at high priority but the queue is full - of tasks already at a lower priority, before the task was simply added to the end. Now it can cut in line to the front. - this applies for both urgent and non urgent tasks. -7) Buffer non urgent tasks even if queueUpdate is called multiple times to improve efficiency. -8) Fix NPE risk that crashes server in getting nibble data - -diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java -index 0c6d513b6bcbd518fe4beee7acd7b96ce801c78e..e6cc5da2fd58bcc2809baeffee01040d0370a92c 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkHolder.java -+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java -@@ -806,6 +806,7 @@ public class ChunkHolder { - ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY; - } - chunkMap.level.asyncChunkTaskManager.raisePriority(pos.x, pos.z, ioPriority); -+ chunkMap.level.getChunkSource().getLightEngine().queue.changePriority(pos.toLong(), this.queueLevel, priority); // Paper // Restore this in chunk priority later? - } - if (currRequestedPriority != newRequestedPriority) { - this.onLevelChange.onLevelChange(this.pos, () -> this.queueLevel, priority, p -> this.queueLevel = p); // use preferred priority -diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 1de84204656d60e4dc91cc37661cb7c1573a22d2..97bf5120d680ab47778c65e28e2ea2954daac031 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -97,6 +97,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureMana - import net.minecraft.world.level.storage.DimensionDataStorage; - import net.minecraft.world.level.storage.LevelStorageSource; - import net.minecraft.world.phys.Vec3; -+import net.minecraft.world.level.storage.PrimaryLevelData; - import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet; // Paper - import org.apache.commons.lang3.mutable.MutableBoolean; - import org.apache.logging.log4j.LogManager; -@@ -324,6 +325,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - // Paper end - -+ private final java.util.concurrent.ExecutorService lightThread; // Paper - public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureManager structureManager, Executor executor, BlockableEventLoop mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier persistentStateManagerFactory, int viewDistance, boolean dsync) { - super(new File(session.getDimensionPath(world.dimension()), "region"), dataFixer, dsync); - //this.visibleChunks = this.updatingChunks.clone(); // Paper - no more cloning -@@ -358,7 +360,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - - this.progressListener = worldGenerationProgressListener; - this.chunkStatusListener = chunkStatusChangeListener; -- ProcessorMailbox lightthreaded; ProcessorMailbox threadedmailbox1 = lightthreaded = ProcessorMailbox.create(executor, "light"); // Paper -+ // Paper start - use light thread -+ ProcessorMailbox lightthreaded; ProcessorMailbox threadedmailbox1 = lightthreaded = ProcessorMailbox.create(lightThread = java.util.concurrent.Executors.newSingleThreadExecutor(r -> { -+ Thread thread = new Thread(r); -+ thread.setName(((PrimaryLevelData)level.getLevelData()).getLevelName() + " - Light"); -+ thread.setDaemon(true); -+ thread.setPriority(Thread.NORM_PRIORITY+1); -+ return thread; -+ }), "light"); -+ // Paper end - - this.queueSorter = new ChunkTaskPriorityQueueSorter(ImmutableList.of(threadedmailbox, mailbox, threadedmailbox1), executor, Integer.MAX_VALUE); - this.worldgenMailbox = this.queueSorter.getProcessor(threadedmailbox, false); -@@ -867,6 +877,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - @Override - public void close() throws IOException { - try { -+ this.lightThread.shutdown(); // Paper - this.queueSorter.close(); - this.level.asyncChunkTaskManager.close(true); // Paper - Required since we're closing regionfiles in the next line - this.poiManager.close(); -diff --git a/src/main/java/net/minecraft/server/level/SectionTracker.java b/src/main/java/net/minecraft/server/level/SectionTracker.java -index d3d6651eb51c852ed1d1eeb5689569d5308b246d..c2d36600a0081c78425868154bdcf7f42d549154 100644 ---- a/src/main/java/net/minecraft/server/level/SectionTracker.java -+++ b/src/main/java/net/minecraft/server/level/SectionTracker.java -@@ -15,13 +15,19 @@ public abstract class SectionTracker extends DynamicGraphMinFixedPoint { - - @Override - protected void checkNeighborsAfterUpdate(long id, int level, boolean decrease) { -+ // Paper start -+ int x = (int) (id >> 42); -+ int y = (int) (id << 44 >> 44); -+ int z = (int) (id << 22 >> 42); -+ // Paper end - for(int i = -1; i <= 1; ++i) { - for(int j = -1; j <= 1; ++j) { - for(int k = -1; k <= 1; ++k) { -- long l = SectionPos.offset(id, i, j, k); -- if (l != id) { -+ if (i == 0 && j == 0 && k == 0) continue; // Paper -+ long l = (((long) (x + i) & 4194303L) << 42) | (((long) (y + j) & 1048575L)) | (((long) (z + k) & 4194303L) << 20); -+ // if (l != id) { // Paper - checked above - this.checkNeighbor(id, l, level, decrease); -- } -+ //} // Paper - } - } - } -@@ -32,10 +38,15 @@ public abstract class SectionTracker extends DynamicGraphMinFixedPoint { - protected int getComputedLevel(long id, long excludedId, int maxLevel) { - int i = maxLevel; - -+ // Paper start -+ int x = (int) (id >> 42); -+ int y = (int) (id << 44 >> 44); -+ int z = (int) (id << 22 >> 42); -+ // Paper end - for(int j = -1; j <= 1; ++j) { - for(int k = -1; k <= 1; ++k) { - for(int l = -1; l <= 1; ++l) { -- long m = SectionPos.offset(id, j, k, l); -+ long m = (((long) (x + j) & 4194303L) << 42) | (((long) (y + k) & 1048575L)) | (((long) (z + l) & 4194303L) << 20); // Paper - if (m == id) { - m = Long.MAX_VALUE; - } -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index d6f0c82b503a0fa73f6589fc4c331d27fbe27638..db6a0246e83045015fc41f27e0aec0e07d1fb568 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -1074,7 +1074,7 @@ public class ServerChunkCache extends ChunkSource { - if (ServerChunkCache.this.runDistanceManagerUpdates()) { - return true; - } else { -- ServerChunkCache.this.lightEngine.tryScheduleUpdate(); -+ ServerChunkCache.this.lightEngine.tryScheduleUpdate(); // Paper - not needed - return super.pollTask() || execChunkTask; // Paper - } - } finally { -diff --git a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java -index 2c4aa4b66d83b6e7a104479860b7982629c63c3b..833b6b2193cf08e123aabb344f2283730aed1bcd 100644 ---- a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java -+++ b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java -@@ -1,6 +1,7 @@ - package net.minecraft.server.level; - - import com.mojang.datafixers.util.Pair; -+import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; // Paper - import it.unimi.dsi.fastutil.objects.ObjectArrayList; - import it.unimi.dsi.fastutil.objects.ObjectList; - import it.unimi.dsi.fastutil.objects.ObjectListIterator; -@@ -16,6 +17,7 @@ import net.minecraft.util.thread.ProcessorMailbox; - import net.minecraft.world.level.ChunkPos; - import net.minecraft.world.level.LightLayer; - import net.minecraft.world.level.chunk.ChunkAccess; -+import net.minecraft.world.level.chunk.ChunkStatus; - import net.minecraft.world.level.chunk.DataLayer; - import net.minecraft.world.level.chunk.LevelChunkSection; - import net.minecraft.world.level.chunk.LightChunkGetter; -@@ -26,15 +28,140 @@ import org.apache.logging.log4j.Logger; - public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCloseable { - private static final Logger LOGGER = LogManager.getLogger(); - private final ProcessorMailbox taskMailbox; -- private final ObjectList> lightTasks = new ObjectArrayList<>(); -- private final ChunkMap chunkMap; -+ // Paper start -+ private static final int MAX_PRIORITIES = ChunkMap.MAX_CHUNK_DISTANCE + 2; -+ -+ static class ChunkLightQueue { -+ public boolean shouldFastUpdate; -+ java.util.ArrayDeque pre = new java.util.ArrayDeque(); -+ java.util.ArrayDeque post = new java.util.ArrayDeque(); -+ -+ ChunkLightQueue(long chunk) {} -+ } -+ -+ static class PendingLightTask { -+ long chunkId; -+ IntSupplier priority; -+ Runnable pre; -+ Runnable post; -+ boolean fastUpdate; -+ -+ public PendingLightTask(long chunkId, IntSupplier priority, Runnable pre, Runnable post, boolean fastUpdate) { -+ this.chunkId = chunkId; -+ this.priority = priority; -+ this.pre = pre; -+ this.post = post; -+ this.fastUpdate = fastUpdate; -+ } -+ } -+ -+ -+ // Retain the chunks priority level for queued light tasks -+ class LightQueue { -+ private int size = 0; -+ private final Long2ObjectLinkedOpenHashMap[] buckets = new Long2ObjectLinkedOpenHashMap[MAX_PRIORITIES]; -+ private final java.util.concurrent.ConcurrentLinkedQueue pendingTasks = new java.util.concurrent.ConcurrentLinkedQueue<>(); -+ private final java.util.concurrent.ConcurrentLinkedQueue priorityChanges = new java.util.concurrent.ConcurrentLinkedQueue<>(); -+ -+ private LightQueue() { -+ for (int i = 0; i < buckets.length; i++) { -+ buckets[i] = new Long2ObjectLinkedOpenHashMap<>(); -+ } -+ } -+ -+ public void changePriority(long pair, int currentPriority, int priority) { -+ this.priorityChanges.add(() -> { -+ ChunkLightQueue remove = this.buckets[currentPriority].remove(pair); -+ if (remove != null) { -+ ChunkLightQueue existing = this.buckets[Math.max(1, priority)].put(pair, remove); -+ if (existing != null) { -+ remove.pre.addAll(existing.pre); -+ remove.post.addAll(existing.post); -+ } -+ } -+ }); -+ } -+ -+ public final void addChunk(long chunkId, IntSupplier priority, Runnable pre, Runnable post) { -+ pendingTasks.add(new PendingLightTask(chunkId, priority, pre, post, true)); -+ tryScheduleUpdate(); -+ } -+ -+ public final void add(long chunkId, IntSupplier priority, ThreadedLevelLightEngine.TaskType type, Runnable run) { -+ pendingTasks.add(new PendingLightTask(chunkId, priority, type == TaskType.PRE_UPDATE ? run : null, type == TaskType.POST_UPDATE ? run : null, false)); -+ } -+ public final void add(PendingLightTask update) { -+ int priority = update.priority.getAsInt(); -+ ChunkLightQueue lightQueue = this.buckets[priority].computeIfAbsent(update.chunkId, ChunkLightQueue::new); -+ -+ if (update.pre != null) { -+ this.size++; -+ lightQueue.pre.add(update.pre); -+ } -+ if (update.post != null) { -+ this.size++; -+ lightQueue.post.add(update.post); -+ } -+ if (update.fastUpdate) { -+ lightQueue.shouldFastUpdate = true; -+ } -+ } -+ -+ public final boolean isEmpty() { -+ return this.size == 0 && this.pendingTasks.isEmpty(); -+ } -+ -+ public final int size() { -+ return this.size; -+ } -+ -+ public boolean poll(java.util.List pre, java.util.List post) { -+ PendingLightTask pending; -+ while ((pending = pendingTasks.poll()) != null) { -+ add(pending); -+ } -+ Runnable run; -+ while ((run = priorityChanges.poll()) != null) { -+ run.run(); -+ } -+ boolean hasWork = false; -+ Long2ObjectLinkedOpenHashMap[] buckets = this.buckets; -+ int priority = 0; -+ while (priority < MAX_PRIORITIES && !isEmpty()) { -+ Long2ObjectLinkedOpenHashMap bucket = buckets[priority]; -+ if (bucket.isEmpty()) { -+ priority++; -+ if (hasWork) { -+ return true; -+ } else { -+ continue; -+ } -+ } -+ ChunkLightQueue queue = bucket.removeFirst(); -+ this.size -= queue.pre.size() + queue.post.size(); -+ pre.addAll(queue.pre); -+ post.addAll(queue.post); -+ queue.pre.clear(); -+ queue.post.clear(); -+ hasWork = true; -+ if (queue.shouldFastUpdate) { -+ return true; -+ } -+ } -+ return hasWork; -+ } -+ } -+ -+ final LightQueue queue = new LightQueue(); -+ // Paper end -+ private final ChunkMap chunkMap; private final ChunkMap playerChunkMap; // Paper - private final ProcessorHandle> sorterMailbox; - private volatile int taskPerBatch = 5; - private final AtomicBoolean scheduled = new AtomicBoolean(); - - public ThreadedLevelLightEngine(LightChunkGetter chunkProvider, ChunkMap chunkStorage, boolean hasBlockLight, ProcessorMailbox processor, ProcessorHandle> executor) { - super(chunkProvider, true, hasBlockLight); -- this.chunkMap = chunkStorage; -+ this.chunkMap = chunkStorage; this.playerChunkMap = chunkMap; // Paper - this.sorterMailbox = executor; - this.taskMailbox = processor; - } -@@ -120,13 +247,9 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl - } - - private void addTask(int x, int z, IntSupplier completedLevelSupplier, ThreadedLevelLightEngine.TaskType stage, Runnable task) { -- this.sorterMailbox.tell(ChunkTaskPriorityQueueSorter.message(() -> { -- this.lightTasks.add(Pair.of(stage, task)); -- if (this.lightTasks.size() >= this.taskPerBatch) { -- this.runUpdate(); -- } -- -- }, ChunkPos.asLong(x, z), completedLevelSupplier)); -+ // Paper start - replace method -+ this.queue.add(ChunkPos.asLong(x, z), completedLevelSupplier, stage, task); -+ // Paper end - } - - @Override -@@ -142,8 +265,14 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl - - public CompletableFuture lightChunk(ChunkAccess chunk, boolean excludeBlocks) { - ChunkPos chunkPos = chunk.getPos(); -- chunk.setLightCorrect(false); -- this.addTask(chunkPos.x, chunkPos.z, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> { -+ // Paper start -+ //ichunkaccess.b(false); // Don't need to disable this -+ long pair = chunkPos.toLong(); -+ CompletableFuture future = new CompletableFuture<>(); -+ IntSupplier prioritySupplier = playerChunkMap.getChunkQueueLevel(pair); -+ boolean[] skippedPre = {false}; -+ this.queue.addChunk(pair, prioritySupplier, Util.name(() -> { -+ // Paper end - LevelChunkSection[] levelChunkSections = chunk.getSections(); - - for(int i = 0; i < chunk.getSectionsCount(); ++i) { -@@ -163,51 +292,45 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl - - }, () -> { - return "lightChunk " + chunkPos + " " + excludeBlocks; -- })); -- return CompletableFuture.supplyAsync(() -> { -+ // Paper start - merge the 2 together -+ }), () -> { -+ this.chunkMap.releaseLightTicket(chunkPos); // Paper - moved from below, we want to call this even when returning early -+ if (skippedPre[0]) return; // Paper - future's already complete - chunk.setLightCorrect(true); - super.retainData(chunkPos, false); -- this.chunkMap.releaseLightTicket(chunkPos); -- return chunk; -- }, (runnable) -> { -- this.addTask(chunkPos.x, chunkPos.z, ThreadedLevelLightEngine.TaskType.POST_UPDATE, runnable); -+ //this.chunkMap.releaseLightTicket(chunkPos); // Paper - moved up -+ future.complete(chunk); - }); -+ return future; -+ // Paper end - } - - public void tryScheduleUpdate() { -- if ((!this.lightTasks.isEmpty() || super.hasLightWork()) && this.scheduled.compareAndSet(false, true)) { -+ if ((!this.queue.isEmpty() || super.hasLightWork()) && this.scheduled.compareAndSet(false, true)) { // Paper - this.taskMailbox.tell(() -> { - this.runUpdate(); - this.scheduled.set(false); -+ tryScheduleUpdate(); // Paper - if we still have work to do, do it! - }); - } - - } - -+ // Paper start - replace impl -+ private final java.util.List pre = new java.util.ArrayList<>(); -+ private final java.util.List post = new java.util.ArrayList<>(); - private void runUpdate() { -- int i = Math.min(this.lightTasks.size(), this.taskPerBatch); -- ObjectListIterator> objectListIterator = this.lightTasks.iterator(); -- -- int j; -- for(j = 0; objectListIterator.hasNext() && j < i; ++j) { -- Pair pair = objectListIterator.next(); -- if (pair.getFirst() == ThreadedLevelLightEngine.TaskType.PRE_UPDATE) { -- pair.getSecond().run(); -- } -+ if (queue.poll(pre, post)) { -+ pre.forEach(Runnable::run); -+ pre.clear(); -+ super.runUpdates(Integer.MAX_VALUE, true, true); -+ post.forEach(Runnable::run); -+ post.clear(); -+ } else { -+ // might have level updates to go still -+ super.runUpdates(Integer.MAX_VALUE, true, true); - } -- -- objectListIterator.back(j); -- super.runUpdates(Integer.MAX_VALUE, true, true); -- -- for(int var5 = 0; objectListIterator.hasNext() && var5 < i; ++var5) { -- Pair pair2 = objectListIterator.next(); -- if (pair2.getFirst() == ThreadedLevelLightEngine.TaskType.POST_UPDATE) { -- pair2.getSecond().run(); -- } -- -- objectListIterator.remove(); -- } -- -+ // Paper end - } - - public void setTaskPerBatch(int taskBatchSize) { -diff --git a/src/main/java/net/minecraft/world/level/chunk/DataLayer.java b/src/main/java/net/minecraft/world/level/chunk/DataLayer.java -index c561d69b4b903cd3625468b239cb1ace3e317700..c4acc7e0035d37167cf5a56f8d90ba17f85cbbe3 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/DataLayer.java -+++ b/src/main/java/net/minecraft/world/level/chunk/DataLayer.java -@@ -14,6 +14,13 @@ public class DataLayer { - @Nullable - protected byte[] data; - // Paper start -+ public static final DataLayer EMPTY_NIBBLE_ARRAY = new DataLayer() { -+ @Override -+ public byte[] getData() { -+ throw new IllegalStateException(); -+ } -+ }; -+ public long lightCacheKey = Long.MIN_VALUE; - public static byte[] EMPTY_NIBBLE = new byte[2048]; - private static final int nibbleBucketSizeMultiplier = Integer.getInteger("Paper.nibbleBucketSize", 3072); - private static final int maxPoolSize = Integer.getInteger("Paper.maxNibblePoolSize", (int) Math.min(6, Math.max(1, Runtime.getRuntime().maxMemory() / 1024 / 1024 / 1024)) * (nibbleBucketSizeMultiplier * 8)); -diff --git a/src/main/java/net/minecraft/world/level/lighting/BlockLightEngine.java b/src/main/java/net/minecraft/world/level/lighting/BlockLightEngine.java -index 37d7165dfd17da03428f8dbbbf95aa8005be289c..63fb8ccd47c21b4ef332daf418c9f70f2248be45 100644 ---- a/src/main/java/net/minecraft/world/level/lighting/BlockLightEngine.java -+++ b/src/main/java/net/minecraft/world/level/lighting/BlockLightEngine.java -@@ -22,9 +22,11 @@ public final class BlockLightEngine extends LayerLightEngine> 38); -+ int j = (int) ((blockPos << 52) >> 52); -+ int k = (int) ((blockPos << 26) >> 38); -+ // Paper end - BlockGetter blockGetter = this.chunkSource.getChunkForLighting(SectionPos.blockToSectionCoord(i), SectionPos.blockToSectionCoord(k)); - return blockGetter != null ? blockGetter.getLightEmission(this.pos.set(i, j, k)) : 0; - } -@@ -38,22 +40,33 @@ public final class BlockLightEngine extends LayerLightEngine= 15) { - return level; - } else { -- int i = Integer.signum(BlockPos.getX(targetId) - BlockPos.getX(sourceId)); -- int j = Integer.signum(BlockPos.getY(targetId) - BlockPos.getY(sourceId)); -- int k = Integer.signum(BlockPos.getZ(targetId) - BlockPos.getZ(sourceId)); -+ // Paper start -+ int ix = (int) (targetId >> 38); -+ int iy = (int) ((targetId << 52) >> 52); -+ int iz = (int) ((targetId << 26) >> 38); -+ int jx = (int) (sourceId >> 38); -+ int jy = (int) ((sourceId << 52) >> 52); -+ int jz = (int) ((sourceId << 26) >> 38); -+ int i = Integer.signum(ix - jx); -+ int j = Integer.signum(iy - jy); -+ int k = Integer.signum(iz - jz); -+ // Paper end - Direction direction = Direction.fromNormal(i, j, k); - if (direction == null) { - return 15; - } else { - //MutableInt mutableint = new MutableInt(); // Paper - share mutableint, single threaded -- BlockState blockState = this.getStateAndOpacity(targetId, mutableInt); -- if (mutableInt.getValue() >= 15) { -+ // Paper start -+ BlockState blockState = this.getBlockOptimized(ix, iy, iz, mutableInt); -+ int blockdLight = mutableInt.getValue(); -+ if (blockdLight >= 15) { -+ // Paper end - return 15; - } else { -- BlockState blockState2 = this.getStateAndOpacity(sourceId, (MutableInt)null); -+ BlockState blockState2 = this.getBlockOptimized(jx, jy, jz); // Paper - VoxelShape voxelShape = this.getShape(blockState2, sourceId, direction); - VoxelShape voxelShape2 = this.getShape(blockState, targetId, direction.getOpposite()); -- return Shapes.faceShapeOccludes(voxelShape, voxelShape2) ? 15 : level + Math.max(1, mutableInt.getValue()); -+ return Shapes.faceShapeOccludes(voxelShape, voxelShape2) ? 15 : level + Math.max(1, blockdLight); // Paper - } - } - } -@@ -61,10 +74,15 @@ public final class BlockLightEngine extends LayerLightEngine> 38); -+ int y = (int) ((id << 52) >> 52); -+ int z = (int) ((id << 26) >> 38); -+ long l = SectionPos.blockPosAsSectionLong(x, y, z); -+ // Paper end - - for(Direction direction : DIRECTIONS) { -- long m = BlockPos.offset(id, direction); -+ long m = BlockPos.getAdjacent(x, y, z, direction); // Paper - long n = SectionPos.blockToSection(m); - if (l == n || this.storage.storingLightForSection(n)) { - this.checkNeighbor(id, m, level, decrease); -@@ -87,22 +105,32 @@ public final class BlockLightEngine extends LayerLightEngine> 38); -+ int baseY = (int) ((id << 52) >> 52); -+ int baseZ = (int) ((id << 26) >> 38); -+ long l = SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ); -+ DataLayer dataLayer = this.storage.updating.getUpdatingOptimized(l); -+ // Paper end - - for(Direction direction : DIRECTIONS) { -- long m = BlockPos.offset(id, direction); -+ // Paper start -+ int newX = baseX + direction.getStepX(); -+ int newY = baseY + direction.getStepY(); -+ int newZ = baseZ + direction.getStepZ(); -+ long m = BlockPos.asLong(newX, newY, newZ); - if (m != excludedId) { -- long n = SectionPos.blockToSection(m); -+ long n = SectionPos.blockPosAsSectionLong(newX, newY, newZ); -+ // Paper end - DataLayer dataLayer2; - if (l == n) { - dataLayer2 = dataLayer; - } else { -- dataLayer2 = this.storage.getDataLayer(n, true); -+ dataLayer2 = this.storage.updating.getUpdatingOptimized(n); // Paper - } - - if (dataLayer2 != null) { -- int k = this.computeLevelFromNeighbor(m, id, this.getLevel(dataLayer2, m)); -+ int k = this.computeLevelFromNeighbor(m, id, this.getLevel(dataLayer2, newX, newY, newZ)); // Paper - if (i > k) { - i = k; - } -diff --git a/src/main/java/net/minecraft/world/level/lighting/BlockLightSectionStorage.java b/src/main/java/net/minecraft/world/level/lighting/BlockLightSectionStorage.java -index 314b46f0becd088d26956b45981217b128d539cb..d8e71e3759e02b3db5603302ad37656d0a72c2b2 100644 ---- a/src/main/java/net/minecraft/world/level/lighting/BlockLightSectionStorage.java -+++ b/src/main/java/net/minecraft/world/level/lighting/BlockLightSectionStorage.java -@@ -14,9 +14,14 @@ public class BlockLightSectionStorage extends LayerLightSectionStorage> 38); -+ int baseY = (int) ((blockPos << 52) >> 52); -+ int baseZ = (int) ((blockPos << 26) >> 38); -+ long l = net.minecraft.core.SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ); -+ DataLayer dataLayer = this.e_visible.lookup.apply(l); -+ return dataLayer == null ? 0 : dataLayer.get(baseX & 15, baseY & 15, baseZ & 15); -+ // Paper end - } - - protected static final class BlockDataLayerStorageMap extends DataLayerStorageMap { -diff --git a/src/main/java/net/minecraft/world/level/lighting/DataLayerStorageMap.java b/src/main/java/net/minecraft/world/level/lighting/DataLayerStorageMap.java -index 52682471adc13dffc0383fc4abacbd3397f3bb10..9d1bf2d68d8d630d5ecb0581df57a99eae9eff0d 100644 ---- a/src/main/java/net/minecraft/world/level/lighting/DataLayerStorageMap.java -+++ b/src/main/java/net/minecraft/world/level/lighting/DataLayerStorageMap.java -@@ -6,13 +6,18 @@ import net.minecraft.world.level.chunk.DataLayer; - - public abstract class DataLayerStorageMap> { - private static final int CACHE_SIZE = 2; -- private final long[] lastSectionKeys = new long[2]; -- private final DataLayer[] lastSections = new DataLayer[2]; -+ // private final long[] b = new long[2]; // Paper - unused -+ private final DataLayer[] lastSections = new DataLayer[]{DataLayer.EMPTY_NIBBLE_ARRAY, DataLayer.EMPTY_NIBBLE_ARRAY}; private final DataLayer[] cache = lastSections; // Paper - OBFHELPER - private boolean cacheEnabled; - protected final com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object data; // Paper - avoid copying light data - protected final boolean isVisible; // Paper - avoid copying light data -- java.util.function.Function lookup; // Paper - faster branchless lookup - -+ // Paper start - faster lookups with less branching, use interface to avoid boxing instead of Function -+ public final NibbleArrayAccess lookup; -+ public interface NibbleArrayAccess { -+ DataLayer apply(long id); -+ } -+ // Paper end - // Paper start - avoid copying light data - protected DataLayerStorageMap(com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object data, boolean isVisible) { - if (isVisible) { -@@ -20,12 +25,14 @@ public abstract class DataLayerStorageMap> { - } - this.data = data; - this.isVisible = isVisible; -+ // Paper end - avoid copying light data -+ // Paper start - faster lookups with less branching - if (isVisible) { - lookup = data::getVisibleAsync; - } else { -- lookup = data::getUpdating; -+ lookup = data.getUpdatingMap()::get; // jump straight the sub map - } -- // Paper end - avoid copying light data -+ // Paper end - this.clearCache(); - this.cacheEnabled = true; - } -@@ -35,7 +42,9 @@ public abstract class DataLayerStorageMap> { - public void copyDataLayer(long pos) { - if (this.isVisible) { throw new IllegalStateException("writing to visible data"); } // Paper - avoid copying light data - DataLayer updating = this.data.getUpdating(pos); // Paper - pool nibbles -- this.data.queueUpdate(pos, new DataLayer().markPoolSafe(updating.getCloneIfSet())); // Paper - avoid copying light data - pool safe clone -+ DataLayer nibblearray = new DataLayer().markPoolSafe(updating.getCloneIfSet()); // Paper -+ nibblearray.lightCacheKey = pos; // Paper -+ this.data.queueUpdate(pos, nibblearray); // Paper - avoid copying light data - pool safe clone - if (updating.cleaner != null) net.minecraft.server.MCUtil.scheduleTask(2, updating.cleaner, "Light Engine Release"); // Paper - delay clean incase anything holding ref was still using it - this.clearCache(); - } -@@ -44,33 +53,34 @@ public abstract class DataLayerStorageMap> { - return lookup.apply(chunkPos) != null; // Paper - avoid copying light data - } - -- @Nullable -- public final DataLayer getLayer(long chunkPos) { // Paper - final -- if (this.cacheEnabled) { -- for(int i = 0; i < 2; ++i) { -- if (chunkPos == this.lastSectionKeys[i]) { -- return this.lastSections[i]; -- } -- } -- } -+ // Paper start - less branching as we know we are using cache and updating -+ public final DataLayer getUpdatingOptimized(final long chunkPos) { // Paper - final -+ final DataLayer[] cache = this.cache; -+ if (cache[0].lightCacheKey == chunkPos) return cache[0]; -+ if (cache[1].lightCacheKey == chunkPos) return cache[1]; - -- DataLayer dataLayer = lookup.apply(chunkPos); // Paper - avoid copying light data -+ final DataLayer dataLayer = this.lookup.apply(chunkPos); // Paper - avoid copying light data - if (dataLayer == null) { - return null; - } else { -- if (this.cacheEnabled) { -- for(int j = 1; j > 0; --j) { -- this.lastSectionKeys[j] = this.lastSectionKeys[j - 1]; -- this.lastSections[j] = this.lastSections[j - 1]; -- } -- -- this.lastSectionKeys[0] = chunkPos; -- this.lastSections[0] = dataLayer; -- } -- -+ cache[1] = cache[0]; -+ cache[0] = dataLayer; - return dataLayer; - } - } -+ // Paper end -+ -+ @Nullable -+ public final DataLayer getLayer(final long chunkPos) { // Paper - final -+ // Paper start - optimize visible case or missed updating cases -+ if (this.cacheEnabled) { -+ // short circuit to optimized -+ return getUpdatingOptimized(chunkPos); -+ } -+ -+ return this.lookup.apply(chunkPos); -+ // Paper end -+ } - - @Nullable - public DataLayer removeLayer(long chunkPos) { -@@ -80,13 +90,14 @@ public abstract class DataLayerStorageMap> { - - public void setLayer(long pos, DataLayer data) { - if (this.isVisible) { throw new IllegalStateException("writing to visible data"); } // Paper - avoid copying light data -+ data.lightCacheKey = pos; // Paper - this.data.queueUpdate(pos, data); // Paper - avoid copying light data - } - - public void clearCache() { - for(int i = 0; i < 2; ++i) { -- this.lastSectionKeys[i] = Long.MAX_VALUE; -- this.lastSections[i] = null; -+ // this.b[i] = Long.MAX_VALUE; // Paper - Unused -+ this.lastSections[i] = DataLayer.EMPTY_NIBBLE_ARRAY; // Paper - } - - } -diff --git a/src/main/java/net/minecraft/world/level/lighting/LayerLightEngine.java b/src/main/java/net/minecraft/world/level/lighting/LayerLightEngine.java -index c5d0d8fb4c0344fbcc2ac87ba6d8027c29d51286..0952dde531aecd8ffc6eb72d4841362c5a6ebdcd 100644 ---- a/src/main/java/net/minecraft/world/level/lighting/LayerLightEngine.java -+++ b/src/main/java/net/minecraft/world/level/lighting/LayerLightEngine.java -@@ -10,6 +10,7 @@ import net.minecraft.world.level.ChunkPos; - import net.minecraft.world.level.LightLayer; - import net.minecraft.world.level.block.Blocks; - import net.minecraft.world.level.block.state.BlockState; -+import net.minecraft.world.level.chunk.ChunkAccess; - import net.minecraft.world.level.chunk.DataLayer; - import net.minecraft.world.level.chunk.LightChunkGetter; - import net.minecraft.world.phys.shapes.Shapes; -@@ -26,8 +27,35 @@ public abstract class LayerLightEngine, S exten - protected final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); - private static final int CACHE_SIZE = 2; - private final long[] lastChunkPos = new long[2]; -- private final BlockGetter[] lastChunk = new BlockGetter[2]; -+ private final ChunkAccess[] lastChunk = new ChunkAccess[2]; // Paper - -+ // Paper start - see fully commented out method below (look for Bedrock) -+ // optimized method with less branching for when scenarios arent needed. -+ // avoid using mutable version if can -+ protected final BlockState getBlockOptimized(int x, int y, int z, MutableInt mutableint) { -+ ChunkAccess iblockaccess = this.getChunk(x >> 4, z >> 4); -+ -+ if (iblockaccess == null) { -+ mutableint.setValue(16); -+ return Blocks.BEDROCK.defaultBlockState(); -+ } else { -+ this.pos.set(x, y, z); -+ BlockState iblockdata = iblockaccess.getType(x, y, z); -+ mutableint.setValue(iblockdata.getLightBlock(this.chunkSource.getLevel(), this.pos)); -+ return iblockdata.canOcclude() && iblockdata.useShapeForLightOcclusion() ? iblockdata : Blocks.AIR.defaultBlockState(); -+ } -+ } -+ protected final BlockState getBlockOptimized(int x, int y, int z) { -+ ChunkAccess iblockaccess = this.getChunk(x >> 4, z >> 4); -+ -+ if (iblockaccess == null) { -+ return Blocks.BEDROCK.defaultBlockState(); -+ } else { -+ BlockState iblockdata = iblockaccess.getType(x, y, z); -+ return iblockdata.canOcclude() && iblockdata.useShapeForLightOcclusion() ? iblockdata : Blocks.AIR.defaultBlockState(); -+ } -+ } -+ // Paper end - public LayerLightEngine(LightChunkGetter chunkProvider, LightLayer type, S lightStorage) { - super(16, 256, 8192); - this.chunkSource = chunkProvider; -@@ -46,7 +74,7 @@ public abstract class LayerLightEngine, S exten - } - - @Nullable -- private BlockGetter getChunk(int chunkX, int chunkZ) { -+ private ChunkAccess getChunk(int chunkX, int chunkZ) { // Paper - long l = ChunkPos.asLong(chunkX, chunkZ); - - for(int i = 0; i < 2; ++i) { -@@ -55,7 +83,7 @@ public abstract class LayerLightEngine, S exten - } - } - -- BlockGetter blockGetter = this.chunkSource.getChunkForLighting(chunkX, chunkZ); -+ ChunkAccess blockGetter = (ChunkAccess) this.chunkSource.getChunkForLighting(chunkX, chunkZ); // Paper - - for(int j = 1; j > 0; --j) { - this.lastChunkPos[j] = this.lastChunkPos[j - 1]; -@@ -72,35 +100,37 @@ public abstract class LayerLightEngine, S exten - Arrays.fill(this.lastChunk, (Object)null); - } - -- protected BlockState getStateAndOpacity(long pos, @Nullable MutableInt mutableInt) { -- if (pos == Long.MAX_VALUE) { -- if (mutableInt != null) { -- mutableInt.setValue(0); -- } -- -- return Blocks.AIR.defaultBlockState(); -- } else { -- int i = SectionPos.blockToSectionCoord(BlockPos.getX(pos)); -- int j = SectionPos.blockToSectionCoord(BlockPos.getZ(pos)); -- BlockGetter blockGetter = this.getChunk(i, j); -- if (blockGetter == null) { -- if (mutableInt != null) { -- mutableInt.setValue(16); -- } -- -- return Blocks.BEDROCK.defaultBlockState(); -- } else { -- this.pos.set(pos); -- BlockState blockState = blockGetter.getBlockState(this.pos); -- boolean bl = blockState.canOcclude() && blockState.useShapeForLightOcclusion(); -- if (mutableInt != null) { -- mutableInt.setValue(blockState.getLightBlock(this.chunkSource.getLevel(), this.pos)); -- } -- -- return bl ? blockState : Blocks.AIR.defaultBlockState(); -- } -- } -- } -+ // Paper start - comment out, see getBlockOptimized -+ // protected BlockState getStateAndOpacity(long pos, @Nullable MutableInt mutableInt) { -+ // if (pos == Long.MAX_VALUE) { -+ // if (mutableInt != null) { -+ // mutableInt.setValue(0); -+ // } -+ // -+ // return Blocks.AIR.defaultBlockState(); -+ // } else { -+ // int i = SectionPos.blockToSectionCoord(BlockPos.getX(pos)); -+ // int j = SectionPos.blockToSectionCoord(BlockPos.getZ(pos)); -+ // BlockGetter blockGetter = this.getChunk(i, j); -+ // if (blockGetter == null) { -+ // if (mutableInt != null) { -+ // mutableInt.setValue(16); -+ // } -+ // -+ // return Blocks.BEDROCK.defaultBlockState(); -+ // } else { -+ // this.pos.set(pos); -+ // BlockState blockState = blockGetter.getBlockState(this.pos); -+ // boolean bl = blockState.canOcclude() && blockState.useShapeForLightOcclusion(); -+ // if (mutableInt != null) { -+ // mutableInt.setValue(blockState.getLightBlock(this.chunkSource.getLevel(), this.pos)); -+ // } -+ // -+ // return bl ? blockState : Blocks.AIR.defaultBlockState(); -+ // } -+ // } -+ // } -+ // Paper end - - protected VoxelShape getShape(BlockState world, long pos, Direction facing) { - return world.canOcclude() ? world.getFaceOcclusionShape(this.chunkSource.getLevel(), this.pos.set(pos), facing) : Shapes.empty(); -@@ -133,8 +163,9 @@ public abstract class LayerLightEngine, S exten - return id == Long.MAX_VALUE ? 0 : 15 - this.storage.getStoredLevel(id); - } - -+ protected int getLevel(DataLayer section, int x, int y, int z) { return 15 - section.get(x & 15, y & 15, z & 15); } // Paper - x/y/z version of below - protected int getLevel(DataLayer section, long blockPos) { -- return 15 - section.get(SectionPos.sectionRelative(BlockPos.getX(blockPos)), SectionPos.sectionRelative(BlockPos.getY(blockPos)), SectionPos.sectionRelative(BlockPos.getZ(blockPos))); -+ return 15 - section.get((int) (blockPos >> 38) & 15, (int) ((blockPos << 52) >> 52) & 15, (int) ((blockPos << 26) >> 38) & 15); // Paper - } - - @Override -diff --git a/src/main/java/net/minecraft/world/level/lighting/LayerLightSectionStorage.java b/src/main/java/net/minecraft/world/level/lighting/LayerLightSectionStorage.java -index fd1cdb6e2023713f947b9497c605cf6f4bae8994..4db3264c45736faf92d560c7d18d892c1200b15d 100644 ---- a/src/main/java/net/minecraft/world/level/lighting/LayerLightSectionStorage.java -+++ b/src/main/java/net/minecraft/world/level/lighting/LayerLightSectionStorage.java -@@ -29,9 +29,9 @@ public abstract class LayerLightSectionStorage> - protected final LongSet toMarkNoData = new LongOpenHashSet(); - protected final LongSet toMarkData = new LongOpenHashSet(); - protected volatile M e_visible; protected final Object visibleUpdateLock = new Object(); // Paper - diff on change, should be "visible" - force compile fail on usage change -- protected final M updatingSectionData; -+ protected final M updatingSectionData; protected final M updating; // Paper - diff on change, should be "updating" - protected final LongSet changedSections = new LongOpenHashSet(); -- protected final LongSet sectionsAffectedByLightUpdates = new LongOpenHashSet(); -+ protected final LongSet sectionsAffectedByLightUpdates = new LongOpenHashSet(); LongSet dirty = sectionsAffectedByLightUpdates; // Paper - OBFHELPER - protected final Long2ObjectMap queuedSections = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); - private final LongSet untrustedSections = new LongOpenHashSet(); - private final LongSet columnsToRetainQueuedDataFor = new LongOpenHashSet(); -@@ -39,33 +39,33 @@ public abstract class LayerLightSectionStorage> - protected volatile boolean hasToRemove; - - protected LayerLightSectionStorage(LightLayer lightType, LightChunkGetter chunkProvider, M lightData) { -- super(3, 16, 256); -+ super(3, 256, 256); // Paper - bump expected size of level sets to improve collisions and reduce rehashing (seen a lot of it) - this.layer = lightType; - this.chunkSource = chunkProvider; -- this.updatingSectionData = lightData; -+ this.updatingSectionData = lightData; updating = lightData; // Paper - this.e_visible = lightData.copy(); // Paper - avoid copying light dat - this.e_visible.disableCache(); // Paper - avoid copying light dat - } - -- protected boolean storingLightForSection(long sectionPos) { -- return this.getDataLayer(sectionPos, true) != null; -+ protected final boolean storingLightForSection(long sectionPos) { // Paper - final to help inlining -+ return this.updating.getUpdatingOptimized(sectionPos) != null; // Paper - inline to avoid branching - } - - @Nullable - protected DataLayer getDataLayer(long sectionPos, boolean cached) { - // Paper start - avoid copying light data - if (cached) { -- return this.getDataLayer(this.updatingSectionData, sectionPos); -+ return this.updating.getUpdatingOptimized(sectionPos); - } else { - synchronized (this.visibleUpdateLock) { -- return this.getDataLayer(this.e_visible, sectionPos); -+ return this.e_visible.lookup.apply(sectionPos); - } - } - // Paper end - avoid copying light data - } - - @Nullable -- protected DataLayer getDataLayer(M storage, long sectionPos) { -+ protected final DataLayer getDataLayer(M storage, long sectionPos) { // Paper - return storage.getLayer(sectionPos); - } - -@@ -78,24 +78,56 @@ public abstract class LayerLightSectionStorage> - protected abstract int getLightValue(long blockPos); - - protected int getStoredLevel(long blockPos) { -- long l = SectionPos.blockToSection(blockPos); -- DataLayer dataLayer = this.getDataLayer(l, true); -- return dataLayer.get(SectionPos.sectionRelative(BlockPos.getX(blockPos)), SectionPos.sectionRelative(BlockPos.getY(blockPos)), SectionPos.sectionRelative(BlockPos.getZ(blockPos))); -+ // Paper start - reuse and inline math, use Optimized Updating path -+ final int x = (int) (blockPos >> 38); -+ final int y = (int) ((blockPos << 52) >> 52); -+ final int z = (int) ((blockPos << 26) >> 38); -+ long j = SectionPos.blockPosAsSectionLong(x, y, z); -+ DataLayer nibblearray = this.updating.getUpdatingOptimized(j); -+ // BUG: Sometimes returns null and crashes, try to recover, but to prevent crash just return no light. -+ if (nibblearray == null) { -+ nibblearray = this.e_visible.lookup.apply(j); -+ } -+ if (nibblearray == null) { -+ System.err.println("Null nibble, preventing crash " + BlockPos.of(blockPos)); -+ return 0; -+ } -+ -+ return nibblearray.get(x & 15, y & 15, z & 15); // Paper - inline operations -+ // Paper end - } - - protected void setStoredLevel(long blockPos, int value) { -- long l = SectionPos.blockToSection(blockPos); -+ // Paper start - cache part of the math done in loop below -+ int x = (int) (blockPos >> 38); -+ int y = (int) ((blockPos << 52) >> 52); -+ int z = (int) ((blockPos << 26) >> 38); -+ long l = SectionPos.blockPosAsSectionLong(x, y, z); -+ // Paper end - if (this.changedSections.add(l)) { - this.updatingSectionData.copyDataLayer(l); - } - - DataLayer dataLayer = this.getDataLayer(l, true); -- dataLayer.set(SectionPos.sectionRelative(BlockPos.getX(blockPos)), SectionPos.sectionRelative(BlockPos.getY(blockPos)), SectionPos.sectionRelative(BlockPos.getZ(blockPos)), value); -- -- for(int i = -1; i <= 1; ++i) { -- for(int j = -1; j <= 1; ++j) { -- for(int k = -1; k <= 1; ++k) { -- this.sectionsAffectedByLightUpdates.add(SectionPos.blockToSection(BlockPos.offset(blockPos, j, k, i))); -+ dataLayer.set(x & 15, y & 15, z & 15, value); // Paper - use already calculated x/y/z -+ -+ // Paper start - credit to JellySquid for a major optimization here: -+ /* -+ * An extremely important optimization is made here in regards to adding items to the pending notification set. The -+ * original implementation attempts to add the coordinate of every chunk which contains a neighboring block position -+ * even though a huge number of loop iterations will simply map to block positions within the same updating chunk. -+ * -+ * Our implementation here avoids this by pre-calculating the min/max chunk coordinates so we can iterate over only -+ * the relevant chunk positions once. This reduces what would always be 27 iterations to just 1-8 iterations. -+ * -+ * @reason Use faster implementation -+ * @author JellySquid -+ */ -+ for (int z2 = (z - 1) >> 4; z2 <= (z + 1) >> 4; ++z2) { -+ for (int x2 = (x - 1) >> 4; x2 <= (x + 1) >> 4; ++x2) { -+ for (int y2 = (y - 1) >> 4; y2 <= (y + 1) >> 4; ++y2) { -+ this.dirty.add(SectionPos.asLong(x2, y2, z2)); -+ // Paper end - } - } - } -@@ -136,17 +168,23 @@ public abstract class LayerLightSectionStorage> - } - - if (i >= 2 && level != 2) { -- if (this.toRemove.contains(id)) { -- this.toRemove.remove(id); -- } else { -+ if (!this.toRemove.remove(id)) { // Paper - remove useless contains - credit to JellySquid -+ //this.toRemove.remove(id); // Paper -+ //} else { // Paper - this.updatingSectionData.setLayer(id, this.createDataLayer(id)); - this.changedSections.add(id); - this.onNodeAdded(id); - -- for(int j = -1; j <= 1; ++j) { -- for(int k = -1; k <= 1; ++k) { -- for(int l = -1; l <= 1; ++l) { -- this.sectionsAffectedByLightUpdates.add(SectionPos.blockToSection(BlockPos.offset(id, k, l, j))); -+ // Paper start - reuse x/y/z and only notify valid chunks - Credit to JellySquid (See above method for notes) -+ int x = (int) (id >> 38); -+ int y = (int) ((id << 52) >> 52); -+ int z = (int) ((id << 26) >> 38); -+ -+ for (int z2 = (z - 1) >> 4; z2 <= (z + 1) >> 4; ++z2) { -+ for (int x2 = (x - 1) >> 4; x2 <= (x + 1) >> 4; ++x2) { -+ for (int y2 = (y - 1) >> 4; y2 <= (y + 1) >> 4; ++y2) { -+ this.dirty.add(SectionPos.asLong(x2, y2, z2)); -+ // Paper end - } - } - } -@@ -171,9 +209,9 @@ public abstract class LayerLightSectionStorage> - return SectionPos.blockToSection(mx) == sectionPos; - }); - } else { -- int i = SectionPos.sectionToBlockCoord(SectionPos.x(sectionPos)); -- int j = SectionPos.sectionToBlockCoord(SectionPos.y(sectionPos)); -- int k = SectionPos.sectionToBlockCoord(SectionPos.z(sectionPos)); -+ int i = (int) (sectionPos >> 42) << 4; // Paper - inline -+ int j = (int) (sectionPos << 44 >> 44) << 4; // Paper - inline -+ int k = (int) (sectionPos << 22 >> 42) << 4; // Paper - inline - - for(int l = 0; l < 16; ++l) { - for(int m = 0; m < 16; ++m) { -@@ -215,11 +253,12 @@ public abstract class LayerLightSectionStorage> - this.toRemove.clear(); - this.hasToRemove = false; - -+ DataLayer test = null; // Paper - for(Entry entry : this.queuedSections.long2ObjectEntrySet()) { - long n = entry.getLongKey(); -- if (this.storingLightForSection(n)) { -+ if ((test = this.updating.getUpdatingOptimized(n)) != null) { // Paper - dont look up data layer twice - DataLayer dataLayer3 = entry.getValue(); -- if (this.updatingSectionData.getLayer(n) != dataLayer3) { -+ if (test != dataLayer3) { // Paper - this.clearQueuedSectionBlocks(lightProvider, n); - this.updatingSectionData.setLayer(n, dataLayer3); - this.changedSections.add(n); -@@ -254,12 +293,17 @@ public abstract class LayerLightSectionStorage> - - private void checkEdgesForSection(LayerLightEngine lightProvider, long sectionPos) { - if (this.storingLightForSection(sectionPos)) { -- int i = SectionPos.sectionToBlockCoord(SectionPos.x(sectionPos)); -- int j = SectionPos.sectionToBlockCoord(SectionPos.y(sectionPos)); -- int k = SectionPos.sectionToBlockCoord(SectionPos.z(sectionPos)); -+ // Paper start -+ int secX = (int) (sectionPos >> 42); -+ int secY = (int) (sectionPos << 44 >> 44); -+ int secZ = (int) (sectionPos << 22 >> 42); -+ int i = secX << 4; // baseX -+ int j = secY << 4; // baseY -+ int k = secZ << 4; // baseZ -+ // Paper end - - for(Direction direction : DIRECTIONS) { -- long l = SectionPos.offset(sectionPos, direction); -+ long l = SectionPos.getAdjacentFromSectionPos(secX, secY, secZ, direction); - if (!this.queuedSections.containsKey(l) && this.storingLightForSection(l)) { - for(int m = 0; m < 16; ++m) { - for(int n = 0; n < 16; ++n) { -diff --git a/src/main/java/net/minecraft/world/level/lighting/SkyLightEngine.java b/src/main/java/net/minecraft/world/level/lighting/SkyLightEngine.java -index d122475c1a9d340046c478087d3ff5bf1ff8932c..a3189dd7b537deb4a30a32c8e9891b07d04af540 100644 ---- a/src/main/java/net/minecraft/world/level/lighting/SkyLightEngine.java -+++ b/src/main/java/net/minecraft/world/level/lighting/SkyLightEngine.java -@@ -4,6 +4,7 @@ import net.minecraft.core.BlockPos; - import net.minecraft.core.Direction; - import net.minecraft.core.SectionPos; - import net.minecraft.world.level.LightLayer; -+import net.minecraft.world.level.block.Blocks; - import net.minecraft.world.level.block.state.BlockState; - import net.minecraft.world.level.chunk.DataLayer; - import net.minecraft.world.level.chunk.LightChunkGetter; -@@ -27,32 +28,37 @@ public final class SkyLightEngine extends LayerLightEngine= 15) { -+ // Paper start - use x/y/z and optimized block lookup -+ int jx = (int) (targetId >> 38); -+ int jy = (int) ((targetId << 52) >> 52); -+ int jz = (int) ((targetId << 26) >> 38); -+ BlockState blockState = this.getBlockOptimized(jx, jy, jz, mutableInt); -+ int blockedLight = mutableInt.getValue(); -+ if (blockedLight >= 15) { -+ // Paper end - return 15; - } else { -- int i = BlockPos.getX(sourceId); -- int j = BlockPos.getY(sourceId); -- int k = BlockPos.getZ(sourceId); -- int l = BlockPos.getX(targetId); -- int m = BlockPos.getY(targetId); -- int n = BlockPos.getZ(targetId); -- int o = Integer.signum(l - i); -- int p = Integer.signum(m - j); -- int q = Integer.signum(n - k); -+ // Paper start - inline math -+ int i = (int) (sourceId >> 38); -+ int j = (int) ((sourceId << 52) >> 52); -+ int k = (int) ((sourceId << 26) >> 38); -+ boolean bl = i == jx && k == jz; -+ int o = Integer.signum(jx - i); -+ int p = Integer.signum(jy - j); -+ int q = Integer.signum(jz - k); -+ // Paper end - Direction direction = Direction.fromNormal(o, p, q); - if (direction == null) { - throw new IllegalStateException(String.format("Light was spread in illegal direction %d, %d, %d", o, p, q)); - } else { -- BlockState blockState2 = this.getStateAndOpacity(sourceId, (MutableInt)null); -+ BlockState blockState2 = sourceId == Long.MAX_VALUE ? net.minecraft.world.level.block.Blocks.AIR.defaultBlockState() : this.getBlockOptimized(i, j, k); // Paper - VoxelShape voxelShape = this.getShape(blockState2, sourceId, direction); - VoxelShape voxelShape2 = this.getShape(blockState, targetId, direction.getOpposite()); - if (Shapes.faceShapeOccludes(voxelShape, voxelShape2)) { - return 15; - } else { -- boolean bl = i == l && k == n; -- boolean bl2 = bl && j > m; -- return bl2 && level == 0 && mutableInt.getValue() == 0 ? 0 : level + Math.max(1, mutableInt.getValue()); -+ boolean bl2 = bl && j > jy; // Paper rename vars to jy from m -+ return bl2 && level == 0 && blockedLight == 0 ? 0 : level + Math.max(1, blockedLight); // Paper - } - } - } -@@ -64,10 +70,14 @@ public final class SkyLightEngine extends LayerLightEngine> 38); -+ int baseY = (int) ((id << 52) >> 52); -+ int baseZ = (int) ((id << 26) >> 38); -+ long l = SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ); -+ int j = baseY & 15; -+ int k = baseY >> 4; -+ // Paper end - int m; - if (j != 0) { - m = 0; -@@ -79,14 +89,15 @@ public final class SkyLightEngine extends LayerLightEngine> 38); -+ int baseY = (int) ((id << 52) >> 52); -+ int baseZ = (int) ((id << 26) >> 38); -+ long l = SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ); -+ DataLayer dataLayer = this.storage.updating.getUpdatingOptimized(l); -+ // Paper end - - for(Direction direction : DIRECTIONS) { -- long m = BlockPos.offset(id, direction); -+ // Paper start -+ int newX = baseX + direction.getStepX(); -+ int newY = baseY + direction.getStepY(); -+ int newZ = baseZ + direction.getStepZ(); -+ long m = BlockPos.asLong(newX, newY, newZ); - if (m != excludedId) { -- long n = SectionPos.blockToSection(m); -+ long n = SectionPos.blockPosAsSectionLong(newX, newY, newZ); -+ // Paper end - DataLayer dataLayer2; - if (l == n) { - dataLayer2 = dataLayer; - } else { -- dataLayer2 = this.storage.getDataLayer(n, true); -+ dataLayer2 = this.storage.updating.getUpdatingOptimized(n); // Paper - } - - int j; - if (dataLayer2 != null) { -- j = this.getLevel(dataLayer2, m); -+ j = this.getLevel(dataLayer2, newX, newY, newZ); // Paper - } else { - if (direction == Direction.DOWN) { - continue; -diff --git a/src/main/java/net/minecraft/world/level/lighting/SkyLightSectionStorage.java b/src/main/java/net/minecraft/world/level/lighting/SkyLightSectionStorage.java -index ec3837a64e8ac6892028611d57a111a7fd5c58f7..006a4b02df0f7d9229b1f3791d4f3b9243919c18 100644 ---- a/src/main/java/net/minecraft/world/level/lighting/SkyLightSectionStorage.java -+++ b/src/main/java/net/minecraft/world/level/lighting/SkyLightSectionStorage.java -@@ -30,7 +30,12 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage> 38); -+ int baseY = (int) ((l << 52) >> 52); -+ int baseZ = (int) ((l << 26) >> 38); -+ long m = SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ); -+ // Paper end - int i = SectionPos.y(m); - synchronized (this.visibleUpdateLock) { // Paper - avoid copying light data - SkyLightSectionStorage.SkyDataLayerStorageMap skyDataLayerStorageMap = (SkyLightSectionStorage.SkyDataLayerStorageMap) this.e_visible; // Paper - avoid copying light data - must be after lock acquire -@@ -49,7 +54,7 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage> 52) & 15, (int) baseZ & 15); - } else { - return bl && !this.lightOnInSection(m) ? 0 : 15; - } -@@ -157,7 +162,7 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage> 42) << 4; // Paper -+ int baseY = (int) (l << 44 >> 44) << 4; // Paper -+ int baseZ = (int) (l << 22 >> 42) << 4; // Paper - int i = this.getLevel(l); - if (i != 2 && !this.sectionsToRemoveSourcesFrom.contains(l) && this.sectionsWithSources.add(l)) { - if (i == 1) { -@@ -197,13 +205,15 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage> 42) << 4; // Paper -+ int baseY = (int) (af << 44 >> 44) << 4; // Paper -+ int baseZ = (int) (af << 22 >> 42) << 4; // Paper - if (this.sectionsWithSources.remove(af) && this.storingLightForSection(af)) { - for(int ag = 0; ag < 16; ++ag) { - for(int ah = 0; ah < 16; ++ah) { -- long ai = BlockPos.asLong(SectionPos.sectionToBlockCoord(SectionPos.x(af), ag), SectionPos.sectionToBlockCoord(SectionPos.y(af), 15), SectionPos.sectionToBlockCoord(SectionPos.z(af), ah)); -+ long ai = BlockPos.asLong(baseX + ag, baseY + 16 - 1, baseZ + ah); // Paper - lightProvider.checkEdge(Long.MAX_VALUE, ai, 15, false); - } - } diff --git a/patches/server/0497-Fix-SPIGOT-5885-Unable-to-disable-advancements.patch b/patches/server/0495-Fix-SPIGOT-5885-Unable-to-disable-advancements.patch similarity index 100% rename from patches/server/0497-Fix-SPIGOT-5885-Unable-to-disable-advancements.patch rename to patches/server/0495-Fix-SPIGOT-5885-Unable-to-disable-advancements.patch diff --git a/patches/server/0498-Fix-AdvancementDataPlayer-leak-due-from-quitting-ear.patch b/patches/server/0496-Fix-AdvancementDataPlayer-leak-due-from-quitting-ear.patch similarity index 100% rename from patches/server/0498-Fix-AdvancementDataPlayer-leak-due-from-quitting-ear.patch rename to patches/server/0496-Fix-AdvancementDataPlayer-leak-due-from-quitting-ear.patch diff --git a/patches/server/0499-Add-missing-strikeLighting-call-to-World-spigot-stri.patch b/patches/server/0497-Add-missing-strikeLighting-call-to-World-spigot-stri.patch similarity index 83% rename from patches/server/0499-Add-missing-strikeLighting-call-to-World-spigot-stri.patch rename to patches/server/0497-Add-missing-strikeLighting-call-to-World-spigot-stri.patch index 9737676ef9..f98d2cae6a 100644 --- a/patches/server/0499-Add-missing-strikeLighting-call-to-World-spigot-stri.patch +++ b/patches/server/0497-Add-missing-strikeLighting-call-to-World-spigot-stri.patch @@ -6,10 +6,10 @@ Subject: [PATCH] Add missing strikeLighting call to diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index ff1cb5f679f57988eb30b77fbcc5de2699a90dca..ffb512ac2e2f827cf4079e9b24dd88e363eb95ee 100644 +index 4b70f3ad3a300dc099c15c70ba846e043d728e11..2606c4009c92e6b781d21de18ca6dbb809ec6215 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -1983,6 +1983,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { +@@ -1972,6 +1972,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { lightning.moveTo( loc.getX(), loc.getY(), loc.getZ() ); lightning.visualOnly = true; lightning.isSilent = isSilent; diff --git a/patches/server/0500-Incremental-player-saving.patch b/patches/server/0498-Incremental-player-saving.patch similarity index 89% rename from patches/server/0500-Incremental-player-saving.patch rename to patches/server/0498-Incremental-player-saving.patch index ed99352548..0e5026a98c 100644 --- a/patches/server/0500-Incremental-player-saving.patch +++ b/patches/server/0498-Incremental-player-saving.patch @@ -5,14 +5,13 @@ Subject: [PATCH] Incremental player saving diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index de8b9be11b996f9d1eff30fa037cfa1823eff21b..b0fe3f0ac4c706c4b64f2778fcb8d6c26e585d3c 100644 +index 9a17483c4c44e5022fccfe42845e4060c1703119..154222698ba1855283f8378b996cbfcc8bbdf81c 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java -@@ -458,4 +458,15 @@ public class PaperConfig { - allowPistonDuplication = getBoolean("settings.unsupported-settings.allow-piston-duplication", config.getBoolean("settings.unsupported-settings.allow-tnt-duplication", false)); +@@ -458,4 +458,14 @@ public class PaperConfig { set("settings.unsupported-settings.allow-tnt-duplication", null); } -+ + + public static int playerAutoSaveRate = -1; + public static int maxPlayerAutoSavePerTick = 10; + private static void playerAutoSaveRate() { @@ -25,10 +24,10 @@ index de8b9be11b996f9d1eff30fa037cfa1823eff21b..b0fe3f0ac4c706c4b64f2778fcb8d6c2 + } } diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 01394284fca2f56c1808e75f70a64c4cc2a196ea..de2c21486f07bc2dbbaa9598354cd4ed39cb94ca 100644 +index c5f7510056cf4f0889d5470b1f1a444555c6ed75..47fbfe7cc1261d65264bb1897c57f72a7b4df46e 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -978,7 +978,6 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0 && this.tickCount % this.autosavePeriod == 0) { // CraftBukkit // Paper - move down // MinecraftServer.LOGGER.debug("Autosave started"); // Paper serverAutoSave = (autosavePeriod > 0 && this.tickCount % autosavePeriod == 0); // Paper @@ -55,7 +54,7 @@ index 01394284fca2f56c1808e75f70a64c4cc2a196ea..de2c21486f07bc2dbbaa9598354cd4ed } // Paper start for (ServerLevel level : this.getAllLevels()) { diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 6cf83d9b1e43ade17cb67843dbdc11937eda1e08..f3e4b9f302fbc4b615a5e735d5aab0f403e16903 100644 +index 1da049b171e26ac422fa0e56d8772577d5c768fa..9015c0e6f277b299a55cd772cc7cf405421f6622 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -175,6 +175,7 @@ public class ServerPlayer extends Player { diff --git a/patches/server/0501-Fix-some-rails-connecting-improperly.patch b/patches/server/0499-Fix-some-rails-connecting-improperly.patch similarity index 100% rename from patches/server/0501-Fix-some-rails-connecting-improperly.patch rename to patches/server/0499-Fix-some-rails-connecting-improperly.patch diff --git a/patches/server/0502-Fix-MC-187716-Use-configured-height.patch b/patches/server/0500-Fix-MC-187716-Use-configured-height.patch similarity index 100% rename from patches/server/0502-Fix-MC-187716-Use-configured-height.patch rename to patches/server/0500-Fix-MC-187716-Use-configured-height.patch diff --git a/patches/server/0503-Fix-regex-mistake-in-CB-NBT-int-deserialization.patch b/patches/server/0501-Fix-regex-mistake-in-CB-NBT-int-deserialization.patch similarity index 100% rename from patches/server/0503-Fix-regex-mistake-in-CB-NBT-int-deserialization.patch rename to patches/server/0501-Fix-regex-mistake-in-CB-NBT-int-deserialization.patch diff --git a/patches/server/0504-Do-not-let-the-server-load-chunks-from-newer-version.patch b/patches/server/0502-Do-not-let-the-server-load-chunks-from-newer-version.patch similarity index 100% rename from patches/server/0504-Do-not-let-the-server-load-chunks-from-newer-version.patch rename to patches/server/0502-Do-not-let-the-server-load-chunks-from-newer-version.patch diff --git a/patches/server/0505-Brand-support.patch b/patches/server/0503-Brand-support.patch similarity index 100% rename from patches/server/0505-Brand-support.patch rename to patches/server/0503-Brand-support.patch diff --git a/patches/server/0506-Add-setMaxPlayers-API.patch b/patches/server/0504-Add-setMaxPlayers-API.patch similarity index 89% rename from patches/server/0506-Add-setMaxPlayers-API.patch rename to patches/server/0504-Add-setMaxPlayers-API.patch index 1113c80416..10f7dfe062 100644 --- a/patches/server/0506-Add-setMaxPlayers-API.patch +++ b/patches/server/0504-Add-setMaxPlayers-API.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add #setMaxPlayers API diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 13f1b62e6f3adce54f82d6d131dd60976dc6f548..f2b139d565662fca1bbad46e50b5ccb0d08c4e37 100644 +index 9a73364f4d56f3a7cecb27bc8034166b8f5731b9..4d1d5dacb175e7059a6af036432ef891bcf77970 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java @@ -145,7 +145,7 @@ public abstract class PlayerList { @@ -18,7 +18,7 @@ index 13f1b62e6f3adce54f82d6d131dd60976dc6f548..f2b139d565662fca1bbad46e50b5ccb0 private boolean allowCheatsForAllPlayers; private static final boolean ALLOW_LOGOUTIVATOR = false; diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 50f85ab4f3ba97728f873cb2facb92a60e5ea765..509f662fcaa02a83b8dd508acce3b8276052d213 100644 +index bfdfbc550306484290989329878e33adbf3f37cd..c1ea62a2c65fb2cbb79c967cfb8fd2bb9cbba7a2 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -627,6 +627,13 @@ public final class CraftServer implements Server { diff --git a/patches/server/0507-Add-playPickupItemAnimation-to-LivingEntity.patch b/patches/server/0505-Add-playPickupItemAnimation-to-LivingEntity.patch similarity index 100% rename from patches/server/0507-Add-playPickupItemAnimation-to-LivingEntity.patch rename to patches/server/0505-Add-playPickupItemAnimation-to-LivingEntity.patch diff --git a/patches/server/0508-Don-t-require-FACING-data.patch b/patches/server/0506-Don-t-require-FACING-data.patch similarity index 100% rename from patches/server/0508-Don-t-require-FACING-data.patch rename to patches/server/0506-Don-t-require-FACING-data.patch diff --git a/patches/server/0509-Fix-SpawnChangeEvent-not-firing-for-all-use-cases.patch b/patches/server/0507-Fix-SpawnChangeEvent-not-firing-for-all-use-cases.patch similarity index 86% rename from patches/server/0509-Fix-SpawnChangeEvent-not-firing-for-all-use-cases.patch rename to patches/server/0507-Fix-SpawnChangeEvent-not-firing-for-all-use-cases.patch index 27e11fa2ef..79b5b0317c 100644 --- a/patches/server/0509-Fix-SpawnChangeEvent-not-firing-for-all-use-cases.patch +++ b/patches/server/0507-Fix-SpawnChangeEvent-not-firing-for-all-use-cases.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Fix SpawnChangeEvent not firing for all use-cases diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 2653a55d3c947577ea153f3060d9b98acdf31c07..7d97ca9449b123a82b41937353d8fd0b2c98ea4e 100644 +index 86d060691405401fe8ae8ae8ef59322ee841b196..1b13c67441c46208a9b2adde5ca109f90bbb92e2 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1621,6 +1621,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1707,6 +1707,7 @@ public class ServerLevel extends Level implements WorldGenLevel { //ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(new BlockPosition(this.worldData.a(), 0, this.worldData.c())); this.levelData.setSpawn(pos, angle); @@ -17,10 +17,10 @@ index 2653a55d3c947577ea153f3060d9b98acdf31c07..7d97ca9449b123a82b41937353d8fd0b // if this keepSpawnInMemory is false a plugin has already removed our tickets, do not re-add this.removeTicketsForSpawn(this.paperConfig.keepLoadedRange, prevSpawn); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index ffb512ac2e2f827cf4079e9b24dd88e363eb95ee..b363af7a8ca8a3bbfd562095ac0f72acdb7dc192 100644 +index 2606c4009c92e6b781d21de18ca6dbb809ec6215..b19e58f55ddd463e3e3f27680456207d66a08ed4 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -246,11 +246,13 @@ public class CraftWorld extends CraftRegionAccessor implements World { +@@ -243,11 +243,13 @@ public class CraftWorld extends CraftRegionAccessor implements World { public boolean setSpawnLocation(int x, int y, int z, float angle) { try { Location previousLocation = this.getSpawnLocation(); diff --git a/patches/server/0510-Add-moon-phase-API.patch b/patches/server/0508-Add-moon-phase-API.patch similarity index 83% rename from patches/server/0510-Add-moon-phase-API.patch rename to patches/server/0508-Add-moon-phase-API.patch index 0e298a446c..5e1e8aa45d 100644 --- a/patches/server/0510-Add-moon-phase-API.patch +++ b/patches/server/0508-Add-moon-phase-API.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add moon phase API diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index b363af7a8ca8a3bbfd562095ac0f72acdb7dc192..abab9734f77014ed2627337656ebbb2f3c018e55 100644 +index b19e58f55ddd463e3e3f27680456207d66a08ed4..7e9a24a9f96cd36f883730e602babbc857971b36 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -184,6 +184,11 @@ public class CraftWorld extends CraftRegionAccessor implements World { +@@ -181,6 +181,11 @@ public class CraftWorld extends CraftRegionAccessor implements World { public int getPlayerCount() { return world.players().size(); } diff --git a/patches/server/0511-Prevent-headless-pistons-from-being-created.patch b/patches/server/0509-Prevent-headless-pistons-from-being-created.patch similarity index 96% rename from patches/server/0511-Prevent-headless-pistons-from-being-created.patch rename to patches/server/0509-Prevent-headless-pistons-from-being-created.patch index 6942d5310e..da7b8b87be 100644 --- a/patches/server/0511-Prevent-headless-pistons-from-being-created.patch +++ b/patches/server/0509-Prevent-headless-pistons-from-being-created.patch @@ -6,10 +6,10 @@ Subject: [PATCH] Prevent headless pistons from being created Prevent headless pistons from being created by explosions or tree/mushroom growth. diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index b0fe3f0ac4c706c4b64f2778fcb8d6c26e585d3c..e7d54a510303653c5f3c7374e4e83101321840c6 100644 +index 154222698ba1855283f8378b996cbfcc8bbdf81c..7fb9135d35ff8746044d1e94915c1cc9b11af86f 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java -@@ -459,6 +459,12 @@ public class PaperConfig { +@@ -458,6 +458,12 @@ public class PaperConfig { set("settings.unsupported-settings.allow-tnt-duplication", null); } diff --git a/patches/server/0512-Add-BellRingEvent.patch b/patches/server/0510-Add-BellRingEvent.patch similarity index 100% rename from patches/server/0512-Add-BellRingEvent.patch rename to patches/server/0510-Add-BellRingEvent.patch diff --git a/patches/server/0513-Add-zombie-targets-turtle-egg-config.patch b/patches/server/0511-Add-zombie-targets-turtle-egg-config.patch similarity index 95% rename from patches/server/0513-Add-zombie-targets-turtle-egg-config.patch rename to patches/server/0511-Add-zombie-targets-turtle-egg-config.patch index 0b5bb1ac74..66f7d80909 100644 --- a/patches/server/0513-Add-zombie-targets-turtle-egg-config.patch +++ b/patches/server/0511-Add-zombie-targets-turtle-egg-config.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add zombie targets turtle egg config diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index acb4fe32835bca485cfb2ec509370c9f596d6a0f..7df8648240d43d01bcc2eb1f05f3dab2dc174f38 100644 +index 7ac92d856d67046a6928e8cd21a57760d4b4954c..5d47b6bce004c21293b66a6e2a4095299e196546 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -38,6 +38,11 @@ public class PaperWorldConfig { diff --git a/patches/server/0514-Buffer-joins-to-world.patch b/patches/server/0512-Buffer-joins-to-world.patch similarity index 95% rename from patches/server/0514-Buffer-joins-to-world.patch rename to patches/server/0512-Buffer-joins-to-world.patch index 969919eda1..907932540a 100644 --- a/patches/server/0514-Buffer-joins-to-world.patch +++ b/patches/server/0512-Buffer-joins-to-world.patch @@ -8,10 +8,10 @@ the world per tick, this attempts to reduce the impact that join floods has on the server diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index e7d54a510303653c5f3c7374e4e83101321840c6..a8585c1a7f7e92a7c91b9e4bba568df5888baf14 100644 +index 7fb9135d35ff8746044d1e94915c1cc9b11af86f..40b93af871b66bae3498c6e9738eaa5debe90d42 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java -@@ -475,4 +475,9 @@ public class PaperConfig { +@@ -474,4 +474,9 @@ public class PaperConfig { maxPlayerAutoSavePerTick = (playerAutoSaveRate == -1 || playerAutoSaveRate > 100) ? 10 : 20; } } diff --git a/patches/server/0515-Optimize-redstone-algorithm.patch b/patches/server/0513-Optimize-redstone-algorithm.patch similarity index 99% rename from patches/server/0515-Optimize-redstone-algorithm.patch rename to patches/server/0513-Optimize-redstone-algorithm.patch index 53e8067c29..4c41583239 100644 --- a/patches/server/0515-Optimize-redstone-algorithm.patch +++ b/patches/server/0513-Optimize-redstone-algorithm.patch @@ -19,7 +19,7 @@ Aside from making the obvious class/function renames and obfhelpers I didn't nee Just added Bukkit's event system and took a few liberties with dead code and comment misspellings. diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 7df8648240d43d01bcc2eb1f05f3dab2dc174f38..f35bf9e1515d55143a1604e0aa7ec0b810a1a4c5 100644 +index 5d47b6bce004c21293b66a6e2a4095299e196546..e04034003a830c9eb65b9dc18949e5a5cb94257c 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -43,6 +43,16 @@ public class PaperWorldConfig { diff --git a/patches/server/0516-Fix-hex-colors-not-working-in-some-kick-messages.patch b/patches/server/0514-Fix-hex-colors-not-working-in-some-kick-messages.patch similarity index 100% rename from patches/server/0516-Fix-hex-colors-not-working-in-some-kick-messages.patch rename to patches/server/0514-Fix-hex-colors-not-working-in-some-kick-messages.patch diff --git a/patches/server/0517-PortalCreateEvent-needs-to-know-its-entity.patch b/patches/server/0515-PortalCreateEvent-needs-to-know-its-entity.patch similarity index 98% rename from patches/server/0517-PortalCreateEvent-needs-to-know-its-entity.patch rename to patches/server/0515-PortalCreateEvent-needs-to-know-its-entity.patch index e5d84e4ccb..5e57902917 100644 --- a/patches/server/0517-PortalCreateEvent-needs-to-know-its-entity.patch +++ b/patches/server/0515-PortalCreateEvent-needs-to-know-its-entity.patch @@ -87,7 +87,7 @@ index 3249b4a7e2267d8c321ae4cf592e9fc26dfdcb98..6754d071fe91f9e89cca0be4a0643100 private static int getFireTickDelay(Random random) { diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java -index 736e38a6ee3dc36ed886d047484bda9516845324..4383936b07c60a12e4368da1b9c36297f1ca7b01 100644 +index bea9c7ac0fe08c3ca3309e8311f192cc557ca67d..983ca190dadc6f0b552133e6296f1c4165b75ad4 100644 --- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java +++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java @@ -32,6 +32,7 @@ import net.minecraft.world.item.DyeColor; diff --git a/patches/server/0518-Fix-CraftTeam-null-check.patch b/patches/server/0516-Fix-CraftTeam-null-check.patch similarity index 100% rename from patches/server/0518-Fix-CraftTeam-null-check.patch rename to patches/server/0516-Fix-CraftTeam-null-check.patch diff --git a/patches/server/0519-Add-more-Evoker-API.patch b/patches/server/0517-Add-more-Evoker-API.patch similarity index 100% rename from patches/server/0519-Add-more-Evoker-API.patch rename to patches/server/0517-Add-more-Evoker-API.patch diff --git a/patches/server/0520-Add-methods-to-get-translation-keys.patch b/patches/server/0518-Add-methods-to-get-translation-keys.patch similarity index 100% rename from patches/server/0520-Add-methods-to-get-translation-keys.patch rename to patches/server/0518-Add-methods-to-get-translation-keys.patch diff --git a/patches/server/0521-Create-HoverEvent-from-ItemStack-Entity.patch b/patches/server/0519-Create-HoverEvent-from-ItemStack-Entity.patch similarity index 100% rename from patches/server/0521-Create-HoverEvent-from-ItemStack-Entity.patch rename to patches/server/0519-Create-HoverEvent-from-ItemStack-Entity.patch diff --git a/patches/server/0522-Cache-block-data-strings.patch b/patches/server/0520-Cache-block-data-strings.patch similarity index 95% rename from patches/server/0522-Cache-block-data-strings.patch rename to patches/server/0520-Cache-block-data-strings.patch index 5371382855..706d0a6ad0 100644 --- a/patches/server/0522-Cache-block-data-strings.patch +++ b/patches/server/0520-Cache-block-data-strings.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Cache block data strings diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index de2c21486f07bc2dbbaa9598354cd4ed39cb94ca..a183452adf5ca5c86fd9739ba776f4df87161426 100644 +index 47fbfe7cc1261d65264bb1897c57f72a7b4df46e..79975b851f73ab308e9f5b295a85327bb62dee29 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -2054,6 +2054,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= level; } -@@ -1545,6 +1546,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -1568,6 +1569,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n } public void moveTo(double x, double y, double z, float yaw, float pitch) { @@ -69,10 +69,10 @@ index b9e738542692aba7b78fc514ae8e3248df9998ea..c601b8b12756682a4cb300be8ebed431 if (entity instanceof Mob) { Mob entityinsentient = (Mob) entity; diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -index 17ad49994499982a08c1e8a4b4635647d669986a..187504b19f0194c700b5e498be191cf58b8d34ad 100644 +index 2ae9392961383cbce07835234b95f415094c11fc..1f41d39fe3378bccc6c005d20e41d98f903762cc 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -@@ -567,7 +567,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { +@@ -576,7 +576,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { } // entity.setLocation() throws no event, and so cannot be cancelled diff --git a/patches/server/0524-Add-additional-open-container-api-to-HumanEntity.patch b/patches/server/0522-Add-additional-open-container-api-to-HumanEntity.patch similarity index 100% rename from patches/server/0524-Add-additional-open-container-api-to-HumanEntity.patch rename to patches/server/0522-Add-additional-open-container-api-to-HumanEntity.patch diff --git a/patches/server/0525-Cache-DataFixerUpper-Rewrite-Rules-on-demand.patch b/patches/server/0523-Cache-DataFixerUpper-Rewrite-Rules-on-demand.patch similarity index 100% rename from patches/server/0525-Cache-DataFixerUpper-Rewrite-Rules-on-demand.patch rename to patches/server/0523-Cache-DataFixerUpper-Rewrite-Rules-on-demand.patch diff --git a/patches/server/0526-Extend-block-drop-capture-to-capture-all-items-added.patch b/patches/server/0524-Extend-block-drop-capture-to-capture-all-items-added.patch similarity index 94% rename from patches/server/0526-Extend-block-drop-capture-to-capture-all-items-added.patch rename to patches/server/0524-Extend-block-drop-capture-to-capture-all-items-added.patch index 574aba2542..85ed04e358 100644 --- a/patches/server/0526-Extend-block-drop-capture-to-capture-all-items-added.patch +++ b/patches/server/0524-Extend-block-drop-capture-to-capture-all-items-added.patch @@ -6,10 +6,10 @@ Subject: [PATCH] Extend block drop capture to capture all items added to the diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 7d97ca9449b123a82b41937353d8fd0b2c98ea4e..9c2d43c95f2f4bab0c8bbae7390500aa676ce4da 100644 +index 1b13c67441c46208a9b2adde5ca109f90bbb92e2..3563690e404a68b8940d9c06a0198dd0a4b2d220 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1166,6 +1166,13 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1252,6 +1252,13 @@ public class ServerLevel extends Level implements WorldGenLevel { // WorldServer.LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityTypes.getName(entity.getEntityType())); // CraftBukkit return false; } else { diff --git a/patches/server/0527-Don-t-mark-dirty-in-invalid-locations-SPIGOT-6086.patch b/patches/server/0525-Don-t-mark-dirty-in-invalid-locations-SPIGOT-6086.patch similarity index 89% rename from patches/server/0527-Don-t-mark-dirty-in-invalid-locations-SPIGOT-6086.patch rename to patches/server/0525-Don-t-mark-dirty-in-invalid-locations-SPIGOT-6086.patch index ec3ee41bb3..8243a5a2c1 100644 --- a/patches/server/0527-Don-t-mark-dirty-in-invalid-locations-SPIGOT-6086.patch +++ b/patches/server/0525-Don-t-mark-dirty-in-invalid-locations-SPIGOT-6086.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Don't mark dirty in invalid locations (SPIGOT-6086) diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java -index e6cc5da2fd58bcc2809baeffee01040d0370a92c..8d59d735b2862ecc12cbfebc57a02ebbfd1e125e 100644 +index e91552020b96d039c5516ddd8e85eadb8ab89c3c..7bf3dd705460c996fb4b575957f87805edb13ba4 100644 --- a/src/main/java/net/minecraft/server/level/ChunkHolder.java +++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java @@ -381,6 +381,7 @@ public class ChunkHolder { diff --git a/patches/server/0528-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch b/patches/server/0526-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch similarity index 90% rename from patches/server/0528-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch rename to patches/server/0526-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch index 056b456084..2d3dae6bb3 100644 --- a/patches/server/0528-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch +++ b/patches/server/0526-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch @@ -6,10 +6,10 @@ Subject: [PATCH] Expose the Entity Counter to allow plugins to use valid and diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 993a7000595181a83754f192a53ec2bfe1a2eddc..77de5d23e508998045fe13680cdf1f032d6f4336 100644 +index a652bbc4b3c768fecfb6f067d21a90315256f885..9e6a2c5fc55674ab144ebd46da6152e51d3c1dba 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -3935,4 +3935,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -3958,4 +3958,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n void accept(Entity entity, double x, double y, double z); } diff --git a/patches/server/0529-Lazily-track-plugin-scoreboards-by-default.patch b/patches/server/0527-Lazily-track-plugin-scoreboards-by-default.patch similarity index 97% rename from patches/server/0529-Lazily-track-plugin-scoreboards-by-default.patch rename to patches/server/0527-Lazily-track-plugin-scoreboards-by-default.patch index 3d37c349f0..c85f35a620 100644 --- a/patches/server/0529-Lazily-track-plugin-scoreboards-by-default.patch +++ b/patches/server/0527-Lazily-track-plugin-scoreboards-by-default.patch @@ -14,10 +14,10 @@ this breaks your workflow you can always force all scoreboards to be tracked wit settings.track-plugin-scoreboards in paper.yml. diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index a8585c1a7f7e92a7c91b9e4bba568df5888baf14..22027cdebcde2157b119f37cb1471f7a69c2a31a 100644 +index 40b93af871b66bae3498c6e9738eaa5debe90d42..75fbffa9628072d2ebb0035bc057924eb6ed31e1 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java -@@ -480,4 +480,9 @@ public class PaperConfig { +@@ -479,4 +479,9 @@ public class PaperConfig { private static void maxJoinsPerTick() { maxJoinsPerTick = getInt("settings.max-joins-per-tick", 3); } diff --git a/patches/server/0530-Entity-isTicking.patch b/patches/server/0528-Entity-isTicking.patch similarity index 83% rename from patches/server/0530-Entity-isTicking.patch rename to patches/server/0528-Entity-isTicking.patch index a5a96e6cda..d63fd95a55 100644 --- a/patches/server/0530-Entity-isTicking.patch +++ b/patches/server/0528-Entity-isTicking.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Entity#isTicking diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 77de5d23e508998045fe13680cdf1f032d6f4336..ae2937e6b6b0ac150f6f608459d2b75a05e16c36 100644 +index 9e6a2c5fc55674ab144ebd46da6152e51d3c1dba..9524771be6268b28f14618523d59cff00a127d9b 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -52,6 +52,7 @@ import net.minecraft.resources.ResourceKey; @@ -16,7 +16,7 @@ index 77de5d23e508998045fe13680cdf1f032d6f4336..ae2937e6b6b0ac150f6f608459d2b75a import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.TicketType; -@@ -3940,5 +3941,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -3963,5 +3964,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n public static int nextEntityId() { return ENTITY_COUNTER.incrementAndGet(); } @@ -27,10 +27,10 @@ index 77de5d23e508998045fe13680cdf1f032d6f4336..ae2937e6b6b0ac150f6f608459d2b75a // Paper end } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -index 187504b19f0194c700b5e498be191cf58b8d34ad..fd210462479ce869b769fae928b9b6f950f71007 100644 +index 1f41d39fe3378bccc6c005d20e41d98f903762cc..0e1dcfdf846f978340380352e092375ffa089cb0 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -@@ -1225,5 +1225,9 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { +@@ -1234,5 +1234,9 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { public boolean isInLava() { return getHandle().isInLava(); } diff --git a/patches/server/0531-Fix-deop-kicking-non-whitelisted-player-when-white-l.patch b/patches/server/0529-Fix-deop-kicking-non-whitelisted-player-when-white-l.patch similarity index 86% rename from patches/server/0531-Fix-deop-kicking-non-whitelisted-player-when-white-l.patch rename to patches/server/0529-Fix-deop-kicking-non-whitelisted-player-when-white-l.patch index 93a57b6bf8..e94815d333 100644 --- a/patches/server/0531-Fix-deop-kicking-non-whitelisted-player-when-white-l.patch +++ b/patches/server/0529-Fix-deop-kicking-non-whitelisted-player-when-white-l.patch @@ -6,10 +6,10 @@ Subject: [PATCH] Fix deop kicking non-whitelisted player when white list is diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index a183452adf5ca5c86fd9739ba776f4df87161426..89507877b3a1c4305d09b7b2af611255c3481cfa 100644 +index 79975b851f73ab308e9f5b295a85327bb62dee29..1a8ef9cca2c855937087a18b30fbd4c116f5ec1e 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -2120,6 +2120,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop completablefuture = CompletableFuture.supplyAsync(() -> { Stream stream = datapacks.stream(); // CraftBukkit - decompile error PackRepository resourcepackrepository = this.packRepository; -@@ -2049,6 +2055,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { this.getServer().execute(() -> { diff --git a/patches/server/0598-Fix-villager-boat-exploit.patch b/patches/server/0596-Fix-villager-boat-exploit.patch similarity index 100% rename from patches/server/0598-Fix-villager-boat-exploit.patch rename to patches/server/0596-Fix-villager-boat-exploit.patch diff --git a/patches/server/0599-Add-sendOpLevel-API.patch b/patches/server/0597-Add-sendOpLevel-API.patch similarity index 95% rename from patches/server/0599-Add-sendOpLevel-API.patch rename to patches/server/0597-Add-sendOpLevel-API.patch index 58b666bf94..a3a0281d75 100644 --- a/patches/server/0599-Add-sendOpLevel-API.patch +++ b/patches/server/0597-Add-sendOpLevel-API.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add sendOpLevel API diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 6ac6d05390359bd858673c4941e7d90f4cf98a02..0e0d0445480862d1e6aded499e906370c24c5680 100644 +index 6f6b0460516da49fa0d0b5dd0dcd554bb03b9fe3..4662c7461a9d9a7f7989003ec00d4bd97a549bde 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java @@ -1123,6 +1123,11 @@ public abstract class PlayerList { diff --git a/patches/server/0600-Add-StructureLocateEvent.patch b/patches/server/0598-Add-StructureLocateEvent.patch similarity index 88% rename from patches/server/0600-Add-StructureLocateEvent.patch rename to patches/server/0598-Add-StructureLocateEvent.patch index a1608b7b39..ce604f303c 100644 --- a/patches/server/0600-Add-StructureLocateEvent.patch +++ b/patches/server/0598-Add-StructureLocateEvent.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add StructureLocateEvent diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java -index 6fbfe7a61df0efd4d85a00f02096410a1d29e62f..f19f0fe89196d0939d16c22dd57585a50b19a15e 100644 +index 6fbfe7a61df0efd4d85a00f02096410a1d29e62f..fde436065aeeb47cd177656a7c9fe8dc34178e87 100644 --- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java +++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java @@ -2,6 +2,7 @@ package net.minecraft.world.level.chunk; @@ -16,7 +16,7 @@ index 6fbfe7a61df0efd4d85a00f02096410a1d29e62f..f19f0fe89196d0939d16c22dd57585a5 import java.util.BitSet; import java.util.Iterator; import java.util.List; -@@ -185,6 +186,22 @@ public abstract class ChunkGenerator { +@@ -185,6 +186,20 @@ public abstract class ChunkGenerator { @Nullable public BlockPos findNearestMapFeature(ServerLevel world, StructureFeature feature, BlockPos center, int radius, boolean skipExistingChunks) { @@ -29,8 +29,6 @@ index 6fbfe7a61df0efd4d85a00f02096410a1d29e62f..f19f0fe89196d0939d16c22dd57585a5 + if(event.getResult() != null) return new BlockPos(event.getResult().getBlockX(), event.getResult().getBlockY(), event.getResult().getBlockZ()); + // Get origin location (re)defined by event call. + center = new BlockPos(event.getOrigin().getBlockX(), event.getOrigin().getBlockY(), event.getOrigin().getBlockZ()); -+ // Get world (re)defined by event call. -+ world = ((org.bukkit.craftbukkit.CraftWorld) event.getOrigin().getWorld()).getHandle(); + // Get radius and whether to find unexplored structures (re)defined by event call. + radius = event.getRadius(); + skipExistingChunks = event.shouldFindUnexplored(); diff --git a/patches/server/0601-Collision-option-for-requiring-a-player-participant.patch b/patches/server/0599-Collision-option-for-requiring-a-player-participant.patch similarity index 94% rename from patches/server/0601-Collision-option-for-requiring-a-player-participant.patch rename to patches/server/0599-Collision-option-for-requiring-a-player-participant.patch index b6745ca88c..94d6449bd1 100644 --- a/patches/server/0601-Collision-option-for-requiring-a-player-participant.patch +++ b/patches/server/0599-Collision-option-for-requiring-a-player-participant.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Collision option for requiring a player participant diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 33709724481370b51de930e7bb2e7b1a329c5e6b..c3e7a26bc6f6206dc2513dd5d937b05951ab5c7a 100644 +index 7f97fae63f2e7c9ffbd0b971a985a041d8f49dd4..99ab8494f4b61c0729a0c27ee4640174638740d0 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -64,6 +64,18 @@ public class PaperWorldConfig { @@ -28,10 +28,10 @@ index 33709724481370b51de930e7bb2e7b1a329c5e6b..c3e7a26bc6f6206dc2513dd5d937b059 public int wanderingTraderSpawnDayTicks = 24000; public int wanderingTraderSpawnChanceFailureIncrement = 25; diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 2528e08aea13a8486c552a938d93aa0429692f14..e48d2dd51c1304aec33ad6cebec3f24c2dfee35f 100644 +index 7b8e747a44d9a60f345154a9aee817b8d923b424..5703949d5ffa56602418d40a85f2fa1827690254 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -1609,6 +1609,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -1632,6 +1632,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n public void push(Entity entity) { if (!this.isPassengerOfSameVehicle(entity)) { if (!entity.noPhysics && !this.noPhysics) { diff --git a/patches/server/0602-Remove-ProjectileHitEvent-call-when-fireballs-dead.patch b/patches/server/0600-Remove-ProjectileHitEvent-call-when-fireballs-dead.patch similarity index 100% rename from patches/server/0602-Remove-ProjectileHitEvent-call-when-fireballs-dead.patch rename to patches/server/0600-Remove-ProjectileHitEvent-call-when-fireballs-dead.patch diff --git a/patches/server/0603-Return-chat-component-with-empty-text-instead-of-thr.patch b/patches/server/0601-Return-chat-component-with-empty-text-instead-of-thr.patch similarity index 100% rename from patches/server/0603-Return-chat-component-with-empty-text-instead-of-thr.patch rename to patches/server/0601-Return-chat-component-with-empty-text-instead-of-thr.patch diff --git a/patches/server/0604-Make-schedule-command-per-world.patch b/patches/server/0602-Make-schedule-command-per-world.patch similarity index 100% rename from patches/server/0604-Make-schedule-command-per-world.patch rename to patches/server/0602-Make-schedule-command-per-world.patch diff --git a/patches/server/0605-Configurable-max-leash-distance.patch b/patches/server/0603-Configurable-max-leash-distance.patch similarity index 96% rename from patches/server/0605-Configurable-max-leash-distance.patch rename to patches/server/0603-Configurable-max-leash-distance.patch index 42b2129ed8..e7f4d28482 100644 --- a/patches/server/0605-Configurable-max-leash-distance.patch +++ b/patches/server/0603-Configurable-max-leash-distance.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Configurable max leash distance diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index c3e7a26bc6f6206dc2513dd5d937b05951ab5c7a..6e15242476e07bb7e2d1b60ff9497687b2d78826 100644 +index 99ab8494f4b61c0729a0c27ee4640174638740d0..54e4f71c777458900c1856013a8aa39f9ae3d660 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -263,6 +263,12 @@ public class PaperWorldConfig { diff --git a/patches/server/0606-Implement-BlockPreDispenseEvent.patch b/patches/server/0604-Implement-BlockPreDispenseEvent.patch similarity index 100% rename from patches/server/0606-Implement-BlockPreDispenseEvent.patch rename to patches/server/0604-Implement-BlockPreDispenseEvent.patch diff --git a/patches/server/0607-Added-Vanilla-Entity-Tags.patch b/patches/server/0605-Added-Vanilla-Entity-Tags.patch similarity index 100% rename from patches/server/0607-Added-Vanilla-Entity-Tags.patch rename to patches/server/0605-Added-Vanilla-Entity-Tags.patch diff --git a/patches/server/0608-added-Wither-API.patch b/patches/server/0606-added-Wither-API.patch similarity index 100% rename from patches/server/0608-added-Wither-API.patch rename to patches/server/0606-added-Wither-API.patch diff --git a/patches/server/0609-Added-firing-of-PlayerChangeBeaconEffectEvent.patch b/patches/server/0607-Added-firing-of-PlayerChangeBeaconEffectEvent.patch similarity index 100% rename from patches/server/0609-Added-firing-of-PlayerChangeBeaconEffectEvent.patch rename to patches/server/0607-Added-firing-of-PlayerChangeBeaconEffectEvent.patch diff --git a/patches/server/0610-Add-toggle-for-always-placing-the-dragon-egg.patch b/patches/server/0608-Add-toggle-for-always-placing-the-dragon-egg.patch similarity index 90% rename from patches/server/0610-Add-toggle-for-always-placing-the-dragon-egg.patch rename to patches/server/0608-Add-toggle-for-always-placing-the-dragon-egg.patch index b9ce7071fe..6e2199bc36 100644 --- a/patches/server/0610-Add-toggle-for-always-placing-the-dragon-egg.patch +++ b/patches/server/0608-Add-toggle-for-always-placing-the-dragon-egg.patch @@ -5,12 +5,12 @@ Subject: [PATCH] Add toggle for always placing the dragon egg diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 6e15242476e07bb7e2d1b60ff9497687b2d78826..c079675b79817108947113d543d4dbd966f32ec7 100644 +index 54e4f71c777458900c1856013a8aa39f9ae3d660..eec6acd8443d7b7d158c1afcf8866443fdeacb95 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -@@ -754,5 +754,10 @@ public class PaperWorldConfig { - private void perPlayerMobSpawns() { - perPlayerMobSpawns = getBoolean("per-player-mob-spawns", false); +@@ -757,5 +757,10 @@ public class PaperWorldConfig { + } + perPlayerMobSpawns = getBoolean("per-player-mob-spawns", true); } + + public boolean enderDragonsDeathAlwaysPlacesDragonEgg = false; diff --git a/patches/server/0611-Added-PlayerStonecutterRecipeSelectEvent.patch b/patches/server/0609-Added-PlayerStonecutterRecipeSelectEvent.patch similarity index 100% rename from patches/server/0611-Added-PlayerStonecutterRecipeSelectEvent.patch rename to patches/server/0609-Added-PlayerStonecutterRecipeSelectEvent.patch diff --git a/patches/server/0612-Add-dropLeash-variable-to-EntityUnleashEvent.patch b/patches/server/0610-Add-dropLeash-variable-to-EntityUnleashEvent.patch similarity index 100% rename from patches/server/0612-Add-dropLeash-variable-to-EntityUnleashEvent.patch rename to patches/server/0610-Add-dropLeash-variable-to-EntityUnleashEvent.patch diff --git a/patches/server/0613-Skip-distance-map-update-when-spawning-disabled.patch b/patches/server/0611-Skip-distance-map-update-when-spawning-disabled.patch similarity index 87% rename from patches/server/0613-Skip-distance-map-update-when-spawning-disabled.patch rename to patches/server/0611-Skip-distance-map-update-when-spawning-disabled.patch index 20a4e9fc1e..98765f2cad 100644 --- a/patches/server/0613-Skip-distance-map-update-when-spawning-disabled.patch +++ b/patches/server/0611-Skip-distance-map-update-when-spawning-disabled.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Skip distance map update when spawning disabled. diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index db6a0246e83045015fc41f27e0aec0e07d1fb568..58061491be6713a21e3f9e67a0b1ac0ecceb24d6 100644 +index eb6a802ea12a19a058fb7e23b7418b8b426b4ca0..16b406067ac7697ab598f9f27f5e688687f206ee 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -814,7 +814,7 @@ public class ServerChunkCache extends ChunkSource { +@@ -973,7 +973,7 @@ public class ServerChunkCache extends ChunkSource { int l = this.distanceManager.getNaturalSpawnChunkCount(); // Paper start - per player mob spawning NaturalSpawner.SpawnState spawnercreature_d; // moved down diff --git a/patches/server/0614-Reset-shield-blocking-on-dimension-change.patch b/patches/server/0612-Reset-shield-blocking-on-dimension-change.patch similarity index 89% rename from patches/server/0614-Reset-shield-blocking-on-dimension-change.patch rename to patches/server/0612-Reset-shield-blocking-on-dimension-change.patch index 28c8cdec51..47abedd2c4 100644 --- a/patches/server/0614-Reset-shield-blocking-on-dimension-change.patch +++ b/patches/server/0612-Reset-shield-blocking-on-dimension-change.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Reset shield blocking on dimension change diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 82b4805b417b130be3ce9ab6ed886c85e811ba98..460f8ce00894065a15d931906c8a05ca990d6e15 100644 +index 8ca66198f3d2ef120f14d6398a02b7ebf8a6a8df..acd8af785067caec8b40b7517a76fa1d9b967e63 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -1144,6 +1144,11 @@ public class ServerPlayer extends Player { diff --git a/patches/server/0615-add-DragonEggFormEvent.patch b/patches/server/0613-add-DragonEggFormEvent.patch similarity index 100% rename from patches/server/0615-add-DragonEggFormEvent.patch rename to patches/server/0613-add-DragonEggFormEvent.patch diff --git a/patches/server/0616-EntityMoveEvent.patch b/patches/server/0614-EntityMoveEvent.patch similarity index 91% rename from patches/server/0616-EntityMoveEvent.patch rename to patches/server/0614-EntityMoveEvent.patch index 94b9a87c32..50301f0b86 100644 --- a/patches/server/0616-EntityMoveEvent.patch +++ b/patches/server/0614-EntityMoveEvent.patch @@ -5,10 +5,10 @@ Subject: [PATCH] EntityMoveEvent diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 9f471af9b0e0fe22ebd3b9a0e91b9c2718927d67..c22ad40718c63507c8fdc11a7bd1077abf254aa4 100644 +index c848ec7a39ac18cd622b681298200769c78b5ae5..9111d3fb84ca4a7dc881e1326ac0c93a81ec7f58 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1541,6 +1541,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper @@ -17,10 +17,10 @@ index 9f471af9b0e0fe22ebd3b9a0e91b9c2718927d67..c22ad40718c63507c8fdc11a7bd1077a this.profiler.push(() -> { diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 0acbd21f755ba805fd0aba68219e9c3849799375..5157d5f1d04d68446cd91507a314d727cce70c79 100644 +index aedb1d7a0b60fba72a952fa140569b72795660e4..d5c7180376562cd576d832d44ab11c9cc323ba12 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -196,6 +196,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -197,6 +197,7 @@ public class ServerLevel extends Level implements WorldGenLevel { public final LevelStorageSource.LevelStorageAccess convertable; public final UUID uuid; public boolean hasPhysicsEvent = true; // Paper diff --git a/patches/server/0617-added-option-to-disable-pathfinding-updates-on-block.patch b/patches/server/0615-added-option-to-disable-pathfinding-updates-on-block.patch similarity index 82% rename from patches/server/0617-added-option-to-disable-pathfinding-updates-on-block.patch rename to patches/server/0615-added-option-to-disable-pathfinding-updates-on-block.patch index 94abc6973e..e1d63001c5 100644 --- a/patches/server/0617-added-option-to-disable-pathfinding-updates-on-block.patch +++ b/patches/server/0615-added-option-to-disable-pathfinding-updates-on-block.patch @@ -5,10 +5,10 @@ Subject: [PATCH] added option to disable pathfinding updates on block changes diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index c079675b79817108947113d543d4dbd966f32ec7..00d2959f0acda36020b553797cff113597772b0b 100644 +index eec6acd8443d7b7d158c1afcf8866443fdeacb95..30fbf72c80e282e06b4928bda0c5f59a01239a1f 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -@@ -759,5 +759,10 @@ public class PaperWorldConfig { +@@ -762,5 +762,10 @@ public class PaperWorldConfig { private void enderDragonsDeathAlwaysPlacesDragonEgg() { enderDragonsDeathAlwaysPlacesDragonEgg = getBoolean("ender-dragons-death-always-places-dragon-egg", enderDragonsDeathAlwaysPlacesDragonEgg); } @@ -20,10 +20,10 @@ index c079675b79817108947113d543d4dbd966f32ec7..00d2959f0acda36020b553797cff1135 } diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 5157d5f1d04d68446cd91507a314d727cce70c79..bec580b4523bdd1982e6668481e6cb9d2beb7137 100644 +index d5c7180376562cd576d832d44ab11c9cc323ba12..044aa3df0c26b7a64eefcacf0b6b5c2d9224938c 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1300,6 +1300,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1386,6 +1386,7 @@ public class ServerLevel extends Level implements WorldGenLevel { @Override public void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) { this.getChunkSource().blockChanged(pos); @@ -31,7 +31,7 @@ index 5157d5f1d04d68446cd91507a314d727cce70c79..bec580b4523bdd1982e6668481e6cb9d VoxelShape voxelshape = oldState.getCollisionShape(this, pos); VoxelShape voxelshape1 = newState.getCollisionShape(this, pos); -@@ -1327,6 +1328,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1413,6 +1414,7 @@ public class ServerLevel extends Level implements WorldGenLevel { } } diff --git a/patches/server/0618-Inline-shift-direction-fields.patch b/patches/server/0616-Inline-shift-direction-fields.patch similarity index 100% rename from patches/server/0618-Inline-shift-direction-fields.patch rename to patches/server/0616-Inline-shift-direction-fields.patch diff --git a/patches/server/0619-Allow-adding-items-to-BlockDropItemEvent.patch b/patches/server/0617-Allow-adding-items-to-BlockDropItemEvent.patch similarity index 100% rename from patches/server/0619-Allow-adding-items-to-BlockDropItemEvent.patch rename to patches/server/0617-Allow-adding-items-to-BlockDropItemEvent.patch diff --git a/patches/server/0620-Add-getMainThreadExecutor-to-BukkitScheduler.patch b/patches/server/0618-Add-getMainThreadExecutor-to-BukkitScheduler.patch similarity index 100% rename from patches/server/0620-Add-getMainThreadExecutor-to-BukkitScheduler.patch rename to patches/server/0618-Add-getMainThreadExecutor-to-BukkitScheduler.patch diff --git a/patches/server/0621-living-entity-allow-attribute-registration.patch b/patches/server/0619-living-entity-allow-attribute-registration.patch similarity index 100% rename from patches/server/0621-living-entity-allow-attribute-registration.patch rename to patches/server/0619-living-entity-allow-attribute-registration.patch diff --git a/patches/server/0622-fix-dead-slime-setSize-invincibility.patch b/patches/server/0620-fix-dead-slime-setSize-invincibility.patch similarity index 100% rename from patches/server/0622-fix-dead-slime-setSize-invincibility.patch rename to patches/server/0620-fix-dead-slime-setSize-invincibility.patch diff --git a/patches/server/0623-Merchant-getRecipes-should-return-an-immutable-list.patch b/patches/server/0621-Merchant-getRecipes-should-return-an-immutable-list.patch similarity index 100% rename from patches/server/0623-Merchant-getRecipes-should-return-an-immutable-list.patch rename to patches/server/0621-Merchant-getRecipes-should-return-an-immutable-list.patch diff --git a/patches/server/0624-misc-debugging-dumps.patch b/patches/server/0622-misc-debugging-dumps.patch similarity index 91% rename from patches/server/0624-misc-debugging-dumps.patch rename to patches/server/0622-misc-debugging-dumps.patch index 81a8a36690..7fb9875e7c 100644 --- a/patches/server/0624-misc-debugging-dumps.patch +++ b/patches/server/0622-misc-debugging-dumps.patch @@ -29,10 +29,10 @@ index 0000000000000000000000000000000000000000..2d5494d2813b773e60ddba6790b750a9 + } +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index c22ad40718c63507c8fdc11a7bd1077abf254aa4..0d3c1cdeab7a95bbac166c27758ed1b57946dfea 100644 +index 9111d3fb84ca4a7dc881e1326ac0c93a81ec7f58..f15cb87d96b3f5028ab6ceba1e9026525cdecbee 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -936,6 +936,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop type, Level world) { this.id = Entity.ENTITY_COUNTER.incrementAndGet(); this.passengers = ImmutableList.of(); -@@ -2514,6 +2545,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -2537,6 +2568,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n } this.processPortalCooldown(); diff --git a/patches/server/0703-Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch b/patches/server/0701-Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch similarity index 100% rename from patches/server/0703-Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch rename to patches/server/0701-Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch diff --git a/patches/server/0704-Make-item-validations-configurable.patch b/patches/server/0702-Make-item-validations-configurable.patch similarity index 97% rename from patches/server/0704-Make-item-validations-configurable.patch rename to patches/server/0702-Make-item-validations-configurable.patch index b3d9a68a77..8eaaa016f4 100644 --- a/patches/server/0704-Make-item-validations-configurable.patch +++ b/patches/server/0702-Make-item-validations-configurable.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Make item validations configurable diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 062d8895f0de77c7391e99e8423d16dc4a1de765..3fedd75bea7197092823745deb7af8d4542efee5 100644 +index 78078e7ee04f9c46f98b7e5829a620730358c233..14841f1df5fa8970b132eb5e8fc24069f687dd6a 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java -@@ -504,4 +504,19 @@ public class PaperConfig { +@@ -503,4 +503,19 @@ public class PaperConfig { enableBrigadierConsoleHighlighting = getBoolean("settings.console.enable-brigadier-highlighting", enableBrigadierConsoleHighlighting); enableBrigadierConsoleCompletions = getBoolean("settings.console.enable-brigadier-completions", enableBrigadierConsoleCompletions); } diff --git a/patches/server/0705-Line-Of-Sight-Changes.patch b/patches/server/0703-Line-Of-Sight-Changes.patch similarity index 96% rename from patches/server/0705-Line-Of-Sight-Changes.patch rename to patches/server/0703-Line-Of-Sight-Changes.patch index 3c1c24bc44..2b3fcbd4b8 100644 --- a/patches/server/0705-Line-Of-Sight-Changes.patch +++ b/patches/server/0703-Line-Of-Sight-Changes.patch @@ -19,10 +19,10 @@ index 6539cd1a63f2d987bda2b91555b94df896089d1f..429c229d7c88060c76b143ff4aa1bea8 } diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index a8523488ec7dfe8fd2a625ca11dcb1b99619b66f..72750570065753df1515f7c99124b5e4e97bee64 100644 +index bfa5ff07faca4728c42062b81be298b91d07ef04..e2f05ab12066cea354eed4f8beb20cb3e9899899 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -189,6 +189,18 @@ public class CraftWorld extends CraftRegionAccessor implements World { +@@ -186,6 +186,18 @@ public class CraftWorld extends CraftRegionAccessor implements World { public io.papermc.paper.world.MoonPhase getMoonPhase() { return io.papermc.paper.world.MoonPhase.getPhase(getFullTime() / 24000L); } diff --git a/patches/server/0706-add-per-world-spawn-limits.patch b/patches/server/0704-add-per-world-spawn-limits.patch similarity index 90% rename from patches/server/0706-add-per-world-spawn-limits.patch rename to patches/server/0704-add-per-world-spawn-limits.patch index 3cb46be371..dd0d84c01b 100644 --- a/patches/server/0706-add-per-world-spawn-limits.patch +++ b/patches/server/0704-add-per-world-spawn-limits.patch @@ -6,7 +6,7 @@ Subject: [PATCH] add per world spawn limits Taken from #2982. Credit to Chasewhip8 diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index be0bc4793976e28d32b36c9d57cf1e4961065b61..a0149014d5ab5b5905caf432cdbfa50b179bb881 100644 +index 3652789ef1ea91b08582d4c43a366911e985fe96..bb6feb0c3b974b206a3250abeaa19ee7c0cc257c 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -676,6 +676,19 @@ public class PaperWorldConfig { @@ -30,10 +30,10 @@ index be0bc4793976e28d32b36c9d57cf1e4961065b61..a0149014d5ab5b5905caf432cdbfa50b private void lightQueueSize() { lightQueueSize = getInt("light-queue-size", lightQueueSize); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 72750570065753df1515f7c99124b5e4e97bee64..1c319b83652d8959d858545a7755d377c4ffd402 100644 +index e2f05ab12066cea354eed4f8beb20cb3e9899899..6dd6008e3e540d48939f7f3f0c03f7fd920d7d4a 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -211,6 +211,13 @@ public class CraftWorld extends CraftRegionAccessor implements World { +@@ -208,6 +208,13 @@ public class CraftWorld extends CraftRegionAccessor implements World { this.biomeProvider = biomeProvider; this.environment = env; diff --git a/patches/server/0707-Fix-PotionSplashEvent-for-water-splash-potions.patch b/patches/server/0705-Fix-PotionSplashEvent-for-water-splash-potions.patch similarity index 100% rename from patches/server/0707-Fix-PotionSplashEvent-for-water-splash-potions.patch rename to patches/server/0705-Fix-PotionSplashEvent-for-water-splash-potions.patch diff --git a/patches/server/0708-Fix-incorrect-status-dataconverter-for-pre-1.13-chun.patch b/patches/server/0706-Fix-incorrect-status-dataconverter-for-pre-1.13-chun.patch similarity index 100% rename from patches/server/0708-Fix-incorrect-status-dataconverter-for-pre-1.13-chun.patch rename to patches/server/0706-Fix-incorrect-status-dataconverter-for-pre-1.13-chun.patch diff --git a/patches/server/0709-Add-more-LimitedRegion-API.patch b/patches/server/0707-Add-more-LimitedRegion-API.patch similarity index 100% rename from patches/server/0709-Add-more-LimitedRegion-API.patch rename to patches/server/0707-Add-more-LimitedRegion-API.patch diff --git a/patches/server/0710-Fix-PlayerDropItemEvent-using-wrong-item.patch b/patches/server/0708-Fix-PlayerDropItemEvent-using-wrong-item.patch similarity index 94% rename from patches/server/0710-Fix-PlayerDropItemEvent-using-wrong-item.patch rename to patches/server/0708-Fix-PlayerDropItemEvent-using-wrong-item.patch index 32e0e834e8..7c1f3c6618 100644 --- a/patches/server/0710-Fix-PlayerDropItemEvent-using-wrong-item.patch +++ b/patches/server/0708-Fix-PlayerDropItemEvent-using-wrong-item.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Fix PlayerDropItemEvent using wrong item diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 0406d7fc88a0d42f0085d6736f54ff699f11e99d..0b648abb4e7a116912b327641a810b4ec574baff 100644 +index 4c0eef82969f4666ad7e30db5e174fdeb396e644..b391cc306c719aee2cd334df8d92c3ed3d282c4d 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -2163,7 +2163,7 @@ public class ServerPlayer extends Player { diff --git a/patches/server/0711-Deobfuscate-stacktraces-in-log-messages-crash-report.patch b/patches/server/0709-Deobfuscate-stacktraces-in-log-messages-crash-report.patch similarity index 99% rename from patches/server/0711-Deobfuscate-stacktraces-in-log-messages-crash-report.patch rename to patches/server/0709-Deobfuscate-stacktraces-in-log-messages-crash-report.patch index eaa29316c6..79efe90ebf 100644 --- a/patches/server/0711-Deobfuscate-stacktraces-in-log-messages-crash-report.patch +++ b/patches/server/0709-Deobfuscate-stacktraces-in-log-messages-crash-report.patch @@ -92,10 +92,10 @@ index 5ee1e44647cfd7dff08ba7ce17d4b04246b7ab57..dc17410ac88e9f069a48ce7eb94b7cbe exclude("org/bukkit/craftbukkit/inventory/ItemStack*Test.class") } diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 3fedd75bea7197092823745deb7af8d4542efee5..2e0191e5cfe8e29fb0a6c4fc6a2a570d4b8ae449 100644 +index 14841f1df5fa8970b132eb5e8fc24069f687dd6a..9d31f1f1a224d1442cb7915df514c0522a4b876b 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java -@@ -505,6 +505,11 @@ public class PaperConfig { +@@ -504,6 +504,11 @@ public class PaperConfig { enableBrigadierConsoleCompletions = getBoolean("settings.console.enable-brigadier-completions", enableBrigadierConsoleCompletions); } diff --git a/patches/server/0712-Missing-Entity-Behavior-API.patch b/patches/server/0710-Missing-Entity-Behavior-API.patch similarity index 100% rename from patches/server/0712-Missing-Entity-Behavior-API.patch rename to patches/server/0710-Missing-Entity-Behavior-API.patch diff --git a/patches/server/0713-Ensure-disconnect-for-book-edit-is-called-on-main.patch b/patches/server/0711-Ensure-disconnect-for-book-edit-is-called-on-main.patch similarity index 100% rename from patches/server/0713-Ensure-disconnect-for-book-edit-is-called-on-main.patch rename to patches/server/0711-Ensure-disconnect-for-book-edit-is-called-on-main.patch diff --git a/patches/server/0714-Add-git-branch-and-commit-to-manifest.patch b/patches/server/0712-Add-git-branch-and-commit-to-manifest.patch similarity index 100% rename from patches/server/0714-Add-git-branch-and-commit-to-manifest.patch rename to patches/server/0712-Add-git-branch-and-commit-to-manifest.patch diff --git a/patches/server/0716-Fix-return-value-of-Block-applyBoneMeal-always-being.patch b/patches/server/0713-Fix-return-value-of-Block-applyBoneMeal-always-being.patch similarity index 100% rename from patches/server/0716-Fix-return-value-of-Block-applyBoneMeal-always-being.patch rename to patches/server/0713-Fix-return-value-of-Block-applyBoneMeal-always-being.patch diff --git a/patches/server/0717-Use-getChunkIfLoadedImmediately-in-places.patch b/patches/server/0714-Use-getChunkIfLoadedImmediately-in-places.patch similarity index 94% rename from patches/server/0717-Use-getChunkIfLoadedImmediately-in-places.patch rename to patches/server/0714-Use-getChunkIfLoadedImmediately-in-places.patch index 48f27ddcb2..16a701cd02 100644 --- a/patches/server/0717-Use-getChunkIfLoadedImmediately-in-places.patch +++ b/patches/server/0714-Use-getChunkIfLoadedImmediately-in-places.patch @@ -8,10 +8,10 @@ ticket level 33 (yes getChunkIfLoaded will actually perform a chunk load in that case). diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 7321e33cbc7d8e92ce4954eb1db8e7d5646d559b..f1b9affe2e971203f9a72d3abbf902559e0b04e5 100644 +index f28aefd9192b26f39e71826fa87cca60a799be39..f8c0574137cab33d0b5efe5d532efb132dcb914a 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -202,7 +202,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -203,7 +203,7 @@ public class ServerLevel extends Level implements WorldGenLevel { } @Override public LevelChunk getChunkIfLoaded(int x, int z) { // Paper - this was added in world too but keeping here for NMS ABI @@ -19,7 +19,7 @@ index 7321e33cbc7d8e92ce4954eb1db8e7d5646d559b..f1b9affe2e971203f9a72d3abbf90255 + return this.chunkSource.getChunkAtIfLoadedImmediately(x, z); // Paper } - // Paper start - Asynchronous IO + // Paper start diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java index 8121ed5ceb7bc6bb2d6dfdfa08d7b157e4a85b19..65f9af14140041a309a035180667b87c3bd4a930 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java diff --git a/patches/server/0718-Fix-commands-from-signs-not-firing-command-events.patch b/patches/server/0715-Fix-commands-from-signs-not-firing-command-events.patch similarity index 98% rename from patches/server/0718-Fix-commands-from-signs-not-firing-command-events.patch rename to patches/server/0715-Fix-commands-from-signs-not-firing-command-events.patch index ded329ad4e..eb64371de1 100644 --- a/patches/server/0718-Fix-commands-from-signs-not-firing-command-events.patch +++ b/patches/server/0715-Fix-commands-from-signs-not-firing-command-events.patch @@ -10,10 +10,10 @@ This patch changes sign command logic so that `run_command` click events: - sends failure messages to the player who clicked the sign diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index a0149014d5ab5b5905caf432cdbfa50b179bb881..e86e780fb5805afaa5d9f9fc146647723d895af2 100644 +index bb6feb0c3b974b206a3250abeaa19ee7c0cc257c..82c1f2adce730caaf1a9e57fb13689a055d1aa45 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -@@ -838,5 +838,10 @@ public class PaperWorldConfig { +@@ -841,5 +841,10 @@ public class PaperWorldConfig { private void fixInvulnerableEndCrystalExploit() { fixInvulnerableEndCrystalExploit = getBoolean("unsupported-settings.fix-invulnerable-end-crystal-exploit", fixInvulnerableEndCrystalExploit); } diff --git a/patches/server/0715-Improve-CraftChunk-getEntities.patch b/patches/server/0715-Improve-CraftChunk-getEntities.patch deleted file mode 100644 index 44eb1feb94..0000000000 --- a/patches/server/0715-Improve-CraftChunk-getEntities.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jake Potrebic -Date: Fri, 25 Jun 2021 12:06:35 -0700 -Subject: [PATCH] Improve CraftChunk#getEntities - - -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java -index 591a66dcdb717ad041120ab27de8f2f3a1975358..0a76032b48af4327580b99730e534f628924fe35 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java -@@ -110,11 +110,13 @@ public class CraftChunk implements Chunk { - this.getWorld().getChunkAt(x, z); // Transient load for this tick - } - -- Location location = new Location(null, 0, 0, 0); -- return this.getWorld().getEntities().stream().filter((entity) -> { -- entity.getLocation(location); -- return location.getBlockX() >> 4 == this.x && location.getBlockZ() >> 4 == this.z; -- }).toArray(Entity[]::new); -+ // Paper start - improve CraftChunk#getEntities -+ return this.worldServer.entityManager.sectionStorage.getExistingSectionsInChunk(ChunkPos.asLong(this.x, this.z)) -+ .flatMap(net.minecraft.world.level.entity.EntitySection::getEntities) -+ .map(net.minecraft.world.entity.Entity::getBukkitEntity) -+ .filter(entity -> entity != null && entity.isValid()) -+ .toArray(Entity[]::new); -+ // Paper end - } - - @Override diff --git a/patches/server/0719-Adds-PlayerArmSwingEvent.patch b/patches/server/0716-Adds-PlayerArmSwingEvent.patch similarity index 100% rename from patches/server/0719-Adds-PlayerArmSwingEvent.patch rename to patches/server/0716-Adds-PlayerArmSwingEvent.patch diff --git a/patches/server/0720-Fixes-kick-event-leave-message-not-being-sent.patch b/patches/server/0717-Fixes-kick-event-leave-message-not-being-sent.patch similarity index 100% rename from patches/server/0720-Fixes-kick-event-leave-message-not-being-sent.patch rename to patches/server/0717-Fixes-kick-event-leave-message-not-being-sent.patch diff --git a/patches/server/0721-Add-config-for-mobs-immune-to-default-effects.patch b/patches/server/0718-Add-config-for-mobs-immune-to-default-effects.patch similarity index 98% rename from patches/server/0721-Add-config-for-mobs-immune-to-default-effects.patch rename to patches/server/0718-Add-config-for-mobs-immune-to-default-effects.patch index eea39806c3..ad0e4cc410 100644 --- a/patches/server/0721-Add-config-for-mobs-immune-to-default-effects.patch +++ b/patches/server/0718-Add-config-for-mobs-immune-to-default-effects.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add config for mobs immune to default effects diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index e86e780fb5805afaa5d9f9fc146647723d895af2..e68e20606f2bcb39bc845649a53568e92dc2d590 100644 +index 82c1f2adce730caaf1a9e57fb13689a055d1aa45..b288b6148211299668ed941b3c5d4c89955d524c 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -666,6 +666,21 @@ public class PaperWorldConfig { diff --git a/patches/server/0722-Fix-incorrect-message-for-outdated-client.patch b/patches/server/0719-Fix-incorrect-message-for-outdated-client.patch similarity index 100% rename from patches/server/0722-Fix-incorrect-message-for-outdated-client.patch rename to patches/server/0719-Fix-incorrect-message-for-outdated-client.patch diff --git a/patches/server/0723-Fix-MerchantOffer-BuyB-Only-AssertionError.patch b/patches/server/0720-Fix-MerchantOffer-BuyB-Only-AssertionError.patch similarity index 100% rename from patches/server/0723-Fix-MerchantOffer-BuyB-Only-AssertionError.patch rename to patches/server/0720-Fix-MerchantOffer-BuyB-Only-AssertionError.patch diff --git a/patches/server/0724-Don-t-apply-cramming-damage-to-players.patch b/patches/server/0721-Don-t-apply-cramming-damage-to-players.patch similarity index 93% rename from patches/server/0724-Don-t-apply-cramming-damage-to-players.patch rename to patches/server/0721-Don-t-apply-cramming-damage-to-players.patch index 2da2ee728c..ef3404564b 100644 --- a/patches/server/0724-Don-t-apply-cramming-damage-to-players.patch +++ b/patches/server/0721-Don-t-apply-cramming-damage-to-players.patch @@ -11,10 +11,10 @@ It does not make a lot of sense to damage players if they get crammed, For those who really want it a config option is provided. diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index e68e20606f2bcb39bc845649a53568e92dc2d590..ea645479e7bc6182438f1f9e48b3cebef5ec85ad 100644 +index b288b6148211299668ed941b3c5d4c89955d524c..ddb0579d1447f5235c254586442eb2dc4ff21305 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -@@ -858,5 +858,10 @@ public class PaperWorldConfig { +@@ -861,5 +861,10 @@ public class PaperWorldConfig { private void showSignClickCommandFailureMessagesToPlayer() { showSignClickCommandFailureMessagesToPlayer = getBoolean("show-sign-click-command-failure-msgs-to-player", showSignClickCommandFailureMessagesToPlayer); } diff --git a/patches/server/0725-Rate-options-and-timings-for-sensors-and-behaviors.patch b/patches/server/0722-Rate-options-and-timings-for-sensors-and-behaviors.patch similarity index 97% rename from patches/server/0725-Rate-options-and-timings-for-sensors-and-behaviors.patch rename to patches/server/0722-Rate-options-and-timings-for-sensors-and-behaviors.patch index a086c2ce3d..eded769532 100644 --- a/patches/server/0725-Rate-options-and-timings-for-sensors-and-behaviors.patch +++ b/patches/server/0722-Rate-options-and-timings-for-sensors-and-behaviors.patch @@ -9,10 +9,10 @@ This adds config options to specify the tick rate for sensors ones might need tweaking. diff --git a/src/main/java/co/aikar/timings/MinecraftTimings.java b/src/main/java/co/aikar/timings/MinecraftTimings.java -index b9cdbf8acccfd6b207a0116f068168f3b8c8e17d..8c883d2f88acc731734a121ebb76821e66658cc9 100644 +index b47b7dce26805badd422c1867733ff4bfd00e9f4..b27021a42cbed3f0648a8d0903d00d03922ae221 100644 --- a/src/main/java/co/aikar/timings/MinecraftTimings.java +++ b/src/main/java/co/aikar/timings/MinecraftTimings.java -@@ -114,6 +114,14 @@ public final class MinecraftTimings { +@@ -113,6 +113,14 @@ public final class MinecraftTimings { return Timings.ofSafe("Minecraft", "## tickEntity - " + entityType + " - " + type, tickEntityTimer); } @@ -28,7 +28,7 @@ index b9cdbf8acccfd6b207a0116f068168f3b8c8e17d..8c883d2f88acc731734a121ebb76821e * Get a named timer for the specified tile entity type to track type specific timings. * @param entity diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index ea645479e7bc6182438f1f9e48b3cebef5ec85ad..862ba4bf39fdd798767ad5b22f1da0db7b27cb94 100644 +index ddb0579d1447f5235c254586442eb2dc4ff21305..7a7b5d68d4a269304f4a9cb20c071b47f19af608 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -3,14 +3,19 @@ package com.destroystokyo.paper; @@ -51,7 +51,7 @@ index ea645479e7bc6182438f1f9e48b3cebef5ec85ad..862ba4bf39fdd798767ad5b22f1da0db import org.bukkit.configuration.file.YamlConfiguration; import org.spigotmc.SpigotWorldConfig; -@@ -863,5 +868,58 @@ public class PaperWorldConfig { +@@ -866,5 +871,58 @@ public class PaperWorldConfig { private void playerCrammingDamage() { allowPlayerCrammingDamage = getBoolean("allow-player-cramming-damage", allowPlayerCrammingDamage); } diff --git a/patches/server/0726-Add-a-bunch-of-missing-forceDrop-toggles.patch b/patches/server/0723-Add-a-bunch-of-missing-forceDrop-toggles.patch similarity index 100% rename from patches/server/0726-Add-a-bunch-of-missing-forceDrop-toggles.patch rename to patches/server/0723-Add-a-bunch-of-missing-forceDrop-toggles.patch diff --git a/patches/server/0727-Stinger-API.patch b/patches/server/0724-Stinger-API.patch similarity index 100% rename from patches/server/0727-Stinger-API.patch rename to patches/server/0724-Stinger-API.patch diff --git a/patches/server/0728-Fix-incosistency-issue-with-empty-map-items-in-CB.patch b/patches/server/0725-Fix-incosistency-issue-with-empty-map-items-in-CB.patch similarity index 100% rename from patches/server/0728-Fix-incosistency-issue-with-empty-map-items-in-CB.patch rename to patches/server/0725-Fix-incosistency-issue-with-empty-map-items-in-CB.patch diff --git a/patches/server/0729-Add-System.out-err-catcher.patch b/patches/server/0726-Add-System.out-err-catcher.patch similarity index 98% rename from patches/server/0729-Add-System.out-err-catcher.patch rename to patches/server/0726-Add-System.out-err-catcher.patch index 1c0a1c5172..2b98767e7e 100644 --- a/patches/server/0729-Add-System.out-err-catcher.patch +++ b/patches/server/0726-Add-System.out-err-catcher.patch @@ -105,7 +105,7 @@ index 0000000000000000000000000000000000000000..88608afed45f25f822121214f87e9f9b + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index ea9f97bc5196a26ae5caaf48fa846bf92e8f6188..41255bc4c03a02e76fbfc3b0b34c907fb3ad05b1 100644 +index 5e8fb79384c6af845b57051ed0244ec8e835822b..4687827c096ecf8872ab39b00fbf9261ba5c3689 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -18,6 +18,7 @@ import com.mojang.serialization.Lifecycle; diff --git a/patches/server/0730-Fix-test-not-bootstrapping.patch b/patches/server/0727-Fix-test-not-bootstrapping.patch similarity index 100% rename from patches/server/0730-Fix-test-not-bootstrapping.patch rename to patches/server/0727-Fix-test-not-bootstrapping.patch diff --git a/patches/server/0731-Rewrite-LogEvents-to-contain-the-source-jars-in-stac.patch b/patches/server/0728-Rewrite-LogEvents-to-contain-the-source-jars-in-stac.patch similarity index 100% rename from patches/server/0731-Rewrite-LogEvents-to-contain-the-source-jars-in-stac.patch rename to patches/server/0728-Rewrite-LogEvents-to-contain-the-source-jars-in-stac.patch diff --git a/patches/server/0732-Improve-boat-collision-performance.patch b/patches/server/0729-Improve-boat-collision-performance.patch similarity index 90% rename from patches/server/0732-Improve-boat-collision-performance.patch rename to patches/server/0729-Improve-boat-collision-performance.patch index 6d28994550..092d003ae6 100644 --- a/patches/server/0732-Improve-boat-collision-performance.patch +++ b/patches/server/0729-Improve-boat-collision-performance.patch @@ -54,21 +54,22 @@ index 2c49851c2113692d666ccdde6043c82370725010..6175360eb2b19c8197cc5b82a0903021 } } diff --git a/src/main/java/net/minecraft/world/entity/vehicle/Boat.java b/src/main/java/net/minecraft/world/entity/vehicle/Boat.java -index aa7c022c4faade23bd9061311d4152cf845d3331..99124b70d82140b108d424a5206657efe94f184e 100644 +index aa7c022c4faade23bd9061311d4152cf845d3331..391454a58d18d7373b974e094fd62514ca0d0b6b 100644 --- a/src/main/java/net/minecraft/world/entity/vehicle/Boat.java +++ b/src/main/java/net/minecraft/world/entity/vehicle/Boat.java -@@ -688,8 +688,7 @@ public class Boat extends Entity { +@@ -688,8 +688,8 @@ public class Boat extends Entity { this.invFriction = 0.05F; if (this.oldStatus == Boat.Status.IN_AIR && this.status != Boat.Status.IN_AIR && this.status != Boat.Status.ON_LAND) { this.waterLevel = this.getY(1.0D); - this.setPos(this.getX(), (double) (this.getWaterLevelAbove() - this.getBbHeight()) + 0.101D, this.getZ()); - this.setDeltaMovement(this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D)); -+ this.setDeltaMovement(this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D).add(0.0, ((double) (this.getWaterLevelAbove() - this.getBbHeight()) + 0.101D) - this.getY(), 0.0)); // Paper ++ this.move(MoverType.SELF, new Vec3(0.0, ((double) (this.getWaterLevelAbove() - this.getBbHeight()) + 0.101D) - this.getY(), 0.0)); // Paper ++ this.setDeltaMovement(this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D)); // Paper this.lastYd = 0.0D; this.status = Boat.Status.IN_WATER; } else { diff --git a/src/main/java/net/minecraft/world/item/BoatItem.java b/src/main/java/net/minecraft/world/item/BoatItem.java -index c0864c833fd313e6ba9339ecc7f9e2359954bda3..9a11248b13d231c1797e14f843cb8cbec0d35a6e 100644 +index c0864c833fd313e6ba9339ecc7f9e2359954bda3..8998f6233a3135fda928469ae8bb7119e16eee1a 100644 --- a/src/main/java/net/minecraft/world/item/BoatItem.java +++ b/src/main/java/net/minecraft/world/item/BoatItem.java @@ -67,7 +67,7 @@ public class BoatItem extends Item { @@ -76,7 +77,7 @@ index c0864c833fd313e6ba9339ecc7f9e2359954bda3..9a11248b13d231c1797e14f843cb8cbe entityboat.setType(this.type); entityboat.setYRot(user.getYRot()); - if (!world.noCollision(entityboat, entityboat.getBoundingBox().inflate(-0.1D))) { -+ if (!world.noCollision(entityboat, entityboat.getBoundingBox().inflate(-net.minecraft.Util.COLLISION_EPSILON))) { // Paper ++ if (!world.noCollision(entityboat, entityboat.getBoundingBox())) { // Paper return InteractionResultHolder.fail(itemstack); } else { if (!world.isClientSide) { diff --git a/patches/server/0733-Prevent-AFK-kick-while-watching-end-credits.patch b/patches/server/0730-Prevent-AFK-kick-while-watching-end-credits.patch similarity index 93% rename from patches/server/0733-Prevent-AFK-kick-while-watching-end-credits.patch rename to patches/server/0730-Prevent-AFK-kick-while-watching-end-credits.patch index 158e650685..8dc2907ae4 100644 --- a/patches/server/0733-Prevent-AFK-kick-while-watching-end-credits.patch +++ b/patches/server/0730-Prevent-AFK-kick-while-watching-end-credits.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Prevent AFK kick while watching end credits. diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 064aecb28f05fcf572ee7d29f611a31cc7b6e49a..ad1a9c6c354d40d5fa589666b1b00792d9cd6161 100644 +index aa66f019024564108570ac72b60c86a755a16c41..73613c05784446c97420af46697cbc09bc4fd481 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -389,7 +389,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser diff --git a/patches/server/0734-Allow-skipping-writing-of-comments-to-server.propert.patch b/patches/server/0731-Allow-skipping-writing-of-comments-to-server.propert.patch similarity index 100% rename from patches/server/0734-Allow-skipping-writing-of-comments-to-server.propert.patch rename to patches/server/0731-Allow-skipping-writing-of-comments-to-server.propert.patch diff --git a/patches/server/0735-Add-PlayerSetSpawnEvent.patch b/patches/server/0732-Add-PlayerSetSpawnEvent.patch similarity index 97% rename from patches/server/0735-Add-PlayerSetSpawnEvent.patch rename to patches/server/0732-Add-PlayerSetSpawnEvent.patch index e51242a58e..37e6c95501 100644 --- a/patches/server/0735-Add-PlayerSetSpawnEvent.patch +++ b/patches/server/0732-Add-PlayerSetSpawnEvent.patch @@ -18,7 +18,7 @@ index e95f2222814e104bf9194a96385737dffe2cb2b5..249ab7357aa19d87179fa4c3ae89d9d3 String string = resourceKey.location().toString(); diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 1f5dfc88e6e9674f7c557848fa1d1a3a2800ddb5..8e1478593fcf79ee7a25d666503736cbe707a52c 100644 +index b242109d2d5248a4342635978def79d32d99928b..3e0c5379e40607554324e8fdc72a0f2a71ec7aaa 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -1262,7 +1262,7 @@ public class ServerPlayer extends Player { @@ -67,7 +67,7 @@ index 1f5dfc88e6e9674f7c557848fa1d1a3a2800ddb5..8e1478593fcf79ee7a25d666503736cb this.respawnPosition = pos; diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index d02667f8623022fae4ad468718504c1c04023034..8ec582cc5c33396fa336a9cc6494f3aacd3b4339 100644 +index eaae43ffe4701c10133ad4fd2256b22b565e6f51..cf8ed790e09987528178519ba99376f27b15245f 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java @@ -893,7 +893,7 @@ public abstract class PlayerList { diff --git a/patches/server/0736-Make-hoppers-respect-inventory-max-stack-size.patch b/patches/server/0733-Make-hoppers-respect-inventory-max-stack-size.patch similarity index 100% rename from patches/server/0736-Make-hoppers-respect-inventory-max-stack-size.patch rename to patches/server/0733-Make-hoppers-respect-inventory-max-stack-size.patch diff --git a/patches/server/0737-Optimize-entity-tracker-passenger-checks.patch b/patches/server/0734-Optimize-entity-tracker-passenger-checks.patch similarity index 100% rename from patches/server/0737-Optimize-entity-tracker-passenger-checks.patch rename to patches/server/0734-Optimize-entity-tracker-passenger-checks.patch diff --git a/patches/server/0738-Config-option-for-Piglins-guarding-chests.patch b/patches/server/0735-Config-option-for-Piglins-guarding-chests.patch similarity index 95% rename from patches/server/0738-Config-option-for-Piglins-guarding-chests.patch rename to patches/server/0735-Config-option-for-Piglins-guarding-chests.patch index 6b2fe6842a..d7359f9659 100644 --- a/patches/server/0738-Config-option-for-Piglins-guarding-chests.patch +++ b/patches/server/0735-Config-option-for-Piglins-guarding-chests.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Config option for Piglins guarding chests diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index ea397cd57ccbf7efc4f31d532cd6dfdccfcee8d8..0f91fbf2ae9735943d3214e8d26c1951cf0b5160 100644 +index 7a7b5d68d4a269304f4a9cb20c071b47f19af608..402b25979a217dc4662c074ee084022f57986f49 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -54,6 +54,11 @@ public class PaperWorldConfig { diff --git a/patches/server/0739-Added-EntityDamageItemEvent.patch b/patches/server/0736-Added-EntityDamageItemEvent.patch similarity index 100% rename from patches/server/0739-Added-EntityDamageItemEvent.patch rename to patches/server/0736-Added-EntityDamageItemEvent.patch diff --git a/patches/server/0740-Optimize-indirect-passenger-iteration.patch b/patches/server/0737-Optimize-indirect-passenger-iteration.patch similarity index 93% rename from patches/server/0740-Optimize-indirect-passenger-iteration.patch rename to patches/server/0737-Optimize-indirect-passenger-iteration.patch index 02ae958641..0d1bb74c9a 100644 --- a/patches/server/0740-Optimize-indirect-passenger-iteration.patch +++ b/patches/server/0737-Optimize-indirect-passenger-iteration.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Optimize indirect passenger iteration diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index b21c0c583abe9f7be5f01fc4d16029be03a5779e..97f4cb48879af136470214d94d37d386d13aee8d 100644 +index 4f1a57c24688d1ae537f5fd1c1649e035f20abfd..b7c0574aa8e3aec3bc13064b59c5f7efb075cf13 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -3478,26 +3478,41 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -3501,26 +3501,41 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n } private Stream getIndirectPassengersStream() { diff --git a/patches/server/0741-Fix-block-drops-position-losing-precision-millions-o.patch b/patches/server/0738-Fix-block-drops-position-losing-precision-millions-o.patch similarity index 100% rename from patches/server/0741-Fix-block-drops-position-losing-precision-millions-o.patch rename to patches/server/0738-Fix-block-drops-position-losing-precision-millions-o.patch diff --git a/patches/server/0742-Configurable-item-frame-map-cursor-update-interval.patch b/patches/server/0739-Configurable-item-frame-map-cursor-update-interval.patch similarity index 93% rename from patches/server/0742-Configurable-item-frame-map-cursor-update-interval.patch rename to patches/server/0739-Configurable-item-frame-map-cursor-update-interval.patch index cbbb7ff404..439df5ffa9 100644 --- a/patches/server/0742-Configurable-item-frame-map-cursor-update-interval.patch +++ b/patches/server/0739-Configurable-item-frame-map-cursor-update-interval.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Configurable item frame map cursor update interval diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index a6957e06d014f4c86feae00ab60195a9244e4d04..cbb8bd956ea90663e9066343cc9a024cdae180ee 100644 +index 402b25979a217dc4662c074ee084022f57986f49..4ae21ba5d04c954592ec4f85d43ff71ec7dadea1 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -@@ -854,6 +854,11 @@ public class PaperWorldConfig { +@@ -857,6 +857,11 @@ public class PaperWorldConfig { mapItemFrameCursorLimit = getInt("map-item-frame-cursor-limit", mapItemFrameCursorLimit); } diff --git a/patches/server/0743-Make-EntityUnleashEvent-cancellable.patch b/patches/server/0740-Make-EntityUnleashEvent-cancellable.patch similarity index 100% rename from patches/server/0743-Make-EntityUnleashEvent-cancellable.patch rename to patches/server/0740-Make-EntityUnleashEvent-cancellable.patch diff --git a/patches/server/0744-Clear-bucket-NBT-after-dispense.patch b/patches/server/0741-Clear-bucket-NBT-after-dispense.patch similarity index 100% rename from patches/server/0744-Clear-bucket-NBT-after-dispense.patch rename to patches/server/0741-Clear-bucket-NBT-after-dispense.patch diff --git a/patches/server/0745-Set-AsyncAppender-dispatch-thread-to-be-a-daemon-thr.patch b/patches/server/0742-Set-AsyncAppender-dispatch-thread-to-be-a-daemon-thr.patch similarity index 100% rename from patches/server/0745-Set-AsyncAppender-dispatch-thread-to-be-a-daemon-thr.patch rename to patches/server/0742-Set-AsyncAppender-dispatch-thread-to-be-a-daemon-thr.patch diff --git a/patches/server/0746-Respect-despawn-rate-in-item-merge-check.patch b/patches/server/0743-Respect-despawn-rate-in-item-merge-check.patch similarity index 100% rename from patches/server/0746-Respect-despawn-rate-in-item-merge-check.patch rename to patches/server/0743-Respect-despawn-rate-in-item-merge-check.patch diff --git a/patches/server/0747-Move-BlockPistonRetractEvent-to-fix-duplication.patch b/patches/server/0744-Move-BlockPistonRetractEvent-to-fix-duplication.patch similarity index 100% rename from patches/server/0747-Move-BlockPistonRetractEvent-to-fix-duplication.patch rename to patches/server/0744-Move-BlockPistonRetractEvent-to-fix-duplication.patch diff --git a/patches/server/0748-Change-EnderEye-target-without-changing-other-things.patch b/patches/server/0745-Change-EnderEye-target-without-changing-other-things.patch similarity index 100% rename from patches/server/0748-Change-EnderEye-target-without-changing-other-things.patch rename to patches/server/0745-Change-EnderEye-target-without-changing-other-things.patch diff --git a/patches/server/0749-Add-BlockBreakBlockEvent.patch b/patches/server/0746-Add-BlockBreakBlockEvent.patch similarity index 100% rename from patches/server/0749-Add-BlockBreakBlockEvent.patch rename to patches/server/0746-Add-BlockBreakBlockEvent.patch diff --git a/patches/server/0750-Vanilla-command-permission-fixes.patch b/patches/server/0747-Vanilla-command-permission-fixes.patch similarity index 100% rename from patches/server/0750-Vanilla-command-permission-fixes.patch rename to patches/server/0747-Vanilla-command-permission-fixes.patch diff --git a/patches/server/0751-Make-CallbackExecutor-strict-again.patch b/patches/server/0748-Make-CallbackExecutor-strict-again.patch similarity index 93% rename from patches/server/0751-Make-CallbackExecutor-strict-again.patch rename to patches/server/0748-Make-CallbackExecutor-strict-again.patch index f236b3b25f..148f7df5d3 100644 --- a/patches/server/0751-Make-CallbackExecutor-strict-again.patch +++ b/patches/server/0748-Make-CallbackExecutor-strict-again.patch @@ -10,10 +10,10 @@ schedules. Effectively, use the callback executor as a tool of finding issues rather than hiding these issues. diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 1c662deee2a3184c7612cf7de5f798753fbd7381..d11c70dc9f65171827190028ebcc4db7331fff20 100644 +index 91364805047c055a8755b7a2900988c750589ff7..527704fccfd905414e25ed53aff2b7dceb5cf94f 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -182,17 +182,28 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -154,17 +154,28 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider public final CallbackExecutor callbackExecutor = new CallbackExecutor(); public static final class CallbackExecutor implements java.util.concurrent.Executor, Runnable { diff --git a/patches/server/0752-Prevent-unload-calls-removing-tickets-for-sync-loads.patch b/patches/server/0749-Prevent-unload-calls-removing-tickets-for-sync-loads.patch similarity index 94% rename from patches/server/0752-Prevent-unload-calls-removing-tickets-for-sync-loads.patch rename to patches/server/0749-Prevent-unload-calls-removing-tickets-for-sync-loads.patch index 293eb1263c..e44eb397c9 100644 --- a/patches/server/0752-Prevent-unload-calls-removing-tickets-for-sync-loads.patch +++ b/patches/server/0749-Prevent-unload-calls-removing-tickets-for-sync-loads.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Prevent unload() calls removing tickets for sync loads diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 58061491be6713a21e3f9e67a0b1ac0ecceb24d6..6b95085a5c8c7b91a326f205e56a978deacbbebd 100644 +index 16b406067ac7697ab598f9f27f5e688687f206ee..f5ccbff7a6b51cf71e28e514d853a500f9acd43f 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -573,6 +573,8 @@ public class ServerChunkCache extends ChunkSource { +@@ -734,6 +734,8 @@ public class ServerChunkCache extends ChunkSource { return completablefuture; } @@ -17,7 +17,7 @@ index 58061491be6713a21e3f9e67a0b1ac0ecceb24d6..6b95085a5c8c7b91a326f205e56a978d private CompletableFuture> getChunkFutureMainThread(int i, int j, ChunkStatus chunkstatus, boolean flag) { // Paper start - add isUrgent - old sig left in place for dirty nms plugins return getChunkFutureMainThread(i, j, chunkstatus, flag, false); -@@ -591,9 +593,12 @@ public class ServerChunkCache extends ChunkSource { +@@ -752,9 +754,12 @@ public class ServerChunkCache extends ChunkSource { ChunkHolder.FullChunkStatus currentChunkState = ChunkHolder.getFullChunkStatus(playerchunk.getTicketLevel()); currentlyUnloading = (oldChunkState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && !currentChunkState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)); } @@ -30,7 +30,7 @@ index 58061491be6713a21e3f9e67a0b1ac0ecceb24d6..6b95085a5c8c7b91a326f205e56a978d if (isUrgent) this.distanceManager.markUrgent(chunkcoordintpair); // Paper - Chunk priority if (this.chunkAbsent(playerchunk, l)) { ProfilerFiller gameprofilerfiller = this.level.getProfiler(); -@@ -604,12 +609,20 @@ public class ServerChunkCache extends ChunkSource { +@@ -765,12 +770,20 @@ public class ServerChunkCache extends ChunkSource { playerchunk = this.getVisibleChunkIfPresent(k); gameprofilerfiller.pop(); if (this.chunkAbsent(playerchunk, l)) { diff --git a/patches/server/0753-Do-not-allow-ticket-level-changes-while-unloading-pl.patch b/patches/server/0750-Do-not-allow-ticket-level-changes-while-unloading-pl.patch similarity index 77% rename from patches/server/0753-Do-not-allow-ticket-level-changes-while-unloading-pl.patch rename to patches/server/0750-Do-not-allow-ticket-level-changes-while-unloading-pl.patch index a028936458..a0f0e55d26 100644 --- a/patches/server/0753-Do-not-allow-ticket-level-changes-while-unloading-pl.patch +++ b/patches/server/0750-Do-not-allow-ticket-level-changes-while-unloading-pl.patch @@ -8,18 +8,18 @@ Sync loading the chunk at this stage would cause it to load older data, as well as screwing our region state. diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index d11c70dc9f65171827190028ebcc4db7331fff20..62d5bfe5912e13bfef4f8e65c9a030e08b2cc4aa 100644 +index 527704fccfd905414e25ed53aff2b7dceb5cf94f..34a609eca833450ef6fb265e84a0c8d7ba89a2e7 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -336,6 +336,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -341,6 +341,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } - // Paper end + // Tuiniy end + boolean unloadingPlayerChunk = false; // Paper - do not allow ticket level changes while unloading chunks - private final java.util.concurrent.ExecutorService lightThread; // Paper public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureManager structureManager, Executor executor, BlockableEventLoop mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier persistentStateManagerFactory, int viewDistance, boolean dsync) { super(new File(session.getDimensionPath(world.dimension()), "region"), dataFixer, dsync); -@@ -853,6 +854,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + this.visibleChunkMap = this.updatingChunkMap.clone(); +@@ -802,6 +803,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @Nullable ChunkHolder updateChunkScheduling(long pos, int level, @Nullable ChunkHolder holder, int k) { @@ -27,7 +27,7 @@ index d11c70dc9f65171827190028ebcc4db7331fff20..62d5bfe5912e13bfef4f8e65c9a030e0 if (k > ChunkMap.MAX_CHUNK_DISTANCE && level > ChunkMap.MAX_CHUNK_DISTANCE) { return holder; } else { -@@ -1106,6 +1108,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1058,6 +1060,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider if (completablefuture1 != completablefuture) { this.scheduleUnload(pos, holder); } else { @@ -37,22 +37,22 @@ index d11c70dc9f65171827190028ebcc4db7331fff20..62d5bfe5912e13bfef4f8e65c9a030e0 + this.unloadingPlayerChunk = true; + try { + // Paper end - do not allow ticket level changes while unloading chunks - if (this.pendingUnloads.remove(pos, holder) && ichunkaccess != null) { - if (ichunkaccess instanceof LevelChunk) { - ((LevelChunk) ichunkaccess).setLoaded(false); -@@ -1132,6 +1140,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - this.lightEngine.tryScheduleUpdate(); - this.progressListener.onStatusChange(ichunkaccess.getPos(), (ChunkStatus) null); - } + // Paper start + boolean removed; + if ((removed = this.pendingUnloads.remove(pos, holder)) && ichunkaccess != null) { +@@ -1094,6 +1102,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + this.regionManagers.get(index).removeChunk(holder.pos.x, holder.pos.z); + } + } // Paper end + } finally { this.unloadingPlayerChunk = unloadingBefore; } // Paper - do not allow ticket level changes while unloading chunks } }; diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 6b95085a5c8c7b91a326f205e56a978deacbbebd..552d3c27abd582fcd09c8494dc0b796c4fc0291d 100644 +index f5ccbff7a6b51cf71e28e514d853a500f9acd43f..11907bdf8c2b26d02aac5d7696d5f41d9c7d334d 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -680,6 +680,7 @@ public class ServerChunkCache extends ChunkSource { +@@ -841,6 +841,7 @@ public class ServerChunkCache extends ChunkSource { public boolean runDistanceManagerUpdates() { if (distanceManager.delayDistanceManagerTick) return false; // Paper - Chunk priority diff --git a/patches/server/0754-Do-not-allow-the-server-to-unload-chunks-at-request-.patch b/patches/server/0751-Do-not-allow-the-server-to-unload-chunks-at-request-.patch similarity index 87% rename from patches/server/0754-Do-not-allow-the-server-to-unload-chunks-at-request-.patch rename to patches/server/0751-Do-not-allow-the-server-to-unload-chunks-at-request-.patch index 1db416a2b0..4ad0b1db95 100644 --- a/patches/server/0754-Do-not-allow-the-server-to-unload-chunks-at-request-.patch +++ b/patches/server/0751-Do-not-allow-the-server-to-unload-chunks-at-request-.patch @@ -10,10 +10,10 @@ to be unloaded will simply be unloaded next tick, rather than immediately. diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 552d3c27abd582fcd09c8494dc0b796c4fc0291d..a1cee90b0435b96908a9095151e4d69255b5d0e6 100644 +index 11907bdf8c2b26d02aac5d7696d5f41d9c7d334d..9196be25ff874a0c81868906c5b86bfe4e2968f5 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -747,6 +747,7 @@ public class ServerChunkCache extends ChunkSource { +@@ -908,6 +908,7 @@ public class ServerChunkCache extends ChunkSource { // CraftBukkit start - modelled on below public void purgeUnload() { diff --git a/patches/server/0755-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch b/patches/server/0752-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch similarity index 96% rename from patches/server/0755-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch rename to patches/server/0752-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch index 0428030fb3..a22cf004f5 100644 --- a/patches/server/0755-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch +++ b/patches/server/0752-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch @@ -9,10 +9,10 @@ chunk through it. This should also be OK from a leak prevention/ state desync POV because the TE is getting unloaded anyways. diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index f1b9affe2e971203f9a72d3abbf902559e0b04e5..34497bf24046225108c47f1faf52bb1830f56d14 100644 +index f8c0574137cab33d0b5efe5d532efb132dcb914a..7000909d2910e3e0e87e05f0a8bda8269f8d7e05 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1206,9 +1206,13 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1292,9 +1292,13 @@ public class ServerLevel extends Level implements WorldGenLevel { // Spigot Start for (net.minecraft.world.level.block.entity.BlockEntity tileentity : chunk.getBlockEntities().values()) { if (tileentity instanceof net.minecraft.world.Container) { diff --git a/patches/server/0756-Correctly-handle-recursion-for-chunkholder-updates.patch b/patches/server/0753-Correctly-handle-recursion-for-chunkholder-updates.patch similarity index 85% rename from patches/server/0756-Correctly-handle-recursion-for-chunkholder-updates.patch rename to patches/server/0753-Correctly-handle-recursion-for-chunkholder-updates.patch index 9ce61f4bf3..850a20267c 100644 --- a/patches/server/0756-Correctly-handle-recursion-for-chunkholder-updates.patch +++ b/patches/server/0753-Correctly-handle-recursion-for-chunkholder-updates.patch @@ -8,21 +8,21 @@ cause a recursive call which would handle the increase but then the caller would think the chunk would be unloaded. diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java -index 8d59d735b2862ecc12cbfebc57a02ebbfd1e125e..ee71e8678e58441287a9cd6cf00d5635daddc60f 100644 +index 823451570e02ee75bfce1c106779a0502187fcea..d4ea81824acdc1220616b24ea0f465db97f4a343 100644 --- a/src/main/java/net/minecraft/server/level/ChunkHolder.java +++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java -@@ -626,8 +626,10 @@ public class ChunkHolder { +@@ -619,8 +619,10 @@ public class ChunkHolder { playerchunkmap.onFullChunkStatusChange(this.pos, playerchunk_state); } + protected long updateCount; // Paper - correctly handle recursion protected void updateFutures(ChunkMap chunkStorage, Executor executor) { - ensureTickThread("Async ticket level update"); // Paper + io.papermc.paper.util.TickThread.ensureTickThread("Async ticket level update"); // Paper + long updateCount = ++this.updateCount; // Paper - correctly handle recursion ChunkStatus chunkstatus = ChunkHolder.getStatus(this.oldTicketLevel); ChunkStatus chunkstatus1 = ChunkHolder.getStatus(this.ticketLevel); boolean flag = this.oldTicketLevel <= ChunkMap.MAX_CHUNK_DISTANCE; -@@ -669,6 +671,12 @@ public class ChunkHolder { +@@ -662,6 +664,12 @@ public class ChunkHolder { // Run callback right away if the future was already done chunkStorage.callbackExecutor.run(); diff --git a/patches/server/0757-Separate-lookup-locking-from-state-access-in-UserCac.patch b/patches/server/0754-Separate-lookup-locking-from-state-access-in-UserCac.patch similarity index 100% rename from patches/server/0757-Separate-lookup-locking-from-state-access-in-UserCac.patch rename to patches/server/0754-Separate-lookup-locking-from-state-access-in-UserCac.patch diff --git a/patches/server/0758-Fix-chunks-refusing-to-unload-at-low-TPS.patch b/patches/server/0755-Fix-chunks-refusing-to-unload-at-low-TPS.patch similarity index 88% rename from patches/server/0758-Fix-chunks-refusing-to-unload-at-low-TPS.patch rename to patches/server/0755-Fix-chunks-refusing-to-unload-at-low-TPS.patch index 05d46f1b22..a072c5a758 100644 --- a/patches/server/0758-Fix-chunks-refusing-to-unload-at-low-TPS.patch +++ b/patches/server/0755-Fix-chunks-refusing-to-unload-at-low-TPS.patch @@ -10,10 +10,10 @@ chunk future to complete. We can simply schedule to the immediate executor to get this effect, rather than the main mailbox. diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 62d5bfe5912e13bfef4f8e65c9a030e08b2cc4aa..2cac6b2df5b92690ad347c0b6dc29525e0a53788 100644 +index 34a609eca833450ef6fb265e84a0c8d7ba89a2e7..a2895a48556558ee83ab0c53137da9e5c6208a83 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -1502,9 +1502,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1451,9 +1451,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider chunk.unpackTicks(); return chunk; }); diff --git a/patches/server/0759-Do-not-allow-ticket-level-changes-when-updating-chun.patch b/patches/server/0756-Do-not-allow-ticket-level-changes-when-updating-chun.patch similarity index 93% rename from patches/server/0759-Do-not-allow-ticket-level-changes-when-updating-chun.patch rename to patches/server/0756-Do-not-allow-ticket-level-changes-when-updating-chun.patch index 1d432decac..b270fb651b 100644 --- a/patches/server/0759-Do-not-allow-ticket-level-changes-when-updating-chun.patch +++ b/patches/server/0756-Do-not-allow-ticket-level-changes-when-updating-chun.patch @@ -8,7 +8,7 @@ This WILL cause state corruption if it happens. So, don't allow it. diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java -index ee71e8678e58441287a9cd6cf00d5635daddc60f..7c0d404e5ac6819326276fb6424e33949c4ee08b 100644 +index d4ea81824acdc1220616b24ea0f465db97f4a343..b0a6eb7846580489e0476e69565676e77fd224cd 100644 --- a/src/main/java/net/minecraft/server/level/ChunkHolder.java +++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java @@ -599,7 +599,13 @@ public class ChunkHolder { @@ -25,7 +25,7 @@ index ee71e8678e58441287a9cd6cf00d5635daddc60f..7c0d404e5ac6819326276fb6424e3394 }, executor); this.pendingFullStateConfirmation = completablefuture1; completablefuture.thenAccept((either) -> { -@@ -623,7 +629,12 @@ public class ChunkHolder { +@@ -616,7 +622,12 @@ public class ChunkHolder { private void demoteFullChunk(ChunkMap playerchunkmap, ChunkHolder.FullChunkStatus playerchunk_state) { this.pendingFullStateConfirmation.cancel(false); diff --git a/patches/server/0760-Do-not-submit-profile-lookups-to-worldgen-threads.patch b/patches/server/0757-Do-not-submit-profile-lookups-to-worldgen-threads.patch similarity index 100% rename from patches/server/0760-Do-not-submit-profile-lookups-to-worldgen-threads.patch rename to patches/server/0757-Do-not-submit-profile-lookups-to-worldgen-threads.patch diff --git a/patches/server/0761-Log-when-the-async-catcher-is-tripped.patch b/patches/server/0758-Log-when-the-async-catcher-is-tripped.patch similarity index 76% rename from patches/server/0761-Log-when-the-async-catcher-is-tripped.patch rename to patches/server/0758-Log-when-the-async-catcher-is-tripped.patch index 2208de0b69..7006040b25 100644 --- a/patches/server/0761-Log-when-the-async-catcher-is-tripped.patch +++ b/patches/server/0758-Log-when-the-async-catcher-is-tripped.patch @@ -7,12 +7,12 @@ The chunk system can swallow the exception given it's all built with completablefuture, so ensure it is at least printed. diff --git a/src/main/java/org/spigotmc/AsyncCatcher.java b/src/main/java/org/spigotmc/AsyncCatcher.java -index 7a23b56752f6733ee626a8b1e4c3b78591855c4e..54bd77934f58bed162574356d8e2b71dd01d6c9b 100644 +index 7585a30e8f063ac2656b5de519b1e9edaceffbc7..41ddd9e0517571c7bffb494766f7097198b50842 100644 --- a/src/main/java/org/spigotmc/AsyncCatcher.java +++ b/src/main/java/org/spigotmc/AsyncCatcher.java @@ -12,6 +12,7 @@ public class AsyncCatcher { - if ( AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().serverThread ) + if ( (AsyncCatcher.enabled || io.papermc.paper.util.TickThread.STRICT_THREAD_CHECKS) && Thread.currentThread() != MinecraftServer.getServer().serverThread ) // Paper { + MinecraftServer.LOGGER.fatal("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable()); // Paper throw new IllegalStateException( "Asynchronous " + reason + "!" ); diff --git a/patches/server/0762-Sanitize-ResourceLocation-error-logging.patch b/patches/server/0759-Sanitize-ResourceLocation-error-logging.patch similarity index 100% rename from patches/server/0762-Sanitize-ResourceLocation-error-logging.patch rename to patches/server/0759-Sanitize-ResourceLocation-error-logging.patch diff --git a/patches/server/0763-Fix-and-optimize-legacy-world-conversion.patch b/patches/server/0760-Fix-and-optimize-legacy-world-conversion.patch similarity index 100% rename from patches/server/0763-Fix-and-optimize-legacy-world-conversion.patch rename to patches/server/0760-Fix-and-optimize-legacy-world-conversion.patch diff --git a/patches/server/0761-Optimise-general-POI-access.patch b/patches/server/0761-Optimise-general-POI-access.patch new file mode 100644 index 0000000000..7a02e74ba0 --- /dev/null +++ b/patches/server/0761-Optimise-general-POI-access.patch @@ -0,0 +1,992 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sun, 31 Jan 2021 02:29:24 -0800 +Subject: [PATCH] Optimise general POI access + +There are a couple of problems with mojang's POI code. +Firstly, it's all streams. Unsurprisingly, stacking +streams on top of each other is horrible for performance +and ultimately took up half of a villager's tick! + +Secondly, sometime's the search radius is large and there are +a significant number of poi entries per chunk section. Even +removing streams at this point doesn't help much. The only solution +is to start at the search point and iterate outwards. This +type of approach shows massive gains for portals, simply because +we can avoid sync loading a large area of chunks. I also tested +a massive farm I found in JellySquid's discord, which showed +to benefit significantly simply because the farm had so many +portal blocks that searching through them all was very slow. + +Great care has been taken so that behavior remains identical to +vanilla, however I cannot account for oddball Stream API +implementations, if they even exist (streams can technically +be loose with iteration order in a sorted stream given its +source stream is not tagged with ordered, and mojang does not +tag the source stream as ordered). However in my testing on openjdk +there showed no difference, as expected. + +This patch also specifically optimises other areas of code to +use PoiAccess. For example, some villager AI and portaling code +had to be specifically modified. + +diff --git a/src/main/java/io/papermc/paper/util/PoiAccess.java b/src/main/java/io/papermc/paper/util/PoiAccess.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0a88c60161b04a733151c15046358f4b3b8b3280 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/PoiAccess.java +@@ -0,0 +1,748 @@ ++package io.papermc.paper.util; ++ ++import it.unimi.dsi.fastutil.doubles.Double2ObjectMap; ++import it.unimi.dsi.fastutil.doubles.Double2ObjectRBTreeMap; ++import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue; ++import it.unimi.dsi.fastutil.longs.LongOpenHashSet; ++import net.minecraft.core.BlockPos; ++import net.minecraft.util.Mth; ++import net.minecraft.world.entity.ai.village.poi.PoiManager; ++import net.minecraft.world.entity.ai.village.poi.PoiRecord; ++import net.minecraft.world.entity.ai.village.poi.PoiSection; ++import net.minecraft.world.entity.ai.village.poi.PoiType; ++import java.util.ArrayList; ++import java.util.HashSet; ++import java.util.Iterator; ++import java.util.List; ++import java.util.Map; ++import java.util.Optional; ++import java.util.Set; ++import java.util.function.Predicate; ++ ++/** ++ * Provides optimised access to POI data. All returned values will be identical to vanilla. ++ */ ++public final class PoiAccess { ++ ++ protected static double clamp(final double val, final double min, final double max) { ++ return (val < min ? min : (val > max ? max : val)); ++ } ++ ++ protected static double getSmallestDistanceSquared(final double boxMinX, final double boxMinY, final double boxMinZ, ++ final double boxMaxX, final double boxMaxY, final double boxMaxZ, ++ ++ final double circleX, final double circleY, final double circleZ) { ++ // is the circle center inside the box? ++ if (circleX >= boxMinX && circleX <= boxMaxX && circleY >= boxMinY && circleY <= boxMaxY && circleZ >= boxMinZ && circleZ <= boxMaxZ) { ++ return 0.0; ++ } ++ ++ final double boxWidthX = (boxMaxX - boxMinX) / 2.0; ++ final double boxWidthY = (boxMaxY - boxMinY) / 2.0; ++ final double boxWidthZ = (boxMaxZ - boxMinZ) / 2.0; ++ ++ final double boxCenterX = (boxMinX + boxMaxX) / 2.0; ++ final double boxCenterY = (boxMinY + boxMaxY) / 2.0; ++ final double boxCenterZ = (boxMinZ + boxMaxZ) / 2.0; ++ ++ double centerDiffX = circleX - boxCenterX; ++ double centerDiffY = circleY - boxCenterY; ++ double centerDiffZ = circleZ - boxCenterZ; ++ ++ centerDiffX = circleX - (clamp(centerDiffX, -boxWidthX, boxWidthX) + boxCenterX); ++ centerDiffY = circleY - (clamp(centerDiffY, -boxWidthY, boxWidthY) + boxCenterY); ++ centerDiffZ = circleZ - (clamp(centerDiffZ, -boxWidthZ, boxWidthZ) + boxCenterZ); ++ ++ return (centerDiffX * centerDiffX) + (centerDiffY * centerDiffY) + (centerDiffZ * centerDiffZ); ++ } ++ ++ ++ // key is: ++ // upper 32 bits: ++ // upper 16 bits: max y section ++ // lower 16 bits: min y section ++ // lower 32 bits: ++ // upper 16 bits: section ++ // lower 16 bits: radius ++ protected static long getKey(final int minSection, final int maxSection, final int section, final int radius) { ++ return ( ++ (maxSection & 0xFFFFL) << (64 - 16) ++ | (minSection & 0xFFFFL) << (64 - 32) ++ | (section & 0xFFFFL) << (64 - 48) ++ | (radius & 0xFFFFL) << (64 - 64) ++ ); ++ } ++ ++ // only includes x/z axis ++ // finds the closest poi data by distance. ++ public static BlockPos findClosestPoiDataPosition(final PoiManager poiStorage, ++ final Predicate villagePlaceType, ++ // position predicate must not modify chunk POI ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final double maxDistance, ++ final PoiManager.Occupancy occupancy, ++ final boolean load) { ++ final PoiRecord ret = findClosestPoiDataRecord( ++ poiStorage, villagePlaceType, positionPredicate, sourcePosition, range, maxDistance, occupancy, load ++ ); ++ ++ return ret == null ? null : ret.getPos(); ++ } ++ ++ // only includes x/z axis ++ // finds the closest poi data by distance. if multiple match the same distance, then they all are returned. ++ public static void findClosestPoiDataPositions(final PoiManager poiStorage, ++ final Predicate villagePlaceType, ++ // position predicate must not modify chunk POI ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final double maxDistance, ++ final PoiManager.Occupancy occupancy, ++ final boolean load, ++ final Set ret) { ++ final Set positions = new HashSet<>(); ++ // pos predicate is last thing that runs before adding to ret. ++ final Predicate newPredicate = (final BlockPos pos) -> { ++ if (positionPredicate != null && !positionPredicate.test(pos)) { ++ return false; ++ } ++ return positions.add(pos.immutable()); ++ }; ++ ++ final List toConvert = new ArrayList<>(); ++ findClosestPoiDataRecords( ++ poiStorage, villagePlaceType, newPredicate, sourcePosition, range, maxDistance, occupancy, load, toConvert ++ ); ++ ++ for (final PoiRecord record : toConvert) { ++ ret.add(record.getPos()); ++ } ++ } ++ ++ // only includes x/z axis ++ // finds the closest poi data by distance. ++ public static PoiRecord findClosestPoiDataRecord(final PoiManager poiStorage, ++ final Predicate villagePlaceType, ++ // position predicate must not modify chunk POI ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final double maxDistance, ++ final PoiManager.Occupancy occupancy, ++ final boolean load) { ++ final List ret = new ArrayList<>(); ++ findClosestPoiDataRecords( ++ poiStorage, villagePlaceType, positionPredicate, sourcePosition, range, maxDistance, occupancy, load, ret ++ ); ++ return ret.isEmpty() ? null : ret.get(0); ++ } ++ ++ // only includes x/z axis ++ // finds the closest poi data by distance. if multiple match the same distance, then they all are returned. ++ public static void findClosestPoiDataRecords(final PoiManager poiStorage, ++ final Predicate villagePlaceType, ++ // position predicate must not modify chunk POI ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final double maxDistance, ++ final PoiManager.Occupancy occupancy, ++ final boolean load, ++ final List ret) { ++ final Predicate occupancyFilter = occupancy.getTest(); ++ ++ final List closestRecords = new ArrayList<>(); ++ double closestDistanceSquared = maxDistance * maxDistance; ++ ++ final int lowerX = Mth.floor(sourcePosition.getX() - range) >> 4; ++ final int lowerY = WorldUtil.getMinSection(poiStorage.world); ++ final int lowerZ = Mth.floor(sourcePosition.getZ() - range) >> 4; ++ final int upperX = Mth.floor(sourcePosition.getX() + range) >> 4; ++ final int upperY = WorldUtil.getMaxSection(poiStorage.world); ++ final int upperZ = Mth.floor(sourcePosition.getZ() + range) >> 4; ++ ++ final int centerX = sourcePosition.getX() >> 4; ++ final int centerY = Mth.clamp(sourcePosition.getY() >> 4, lowerY, upperY); ++ final int centerZ = sourcePosition.getZ() >> 4; ++ ++ final LongArrayFIFOQueue queue = new LongArrayFIFOQueue(); ++ queue.enqueue(CoordinateUtils.getChunkSectionKey(centerX, centerY, centerZ)); ++ final LongOpenHashSet seen = new LongOpenHashSet(); ++ ++ while (!queue.isEmpty()) { ++ final long key = queue.dequeueLong(); ++ final int sectionX = CoordinateUtils.getChunkSectionX(key); ++ final int sectionY = CoordinateUtils.getChunkSectionY(key); ++ final int sectionZ = CoordinateUtils.getChunkSectionZ(key); ++ ++ if (sectionX < lowerX || sectionX > upperX || sectionY < lowerY || sectionY > upperY || sectionZ < lowerZ || sectionZ > upperZ) { ++ // out of bound chunk ++ continue; ++ } ++ ++ final double sectionDistanceSquared = getSmallestDistanceSquared( ++ (sectionX << 4) + 0.5, ++ (sectionY << 4) + 0.5, ++ (sectionZ << 4) + 0.5, ++ (sectionX << 4) + 15.5, ++ (sectionY << 4) + 15.5, ++ (sectionZ << 4) + 15.5, ++ (double)sourcePosition.getX(), (double)sourcePosition.getY(), (double)sourcePosition.getZ() ++ ); ++ if (sectionDistanceSquared > closestDistanceSquared) { ++ continue; ++ } ++ ++ // queue all neighbours ++ for (int dz = -1; dz <= 1; ++dz) { ++ for (int dx = -1; dx <= 1; ++dx) { ++ for (int dy = -1; dy <= 1; ++dy) { ++ // -1 and 1 have the 1st bit set. so just add up the first bits, and it will tell us how many ++ // values are set. we only care about cardinal neighbours, so, we only care if one value is set ++ if ((dx & 1) + (dy & 1) + (dz & 1) != 1) { ++ continue; ++ } ++ ++ final int neighbourX = sectionX + dx; ++ final int neighbourY = sectionY + dy; ++ final int neighbourZ = sectionZ + dz; ++ ++ final long neighbourKey = CoordinateUtils.getChunkSectionKey(neighbourX, neighbourY, neighbourZ); ++ if (seen.add(neighbourKey)) { ++ queue.enqueue(neighbourKey); ++ } ++ } ++ } ++ } ++ ++ final Optional poiSectionOptional = load ? poiStorage.getOrLoad(key) : poiStorage.get(key); ++ ++ if (poiSectionOptional == null || !poiSectionOptional.isPresent()) { ++ continue; ++ } ++ ++ final PoiSection poiSection = poiSectionOptional.orElse(null); ++ ++ final Map> sectionData = poiSection.getData(); ++ if (sectionData.isEmpty()) { ++ continue; ++ } ++ ++ // now we search the section data ++ for (final Map.Entry> entry : sectionData.entrySet()) { ++ if (!villagePlaceType.test(entry.getKey())) { ++ // filter out by poi type ++ continue; ++ } ++ ++ // now we can look at the poi data ++ for (final PoiRecord poiData : entry.getValue()) { ++ if (!occupancyFilter.test(poiData)) { ++ // filter by occupancy ++ continue; ++ } ++ ++ final BlockPos poiPosition = poiData.getPos(); ++ ++ if (Math.abs(poiPosition.getX() - sourcePosition.getX()) > range ++ || Math.abs(poiPosition.getZ() - sourcePosition.getZ()) > range) { ++ // out of range for square radius ++ continue; ++ } ++ ++ // it's important that it's poiPosition.distSqr(source) : the value actually is different IF the values are swapped! ++ final double dataRange = poiPosition.distSqr(sourcePosition); ++ ++ if (dataRange > closestDistanceSquared) { ++ // out of range for distance check ++ continue; ++ } ++ ++ if (positionPredicate != null && !positionPredicate.test(poiPosition)) { ++ // filter by position ++ continue; ++ } ++ ++ if (dataRange < closestDistanceSquared) { ++ closestRecords.clear(); ++ closestDistanceSquared = dataRange; ++ } ++ closestRecords.add(poiData); ++ } ++ } ++ } ++ ++ // uh oh! we might have multiple records that match the distance sorting! ++ // we need to re-order our results by the way vanilla would have iterated over them. ++ closestRecords.sort((record1, record2) -> { ++ // vanilla iterates the same way we do for data inside sections, so we know the ordering inside a section ++ // is fine and should be preserved (this sort is stable so we're good there) ++ // but they iterate sections by x then by z (like the following) ++ // for (int x = -dx; x <= dx; ++x) ++ // for (int z = -dz; z <= dz; ++z) ++ // .... ++ // so we need to reorder such that records with lower chunk z, then lower chunk x come first ++ final BlockPos pos1 = record1.getPos(); ++ final BlockPos pos2 = record2.getPos(); ++ ++ final int cx1 = pos1.getX() >> 4; ++ final int cz1 = pos1.getZ() >> 4; ++ ++ final int cx2 = pos2.getX() >> 4; ++ final int cz2 = pos2.getZ() >> 4; ++ ++ if (cz2 != cz1) { ++ // want smaller z ++ return Integer.compare(cz1, cz2); ++ } ++ ++ if (cx2 != cx1) { ++ // want smaller x ++ return Integer.compare(cx1, cx2); ++ } ++ ++ // same chunk ++ // once vanilla has the chunk, it will iterate from all of the chunk sections starting from smaller y ++ // so now we just compare section y, wanting smaller y ++ ++ return Integer.compare(pos1.getY() >> 4, pos2.getY() >> 4); ++ }); ++ ++ // now we match perfectly what vanilla would have outputted, without having to search the whole radius (hopefully). ++ ret.addAll(closestRecords); ++ } ++ ++ // finds the closest poi entry pos. ++ public static BlockPos findNearestPoiPosition(final PoiManager poiStorage, ++ final Predicate villagePlaceType, ++ // position predicate must not modify chunk POI ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final double maxDistance, ++ final PoiManager.Occupancy occupancy, ++ final boolean load) { ++ final PoiRecord ret = findNearestPoiRecord( ++ poiStorage, villagePlaceType, positionPredicate, sourcePosition, range, maxDistance, occupancy, load ++ ); ++ return ret == null ? null : ret.getPos(); ++ } ++ ++ // finds the closest `max` poi entry positions. ++ public static void findNearestPoiPositions(final PoiManager poiStorage, ++ final Predicate villagePlaceType, ++ // position predicate must not modify chunk POI ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final double maxDistance, ++ final PoiManager.Occupancy occupancy, ++ final boolean load, ++ final int max, ++ final List ret) { ++ final Set positions = new HashSet<>(); ++ // pos predicate is last thing that runs before adding to ret. ++ final Predicate newPredicate = (final BlockPos pos) -> { ++ if (positionPredicate != null && !positionPredicate.test(pos)) { ++ return false; ++ } ++ return positions.add(pos.immutable()); ++ }; ++ ++ final List toConvert = new ArrayList<>(); ++ findNearestPoiRecords( ++ poiStorage, villagePlaceType, newPredicate, sourcePosition, range, maxDistance, occupancy, load, max, toConvert ++ ); ++ ++ for (final PoiRecord record : toConvert) { ++ ret.add(record.getPos()); ++ } ++ } ++ ++ // finds the closest poi entry. ++ public static PoiRecord findNearestPoiRecord(final PoiManager poiStorage, ++ final Predicate villagePlaceType, ++ // position predicate must not modify chunk POI ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final double maxDistance, ++ final PoiManager.Occupancy occupancy, ++ final boolean load) { ++ final List ret = new ArrayList<>(); ++ findNearestPoiRecords( ++ poiStorage, villagePlaceType, positionPredicate, sourcePosition, range, maxDistance, occupancy, load, ++ 1, ret ++ ); ++ return ret.isEmpty() ? null : ret.get(0); ++ } ++ ++ // finds the closest `max` poi entries. ++ public static void findNearestPoiRecords(final PoiManager poiStorage, ++ final Predicate villagePlaceType, ++ // position predicate must not modify chunk POI ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final double maxDistance, ++ final PoiManager.Occupancy occupancy, ++ final boolean load, ++ final int max, ++ final List ret) { ++ final Predicate occupancyFilter = occupancy.getTest(); ++ ++ final double maxDistanceSquared = maxDistance * maxDistance; ++ final Double2ObjectRBTreeMap> closestRecords = new Double2ObjectRBTreeMap<>(); ++ int totalRecords = 0; ++ double furthestDistanceSquared = maxDistanceSquared; ++ ++ final int lowerX = Mth.floor(sourcePosition.getX() - range) >> 4; ++ final int lowerY = WorldUtil.getMinSection(poiStorage.world); ++ final int lowerZ = Mth.floor(sourcePosition.getZ() - range) >> 4; ++ final int upperX = Mth.floor(sourcePosition.getX() + range) >> 4; ++ final int upperY = WorldUtil.getMaxSection(poiStorage.world); ++ final int upperZ = Mth.floor(sourcePosition.getZ() + range) >> 4; ++ ++ final int centerX = sourcePosition.getX() >> 4; ++ final int centerY = Mth.clamp(sourcePosition.getY() >> 4, lowerY, upperY); ++ final int centerZ = sourcePosition.getZ() >> 4; ++ ++ final LongArrayFIFOQueue queue = new LongArrayFIFOQueue(); ++ queue.enqueue(CoordinateUtils.getChunkSectionKey(centerX, centerY, centerZ)); ++ final LongOpenHashSet seen = new LongOpenHashSet(); ++ ++ while (!queue.isEmpty()) { ++ final long key = queue.dequeueLong(); ++ final int sectionX = CoordinateUtils.getChunkSectionX(key); ++ final int sectionY = CoordinateUtils.getChunkSectionY(key); ++ final int sectionZ = CoordinateUtils.getChunkSectionZ(key); ++ ++ if (sectionX < lowerX || sectionX > upperX || sectionY < lowerY || sectionY > upperY || sectionZ < lowerZ || sectionZ > upperZ) { ++ // out of bound chunk ++ continue; ++ } ++ ++ final double sectionDistanceSquared = getSmallestDistanceSquared( ++ (sectionX << 4) + 0.5, ++ (sectionY << 4) + 0.5, ++ (sectionZ << 4) + 0.5, ++ (sectionX << 4) + 15.5, ++ (sectionY << 4) + 15.5, ++ (sectionZ << 4) + 15.5, ++ (double) sourcePosition.getX(), (double) sourcePosition.getY(), (double) sourcePosition.getZ() ++ ); ++ ++ if (sectionDistanceSquared > (totalRecords >= max ? furthestDistanceSquared : maxDistanceSquared)) { ++ continue; ++ } ++ ++ // queue all neighbours ++ for (int dz = -1; dz <= 1; ++dz) { ++ for (int dx = -1; dx <= 1; ++dx) { ++ for (int dy = -1; dy <= 1; ++dy) { ++ // -1 and 1 have the 1st bit set. so just add up the first bits, and it will tell us how many ++ // values are set. we only care about cardinal neighbours, so, we only care if one value is set ++ if ((dx & 1) + (dy & 1) + (dz & 1) != 1) { ++ continue; ++ } ++ ++ final int neighbourX = sectionX + dx; ++ final int neighbourY = sectionY + dy; ++ final int neighbourZ = sectionZ + dz; ++ ++ final long neighbourKey = CoordinateUtils.getChunkSectionKey(neighbourX, neighbourY, neighbourZ); ++ if (seen.add(neighbourKey)) { ++ queue.enqueue(neighbourKey); ++ } ++ } ++ } ++ } ++ ++ final Optional poiSectionOptional = load ? poiStorage.getOrLoad(key) : poiStorage.get(key); ++ ++ if (poiSectionOptional == null || !poiSectionOptional.isPresent()) { ++ continue; ++ } ++ ++ final PoiSection poiSection = poiSectionOptional.orElse(null); ++ ++ final Map> sectionData = poiSection.getData(); ++ if (sectionData.isEmpty()) { ++ continue; ++ } ++ ++ // now we search the section data ++ for (final Map.Entry> entry : sectionData.entrySet()) { ++ if (!villagePlaceType.test(entry.getKey())) { ++ // filter out by poi type ++ continue; ++ } ++ ++ // now we can look at the poi data ++ for (final PoiRecord poiData : entry.getValue()) { ++ if (!occupancyFilter.test(poiData)) { ++ // filter by occupancy ++ continue; ++ } ++ ++ final BlockPos poiPosition = poiData.getPos(); ++ ++ if (Math.abs(poiPosition.getX() - sourcePosition.getX()) > range ++ || Math.abs(poiPosition.getZ() - sourcePosition.getZ()) > range) { ++ // out of range for square radius ++ continue; ++ } ++ ++ // it's important that it's poiPosition.distSqr(source) : the value actually is different IF the values are swapped! ++ final double dataRange = poiPosition.distSqr(sourcePosition); ++ ++ if (dataRange > maxDistanceSquared) { ++ // out of range for distance check ++ continue; ++ } ++ ++ if (dataRange > furthestDistanceSquared && totalRecords >= max) { ++ // out of range for distance check ++ continue; ++ } ++ ++ if (positionPredicate != null && !positionPredicate.test(poiPosition)) { ++ // filter by position ++ continue; ++ } ++ ++ if (dataRange > furthestDistanceSquared) { ++ // we know totalRecords < max, so this entry is now our furthest ++ furthestDistanceSquared = dataRange; ++ } ++ ++ closestRecords.computeIfAbsent(dataRange, (final double unused) -> { ++ return new ArrayList<>(); ++ }).add(poiData); ++ ++ if (++totalRecords >= max) { ++ if (closestRecords.size() >= 2) { ++ int entriesInClosest = 0; ++ final Iterator>> iterator = closestRecords.double2ObjectEntrySet().iterator(); ++ double nextFurthestDistanceSquared = 0.0; ++ ++ for (int i = 0, len = closestRecords.size() - 1; i < len; ++i) { ++ final Double2ObjectMap.Entry> recordEntry = iterator.next(); ++ entriesInClosest += recordEntry.getValue().size(); ++ nextFurthestDistanceSquared = recordEntry.getDoubleKey(); ++ } ++ ++ if (entriesInClosest >= max) { ++ // the last set of entries at range wont even be considered for sure... nuke em ++ final Double2ObjectMap.Entry> recordEntry = iterator.next(); ++ totalRecords -= recordEntry.getValue().size(); ++ iterator.remove(); ++ ++ furthestDistanceSquared = nextFurthestDistanceSquared; ++ } ++ } ++ } ++ } ++ } ++ } ++ ++ final List closestRecordsUnsorted = new ArrayList<>(); ++ ++ // we're done here, so now just flatten the map and sort it. ++ ++ for (final List records : closestRecords.values()) { ++ closestRecordsUnsorted.addAll(records); ++ } ++ ++ // uh oh! we might have multiple records that match the distance sorting! ++ // we need to re-order our results by the way vanilla would have iterated over them. ++ closestRecordsUnsorted.sort((record1, record2) -> { ++ // vanilla iterates the same way we do for data inside sections, so we know the ordering inside a section ++ // is fine and should be preserved (this sort is stable so we're good there) ++ // but they iterate sections by x then by z (like the following) ++ // for (int x = -dx; x <= dx; ++x) ++ // for (int z = -dz; z <= dz; ++z) ++ // .... ++ // so we need to reorder such that records with lower chunk z, then lower chunk x come first ++ final BlockPos pos1 = record1.getPos(); ++ final BlockPos pos2 = record2.getPos(); ++ ++ final int cx1 = pos1.getX() >> 4; ++ final int cz1 = pos1.getZ() >> 4; ++ ++ final int cx2 = pos2.getX() >> 4; ++ final int cz2 = pos2.getZ() >> 4; ++ ++ if (cz2 != cz1) { ++ // want smaller z ++ return Integer.compare(cz1, cz2); ++ } ++ ++ if (cx2 != cx1) { ++ // want smaller x ++ return Integer.compare(cx1, cx2); ++ } ++ ++ // same chunk ++ // once vanilla has the chunk, it will iterate from all of the chunk sections starting from smaller y ++ // so now we just compare section y, wanting smaller section y ++ ++ return Integer.compare(pos1.getY() >> 4, pos2.getY() >> 4); ++ }); ++ ++ // trim out any entries exceeding our maximum ++ for (int i = closestRecordsUnsorted.size() - 1; i >= max; --i) { ++ closestRecordsUnsorted.remove(i); ++ } ++ ++ // now we match perfectly what vanilla would have outputted, without having to search the whole radius (hopefully). ++ ret.addAll(closestRecordsUnsorted); ++ } ++ ++ public static BlockPos findAnyPoiPosition(final PoiManager poiStorage, ++ final Predicate villagePlaceType, ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final PoiManager.Occupancy occupancy, ++ final boolean load) { ++ final PoiRecord ret = findAnyPoiRecord( ++ poiStorage, villagePlaceType, positionPredicate, sourcePosition, range, occupancy, load ++ ); ++ ++ return ret == null ? null : ret.getPos(); ++ } ++ ++ public static void findAnyPoiPositions(final PoiManager poiStorage, ++ final Predicate villagePlaceType, ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final PoiManager.Occupancy occupancy, ++ final boolean load, ++ final int max, ++ final List ret) { ++ final Set positions = new HashSet<>(); ++ // pos predicate is last thing that runs before adding to ret. ++ final Predicate newPredicate = (final BlockPos pos) -> { ++ if (positionPredicate != null && !positionPredicate.test(pos)) { ++ return false; ++ } ++ return positions.add(pos.immutable()); ++ }; ++ ++ final List toConvert = new ArrayList<>(); ++ findAnyPoiRecords( ++ poiStorage, villagePlaceType, newPredicate, sourcePosition, range, occupancy, load, max, toConvert ++ ); ++ ++ for (final PoiRecord record : toConvert) { ++ ret.add(record.getPos()); ++ } ++ } ++ ++ public static PoiRecord findAnyPoiRecord(final PoiManager poiStorage, ++ final Predicate villagePlaceType, ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final PoiManager.Occupancy occupancy, ++ final boolean load) { ++ final List ret = new ArrayList<>(); ++ findAnyPoiRecords(poiStorage, villagePlaceType, positionPredicate, sourcePosition, range, occupancy, load, 1, ret); ++ return ret.isEmpty() ? null : ret.get(0); ++ } ++ ++ public static void findAnyPoiRecords(final PoiManager poiStorage, ++ final Predicate villagePlaceType, ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final PoiManager.Occupancy occupancy, ++ final boolean load, ++ final int max, ++ final List ret) { ++ // the biggest issue with the original mojang implementation is that they chain so many streams together ++ // the amount of streams chained just rolls performance, even if nothing is iterated over ++ final Predicate occupancyFilter = occupancy.getTest(); ++ final double rangeSquared = range * range; ++ ++ int added = 0; ++ ++ // First up, we need to iterate the chunks ++ // all the values here are in chunk sections ++ final int lowerX = Mth.floor(sourcePosition.getX() - range) >> 4; ++ final int lowerY = Math.max(WorldUtil.getMinSection(poiStorage.world), Mth.floor(sourcePosition.getY() - range) >> 4); ++ final int lowerZ = Mth.floor(sourcePosition.getZ() - range) >> 4; ++ final int upperX = Mth.floor(sourcePosition.getX() + range) >> 4; ++ final int upperY = Math.min(WorldUtil.getMaxSection(poiStorage.world), Mth.floor(sourcePosition.getY() + range) >> 4); ++ final int upperZ = Mth.floor(sourcePosition.getZ() + range) >> 4; ++ ++ // Vanilla iterates by x until max is reached then increases z ++ // vanilla also searches by increasing Y section value ++ for (int currZ = lowerZ; currZ <= upperZ; ++currZ) { ++ for (int currX = lowerX; currX <= upperX; ++currX) { ++ for (int currY = lowerY; currY <= upperY; ++currY) { // vanilla searches the entire chunk because they're actually stupid. just search the sections we need ++ final Optional poiSectionOptional = load ? poiStorage.getOrLoad(CoordinateUtils.getChunkSectionKey(currX, currY, currZ)) : ++ poiStorage.get(CoordinateUtils.getChunkSectionKey(currX, currY, currZ)); ++ final PoiSection poiSection = poiSectionOptional == null ? null : poiSectionOptional.orElse(null); ++ if (poiSection == null) { ++ continue; ++ } ++ ++ final Map> sectionData = poiSection.getData(); ++ if (sectionData.isEmpty()) { ++ continue; ++ } ++ ++ // now we search the section data ++ for (final Map.Entry> entry : sectionData.entrySet()) { ++ if (!villagePlaceType.test(entry.getKey())) { ++ // filter out by poi type ++ continue; ++ } ++ ++ // now we can look at the poi data ++ for (final PoiRecord poiData : entry.getValue()) { ++ if (!occupancyFilter.test(poiData)) { ++ // filter by occupancy ++ continue; ++ } ++ ++ final BlockPos poiPosition = poiData.getPos(); ++ ++ if (Math.abs(poiPosition.getX() - sourcePosition.getX()) > range ++ || Math.abs(poiPosition.getZ() - sourcePosition.getZ()) > range) { ++ // out of range for square radius ++ continue; ++ } ++ ++ if (poiPosition.distSqr(sourcePosition) > rangeSquared) { ++ // out of range for distance check ++ continue; ++ } ++ ++ if (positionPredicate != null && !positionPredicate.test(poiPosition)) { ++ // filter by position ++ continue; ++ } ++ ++ // found one! ++ ret.add(poiData); ++ if (++added >= max) { ++ return; ++ } ++ } ++ } ++ } ++ } ++ } ++ } ++ ++ private PoiAccess() { ++ throw new RuntimeException(); ++ } ++} +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java +index 84a0ee595bebcc1947c602c4c06e7437706ce37c..afbb2acd27416c801af3d718850b82a170734cd3 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java +@@ -83,7 +83,11 @@ public class AcquirePoi extends Behavior { + return true; + } + }; +- Set set = poiManager.findAllClosestFirst(this.poiType.getPredicate(), predicate, entity.blockPosition(), 48, PoiManager.Occupancy.HAS_SPACE).limit(5L).collect(Collectors.toSet()); ++ // Paper start - optimise POI access ++ java.util.List poiposes = new java.util.ArrayList<>(); ++ io.papermc.paper.util.PoiAccess.findNearestPoiPositions(poiManager, this.poiType.getPredicate(), predicate, entity.blockPosition(), 48, 48*48, PoiManager.Occupancy.HAS_SPACE, false, 5, poiposes); ++ Set set = new java.util.HashSet<>(poiposes); ++ // Paper end - optimise POI access + Path path = entity.getNavigation().createPath(set, this.poiType.getValidRange()); + if (path != null && path.canReach()) { + BlockPos blockPos = path.getTarget(); +diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java +index e41b2fa1db6fb77a26cdb498904021b430e35be0..488d1e24b3e8f0fd8dc973d450215e4216720db3 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java ++++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java +@@ -49,8 +49,12 @@ public class NearestBedSensor extends Sensor { + return true; + } + }; +- Stream stream = poiManager.findAll(PoiType.HOME.getPredicate(), predicate, entity.blockPosition(), 48, PoiManager.Occupancy.ANY); +- Path path = entity.getNavigation().createPath(stream, PoiType.HOME.getValidRange()); ++ // Paper start - optimise POI access ++ java.util.List poiposes = new java.util.ArrayList<>(); ++ // don't ask me why it's unbounded. ask mojang. ++ io.papermc.paper.util.PoiAccess.findAnyPoiPositions(poiManager, PoiType.HOME.getPredicate(), predicate, entity.blockPosition(), 48, PoiManager.Occupancy.ANY, false, Integer.MAX_VALUE, poiposes); ++ Path path = entity.getNavigation().createPath(new java.util.HashSet<>(poiposes), PoiType.HOME.getValidRange()); ++ // Paper end - optimise POI access + if (path != null && path.canReach()) { + BlockPos blockPos = path.getTarget(); + Optional optional = poiManager.getType(blockPos); +diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java +index 7b1d2748328ffc1447bcacd1316f2c6fdbaf92b0..2b79ace854461b216dc4970d1cc4a3953a51dd50 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java ++++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java +@@ -37,7 +37,7 @@ public class PoiManager extends SectionStorage { + public static final int VILLAGE_SECTION_SIZE = 1; + private final PoiManager.DistanceTracker distanceTracker; + private final LongSet loadedChunks = new LongOpenHashSet(); +- private final net.minecraft.server.level.ServerLevel world; // Paper ++ public final net.minecraft.server.level.ServerLevel world; // Paper // Paper public + + public PoiManager(File directory, DataFixer dataFixer, boolean dsync, LevelHeightAccessor world) { + super(directory, PoiSection::codec, PoiSection::new, dataFixer, DataFixTypes.POI_CHUNK, dsync, world); +@@ -100,36 +100,55 @@ public class PoiManager extends SectionStorage { + } + + public Optional find(Predicate typePredicate, Predicate posPredicate, BlockPos pos, int radius, PoiManager.Occupancy occupationStatus) { +- return this.findAll(typePredicate, posPredicate, pos, radius, occupationStatus).findFirst(); ++ // Paper start - re-route to faster logic ++ BlockPos ret = io.papermc.paper.util.PoiAccess.findAnyPoiPosition(this, typePredicate, posPredicate, pos, radius, occupationStatus, false); ++ return Optional.ofNullable(ret); ++ // Paper end - re-route to faster logic + } + + public Optional findClosest(Predicate typePredicate, BlockPos pos, int radius, PoiManager.Occupancy occupationStatus) { +- return this.getInRange(typePredicate, pos, radius, occupationStatus).map(PoiRecord::getPos).min(Comparator.comparingDouble((blockPos2) -> { +- return blockPos2.distSqr(pos); +- })); ++ // Paper start - re-route to faster logic ++ BlockPos ret = io.papermc.paper.util.PoiAccess.findClosestPoiDataPosition(this, typePredicate, null, pos, radius, radius*radius, occupationStatus, false); ++ return Optional.ofNullable(ret); ++ // Paper end - re-route to faster logic + } + + public Optional findClosest(Predicate predicate, Predicate predicate2, BlockPos blockPos, int i, PoiManager.Occupancy occupancy) { +- return this.getInRange(predicate, blockPos, i, occupancy).map(PoiRecord::getPos).filter(predicate2).min(Comparator.comparingDouble((blockPos2) -> { +- return blockPos2.distSqr(blockPos); +- })); ++ // Paper start - re-route to faster logic ++ BlockPos ret = io.papermc.paper.util.PoiAccess.findClosestPoiDataPosition(this, predicate, predicate2, blockPos, i, i*i, occupancy, false); ++ return Optional.ofNullable(ret); ++ // Paper end - re-route to faster logic + } + + public Optional take(Predicate typePredicate, Predicate positionPredicate, BlockPos pos, int radius) { +- return this.getInRange(typePredicate, pos, radius, PoiManager.Occupancy.HAS_SPACE).filter((poi) -> { +- return positionPredicate.test(poi.getPos()); +- }).findFirst().map((poi) -> { +- poi.acquireTicket(); +- return poi.getPos(); +- }); ++ // Paper start - re-route to faster logic ++ PoiRecord ret = io.papermc.paper.util.PoiAccess.findAnyPoiRecord( ++ this, typePredicate, positionPredicate, pos, radius, PoiManager.Occupancy.HAS_SPACE, false ++ ); ++ if (ret == null) { ++ return Optional.empty(); ++ } ++ ret.acquireTicket(); ++ return Optional.of(ret.getPos()); ++ // Paper end - re-route to faster logic + } + + public Optional getRandom(Predicate typePredicate, Predicate positionPredicate, PoiManager.Occupancy occupationStatus, BlockPos pos, int radius, Random random) { +- List list = this.getInRange(typePredicate, pos, radius, occupationStatus).collect(Collectors.toList()); +- Collections.shuffle(list, random); +- return list.stream().filter((poiRecord) -> { +- return positionPredicate.test(poiRecord.getPos()); +- }).findFirst().map(PoiRecord::getPos); ++ // Paper start - re-route to faster logic ++ List list = new java.util.ArrayList<>(); ++ io.papermc.paper.util.PoiAccess.findAnyPoiRecords( ++ this, typePredicate, positionPredicate, pos, radius, occupationStatus, false, Integer.MAX_VALUE, list ++ ); ++ ++ // the old method shuffled the list and then tried to find the first element in it that ++ // matched positionPredicate, however we moved positionPredicate into the poi search. This means we can avoid a ++ // shuffle entirely, and just pick a random element from list ++ if (list.isEmpty()) { ++ return Optional.empty(); ++ } ++ ++ return Optional.of(list.get(random.nextInt(list.size())).getPos()); ++ // Paper end - re-route to faster logic + } + + public boolean release(BlockPos pos) { +diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java +index 5a86bc6f552913e2978c61233148db22e3a240f1..16f9796adcb83350e97220ba0e96bfee998f1ff4 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java ++++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java +@@ -25,7 +25,7 @@ import org.apache.logging.log4j.Logger; + public class PoiSection { + private static final Logger LOGGER = LogManager.getLogger(); + private final Short2ObjectMap records = new Short2ObjectOpenHashMap<>(); +- private final Map> byType = Maps.newHashMap(); ++ private final Map> byType = Maps.newHashMap(); public final Map> getData() { return this.byType; } // Paper - public accessor + private final Runnable setDirty; + private boolean isValid; + +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 90f7b06bd2c558be35c4577044fa033e1fb5cc22..e2d1149cbe75b0689c9f816b87ebb7ba0d6f56c8 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 +@@ -61,11 +61,11 @@ public class SectionStorage extends RegionFileStorage implements AutoCloseabl + } + + @Nullable +- protected Optional get(long pos) { ++ public Optional get(long pos) { // Paper - public + return this.storage.get(pos); + } + +- protected Optional getOrLoad(long pos) { ++ public Optional getOrLoad(long pos) { // Paper - public + if (this.outsideStoredRange(pos)) { + return Optional.empty(); + } else { +diff --git a/src/main/java/net/minecraft/world/level/portal/PortalForcer.java b/src/main/java/net/minecraft/world/level/portal/PortalForcer.java +index d5ba2e679ed1858ea18e18feffce50544ae036c2..204253dfcbe4e4fda64f9102020eb78c6f9b4e5e 100644 +--- a/src/main/java/net/minecraft/world/level/portal/PortalForcer.java ++++ b/src/main/java/net/minecraft/world/level/portal/PortalForcer.java +@@ -52,16 +52,37 @@ public class PortalForcer { + // int i = flag ? 16 : 128; + // CraftBukkit end + +- villageplace.ensureLoadedAndValid(this.level, blockposition, i); +- Optional optional = villageplace.getInSquare((villageplacetype) -> { +- return villageplacetype == PoiType.NETHER_PORTAL; +- }, blockposition, i, PoiManager.Occupancy.ANY).sorted(Comparator.comparingDouble((PoiRecord villageplacerecord) -> { // CraftBukkit - decompile error +- return villageplacerecord.getPos().distSqr(blockposition); +- }).thenComparingInt((villageplacerecord) -> { +- return villageplacerecord.getPos().getY(); +- })).filter((villageplacerecord) -> { +- return this.level.getBlockState(villageplacerecord.getPos()).hasProperty(BlockStateProperties.HORIZONTAL_AXIS); +- }).findFirst(); ++ // Paper start - optimise portals ++ Optional optional; ++ java.util.List records = new java.util.ArrayList<>(); ++ io.papermc.paper.util.PoiAccess.findClosestPoiDataRecords( ++ villageplace, ++ (PoiType type) -> { ++ return type == PoiType.NETHER_PORTAL; ++ }, ++ (BlockPos pos) -> { ++ net.minecraft.world.level.chunk.ChunkAccess lowest = this.level.getChunk(pos.getX() >> 4, pos.getZ() >> 4, net.minecraft.world.level.chunk.ChunkStatus.EMPTY); ++ if (!lowest.getStatus().isOrAfter(net.minecraft.world.level.chunk.ChunkStatus.FULL)) { ++ // why would we generate the chunk? ++ return false; ++ } ++ return lowest.getBlockState(pos).hasProperty(BlockStateProperties.HORIZONTAL_AXIS); ++ }, ++ blockposition, i, Double.MAX_VALUE, PoiManager.Occupancy.ANY, true, records ++ ); ++ ++ // this gets us most of the way there, but we bias towards lower y values. ++ PoiRecord lowestYRecord = null; ++ for (PoiRecord record : records) { ++ if (lowestYRecord == null) { ++ lowestYRecord = record; ++ } else if (lowestYRecord.getPos().getY() > record.getPos().getY()) { ++ lowestYRecord = record; ++ } ++ } ++ // now we're done ++ optional = Optional.ofNullable(lowestYRecord); ++ // Paper end - optimise portals + + return optional.map((villageplacerecord) -> { + BlockPos blockposition1 = villageplacerecord.getPos(); diff --git a/patches/server/0762-Allow-controlled-flushing-for-network-manager.patch b/patches/server/0762-Allow-controlled-flushing-for-network-manager.patch new file mode 100644 index 0000000000..3d0284be34 --- /dev/null +++ b/patches/server/0762-Allow-controlled-flushing-for-network-manager.patch @@ -0,0 +1,147 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sat, 4 Apr 2020 15:27:44 -0700 +Subject: [PATCH] Allow controlled flushing for network manager + +Only make one flush call when emptying the packet queue too + +This patch will be used to optimise out flush calls in later +patches. + +diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java +index 9d09ec3b127e3440bef6b248578dec109407f9ff..103657ad936f1a75ffbb92ca5eafb816e977e363 100644 +--- a/src/main/java/net/minecraft/network/Connection.java ++++ b/src/main/java/net/minecraft/network/Connection.java +@@ -94,6 +94,39 @@ public class Connection extends SimpleChannelInboundHandler> { + public ConnectionProtocol protocol; + // Paper end + ++ // Paper start - allow controlled flushing ++ volatile boolean canFlush = true; ++ private final java.util.concurrent.atomic.AtomicInteger packetWrites = new java.util.concurrent.atomic.AtomicInteger(); ++ private int flushPacketsStart; ++ private final Object flushLock = new Object(); ++ ++ public void disableAutomaticFlush() { ++ synchronized (this.flushLock) { ++ this.flushPacketsStart = this.packetWrites.get(); // must be volatile and before canFlush = false ++ this.canFlush = false; ++ } ++ } ++ ++ public void enableAutomaticFlush() { ++ synchronized (this.flushLock) { ++ this.canFlush = true; ++ if (this.packetWrites.get() != this.flushPacketsStart) { // must be after canFlush = true ++ this.flush(); // only make the flush call if we need to ++ } ++ } ++ } ++ ++ private final void flush() { ++ if (this.channel.eventLoop().inEventLoop()) { ++ this.channel.flush(); ++ } else { ++ this.channel.eventLoop().execute(() -> { ++ this.channel.flush(); ++ }); ++ } ++ } ++ // Paper end - allow controlled flushing ++ + public Connection(PacketFlow side) { + this.receiving = side; + } +@@ -255,7 +288,7 @@ public class Connection extends SimpleChannelInboundHandler> { + net.minecraft.server.MCUtil.isMainThread() && packet.isReady() && this.queue.isEmpty() && + (packet.getExtraPackets() == null || packet.getExtraPackets().isEmpty()) + ))) { +- this.sendPacket(packet, callback); ++ this.writePacket(packet, callback, null); // Paper + return; + } + // write the packets to the queue, then flush - antixray hooks there already +@@ -279,6 +312,14 @@ public class Connection extends SimpleChannelInboundHandler> { + } + + private void sendPacket(Packet packet, @Nullable GenericFutureListener> callback) { ++ // Paper start - add flush parameter ++ this.writePacket(packet, callback, Boolean.TRUE); ++ } ++ private void writePacket(Packet packet, @Nullable GenericFutureListener> callback, Boolean flushConditional) { ++ this.packetWrites.getAndIncrement(); // must be befeore using canFlush ++ boolean effectiveFlush = flushConditional == null ? this.canFlush : flushConditional.booleanValue(); ++ final boolean flush = effectiveFlush || packet instanceof net.minecraft.network.protocol.game.ClientboundKeepAlivePacket || packet instanceof ClientboundDisconnectPacket; // no delay for certain packets ++ // Paper end - add flush parameter + ConnectionProtocol enumprotocol = ConnectionProtocol.getProtocolForPacket(packet); + ConnectionProtocol enumprotocol1 = this.getCurrentProtocol(); + +@@ -289,16 +330,21 @@ public class Connection extends SimpleChannelInboundHandler> { + } + + if (this.channel.eventLoop().inEventLoop()) { +- this.a(packet, callback, enumprotocol, enumprotocol1); ++ this.a(packet, callback, enumprotocol, enumprotocol1, flush); // Paper - add flush parameter + } else { + this.channel.eventLoop().execute(() -> { +- this.a(packet, callback, enumprotocol, enumprotocol1); ++ this.a(packet, callback, enumprotocol, enumprotocol1, flush); // Paper - add flush parameter + }); + } + + } + + private void a(Packet packet, @Nullable GenericFutureListener> genericfuturelistener, ConnectionProtocol enumprotocol, ConnectionProtocol enumprotocol1) { ++ // Paper start - add flush parameter ++ this.a(packet, genericfuturelistener, enumprotocol, enumprotocol1, true); ++ } ++ private void a(Packet packet, @Nullable GenericFutureListener> genericfuturelistener, ConnectionProtocol enumprotocol, ConnectionProtocol enumprotocol1, boolean flush) { ++ // Paper end - add flush parameter + if (enumprotocol != enumprotocol1) { + this.setProtocol(enumprotocol); + } +@@ -312,7 +358,7 @@ public class Connection extends SimpleChannelInboundHandler> { + + try { + // Paper end +- ChannelFuture channelfuture = this.channel.writeAndFlush(packet); ++ ChannelFuture channelfuture = flush ? this.channel.writeAndFlush(packet) : this.channel.write(packet); // Paper - add flush parameter + + if (genericfuturelistener != null) { + channelfuture.addListener(genericfuturelistener); +@@ -354,6 +400,10 @@ public class Connection extends SimpleChannelInboundHandler> { + } + private boolean processQueue() { + if (this.queue.isEmpty()) return true; ++ // Paper start - make only one flush call per sendPacketQueue() call ++ final boolean needsFlush = this.canFlush; ++ boolean hasWrotePacket = false; ++ // Paper end - make only one flush call per sendPacketQueue() call + // If we are on main, we are safe here in that nothing else should be processing queue off main anymore + // But if we are not on main due to login/status, the parent is synchronized on packetQueue + java.util.Iterator iterator = this.queue.iterator(); +@@ -361,16 +411,22 @@ public class Connection extends SimpleChannelInboundHandler> { + PacketHolder queued = iterator.next(); // poll -> peek + + // Fix NPE (Spigot bug caused by handleDisconnection()) +- if (queued == null) { ++ if (false && queued == null) { // Paper - diff on change, this logic is redundant: iterator guarantees ret of an element - on change, hook the flush logic here + return true; + } + + Packet packet = queued.packet; + if (!packet.isReady()) { ++ // Paper start - make only one flush call per sendPacketQueue() call ++ if (hasWrotePacket && (needsFlush || this.canFlush)) { ++ this.flush(); ++ } ++ // Paper end - make only one flush call per sendPacketQueue() call + return false; + } else { + iterator.remove(); +- this.sendPacket(packet, queued.listener); ++ this.writePacket(packet, queued.listener, (!iterator.hasNext() && (needsFlush || this.canFlush)) ? Boolean.TRUE : Boolean.FALSE); // Paper - make only one flush call per sendPacketQueue() call ++ hasWrotePacket = true; // Paper - make only one flush call per sendPacketQueue() call + } + } + return true; diff --git a/patches/server/0763-Add-more-async-catchers.patch b/patches/server/0763-Add-more-async-catchers.patch new file mode 100644 index 0000000000..0702057360 --- /dev/null +++ b/patches/server/0763-Add-more-async-catchers.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Thu, 15 Jul 2021 01:41:53 -0700 +Subject: [PATCH] Add more async catchers + + +diff --git a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java +index f01182a0ac8a14bcd5b1deb778306e7bf1bf70ed..b27c8db914cca3ff0ea8a24acddb9cb9870ce21d 100644 +--- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java ++++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java +@@ -30,11 +30,13 @@ public class EntityTickList { + } + + public void add(Entity entity) { ++ io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist addition"); // Paper + this.ensureActiveIsNotIterated(); + this.active.put(entity.getId(), entity); + } + + public void remove(Entity entity) { ++ io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist removal"); // Paper + this.ensureActiveIsNotIterated(); + this.active.remove(entity.getId()); + } +@@ -44,6 +46,7 @@ public class EntityTickList { + } + + public void forEach(Consumer action) { ++ io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist iteration"); // Paper + if (this.iterated != null) { + throw new UnsupportedOperationException("Only one concurrent iteration supported"); + } else { +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 b93ed663a565056adc902bb6263c9014fda6a9d2..e4e4b48250d32d9e6c034d62e13ac2277bffda8f 100644 +--- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java ++++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java +@@ -151,6 +151,7 @@ public class PersistentEntitySectionManager implements A + } + + public void updateChunkStatus(ChunkPos chunkPos, ChunkHolder.FullChunkStatus levelType) { ++ io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous chunk ticking status update"); // Paper + Visibility visibility = Visibility.fromFullChunkStatus(levelType); + + this.updateChunkStatus(chunkPos, visibility); diff --git a/patches/server/0764-Rewrite-the-light-engine.patch b/patches/server/0764-Rewrite-the-light-engine.patch new file mode 100644 index 0000000000..401cf8bb81 --- /dev/null +++ b/patches/server/0764-Rewrite-the-light-engine.patch @@ -0,0 +1,4782 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Wed, 28 Oct 2020 16:51:55 -0700 +Subject: [PATCH] Rewrite the light engine + +The standard vanilla light engine is plagued by +awful performance. Paper's changes to the light engine +help a bit, however they appear to cause some lighting +errors - most easily noticed in coral generation. + +The vanilla light engine's is too abstract to be modified - +so an entirely new implementation is required to fix the +performance and lighting errors. + +The new implementation is designed primarily to optimise +light level propagations (increase and decrease). Unlike +the vanilla light engine, this implementation tracks more +information per queued value when performing a +breadth first search. Vanilla just tracks coordinate, which +means every time they handle a queued value, they must +also determine the coordinate's target light level +from its neighbours - very wasteful, especially considering +these checks read neighbour block data. +The new light engine tracks both position and target level, +as well as whether the target block needs to be read at all +(for checking sided propagation). So, the work done per coordinate +is significantly reduced because no work is done for calculating +the target level. +In my testing, the block get calls were reduced by approximately +an order of magnitude. However, the light read checks were only +reduced by approximately 2x - but this is fine, light read checks +are extremely cheap compared to block gets. + +Generation testing showed that the new light engine improved +total generation (not lighting itself, but the whole generation process) +by 2x. According to cpu time, the light engine itself spent 10x less time +lighting chunks for generation. + +diff --git a/src/main/java/ca/spottedleaf/starlight/light/BlockStarLightEngine.java b/src/main/java/ca/spottedleaf/starlight/light/BlockStarLightEngine.java +new file mode 100644 +index 0000000000000000000000000000000000000000..9efbdba758aebcad3454a9a52c8a7eae4b7fc7eb +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/starlight/light/BlockStarLightEngine.java +@@ -0,0 +1,283 @@ ++package ca.spottedleaf.starlight.light; ++ ++import net.minecraft.core.BlockPos; ++import net.minecraft.world.level.Level; ++import net.minecraft.world.level.block.state.BlockState; ++import net.minecraft.world.level.chunk.*; ++import net.minecraft.world.phys.shapes.Shapes; ++import net.minecraft.world.phys.shapes.VoxelShape; ++ ++import java.util.ArrayList; ++import java.util.Iterator; ++import java.util.List; ++import java.util.Set; ++import java.util.stream.Collectors; ++ ++public final class BlockStarLightEngine extends StarLightEngine { ++ ++ public BlockStarLightEngine(final Level world) { ++ super(false, world); ++ } ++ ++ @Override ++ protected boolean[] getEmptinessMap(final ChunkAccess chunk) { ++ return chunk.getBlockEmptinessMap(); ++ } ++ ++ @Override ++ protected void setEmptinessMap(final ChunkAccess chunk, final boolean[] to) { ++ chunk.setBlockEmptinessMap(to); ++ } ++ ++ @Override ++ protected SWMRNibbleArray[] getNibblesOnChunk(final ChunkAccess chunk) { ++ return chunk.getBlockNibbles(); ++ } ++ ++ @Override ++ protected void setNibbles(final ChunkAccess chunk, final SWMRNibbleArray[] to) { ++ chunk.setBlockNibbles(to); ++ } ++ ++ @Override ++ protected boolean canUseChunk(final ChunkAccess chunk) { ++ return chunk.getStatus().isOrAfter(ChunkStatus.LIGHT) && (this.isClientSide || chunk.isLightCorrect()); ++ } ++ ++ @Override ++ protected void setNibbleNull(final int chunkX, final int chunkY, final int chunkZ) { ++ final SWMRNibbleArray nibble = this.getNibbleFromCache(chunkX, chunkY, chunkZ); ++ if (nibble != null) { ++ // de-initialisation is not as straightforward as with sky data, since deinit of block light is typically ++ // because a block was removed - which can decrease light. with sky data, block breaking can only result ++ // in increases, and thus the existing sky block check will actually correctly propagate light through ++ // a null section. so in order to propagate decreases correctly, we can do a couple of things: not remove ++ // the data section, or do edge checks on ALL axis (x, y, z). however I do not want edge checks running ++ // for clients at all, as they are expensive. so we don't remove the section, but to maintain the appearence ++ // of vanilla data management we "hide" them. ++ nibble.setHidden(); ++ } ++ } ++ ++ @Override ++ protected void initNibble(final int chunkX, final int chunkY, final int chunkZ, final boolean extrude, final boolean initRemovedNibbles) { ++ if (chunkY < this.minLightSection || chunkY > this.maxLightSection || this.getChunkInCache(chunkX, chunkZ) == null) { ++ return; ++ } ++ ++ final SWMRNibbleArray nibble = this.getNibbleFromCache(chunkX, chunkY, chunkZ); ++ if (nibble == null) { ++ if (!initRemovedNibbles) { ++ throw new IllegalStateException(); ++ } else { ++ this.setNibbleInCache(chunkX, chunkY, chunkZ, new SWMRNibbleArray()); ++ } ++ } else { ++ nibble.setNonNull(); ++ } ++ } ++ ++ @Override ++ protected final void checkBlock(final LightChunkGetter lightAccess, final int worldX, final int worldY, final int worldZ) { ++ // blocks can change opacity ++ // blocks can change emitted light ++ // blocks can change direction of propagation ++ ++ final int encodeOffset = this.coordinateOffset; ++ final int emittedMask = this.emittedLightMask; ++ ++ final int currentLevel = this.getLightLevel(worldX, worldY, worldZ); ++ final BlockState blockState = this.getBlockState(worldX, worldY, worldZ); ++ final int emittedLevel = blockState.getLightEmission() & emittedMask; ++ ++ this.setLightLevel(worldX, worldY, worldZ, emittedLevel); ++ // this accounts for change in emitted light that would cause an increase ++ if (emittedLevel != 0) { ++ this.appendToIncreaseQueue( ++ ((worldX + (worldZ << 6) + (worldY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (emittedLevel & 0xFL) << (6 + 6 + 16) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) ++ | (blockState.isConditionallyFullOpaque() ? FLAG_HAS_SIDED_TRANSPARENT_BLOCKS : 0) ++ ); ++ } ++ // this also accounts for a change in emitted light that would cause a decrease ++ // this also accounts for the change of direction of propagation (i.e old block was full transparent, new block is full opaque or vice versa) ++ // as it checks all neighbours (even if current level is 0) ++ this.appendToDecreaseQueue( ++ ((worldX + (worldZ << 6) + (worldY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (currentLevel & 0xFL) << (6 + 6 + 16) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) ++ // always keep sided transparent false here, new block might be conditionally transparent which would ++ // prevent us from decreasing sources in the directions where the new block is opaque ++ // if it turns out we were wrong to de-propagate the source, the re-propagate logic WILL always ++ // catch that and fix it. ++ ); ++ // re-propagating neighbours (done by the decrease queue) will also account for opacity changes in this block ++ } ++ ++ protected final BlockPos.MutableBlockPos recalcCenterPos = new BlockPos.MutableBlockPos(); ++ protected final BlockPos.MutableBlockPos recalcNeighbourPos = new BlockPos.MutableBlockPos(); ++ ++ @Override ++ protected int calculateLightValue(final LightChunkGetter lightAccess, final int worldX, final int worldY, final int worldZ, ++ final int expect) { ++ final BlockState centerState = this.getBlockState(worldX, worldY, worldZ); ++ int level = centerState.getLightEmission() & 0xF; ++ ++ if (level >= (15 - 1) || level > expect) { ++ return level; ++ } ++ ++ final int sectionOffset = this.chunkSectionIndexOffset; ++ final BlockState conditionallyOpaqueState; ++ int opacity = centerState.getOpacityIfCached(); ++ ++ if (opacity == -1) { ++ this.recalcCenterPos.set(worldX, worldY, worldZ); ++ opacity = centerState.getLightBlock(lightAccess.getLevel(), this.recalcCenterPos); ++ if (centerState.isConditionallyFullOpaque()) { ++ conditionallyOpaqueState = centerState; ++ } else { ++ conditionallyOpaqueState = null; ++ } ++ } else if (opacity >= 15) { ++ return level; ++ } else { ++ conditionallyOpaqueState = null; ++ } ++ opacity = Math.max(1, opacity); ++ ++ for (final AxisDirection direction : AXIS_DIRECTIONS) { ++ final int offX = worldX + direction.x; ++ final int offY = worldY + direction.y; ++ final int offZ = worldZ + direction.z; ++ ++ final int sectionIndex = (offX >> 4) + 5 * (offZ >> 4) + (5 * 5) * (offY >> 4) + sectionOffset; ++ ++ final int neighbourLevel = this.getLightLevel(sectionIndex, (offX & 15) | ((offZ & 15) << 4) | ((offY & 15) << 8)); ++ ++ if ((neighbourLevel - 1) <= level) { ++ // don't need to test transparency, we know it wont affect the result. ++ continue; ++ } ++ ++ final BlockState neighbourState = this.getBlockState(offX, offY, offZ); ++ if (neighbourState.isConditionallyFullOpaque()) { ++ // here the block can be conditionally opaque (i.e light cannot propagate from it), so we need to test that ++ // we don't read the blockstate because most of the time this is false, so using the faster ++ // known transparency lookup results in a net win ++ this.recalcNeighbourPos.set(offX, offY, offZ); ++ final VoxelShape neighbourFace = neighbourState.getFaceOcclusionShape(lightAccess.getLevel(), this.recalcNeighbourPos, direction.opposite.nms); ++ final VoxelShape thisFace = conditionallyOpaqueState == null ? Shapes.empty() : conditionallyOpaqueState.getFaceOcclusionShape(lightAccess.getLevel(), this.recalcCenterPos, direction.nms); ++ if (Shapes.faceShapeOccludes(thisFace, neighbourFace)) { ++ // not allowed to propagate ++ continue; ++ } ++ } ++ ++ // passed transparency, ++ ++ final int calculated = neighbourLevel - opacity; ++ level = Math.max(calculated, level); ++ if (level > expect) { ++ return level; ++ } ++ } ++ ++ return level; ++ } ++ ++ @Override ++ protected void propagateBlockChanges(final LightChunkGetter lightAccess, final ChunkAccess atChunk, final Set positions) { ++ for (final BlockPos pos : positions) { ++ this.checkBlock(lightAccess, pos.getX(), pos.getY(), pos.getZ()); ++ } ++ ++ this.performLightDecrease(lightAccess); ++ } ++ ++ protected Iterator getSources(final LightChunkGetter lightAccess, final ChunkAccess chunk) { ++ if (chunk instanceof ImposterProtoChunk || chunk instanceof LevelChunk) { ++ // implementation on Chunk is pretty awful, so write our own here. The big optimisation is ++ // skipping empty sections, and the far more optimised reading of types. ++ List sources = new ArrayList<>(); ++ ++ int offX = chunk.getPos().x << 4; ++ int offZ = chunk.getPos().z << 4; ++ ++ final LevelChunkSection[] sections = chunk.getSections(); ++ for (int sectionY = this.minSection; sectionY <= this.maxSection; ++sectionY) { ++ final LevelChunkSection section = sections[sectionY - this.minSection]; ++ if (section == null || section.isEmpty()) { ++ // no sources in empty sections ++ continue; ++ } ++ final PalettedContainer states = section.states; ++ final int offY = sectionY << 4; ++ ++ for (int index = 0; index < (16 * 16 * 16); ++index) { ++ final BlockState state = states.get(index); ++ if (state.getLightEmission() <= 0) { ++ continue; ++ } ++ ++ // index = x | (z << 4) | (y << 8) ++ sources.add(new BlockPos(offX | (index & 15), offY | (index >>> 8), offZ | ((index >>> 4) & 15))); ++ } ++ } ++ ++ return sources.iterator(); ++ } else { ++ // world gen and lighting run in parallel, and if lighting keeps up it can be lighting chunks that are ++ // being generated. In the nether, lava will add a lot of sources. This resulted in quite a few CME crashes. ++ // So all we do spinloop until we can collect a list of sources, and even if it is out of date we will pick up ++ // the missing sources from checkBlock. ++ for (;;) { ++ try { ++ return chunk.getLights().collect(Collectors.toList()).iterator(); ++ } catch (final Exception cme) { ++ continue; ++ } ++ } ++ } ++ } ++ ++ @Override ++ public void lightChunk(final LightChunkGetter lightAccess, final ChunkAccess chunk, final boolean needsEdgeChecks) { ++ // setup sources ++ final int emittedMask = this.emittedLightMask; ++ for (final Iterator positions = this.getSources(lightAccess, chunk); positions.hasNext();) { ++ final BlockPos pos = positions.next(); ++ final BlockState blockState = this.getBlockState(pos.getX(), pos.getY(), pos.getZ()); ++ final int emittedLight = blockState.getLightEmission() & emittedMask; ++ ++ if (emittedLight <= this.getLightLevel(pos.getX(), pos.getY(), pos.getZ())) { ++ // some other source is brighter ++ continue; ++ } ++ ++ this.appendToIncreaseQueue( ++ ((pos.getX() + (pos.getZ() << 6) + (pos.getY() << (6 + 6)) + this.coordinateOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (emittedLight & 0xFL) << (6 + 6 + 16) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) ++ | (blockState.isConditionallyFullOpaque() ? FLAG_HAS_SIDED_TRANSPARENT_BLOCKS : 0) ++ ); ++ ++ ++ // propagation wont set this for us ++ this.setLightLevel(pos.getX(), pos.getY(), pos.getZ(), emittedLight); ++ } ++ ++ if (needsEdgeChecks) { ++ // not required to propagate here, but this will reduce the hit of the edge checks ++ this.performLightIncrease(lightAccess); ++ ++ // verify neighbour edges ++ this.checkChunkEdges(lightAccess, chunk, this.minLightSection, this.maxLightSection); ++ } else { ++ this.propagateNeighbourLevels(lightAccess, chunk, this.minLightSection, this.maxLightSection); ++ ++ this.performLightIncrease(lightAccess); ++ } ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/starlight/light/SWMRNibbleArray.java b/src/main/java/ca/spottedleaf/starlight/light/SWMRNibbleArray.java +new file mode 100644 +index 0000000000000000000000000000000000000000..174dc7ffa66258da0b867fba5c54880e81daa6ce +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/starlight/light/SWMRNibbleArray.java +@@ -0,0 +1,439 @@ ++package ca.spottedleaf.starlight.light; ++ ++import net.minecraft.world.level.chunk.DataLayer; ++ ++import java.util.ArrayDeque; ++import java.util.Arrays; ++ ++// SWMR -> Single Writer Multi Reader Nibble Array ++public final class SWMRNibbleArray { ++ ++ /* ++ * Null nibble - nibble does not exist, and should not be written to. Just like vanilla - null ++ * nibbles are always 0 - and they are never written to directly. Only initialised/uninitialised ++ * nibbles can be written to. ++ * ++ * Uninitialised nibble - They are all 0, but the backing array isn't initialised. ++ * ++ * Initialised nibble - Has light data. ++ */ ++ ++ protected static final int INIT_STATE_NULL = 0; // null ++ protected static final int INIT_STATE_UNINIT = 1; // uninitialised ++ protected static final int INIT_STATE_INIT = 2; // initialised ++ protected static final int INIT_STATE_HIDDEN = 3; // initialised, but conversion to Vanilla data should be treated as if NULL ++ ++ public static final int ARRAY_SIZE = 16 * 16 * 16 / (8/4); // blocks / bytes per block ++ // this allows us to maintain only 1 byte array when we're not updating ++ static final ThreadLocal> WORKING_BYTES_POOL = ThreadLocal.withInitial(ArrayDeque::new); ++ ++ private static byte[] allocateBytes() { ++ final byte[] inPool = WORKING_BYTES_POOL.get().pollFirst(); ++ if (inPool != null) { ++ return inPool; ++ } ++ ++ return new byte[ARRAY_SIZE]; ++ } ++ ++ private static void freeBytes(final byte[] bytes) { ++ WORKING_BYTES_POOL.get().addFirst(bytes); ++ } ++ ++ public static SWMRNibbleArray fromVanilla(final DataLayer nibble) { ++ if (nibble == null) { ++ return new SWMRNibbleArray(null, true); ++ } else if (nibble.isEmpty()) { ++ return new SWMRNibbleArray(); ++ } else { ++ return new SWMRNibbleArray(nibble.getData().clone()); // make sure we don't write to the parameter later ++ } ++ } ++ ++ protected int stateUpdating; ++ protected volatile int stateVisible; ++ ++ protected byte[] storageUpdating; ++ protected boolean updatingDirty; // only returns whether storageUpdating is dirty ++ protected byte[] storageVisible; ++ ++ public SWMRNibbleArray() { ++ this(null, false); // lazy init ++ } ++ ++ public SWMRNibbleArray(final byte[] bytes) { ++ this(bytes, false); ++ } ++ ++ public SWMRNibbleArray(final byte[] bytes, final boolean isNullNibble) { ++ if (bytes != null && bytes.length != ARRAY_SIZE) { ++ throw new IllegalArgumentException("Data of wrong length: " + bytes.length); ++ } ++ this.stateVisible = this.stateUpdating = bytes == null ? (isNullNibble ? INIT_STATE_NULL : INIT_STATE_UNINIT) : INIT_STATE_INIT; ++ this.storageUpdating = this.storageVisible = bytes; ++ } ++ ++ public SWMRNibbleArray(final byte[] bytes, final int state) { ++ if (bytes != null && bytes.length != ARRAY_SIZE) { ++ throw new IllegalArgumentException("Data of wrong length: " + bytes.length); ++ } ++ if (bytes == null && (state == INIT_STATE_INIT || state == INIT_STATE_HIDDEN)) { ++ throw new IllegalArgumentException("Data cannot be null and have state be initialised"); ++ } ++ this.stateUpdating = this.stateVisible = state; ++ this.storageUpdating = this.storageVisible = bytes; ++ } ++ ++ @Override ++ public String toString() { ++ StringBuilder stringBuilder = new StringBuilder(); ++ stringBuilder.append("State: "); ++ switch (this.stateVisible) { ++ case INIT_STATE_NULL: ++ stringBuilder.append("null"); ++ break; ++ case INIT_STATE_UNINIT: ++ stringBuilder.append("uninitialised"); ++ break; ++ case INIT_STATE_INIT: ++ stringBuilder.append("initialised"); ++ break; ++ case INIT_STATE_HIDDEN: ++ stringBuilder.append("hidden"); ++ break; ++ default: ++ stringBuilder.append("unknown"); ++ break; ++ } ++ stringBuilder.append("\nData:\n"); ++ ++ final byte[] data = this.storageVisible; ++ if (data != null) { ++ for (int i = 0; i < 4096; ++i) { ++ // Copied from NibbleArray#toString ++ final int level = ((data[i >>> 1] >>> ((i & 1) << 2)) & 0xF); ++ ++ stringBuilder.append(Integer.toHexString(level)); ++ if ((i & 15) == 15) { ++ stringBuilder.append("\n"); ++ } ++ ++ if ((i & 255) == 255) { ++ stringBuilder.append("\n"); ++ } ++ } ++ } else { ++ stringBuilder.append("null"); ++ } ++ ++ return stringBuilder.toString(); ++ } ++ ++ public SaveState getSaveState() { ++ synchronized (this) { ++ final int state = this.stateVisible; ++ final byte[] data = this.storageVisible; ++ if (state == INIT_STATE_NULL) { ++ return null; ++ } ++ if (state == INIT_STATE_UNINIT) { ++ return new SaveState(null, state); ++ } ++ final boolean zero = isAllZero(data); ++ if (zero) { ++ return state == INIT_STATE_INIT ? new SaveState(null, INIT_STATE_UNINIT) : null; ++ } else { ++ return new SaveState(data.clone(), state); ++ } ++ } ++ } ++ ++ protected static boolean isAllZero(final byte[] data) { ++ for (int i = 0; i < (ARRAY_SIZE >>> 4); ++i) { ++ byte whole = data[i << 4]; ++ ++ for (int k = 1; k < (1 << 4); ++k) { ++ whole |= data[(i << 4) | k]; ++ } ++ ++ if (whole != 0) { ++ return false; ++ } ++ } ++ ++ return true; ++ } ++ ++ // operation type: updating on src, updating on other ++ public void extrudeLower(final SWMRNibbleArray other) { ++ if (other.stateUpdating == INIT_STATE_NULL) { ++ throw new IllegalArgumentException(); ++ } ++ ++ if (other.storageUpdating == null) { ++ this.setUninitialised(); ++ return; ++ } ++ ++ final byte[] src = other.storageUpdating; ++ final byte[] into; ++ ++ if (this.storageUpdating != null) { ++ into = this.storageUpdating; ++ } else { ++ this.storageUpdating = into = allocateBytes(); ++ this.stateUpdating = INIT_STATE_INIT; ++ } ++ this.updatingDirty = true; ++ ++ final int start = 0; ++ final int end = (15 | (15 << 4)) >>> 1; ++ ++ /* x | (z << 4) | (y << 8) */ ++ for (int y = 0; y <= 15; ++y) { ++ System.arraycopy(src, start, into, y << (8 - 1), end - start + 1); ++ } ++ } ++ ++ // operation type: updating ++ public void setFull() { ++ if (this.stateUpdating != INIT_STATE_HIDDEN) { ++ this.stateUpdating = INIT_STATE_INIT; ++ } ++ Arrays.fill(this.storageUpdating == null || !this.updatingDirty ? this.storageUpdating = allocateBytes() : this.storageUpdating, (byte)-1); ++ this.updatingDirty = true; ++ } ++ ++ // operation type: updating ++ public void setZero() { ++ if (this.stateUpdating != INIT_STATE_HIDDEN) { ++ this.stateUpdating = INIT_STATE_INIT; ++ } ++ Arrays.fill(this.storageUpdating == null || !this.updatingDirty ? this.storageUpdating = allocateBytes() : this.storageUpdating, (byte)0); ++ this.updatingDirty = true; ++ } ++ ++ // operation type: updating ++ public void setNonNull() { ++ if (this.stateUpdating == INIT_STATE_HIDDEN) { ++ this.stateUpdating = INIT_STATE_INIT; ++ return; ++ } ++ if (this.stateUpdating != INIT_STATE_NULL) { ++ return; ++ } ++ this.stateUpdating = INIT_STATE_UNINIT; ++ } ++ ++ // operation type: updating ++ public void setNull() { ++ this.stateUpdating = INIT_STATE_NULL; ++ if (this.updatingDirty && this.storageUpdating != null) { ++ freeBytes(this.storageUpdating); ++ } ++ this.storageUpdating = null; ++ this.updatingDirty = false; ++ } ++ ++ // operation type: updating ++ public void setUninitialised() { ++ this.stateUpdating = INIT_STATE_UNINIT; ++ if (this.storageUpdating != null && this.updatingDirty) { ++ freeBytes(this.storageUpdating); ++ } ++ this.storageUpdating = null; ++ this.updatingDirty = false; ++ } ++ ++ // operation type: updating ++ public void setHidden() { ++ if (this.stateUpdating == INIT_STATE_HIDDEN) { ++ return; ++ } ++ if (this.stateUpdating != INIT_STATE_INIT) { ++ this.setNull(); ++ } else { ++ this.stateUpdating = INIT_STATE_HIDDEN; ++ } ++ } ++ ++ // operation type: updating ++ public boolean isDirty() { ++ return this.stateUpdating != this.stateVisible || this.updatingDirty; ++ } ++ ++ // operation type: updating ++ public boolean isNullNibbleUpdating() { ++ return this.stateUpdating == INIT_STATE_NULL; ++ } ++ ++ // operation type: visible ++ public boolean isNullNibbleVisible() { ++ return this.stateVisible == INIT_STATE_NULL; ++ } ++ ++ // opeartion type: updating ++ public boolean isUninitialisedUpdating() { ++ return this.stateUpdating == INIT_STATE_UNINIT; ++ } ++ ++ // operation type: visible ++ public boolean isUninitialisedVisible() { ++ return this.stateVisible == INIT_STATE_UNINIT; ++ } ++ ++ // operation type: updating ++ public boolean isInitialisedUpdating() { ++ return this.stateUpdating == INIT_STATE_INIT; ++ } ++ ++ // operation type: visible ++ public boolean isInitialisedVisible() { ++ return this.stateVisible == INIT_STATE_INIT; ++ } ++ ++ // operation type: updating ++ public boolean isHiddenUpdating() { ++ return this.stateUpdating == INIT_STATE_HIDDEN; ++ } ++ ++ // operation type: updating ++ public boolean isHiddenVisible() { ++ return this.stateVisible == INIT_STATE_HIDDEN; ++ } ++ ++ // operation type: updating ++ protected void swapUpdatingAndMarkDirty() { ++ if (this.updatingDirty) { ++ return; ++ } ++ ++ if (this.storageUpdating == null) { ++ this.storageUpdating = allocateBytes(); ++ Arrays.fill(this.storageUpdating, (byte)0); ++ } else { ++ System.arraycopy(this.storageUpdating, 0, this.storageUpdating = allocateBytes(), 0, ARRAY_SIZE); ++ } ++ ++ if (this.stateUpdating != INIT_STATE_HIDDEN) { ++ this.stateUpdating = INIT_STATE_INIT; ++ } ++ this.updatingDirty = true; ++ } ++ ++ // operation type: updating ++ public boolean updateVisible() { ++ if (!this.isDirty()) { ++ return false; ++ } ++ ++ synchronized (this) { ++ if (this.stateUpdating == INIT_STATE_NULL || this.stateUpdating == INIT_STATE_UNINIT) { ++ this.storageVisible = null; ++ } else { ++ if (this.storageVisible == null) { ++ this.storageVisible = this.storageUpdating.clone(); ++ } else { ++ if (this.storageUpdating != this.storageVisible) { ++ System.arraycopy(this.storageUpdating, 0, this.storageVisible, 0, ARRAY_SIZE); ++ } ++ } ++ ++ if (this.storageUpdating != this.storageVisible) { ++ freeBytes(this.storageUpdating); ++ } ++ this.storageUpdating = this.storageVisible; ++ } ++ this.updatingDirty = false; ++ this.stateVisible = this.stateUpdating; ++ } ++ ++ return true; ++ } ++ ++ // operation type: visible ++ public DataLayer toVanillaNibble() { ++ synchronized (this) { ++ switch (this.stateVisible) { ++ case INIT_STATE_HIDDEN: ++ case INIT_STATE_NULL: ++ return null; ++ case INIT_STATE_UNINIT: ++ return new DataLayer(); ++ case INIT_STATE_INIT: ++ return new DataLayer(this.storageVisible.clone()); ++ default: ++ throw new IllegalStateException(); ++ } ++ } ++ } ++ ++ /* x | (z << 4) | (y << 8) */ ++ ++ // operation type: updating ++ public int getUpdating(final int x, final int y, final int z) { ++ return this.getUpdating((x & 15) | ((z & 15) << 4) | ((y & 15) << 8)); ++ } ++ ++ // operation type: updating ++ public int getUpdating(final int index) { ++ // indices range from 0 -> 4096 ++ final byte[] bytes = this.storageUpdating; ++ if (bytes == null) { ++ return 0; ++ } ++ final byte value = bytes[index >>> 1]; ++ ++ // if we are an even index, we want lower 4 bits ++ // if we are an odd index, we want upper 4 bits ++ return ((value >>> ((index & 1) << 2)) & 0xF); ++ } ++ ++ // operation type: visible ++ public int getVisible(final int x, final int y, final int z) { ++ return this.getVisible((x & 15) | ((z & 15) << 4) | ((y & 15) << 8)); ++ } ++ ++ // operation type: visible ++ public int getVisible(final int index) { ++ synchronized (this) { ++ // indices range from 0 -> 4096 ++ final byte[] visibleBytes = this.storageVisible; ++ if (visibleBytes == null) { ++ return 0; ++ } ++ final byte value = visibleBytes[index >>> 1]; ++ ++ // if we are an even index, we want lower 4 bits ++ // if we are an odd index, we want upper 4 bits ++ return ((value >>> ((index & 1) << 2)) & 0xF); ++ } ++ } ++ ++ // operation type: updating ++ public void set(final int x, final int y, final int z, final int value) { ++ this.set((x & 15) | ((z & 15) << 4) | ((y & 15) << 8), value); ++ } ++ ++ // operation type: updating ++ public void set(final int index, final int value) { ++ if (!this.updatingDirty) { ++ this.swapUpdatingAndMarkDirty(); ++ } ++ final int shift = (index & 1) << 2; ++ final int i = index >>> 1; ++ ++ this.storageUpdating[i] = (byte)((this.storageUpdating[i] & (0xF0 >>> shift)) | (value << shift)); ++ } ++ ++ public static final class SaveState { ++ ++ public final byte[] data; ++ public final int state; ++ ++ public SaveState(final byte[] data, final int state) { ++ this.data = data; ++ this.state = state; ++ } ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/starlight/light/SkyStarLightEngine.java b/src/main/java/ca/spottedleaf/starlight/light/SkyStarLightEngine.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e843ceb27bce134f7785e8c45fac25d5ec747233 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/starlight/light/SkyStarLightEngine.java +@@ -0,0 +1,715 @@ ++package ca.spottedleaf.starlight.light; ++ ++import io.papermc.paper.util.WorldUtil; ++import it.unimi.dsi.fastutil.shorts.ShortCollection; ++import it.unimi.dsi.fastutil.shorts.ShortIterator; ++import net.minecraft.core.BlockPos; ++import net.minecraft.world.level.BlockGetter; ++import net.minecraft.world.level.ChunkPos; ++import net.minecraft.world.level.Level; ++import net.minecraft.world.level.block.state.BlockState; ++import net.minecraft.world.level.chunk.ChunkAccess; ++import net.minecraft.world.level.chunk.ChunkStatus; ++import net.minecraft.world.level.chunk.LevelChunkSection; ++import net.minecraft.world.level.chunk.LightChunkGetter; ++import net.minecraft.world.phys.shapes.Shapes; ++import net.minecraft.world.phys.shapes.VoxelShape; ++import java.util.Arrays; ++import java.util.Set; ++ ++public final class SkyStarLightEngine extends StarLightEngine { ++ ++ /* ++ Specification for managing the initialisation and de-initialisation of skylight nibble arrays: ++ ++ Skylight nibble initialisation requires that non-empty chunk sections have 1 radius nibbles non-null. ++ ++ This presents some problems, as vanilla is only guaranteed to have 0 radius neighbours loaded when editing blocks. ++ However starlight fixes this so that it has 1 radius loaded. Still, we don't actually have guarantees ++ that we have the necessary chunks loaded to de-initialise neighbour sections (but we do have enough to de-initialise ++ our own) - we need a radius of 2 to de-initialise neighbour nibbles. ++ How do we solve this? ++ ++ Each chunk will store the last known "emptiness" of sections for each of their 1 radius neighbour chunk sections. ++ If the chunk does not have full data, then its nibbles are NOT de-initialised. This is because obviously the ++ chunk did not go through the light stage yet - or its neighbours are not lit. In either case, once the last ++ known "emptiness" of neighbouring sections is filled with data, the chunk will run a full check of the data ++ to see if any of its nibbles need to be de-initialised. ++ ++ The emptiness map allows us to de-initialise neighbour nibbles if the neighbour has it filled with data, ++ and if it doesn't have data then we know it will correctly de-initialise once it fills up. ++ ++ Unlike vanilla, we store whether nibbles are uninitialised on disk - so we don't need any dumb hacking ++ around those. ++ */ ++ ++ protected final int[] heightMapBlockChange = new int[16 * 16]; ++ { ++ Arrays.fill(this.heightMapBlockChange, Integer.MIN_VALUE); // clear heightmap ++ } ++ ++ protected final boolean[] nullPropagationCheckCache; ++ ++ public SkyStarLightEngine(final Level world) { ++ super(true, world); ++ this.nullPropagationCheckCache = new boolean[WorldUtil.getTotalLightSections(world)]; ++ } ++ ++ @Override ++ protected void initNibble(final int chunkX, final int chunkY, final int chunkZ, final boolean extrude, final boolean initRemovedNibbles) { ++ if (chunkY < this.minLightSection || chunkY > this.maxLightSection || this.getChunkInCache(chunkX, chunkZ) == null) { ++ return; ++ } ++ SWMRNibbleArray nibble = this.getNibbleFromCache(chunkX, chunkY, chunkZ); ++ if (nibble == null) { ++ if (!initRemovedNibbles) { ++ throw new IllegalStateException(); ++ } else { ++ this.setNibbleInCache(chunkX, chunkY, chunkZ, nibble = new SWMRNibbleArray(null, true)); ++ } ++ } ++ this.initNibble(nibble, chunkX, chunkY, chunkZ, extrude); ++ } ++ ++ @Override ++ protected void setNibbleNull(final int chunkX, final int chunkY, final int chunkZ) { ++ final SWMRNibbleArray nibble = this.getNibbleFromCache(chunkX, chunkY, chunkZ); ++ if (nibble != null) { ++ nibble.setNull(); ++ } ++ } ++ ++ protected final void initNibble(final SWMRNibbleArray currNibble, final int chunkX, final int chunkY, final int chunkZ, final boolean extrude) { ++ if (!currNibble.isNullNibbleUpdating()) { ++ // already initialised ++ return; ++ } ++ ++ final boolean[] emptinessMap = this.getEmptinessMap(chunkX, chunkZ); ++ ++ // are we above this chunk's lowest empty section? ++ int lowestY = this.minLightSection - 1; ++ for (int currY = this.maxSection; currY >= this.minSection; --currY) { ++ if (emptinessMap == null) { ++ // cannot delay nibble init for lit chunks, as we need to init to propagate into them. ++ final LevelChunkSection current = this.getChunkSection(chunkX, currY, chunkZ); ++ if (current == null || current == EMPTY_CHUNK_SECTION) { ++ continue; ++ } ++ } else { ++ if (emptinessMap[currY - this.minSection]) { ++ continue; ++ } ++ } ++ ++ // should always be full lit here ++ lowestY = currY; ++ break; ++ } ++ ++ if (chunkY > lowestY) { ++ // we need to set this one to full ++ final SWMRNibbleArray nibble = this.getNibbleFromCache(chunkX, chunkY, chunkZ); ++ nibble.setNonNull(); ++ nibble.setFull(); ++ return; ++ } ++ ++ if (extrude) { ++ // this nibble is going to depend solely on the skylight data above it ++ // find first non-null data above (there does exist one, as we just found it above) ++ for (int currY = chunkY + 1; currY <= this.maxLightSection; ++currY) { ++ final SWMRNibbleArray nibble = this.getNibbleFromCache(chunkX, currY, chunkZ); ++ if (nibble != null && !nibble.isNullNibbleUpdating()) { ++ currNibble.setNonNull(); ++ currNibble.extrudeLower(nibble); ++ break; ++ } ++ } ++ } else { ++ currNibble.setNonNull(); ++ } ++ } ++ ++ protected final void rewriteNibbleCacheForSkylight(final ChunkAccess chunk) { ++ for (int index = 0, max = this.nibbleCache.length; index < max; ++index) { ++ final SWMRNibbleArray nibble = this.nibbleCache[index]; ++ if (nibble != null && nibble.isNullNibbleUpdating()) { ++ // stop propagation in these areas ++ this.nibbleCache[index] = null; ++ nibble.updateVisible(); ++ } ++ } ++ } ++ ++ // rets whether neighbours were init'd ++ ++ protected final boolean checkNullSection(final int chunkX, final int chunkY, final int chunkZ, ++ final boolean extrudeInitialised) { ++ // null chunk sections may have nibble neighbours in the horizontal 1 radius that are ++ // non-null. Propagation to these neighbours is necessary. ++ // What makes this easy is we know none of these neighbours are non-empty (otherwise ++ // this nibble would be initialised). So, we don't have to initialise ++ // the neighbours in the full 1 radius, because there's no worry that any "paths" ++ // to the neighbours on this horizontal plane are blocked. ++ if (chunkY < this.minLightSection || chunkY > this.maxLightSection || this.nullPropagationCheckCache[chunkY - this.minLightSection]) { ++ return false; ++ } ++ this.nullPropagationCheckCache[chunkY - this.minLightSection] = true; ++ ++ // check horizontal neighbours ++ boolean needInitNeighbours = false; ++ neighbour_search: ++ for (int dz = -1; dz <= 1; ++dz) { ++ for (int dx = -1; dx <= 1; ++dx) { ++ final SWMRNibbleArray nibble = this.getNibbleFromCache(dx + chunkX, chunkY, dz + chunkZ); ++ if (nibble != null && !nibble.isNullNibbleUpdating()) { ++ needInitNeighbours = true; ++ break neighbour_search; ++ } ++ } ++ } ++ ++ if (needInitNeighbours) { ++ for (int dz = -1; dz <= 1; ++dz) { ++ for (int dx = -1; dx <= 1; ++dx) { ++ this.initNibble(dx + chunkX, chunkY, dz + chunkZ, (dx | dz) == 0 ? extrudeInitialised : true, true); ++ } ++ } ++ } ++ ++ return needInitNeighbours; ++ } ++ ++ protected final int getLightLevelExtruded(final int worldX, final int worldY, final int worldZ) { ++ final int chunkX = worldX >> 4; ++ int chunkY = worldY >> 4; ++ final int chunkZ = worldZ >> 4; ++ ++ SWMRNibbleArray nibble = this.getNibbleFromCache(chunkX, chunkY, chunkZ); ++ if (nibble != null) { ++ return nibble.getUpdating(worldX, worldY, worldZ); ++ } ++ ++ for (;;) { ++ if (++chunkY > this.maxLightSection) { ++ return 15; ++ } ++ ++ nibble = this.getNibbleFromCache(chunkX, chunkY, chunkZ); ++ ++ if (nibble != null) { ++ return nibble.getUpdating(worldX, 0, worldZ); ++ } ++ } ++ } ++ ++ @Override ++ protected boolean[] getEmptinessMap(final ChunkAccess chunk) { ++ return chunk.getSkyEmptinessMap(); ++ } ++ ++ @Override ++ protected void setEmptinessMap(final ChunkAccess chunk, final boolean[] to) { ++ chunk.setSkyEmptinessMap(to); ++ } ++ ++ @Override ++ protected SWMRNibbleArray[] getNibblesOnChunk(final ChunkAccess chunk) { ++ return chunk.getSkyNibbles(); ++ } ++ ++ @Override ++ protected void setNibbles(final ChunkAccess chunk, final SWMRNibbleArray[] to) { ++ chunk.setSkyNibbles(to); ++ } ++ ++ @Override ++ protected boolean canUseChunk(final ChunkAccess chunk) { ++ // can only use chunks for sky stuff if their sections have been init'd ++ return chunk.getStatus().isOrAfter(ChunkStatus.LIGHT) && (this.isClientSide || chunk.isLightCorrect()); ++ } ++ ++ @Override ++ protected void checkChunkEdges(final LightChunkGetter lightAccess, final ChunkAccess chunk, final int fromSection, ++ final int toSection) { ++ Arrays.fill(this.nullPropagationCheckCache, false); ++ this.rewriteNibbleCacheForSkylight(chunk); ++ final int chunkX = chunk.getPos().x; ++ final int chunkZ = chunk.getPos().z; ++ for (int y = toSection; y >= fromSection; --y) { ++ this.checkNullSection(chunkX, y, chunkZ, true); ++ } ++ ++ super.checkChunkEdges(lightAccess, chunk, fromSection, toSection); ++ } ++ ++ @Override ++ protected void checkChunkEdges(final LightChunkGetter lightAccess, final ChunkAccess chunk, final ShortCollection sections) { ++ Arrays.fill(this.nullPropagationCheckCache, false); ++ this.rewriteNibbleCacheForSkylight(chunk); ++ final int chunkX = chunk.getPos().x; ++ final int chunkZ = chunk.getPos().z; ++ for (final ShortIterator iterator = sections.iterator(); iterator.hasNext();) { ++ final int y = (int)iterator.nextShort(); ++ this.checkNullSection(chunkX, y, chunkZ, true); ++ } ++ ++ super.checkChunkEdges(lightAccess, chunk, sections); ++ } ++ ++ @Override ++ protected void checkBlock(final LightChunkGetter lightAccess, final int worldX, final int worldY, final int worldZ) { ++ // blocks can change opacity ++ // blocks can change direction of propagation ++ ++ // same logic applies from BlockStarLightEngine#checkBlock ++ ++ final int encodeOffset = this.coordinateOffset; ++ ++ final int currentLevel = this.getLightLevel(worldX, worldY, worldZ); ++ ++ if (currentLevel == 15) { ++ // must re-propagate clobbered source ++ this.appendToIncreaseQueue( ++ ((worldX + (worldZ << 6) + (worldY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (currentLevel & 0xFL) << (6 + 6 + 16) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) ++ | FLAG_HAS_SIDED_TRANSPARENT_BLOCKS // don't know if the block is conditionally transparent ++ ); ++ } else { ++ this.setLightLevel(worldX, worldY, worldZ, 0); ++ } ++ ++ this.appendToDecreaseQueue( ++ ((worldX + (worldZ << 6) + (worldY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (currentLevel & 0xFL) << (6 + 6 + 16) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) ++ ); ++ } ++ ++ protected final BlockPos.MutableBlockPos recalcCenterPos = new BlockPos.MutableBlockPos(); ++ protected final BlockPos.MutableBlockPos recalcNeighbourPos = new BlockPos.MutableBlockPos(); ++ ++ @Override ++ protected int calculateLightValue(final LightChunkGetter lightAccess, final int worldX, final int worldY, final int worldZ, ++ final int expect) { ++ if (expect == 15) { ++ return expect; ++ } ++ ++ final int sectionOffset = this.chunkSectionIndexOffset; ++ final BlockState centerState = this.getBlockState(worldX, worldY, worldZ); ++ int opacity = centerState.getOpacityIfCached(); ++ ++ BlockState conditionallyOpaqueState; ++ if (opacity < 0) { ++ this.recalcCenterPos.set(worldX, worldY, worldZ); ++ opacity = Math.max(1, centerState.getLightBlock(lightAccess.getLevel(), this.recalcCenterPos)); ++ if (centerState.isConditionallyFullOpaque()) { ++ conditionallyOpaqueState = centerState; ++ } else { ++ conditionallyOpaqueState = null; ++ } ++ } else { ++ conditionallyOpaqueState = null; ++ opacity = Math.max(1, opacity); ++ } ++ ++ int level = 0; ++ ++ for (final AxisDirection direction : AXIS_DIRECTIONS) { ++ final int offX = worldX + direction.x; ++ final int offY = worldY + direction.y; ++ final int offZ = worldZ + direction.z; ++ ++ final int sectionIndex = (offX >> 4) + 5 * (offZ >> 4) + (5 * 5) * (offY >> 4) + sectionOffset; ++ ++ final int neighbourLevel = this.getLightLevel(sectionIndex, (offX & 15) | ((offZ & 15) << 4) | ((offY & 15) << 8)); ++ ++ if ((neighbourLevel - 1) <= level) { ++ // don't need to test transparency, we know it wont affect the result. ++ continue; ++ } ++ ++ final BlockState neighbourState = this.getBlockState(offX, offY, offZ); ++ ++ if (neighbourState.isConditionallyFullOpaque()) { ++ // here the block can be conditionally opaque (i.e light cannot propagate from it), so we need to test that ++ // we don't read the blockstate because most of the time this is false, so using the faster ++ // known transparency lookup results in a net win ++ this.recalcNeighbourPos.set(offX, offY, offZ); ++ final VoxelShape neighbourFace = neighbourState.getFaceOcclusionShape(lightAccess.getLevel(), this.recalcNeighbourPos, direction.opposite.nms); ++ final VoxelShape thisFace = conditionallyOpaqueState == null ? Shapes.empty() : conditionallyOpaqueState.getFaceOcclusionShape(lightAccess.getLevel(), this.recalcCenterPos, direction.nms); ++ if (Shapes.faceShapeOccludes(thisFace, neighbourFace)) { ++ // not allowed to propagate ++ continue; ++ } ++ } ++ ++ final int calculated = neighbourLevel - opacity; ++ level = Math.max(calculated, level); ++ if (level > expect) { ++ return level; ++ } ++ } ++ ++ return level; ++ } ++ ++ @Override ++ protected void propagateBlockChanges(final LightChunkGetter lightAccess, final ChunkAccess atChunk, final Set positions) { ++ this.rewriteNibbleCacheForSkylight(atChunk); ++ Arrays.fill(this.nullPropagationCheckCache, false); ++ ++ final BlockGetter world = lightAccess.getLevel(); ++ final int chunkX = atChunk.getPos().x; ++ final int chunkZ = atChunk.getPos().z; ++ final int heightMapOffset = chunkX * -16 + (chunkZ * (-16 * 16)); ++ ++ // setup heightmap for changes ++ for (final BlockPos pos : positions) { ++ final int index = pos.getX() + (pos.getZ() << 4) + heightMapOffset; ++ final int curr = this.heightMapBlockChange[index]; ++ if (pos.getY() > curr) { ++ this.heightMapBlockChange[index] = pos.getY(); ++ } ++ } ++ ++ // note: light sets are delayed while processing skylight source changes due to how ++ // nibbles are initialised, as we want to avoid clobbering nibble values so what when ++ // below nibbles are initialised they aren't reading from partially modified nibbles ++ ++ // now we can recalculate the sources for the changed columns ++ for (int index = 0; index < (16 * 16); ++index) { ++ final int maxY = this.heightMapBlockChange[index]; ++ if (maxY == Integer.MIN_VALUE) { ++ // not changed ++ continue; ++ } ++ this.heightMapBlockChange[index] = Integer.MIN_VALUE; // restore default for next caller ++ ++ final int columnX = (index & 15) | (chunkX << 4); ++ final int columnZ = (index >>> 4) | (chunkZ << 4); ++ ++ // try and propagate from the above y ++ // delay light set until after processing all sources to setup ++ final int maxPropagationY = this.tryPropagateSkylight(world, columnX, maxY, columnZ, true, true); ++ ++ // maxPropagationY is now the highest block that could not be propagated to ++ ++ // remove all sources below that are 15 ++ final long propagateDirection = AxisDirection.POSITIVE_Y.everythingButThisDirection; ++ final int encodeOffset = this.coordinateOffset; ++ ++ if (this.getLightLevelExtruded(columnX, maxPropagationY, columnZ) == 15) { ++ // ensure section is checked ++ this.checkNullSection(columnX >> 4, maxPropagationY >> 4, columnZ >> 4, true); ++ ++ for (int currY = maxPropagationY; currY >= (this.minLightSection << 4); --currY) { ++ if ((currY & 15) == 15) { ++ // ensure section is checked ++ this.checkNullSection(columnX >> 4, (currY >> 4), columnZ >> 4, true); ++ } ++ ++ // ensure section below is always checked ++ final SWMRNibbleArray nibble = this.getNibbleFromCache(columnX >> 4, currY >> 4, columnZ >> 4); ++ if (nibble == null) { ++ // advance currY to the the top of the section below ++ currY = (currY) & (~15); ++ // note: this value ^ is actually 1 above the top, but the loop decrements by 1 so we actually ++ // end up there ++ continue; ++ } ++ ++ if (nibble.getUpdating(columnX, currY, columnZ) != 15) { ++ break; ++ } ++ ++ // delay light set until after processing all sources to setup ++ this.appendToDecreaseQueue( ++ ((columnX + (columnZ << 6) + (currY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (15L << (6 + 6 + 16)) ++ | (propagateDirection << (6 + 6 + 16 + 4)) ++ // do not set transparent blocks for the same reason we don't in the checkBlock method ++ ); ++ } ++ } ++ } ++ ++ // delayed light sets are processed here, and must be processed before checkBlock as checkBlock reads ++ // immediate light value ++ this.processDelayedIncreases(); ++ this.processDelayedDecreases(); ++ ++ for (final BlockPos pos : positions) { ++ this.checkBlock(lightAccess, pos.getX(), pos.getY(), pos.getZ()); ++ } ++ ++ this.performLightDecrease(lightAccess); ++ } ++ ++ protected final int[] heightMapGen = new int[32 * 32]; ++ ++ @Override ++ protected void lightChunk(final LightChunkGetter lightAccess, final ChunkAccess chunk, final boolean needsEdgeChecks) { ++ this.rewriteNibbleCacheForSkylight(chunk); ++ Arrays.fill(this.nullPropagationCheckCache, false); ++ ++ final BlockGetter world = lightAccess.getLevel(); ++ final ChunkPos chunkPos = chunk.getPos(); ++ final int chunkX = chunkPos.x; ++ final int chunkZ = chunkPos.z; ++ ++ final LevelChunkSection[] sections = chunk.getSections(); ++ ++ int highestNonEmptySection = this.maxSection; ++ while (highestNonEmptySection == (this.minSection - 1) || ++ sections[highestNonEmptySection - this.minSection] == null || sections[highestNonEmptySection - this.minSection].isEmpty()) { ++ this.checkNullSection(chunkX, highestNonEmptySection, chunkZ, false); ++ // try propagate FULL to neighbours ++ ++ // check neighbours to see if we need to propagate into them ++ for (final AxisDirection direction : ONLY_HORIZONTAL_DIRECTIONS) { ++ final int neighbourX = chunkX + direction.x; ++ final int neighbourZ = chunkZ + direction.z; ++ final SWMRNibbleArray neighbourNibble = this.getNibbleFromCache(neighbourX, highestNonEmptySection, neighbourZ); ++ if (neighbourNibble == null) { ++ // unloaded neighbour ++ // most of the time we fall here ++ continue; ++ } ++ ++ // it looks like we need to propagate into the neighbour ++ ++ final int incX; ++ final int incZ; ++ final int startX; ++ final int startZ; ++ ++ if (direction.x != 0) { ++ // x direction ++ incX = 0; ++ incZ = 1; ++ ++ if (direction.x < 0) { ++ // negative ++ startX = chunkX << 4; ++ } else { ++ startX = chunkX << 4 | 15; ++ } ++ startZ = chunkZ << 4; ++ } else { ++ // z direction ++ incX = 1; ++ incZ = 0; ++ ++ if (direction.z < 0) { ++ // negative ++ startZ = chunkZ << 4; ++ } else { ++ startZ = chunkZ << 4 | 15; ++ } ++ startX = chunkX << 4; ++ } ++ ++ final int encodeOffset = this.coordinateOffset; ++ final long propagateDirection = 1L << direction.ordinal(); // we only want to check in this direction ++ ++ for (int currY = highestNonEmptySection << 4, maxY = currY | 15; currY <= maxY; ++currY) { ++ for (int i = 0, currX = startX, currZ = startZ; i < 16; ++i, currX += incX, currZ += incZ) { ++ this.appendToIncreaseQueue( ++ ((currX + (currZ << 6) + (currY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (15L << (6 + 6 + 16)) // we know we're at full lit here ++ | (propagateDirection << (6 + 6 + 16 + 4)) ++ // no transparent flag, we know for a fact there are no blocks here that could be directionally transparent (as the section is EMPTY) ++ ); ++ } ++ } ++ } ++ ++ if (highestNonEmptySection-- == (this.minSection - 1)) { ++ break; ++ } ++ } ++ ++ if (highestNonEmptySection >= this.minSection) { ++ // fill out our other sources ++ final int minX = chunkPos.x << 4; ++ final int maxX = chunkPos.x << 4 | 15; ++ final int minZ = chunkPos.z << 4; ++ final int maxZ = chunkPos.z << 4 | 15; ++ final int startY = highestNonEmptySection << 4 | 15; ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ for (int currX = minX; currX <= maxX; ++currX) { ++ this.tryPropagateSkylight(world, currX, startY + 1, currZ, false, false); ++ } ++ } ++ } // else: apparently the chunk is empty ++ ++ if (needsEdgeChecks) { ++ // not required to propagate here, but this will reduce the hit of the edge checks ++ this.performLightIncrease(lightAccess); ++ ++ for (int y = highestNonEmptySection; y >= this.minLightSection; --y) { ++ this.checkNullSection(chunkX, y, chunkZ, false); ++ } ++ // no need to rewrite the nibble cache again ++ super.checkChunkEdges(lightAccess, chunk, this.minLightSection, highestNonEmptySection); ++ } else { ++ for (int y = highestNonEmptySection; y >= this.minLightSection; --y) { ++ this.checkNullSection(chunkX, y, chunkZ, false); ++ } ++ this.propagateNeighbourLevels(lightAccess, chunk, this.minLightSection, highestNonEmptySection); ++ ++ this.performLightIncrease(lightAccess); ++ } ++ } ++ ++ protected final void processDelayedIncreases() { ++ // copied from performLightIncrease ++ final long[] queue = this.increaseQueue; ++ final int decodeOffsetX = -this.encodeOffsetX; ++ final int decodeOffsetY = -this.encodeOffsetY; ++ final int decodeOffsetZ = -this.encodeOffsetZ; ++ ++ for (int i = 0, len = this.increaseQueueInitialLength; i < len; ++i) { ++ final long queueValue = queue[i]; ++ ++ final int posX = ((int)queueValue & 63) + decodeOffsetX; ++ final int posZ = (((int)queueValue >>> 6) & 63) + decodeOffsetZ; ++ final int posY = (((int)queueValue >>> 12) & ((1 << 16) - 1)) + decodeOffsetY; ++ final int propagatedLightLevel = (int)((queueValue >>> (6 + 6 + 16)) & 0xF); ++ ++ this.setLightLevel(posX, posY, posZ, propagatedLightLevel); ++ } ++ } ++ ++ protected final void processDelayedDecreases() { ++ // copied from performLightDecrease ++ final long[] queue = this.decreaseQueue; ++ final int decodeOffsetX = -this.encodeOffsetX; ++ final int decodeOffsetY = -this.encodeOffsetY; ++ final int decodeOffsetZ = -this.encodeOffsetZ; ++ ++ for (int i = 0, len = this.decreaseQueueInitialLength; i < len; ++i) { ++ final long queueValue = queue[i]; ++ ++ final int posX = ((int)queueValue & 63) + decodeOffsetX; ++ final int posZ = (((int)queueValue >>> 6) & 63) + decodeOffsetZ; ++ final int posY = (((int)queueValue >>> 12) & ((1 << 16) - 1)) + decodeOffsetY; ++ ++ this.setLightLevel(posX, posY, posZ, 0); ++ } ++ } ++ ++ // delaying the light set is useful for block changes since they need to worry about initialising nibblearrays ++ // while also queueing light at the same time (initialising nibblearrays might depend on nibbles above, so ++ // clobbering the light values will result in broken propagation) ++ protected final int tryPropagateSkylight(final BlockGetter world, final int worldX, int startY, final int worldZ, ++ final boolean extrudeInitialised, final boolean delayLightSet) { ++ final BlockPos.MutableBlockPos mutablePos = this.mutablePos3; ++ final int encodeOffset = this.coordinateOffset; ++ final long propagateDirection = AxisDirection.POSITIVE_Y.everythingButThisDirection; // just don't check upwards. ++ ++ if (this.getLightLevelExtruded(worldX, startY + 1, worldZ) != 15) { ++ return startY; ++ } ++ ++ // ensure this section is always checked ++ this.checkNullSection(worldX >> 4, startY >> 4, worldZ >> 4, extrudeInitialised); ++ ++ BlockState above = this.getBlockState(worldX, startY + 1, worldZ); ++ if (above == null) { ++ above = AIR_BLOCK_STATE; ++ } ++ ++ for (;startY >= (this.minLightSection << 4); --startY) { ++ if ((startY & 15) == 15) { ++ // ensure this section is always checked ++ this.checkNullSection(worldX >> 4, startY >> 4, worldZ >> 4, extrudeInitialised); ++ } ++ BlockState current = this.getBlockState(worldX, startY, worldZ); ++ if (current == null) { ++ current = AIR_BLOCK_STATE; ++ } ++ ++ final VoxelShape fromShape; ++ if (above.isConditionallyFullOpaque()) { ++ this.mutablePos2.set(worldX, startY + 1, worldZ); ++ fromShape = above.getFaceOcclusionShape(world, this.mutablePos2, AxisDirection.NEGATIVE_Y.nms); ++ if (Shapes.faceShapeOccludes(Shapes.empty(), fromShape)) { ++ // above wont let us propagate ++ break; ++ } ++ } else { ++ fromShape = Shapes.empty(); ++ } ++ ++ final int opacityIfCached = current.getOpacityIfCached(); ++ // does light propagate from the top down? ++ if (opacityIfCached != -1) { ++ if (opacityIfCached != 0) { ++ // we cannot propagate 15 through this ++ break; ++ } ++ // most of the time it falls here. ++ // add to propagate ++ // light set delayed until we determine if this nibble section is null ++ this.appendToIncreaseQueue( ++ ((worldX + (worldZ << 6) + (startY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (15L << (6 + 6 + 16)) // we know we're at full lit here ++ | (propagateDirection << (6 + 6 + 16 + 4)) ++ ); ++ } else { ++ mutablePos.set(worldX, startY, worldZ); ++ long flags = 0L; ++ if (current.isConditionallyFullOpaque()) { ++ final VoxelShape cullingFace = current.getFaceOcclusionShape(world, mutablePos, AxisDirection.POSITIVE_Y.nms); ++ ++ if (Shapes.faceShapeOccludes(fromShape, cullingFace)) { ++ // can't propagate here, we're done on this column. ++ break; ++ } ++ flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS; ++ } ++ ++ final int opacity = current.getLightBlock(world, mutablePos); ++ if (opacity > 0) { ++ // let the queued value (if any) handle it from here. ++ break; ++ } ++ ++ // light set delayed until we determine if this nibble section is null ++ this.appendToIncreaseQueue( ++ ((worldX + (worldZ << 6) + (startY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (15L << (6 + 6 + 16)) // we know we're at full lit here ++ | (propagateDirection << (6 + 6 + 16 + 4)) ++ | flags ++ ); ++ } ++ ++ above = current; ++ ++ if (this.getNibbleFromCache(worldX >> 4, startY >> 4, worldZ >> 4) == null) { ++ // we skip empty sections here, as this is just an easy way of making sure the above block ++ // can propagate through air. ++ ++ // nothing can propagate in null sections, remove the queue entry for it ++ --this.increaseQueueInitialLength; ++ ++ // advance currY to the the top of the section below ++ startY = (startY) & (~15); ++ // note: this value ^ is actually 1 above the top, but the loop decrements by 1 so we actually ++ // end up there ++ ++ // make sure this is marked as AIR ++ above = AIR_BLOCK_STATE; ++ } else if (!delayLightSet) { ++ this.setLightLevel(worldX, startY, worldZ, 15); ++ } ++ } ++ ++ return startY; ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/starlight/light/StarLightEngine.java b/src/main/java/ca/spottedleaf/starlight/light/StarLightEngine.java +new file mode 100644 +index 0000000000000000000000000000000000000000..319e5c674f027e2e06322bb75b38acd46b51cc7a +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/starlight/light/StarLightEngine.java +@@ -0,0 +1,1577 @@ ++package ca.spottedleaf.starlight.light; ++ ++import io.papermc.paper.util.CoordinateUtils; ++import io.papermc.paper.util.IntegerUtil; ++import io.papermc.paper.util.WorldUtil; ++import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; ++import it.unimi.dsi.fastutil.shorts.ShortCollection; ++import it.unimi.dsi.fastutil.shorts.ShortIterator; ++import net.minecraft.core.BlockPos; ++import net.minecraft.core.Direction; ++import net.minecraft.core.SectionPos; ++import net.minecraft.world.level.*; ++import net.minecraft.world.level.block.Blocks; ++import net.minecraft.world.level.block.state.BlockState; ++import net.minecraft.world.level.chunk.ChunkAccess; ++import net.minecraft.world.level.chunk.LevelChunkSection; ++import net.minecraft.world.level.chunk.LightChunkGetter; ++import net.minecraft.world.phys.shapes.Shapes; ++import net.minecraft.world.phys.shapes.VoxelShape; ++import java.util.ArrayList; ++import java.util.Arrays; ++import java.util.List; ++import java.util.Set; ++import java.util.function.Consumer; ++import java.util.function.IntConsumer; ++ ++public abstract class StarLightEngine { ++ ++ protected static final BlockState AIR_BLOCK_STATE = Blocks.AIR.defaultBlockState(); ++ ++ protected static final LevelChunkSection EMPTY_CHUNK_SECTION = new LevelChunkSection(0); ++ ++ protected static final AxisDirection[] DIRECTIONS = AxisDirection.values(); ++ protected static final AxisDirection[] AXIS_DIRECTIONS = DIRECTIONS; ++ protected static final AxisDirection[] ONLY_HORIZONTAL_DIRECTIONS = new AxisDirection[] { ++ AxisDirection.POSITIVE_X, AxisDirection.NEGATIVE_X, ++ AxisDirection.POSITIVE_Z, AxisDirection.NEGATIVE_Z ++ }; ++ ++ protected static enum AxisDirection { ++ ++ // Declaration order is important and relied upon. Do not change without modifying propagation code. ++ POSITIVE_X(1, 0, 0), NEGATIVE_X(-1, 0, 0), ++ POSITIVE_Z(0, 0, 1), NEGATIVE_Z(0, 0, -1), ++ POSITIVE_Y(0, 1, 0), NEGATIVE_Y(0, -1, 0); ++ ++ static { ++ POSITIVE_X.opposite = NEGATIVE_X; NEGATIVE_X.opposite = POSITIVE_X; ++ POSITIVE_Z.opposite = NEGATIVE_Z; NEGATIVE_Z.opposite = POSITIVE_Z; ++ POSITIVE_Y.opposite = NEGATIVE_Y; NEGATIVE_Y.opposite = POSITIVE_Y; ++ } ++ ++ protected AxisDirection opposite; ++ ++ public final int x; ++ public final int y; ++ public final int z; ++ public final Direction nms; ++ public final long everythingButThisDirection; ++ public final long everythingButTheOppositeDirection; ++ ++ AxisDirection(final int x, final int y, final int z) { ++ this.x = x; ++ this.y = y; ++ this.z = z; ++ this.nms = Direction.fromNormal(x, y, z); ++ this.everythingButThisDirection = (long)(ALL_DIRECTIONS_BITSET ^ (1 << this.ordinal())); ++ // positive is always even, negative is always odd. Flip the 1 bit to get the negative direction. ++ this.everythingButTheOppositeDirection = (long)(ALL_DIRECTIONS_BITSET ^ (1 << (this.ordinal() ^ 1))); ++ } ++ ++ public AxisDirection getOpposite() { ++ return this.opposite; ++ } ++ } ++ ++ // I'd like to thank https://www.seedofandromeda.com/blogs/29-fast-flood-fill-lighting-in-a-blocky-voxel-game-pt-1 ++ // for explaining how light propagates via breadth-first search ++ ++ // While the above is a good start to understanding the general idea of what the general principles are, it's not ++ // exactly how the vanilla light engine should behave for minecraft. ++ ++ // similar to the above, except the chunk section indices vary from [-1, 1], or [0, 2] ++ // for the y chunk section it's from [minLightSection, maxLightSection] or [0, maxLightSection - minLightSection] ++ // index = x + (z * 5) + (y * 25) ++ // null index indicates the chunk section doesn't exist (empty or out of bounds) ++ protected final LevelChunkSection[] sectionCache; ++ ++ // the exact same as above, except for storing fast access to SWMRNibbleArray ++ // for the y chunk section it's from [minLightSection, maxLightSection] or [0, maxLightSection - minLightSection] ++ // index = x + (z * 5) + (y * 25) ++ protected final SWMRNibbleArray[] nibbleCache; ++ ++ // the exact same as above, except for storing fast access to nibbles to call change callbacks for ++ // for the y chunk section it's from [minLightSection, maxLightSection] or [0, maxLightSection - minLightSection] ++ // index = x + (z * 5) + (y * 25) ++ protected final boolean[] notifyUpdateCache; ++ ++ // always initialsed during start of lighting. ++ // index = x + (z * 5) ++ protected final ChunkAccess[] chunkCache = new ChunkAccess[5 * 5]; ++ ++ // index = x + (z * 5) ++ protected final boolean[][] emptinessMapCache = new boolean[5 * 5][]; ++ ++ protected final BlockPos.MutableBlockPos mutablePos1 = new BlockPos.MutableBlockPos(); ++ protected final BlockPos.MutableBlockPos mutablePos2 = new BlockPos.MutableBlockPos(); ++ protected final BlockPos.MutableBlockPos mutablePos3 = new BlockPos.MutableBlockPos(); ++ ++ protected int encodeOffsetX; ++ protected int encodeOffsetY; ++ protected int encodeOffsetZ; ++ ++ protected int coordinateOffset; ++ ++ protected int chunkOffsetX; ++ protected int chunkOffsetY; ++ protected int chunkOffsetZ; ++ ++ protected int chunkIndexOffset; ++ protected int chunkSectionIndexOffset; ++ ++ protected final boolean skylightPropagator; ++ protected final int emittedLightMask; ++ protected final boolean isClientSide; ++ ++ protected final Level world; ++ protected final int minLightSection; ++ protected final int maxLightSection; ++ protected final int minSection; ++ protected final int maxSection; ++ ++ protected StarLightEngine(final boolean skylightPropagator, final Level world) { ++ this.skylightPropagator = skylightPropagator; ++ this.emittedLightMask = skylightPropagator ? 0 : 0xF; ++ this.isClientSide = world.isClientSide; ++ this.world = world; ++ this.minLightSection = WorldUtil.getMinLightSection(world); ++ this.maxLightSection = WorldUtil.getMaxLightSection(world); ++ this.minSection = WorldUtil.getMinSection(world); ++ this.maxSection = WorldUtil.getMaxSection(world); ++ ++ this.sectionCache = new LevelChunkSection[5 * 5 * ((this.maxLightSection - this.minLightSection + 1) + 2)]; // add two extra sections for buffer ++ this.nibbleCache = new SWMRNibbleArray[5 * 5 * ((this.maxLightSection - this.minLightSection + 1) + 2)]; // add two extra sections for buffer ++ this.notifyUpdateCache = new boolean[5 * 5 * ((this.maxLightSection - this.minLightSection + 1) + 2)]; // add two extra sections for buffer ++ } ++ ++ protected final void setupEncodeOffset(final int centerX, final int centerY, final int centerZ) { ++ // 31 = center + encodeOffset ++ this.encodeOffsetX = 31 - centerX; ++ this.encodeOffsetY = (-(this.minLightSection - 1) << 4); // we want 0 to be the smallest encoded value ++ this.encodeOffsetZ = 31 - centerZ; ++ ++ // coordinateIndex = x | (z << 6) | (y << 12) ++ this.coordinateOffset = this.encodeOffsetX + (this.encodeOffsetZ << 6) + (this.encodeOffsetY << 12); ++ ++ // 2 = (centerX >> 4) + chunkOffset ++ this.chunkOffsetX = 2 - (centerX >> 4); ++ this.chunkOffsetY = -(this.minLightSection - 1); // lowest should be 0 ++ this.chunkOffsetZ = 2 - (centerZ >> 4); ++ ++ // chunk index = x + (5 * z) ++ this.chunkIndexOffset = this.chunkOffsetX + (5 * this.chunkOffsetZ); ++ ++ // chunk section index = x + (5 * z) + ((5*5) * y) ++ this.chunkSectionIndexOffset = this.chunkIndexOffset + ((5 * 5) * this.chunkOffsetY); ++ } ++ ++ protected final void setupCaches(final LightChunkGetter chunkProvider, final int centerX, final int centerY, final int centerZ, ++ final boolean relaxed, final boolean tryToLoadChunksFor2Radius) { ++ final int centerChunkX = centerX >> 4; ++ final int centerChunkY = centerY >> 4; ++ final int centerChunkZ = centerZ >> 4; ++ ++ this.setupEncodeOffset(centerChunkX * 16 + 7, centerChunkY * 16 + 7, centerChunkZ * 16 + 7); ++ ++ final int radius = tryToLoadChunksFor2Radius ? 2 : 1; ++ ++ for (int dz = -radius; dz <= radius; ++dz) { ++ for (int dx = -radius; dx <= radius; ++dx) { ++ final int cx = centerChunkX + dx; ++ final int cz = centerChunkZ + dz; ++ final boolean isTwoRadius = Math.max(IntegerUtil.branchlessAbs(dx), IntegerUtil.branchlessAbs(dz)) == 2; ++ final ChunkAccess chunk = (ChunkAccess)chunkProvider.getChunkForLighting(cx, cz); ++ ++ if (chunk == null) { ++ if (relaxed | isTwoRadius) { ++ continue; ++ } ++ throw new IllegalArgumentException("Trying to propagate light update before 1 radius neighbours ready"); ++ } ++ ++ if (!this.canUseChunk(chunk)) { ++ continue; ++ } ++ ++ this.setChunkInCache(cx, cz, chunk); ++ this.setEmptinessMapCache(cx, cz, this.getEmptinessMap(chunk)); ++ if (!isTwoRadius) { ++ this.setBlocksForChunkInCache(cx, cz, chunk.getSections()); ++ this.setNibblesForChunkInCache(cx, cz, this.getNibblesOnChunk(chunk)); ++ } ++ } ++ } ++ } ++ ++ protected final ChunkAccess getChunkInCache(final int chunkX, final int chunkZ) { ++ return this.chunkCache[chunkX + 5*chunkZ + this.chunkIndexOffset]; ++ } ++ ++ protected final void setChunkInCache(final int chunkX, final int chunkZ, final ChunkAccess chunk) { ++ this.chunkCache[chunkX + 5*chunkZ + this.chunkIndexOffset] = chunk; ++ } ++ ++ protected final LevelChunkSection getChunkSection(final int chunkX, final int chunkY, final int chunkZ) { ++ return this.sectionCache[chunkX + 5*chunkZ + (5 * 5) * chunkY + this.chunkSectionIndexOffset]; ++ } ++ ++ protected final void setChunkSectionInCache(final int chunkX, final int chunkY, final int chunkZ, final LevelChunkSection section) { ++ this.sectionCache[chunkX + 5*chunkZ + 5*5*chunkY + this.chunkSectionIndexOffset] = section; ++ } ++ ++ protected final void setBlocksForChunkInCache(final int chunkX, final int chunkZ, final LevelChunkSection[] sections) { ++ for (int cy = this.minLightSection; cy <= this.maxLightSection; ++cy) { ++ this.setChunkSectionInCache(chunkX, cy, chunkZ, ++ sections == null ? null : (cy >= this.minSection && cy <= this.maxSection ? (sections[cy - this.minSection] == null || sections[cy - this.minSection].isEmpty() ? EMPTY_CHUNK_SECTION : sections[cy - this.minSection]) : EMPTY_CHUNK_SECTION)); ++ } ++ } ++ ++ protected final SWMRNibbleArray getNibbleFromCache(final int chunkX, final int chunkY, final int chunkZ) { ++ return this.nibbleCache[chunkX + 5*chunkZ + (5 * 5) * chunkY + this.chunkSectionIndexOffset]; ++ } ++ ++ protected final SWMRNibbleArray[] getNibblesForChunkFromCache(final int chunkX, final int chunkZ) { ++ final SWMRNibbleArray[] ret = new SWMRNibbleArray[this.maxLightSection - this.minLightSection + 1]; ++ ++ for (int cy = this.minLightSection; cy <= this.maxLightSection; ++cy) { ++ ret[cy - this.minLightSection] = this.nibbleCache[chunkX + 5*chunkZ + (cy * (5 * 5)) + this.chunkSectionIndexOffset]; ++ } ++ ++ return ret; ++ } ++ ++ protected final void setNibbleInCache(final int chunkX, final int chunkY, final int chunkZ, final SWMRNibbleArray nibble) { ++ this.nibbleCache[chunkX + 5*chunkZ + (5 * 5) * chunkY + this.chunkSectionIndexOffset] = nibble; ++ } ++ ++ protected final void setNibblesForChunkInCache(final int chunkX, final int chunkZ, final SWMRNibbleArray[] nibbles) { ++ for (int cy = this.minLightSection; cy <= this.maxLightSection; ++cy) { ++ this.setNibbleInCache(chunkX, cy, chunkZ, nibbles == null ? null : nibbles[cy - this.minLightSection]); ++ } ++ } ++ ++ protected final void updateVisible(final LightChunkGetter lightAccess) { ++ for (int index = 0, max = this.nibbleCache.length; index < max; ++index) { ++ final SWMRNibbleArray nibble = this.nibbleCache[index]; ++ if (!this.notifyUpdateCache[index] && (nibble == null || !nibble.isDirty())) { ++ continue; ++ } ++ ++ final int chunkX = (index % 5) - this.chunkOffsetX; ++ final int chunkZ = ((index / 5) % 5) - this.chunkOffsetZ; ++ final int chunkY = ((index / (5*5)) % (16 + 2 + 2)) - this.chunkOffsetY; ++ if ((nibble != null && nibble.updateVisible()) || this.notifyUpdateCache[index]) { ++ lightAccess.onLightUpdate(this.skylightPropagator ? LightLayer.SKY : LightLayer.BLOCK, SectionPos.of(chunkX, chunkY, chunkZ)); ++ } ++ } ++ } ++ ++ protected final void destroyCaches() { ++ Arrays.fill(this.sectionCache, null); ++ Arrays.fill(this.nibbleCache, null); ++ Arrays.fill(this.chunkCache, null); ++ Arrays.fill(this.emptinessMapCache, null); ++ if (this.isClientSide) { ++ Arrays.fill(this.notifyUpdateCache, false); ++ } ++ } ++ ++ protected final BlockState getBlockState(final int worldX, final int worldY, final int worldZ) { ++ final LevelChunkSection section = this.sectionCache[(worldX >> 4) + 5 * (worldZ >> 4) + (5 * 5) * (worldY >> 4) + this.chunkSectionIndexOffset]; ++ ++ if (section != null) { ++ return section == EMPTY_CHUNK_SECTION ? AIR_BLOCK_STATE : section.getBlockState(worldX & 15, worldY & 15, worldZ & 15); ++ } ++ ++ return null; ++ } ++ ++ protected final BlockState getBlockState(final int sectionIndex, final int localIndex) { ++ final LevelChunkSection section = this.sectionCache[sectionIndex]; ++ ++ if (section != null) { ++ return section == EMPTY_CHUNK_SECTION ? AIR_BLOCK_STATE : section.states.get(localIndex); ++ } ++ ++ return null; ++ } ++ ++ protected final int getLightLevel(final int worldX, final int worldY, final int worldZ) { ++ final SWMRNibbleArray nibble = this.nibbleCache[(worldX >> 4) + 5 * (worldZ >> 4) + (5 * 5) * (worldY >> 4) + this.chunkSectionIndexOffset]; ++ ++ return nibble == null ? 0 : nibble.getUpdating((worldX & 15) | ((worldZ & 15) << 4) | ((worldY & 15) << 8)); ++ } ++ ++ protected final int getLightLevel(final int sectionIndex, final int localIndex) { ++ final SWMRNibbleArray nibble = this.nibbleCache[sectionIndex]; ++ ++ return nibble == null ? 0 : nibble.getUpdating(localIndex); ++ } ++ ++ protected final void setLightLevel(final int worldX, final int worldY, final int worldZ, final int level) { ++ final int sectionIndex = (worldX >> 4) + 5 * (worldZ >> 4) + (5 * 5) * (worldY >> 4) + this.chunkSectionIndexOffset; ++ final SWMRNibbleArray nibble = this.nibbleCache[sectionIndex]; ++ ++ if (nibble != null) { ++ nibble.set((worldX & 15) | ((worldZ & 15) << 4) | ((worldY & 15) << 8), level); ++ if (this.isClientSide) { ++ int cx1 = (worldX - 1) >> 4; ++ int cx2 = (worldX + 1) >> 4; ++ int cy1 = (worldY - 1) >> 4; ++ int cy2 = (worldY + 1) >> 4; ++ int cz1 = (worldZ - 1) >> 4; ++ int cz2 = (worldZ + 1) >> 4; ++ for (int x = cx1; x <= cx2; ++x) { ++ for (int y = cy1; y <= cy2; ++y) { ++ for (int z = cz1; z <= cz2; ++z) { ++ this.notifyUpdateCache[x + 5 * z + (5 * 5) * y + this.chunkSectionIndexOffset] = true; ++ } ++ } ++ } ++ } ++ } ++ } ++ ++ protected final void postLightUpdate(final int worldX, final int worldY, final int worldZ) { ++ if (this.isClientSide) { ++ int cx1 = (worldX - 1) >> 4; ++ int cx2 = (worldX + 1) >> 4; ++ int cy1 = (worldY - 1) >> 4; ++ int cy2 = (worldY + 1) >> 4; ++ int cz1 = (worldZ - 1) >> 4; ++ int cz2 = (worldZ + 1) >> 4; ++ for (int x = cx1; x <= cx2; ++x) { ++ for (int y = cy1; y <= cy2; ++y) { ++ for (int z = cz1; z <= cz2; ++z) { ++ this.notifyUpdateCache[x + (5 * z) + (5 * 5 * y) + this.chunkSectionIndexOffset] = true; ++ } ++ } ++ } ++ } ++ } ++ ++ protected final void setLightLevel(final int sectionIndex, final int localIndex, final int worldX, final int worldY, final int worldZ, final int level) { ++ final SWMRNibbleArray nibble = this.nibbleCache[sectionIndex]; ++ ++ if (nibble != null) { ++ nibble.set(localIndex, level); ++ if (this.isClientSide) { ++ int cx1 = (worldX - 1) >> 4; ++ int cx2 = (worldX + 1) >> 4; ++ int cy1 = (worldY - 1) >> 4; ++ int cy2 = (worldY + 1) >> 4; ++ int cz1 = (worldZ - 1) >> 4; ++ int cz2 = (worldZ + 1) >> 4; ++ for (int x = cx1; x <= cx2; ++x) { ++ for (int y = cy1; y <= cy2; ++y) { ++ for (int z = cz1; z <= cz2; ++z) { ++ this.notifyUpdateCache[x + (5 * z) + (5 * 5 * y) + this.chunkSectionIndexOffset] = true; ++ } ++ } ++ } ++ } ++ } ++ } ++ ++ protected final boolean[] getEmptinessMap(final int chunkX, final int chunkZ) { ++ return this.emptinessMapCache[chunkX + 5*chunkZ + this.chunkIndexOffset]; ++ } ++ ++ protected final void setEmptinessMapCache(final int chunkX, final int chunkZ, final boolean[] emptinessMap) { ++ this.emptinessMapCache[chunkX + 5*chunkZ + this.chunkIndexOffset] = emptinessMap; ++ } ++ ++ protected final long getKnownTransparency(final int worldX, final int worldY, final int worldZ) { ++ throw new UnsupportedOperationException(); // :( ++ } ++ ++ // warn: localIndex = y | (x << 4) | (z << 8) ++ protected final long getKnownTransparency(final int sectionIndex, final int localIndex) { ++ throw new UnsupportedOperationException(); // :( ++ } ++ ++ public static SWMRNibbleArray[] getFilledEmptyLight(final LevelHeightAccessor world) { ++ return getFilledEmptyLight(WorldUtil.getTotalLightSections(world)); ++ } ++ ++ private static SWMRNibbleArray[] getFilledEmptyLight(final int totalLightSections) { ++ final SWMRNibbleArray[] ret = new SWMRNibbleArray[totalLightSections]; ++ ++ for (int i = 0, len = ret.length; i < len; ++i) { ++ ret[i] = new SWMRNibbleArray(null, true); ++ } ++ ++ return ret; ++ } ++ ++ protected abstract boolean[] getEmptinessMap(final ChunkAccess chunk); ++ ++ protected abstract void setEmptinessMap(final ChunkAccess chunk, final boolean[] to); ++ ++ protected abstract SWMRNibbleArray[] getNibblesOnChunk(final ChunkAccess chunk); ++ ++ protected abstract void setNibbles(final ChunkAccess chunk, final SWMRNibbleArray[] to); ++ ++ protected abstract boolean canUseChunk(final ChunkAccess chunk); ++ ++ public final void blocksChangedInChunk(final LightChunkGetter lightAccess, final int chunkX, final int chunkZ, ++ final Set positions, final Boolean[] changedSections) { ++ this.setupCaches(lightAccess, chunkX * 16 + 7, 128, chunkZ * 16 + 7, true, true); ++ try { ++ final ChunkAccess chunk = this.getChunkInCache(chunkX, chunkZ); ++ if (chunk == null) { ++ return; ++ } ++ if (changedSections != null) { ++ final boolean[] ret = this.handleEmptySectionChanges(lightAccess, chunk, changedSections, false); ++ if (ret != null) { ++ this.setEmptinessMap(chunk, ret); ++ } ++ } ++ if (!positions.isEmpty()) { ++ this.propagateBlockChanges(lightAccess, chunk, positions); ++ } ++ this.updateVisible(lightAccess); ++ } finally { ++ this.destroyCaches(); ++ } ++ } ++ ++ // subclasses should not initialise caches, as this will always be done by the super call ++ // subclasses should not invoke updateVisible, as this will always be done by the super call ++ protected abstract void propagateBlockChanges(final LightChunkGetter lightAccess, final ChunkAccess atChunk, final Set positions); ++ ++ protected abstract void checkBlock(final LightChunkGetter lightAccess, final int worldX, final int worldY, final int worldZ); ++ ++ // if ret > expect, then the real value is at least ret (early returns if ret > expect, rather than calculating actual) ++ // if ret == expect, then expect is the correct light value for pos ++ // if ret < expect, then ret is the real light value ++ protected abstract int calculateLightValue(final LightChunkGetter lightAccess, final int worldX, final int worldY, final int worldZ, ++ final int expect); ++ ++ protected final int[] chunkCheckDelayedUpdatesCenter = new int[16 * 16]; ++ protected final int[] chunkCheckDelayedUpdatesNeighbour = new int[16 * 16]; ++ ++ protected void checkChunkEdge(final LightChunkGetter lightAccess, final ChunkAccess chunk, ++ final int chunkX, final int chunkY, final int chunkZ) { ++ final SWMRNibbleArray currNibble = this.getNibbleFromCache(chunkX, chunkY, chunkZ); ++ if (currNibble == null) { ++ return; ++ } ++ ++ for (final AxisDirection direction : ONLY_HORIZONTAL_DIRECTIONS) { ++ final int neighbourOffX = direction.x; ++ final int neighbourOffZ = direction.z; ++ ++ final SWMRNibbleArray neighbourNibble = this.getNibbleFromCache(chunkX + neighbourOffX, ++ chunkY, chunkZ + neighbourOffZ); ++ ++ if (neighbourNibble == null) { ++ continue; ++ } ++ ++ if (!currNibble.isInitialisedUpdating() && !neighbourNibble.isInitialisedUpdating()) { ++ // both are zero, nothing to check. ++ continue; ++ } ++ ++ // this chunk ++ final int incX; ++ final int incZ; ++ final int startX; ++ final int startZ; ++ ++ if (neighbourOffX != 0) { ++ // x direction ++ incX = 0; ++ incZ = 1; ++ ++ if (direction.x < 0) { ++ // negative ++ startX = chunkX << 4; ++ } else { ++ startX = chunkX << 4 | 15; ++ } ++ startZ = chunkZ << 4; ++ } else { ++ // z direction ++ incX = 1; ++ incZ = 0; ++ ++ if (neighbourOffZ < 0) { ++ // negative ++ startZ = chunkZ << 4; ++ } else { ++ startZ = chunkZ << 4 | 15; ++ } ++ startX = chunkX << 4; ++ } ++ ++ int centerDelayedChecks = 0; ++ int neighbourDelayedChecks = 0; ++ for (int currY = chunkY << 4, maxY = currY | 15; currY <= maxY; ++currY) { ++ for (int i = 0, currX = startX, currZ = startZ; i < 16; ++i, currX += incX, currZ += incZ) { ++ final int neighbourX = currX + neighbourOffX; ++ final int neighbourZ = currZ + neighbourOffZ; ++ ++ final int currentIndex = (currX & 15) | ++ ((currZ & 15)) << 4 | ++ ((currY & 15) << 8); ++ final int currentLevel = currNibble.getUpdating(currentIndex); ++ ++ final int neighbourIndex = ++ (neighbourX & 15) | ++ ((neighbourZ & 15)) << 4 | ++ ((currY & 15) << 8); ++ final int neighbourLevel = neighbourNibble.getUpdating(neighbourIndex); ++ ++ // the checks are delayed because the checkBlock method clobbers light values - which then ++ // affect later calculate light value operations. While they don't affect it in a behaviourly significant ++ // way, they do have a negative performance impact due to simply queueing more values ++ ++ if (this.calculateLightValue(lightAccess, currX, currY, currZ, currentLevel) != currentLevel) { ++ this.chunkCheckDelayedUpdatesCenter[centerDelayedChecks++] = currentIndex; ++ } ++ ++ if (this.calculateLightValue(lightAccess, neighbourX, currY, neighbourZ, neighbourLevel) != neighbourLevel) { ++ this.chunkCheckDelayedUpdatesNeighbour[neighbourDelayedChecks++] = neighbourIndex; ++ } ++ } ++ } ++ ++ final int currentChunkOffX = chunkX << 4; ++ final int currentChunkOffZ = chunkZ << 4; ++ final int neighbourChunkOffX = (chunkX + direction.x) << 4; ++ final int neighbourChunkOffZ = (chunkZ + direction.z) << 4; ++ final int chunkOffY = chunkY << 4; ++ for (int i = 0, len = Math.max(centerDelayedChecks, neighbourDelayedChecks); i < len; ++i) { ++ // try to queue neighbouring data together ++ // index = x | (z << 4) | (y << 8) ++ if (i < centerDelayedChecks) { ++ final int value = this.chunkCheckDelayedUpdatesCenter[i]; ++ this.checkBlock(lightAccess, currentChunkOffX | (value & 15), ++ chunkOffY | (value >>> 8), ++ currentChunkOffZ | ((value >>> 4) & 0xF)); ++ } ++ if (i < neighbourDelayedChecks) { ++ final int value = this.chunkCheckDelayedUpdatesNeighbour[i]; ++ this.checkBlock(lightAccess, neighbourChunkOffX | (value & 15), ++ chunkOffY | (value >>> 8), ++ neighbourChunkOffZ | ((value >>> 4) & 0xF)); ++ } ++ } ++ } ++ } ++ ++ protected void checkChunkEdges(final LightChunkGetter lightAccess, final ChunkAccess chunk, final ShortCollection sections) { ++ final ChunkPos chunkPos = chunk.getPos(); ++ final int chunkX = chunkPos.x; ++ final int chunkZ = chunkPos.z; ++ ++ for (final ShortIterator iterator = sections.iterator(); iterator.hasNext();) { ++ this.checkChunkEdge(lightAccess, chunk, chunkX, iterator.nextShort(), chunkZ); ++ } ++ ++ this.performLightDecrease(lightAccess); ++ } ++ ++ // subclasses should not initialise caches, as this will always be done by the super call ++ // subclasses should not invoke updateVisible, as this will always be done by the super call ++ // verifies that light levels on this chunks edges are consistent with this chunk's neighbours ++ // edges. if they are not, they are decreased (effectively performing the logic in checkBlock). ++ // This does not resolve skylight source problems. ++ protected void checkChunkEdges(final LightChunkGetter lightAccess, final ChunkAccess chunk, final int fromSection, final int toSection) { ++ final ChunkPos chunkPos = chunk.getPos(); ++ final int chunkX = chunkPos.x; ++ final int chunkZ = chunkPos.z; ++ ++ for (int currSectionY = toSection; currSectionY >= fromSection; --currSectionY) { ++ this.checkChunkEdge(lightAccess, chunk, chunkX, currSectionY, chunkZ); ++ } ++ ++ this.performLightDecrease(lightAccess); ++ } ++ ++ // pulls light from neighbours, and adds them into the increase queue. does not actually propagate. ++ protected final void propagateNeighbourLevels(final LightChunkGetter lightAccess, final ChunkAccess chunk, final int fromSection, final int toSection) { ++ final ChunkPos chunkPos = chunk.getPos(); ++ final int chunkX = chunkPos.x; ++ final int chunkZ = chunkPos.z; ++ ++ for (int currSectionY = toSection; currSectionY >= fromSection; --currSectionY) { ++ final SWMRNibbleArray currNibble = this.getNibbleFromCache(chunkX, currSectionY, chunkZ); ++ if (currNibble == null) { ++ continue; ++ } ++ for (final AxisDirection direction : ONLY_HORIZONTAL_DIRECTIONS) { ++ final int neighbourOffX = direction.x; ++ final int neighbourOffZ = direction.z; ++ ++ final SWMRNibbleArray neighbourNibble = this.getNibbleFromCache(chunkX + neighbourOffX, ++ currSectionY, chunkZ + neighbourOffZ); ++ ++ if (neighbourNibble == null || !neighbourNibble.isInitialisedUpdating()) { ++ // can't pull from 0 ++ continue; ++ } ++ ++ // neighbour chunk ++ final int incX; ++ final int incZ; ++ final int startX; ++ final int startZ; ++ ++ if (neighbourOffX != 0) { ++ // x direction ++ incX = 0; ++ incZ = 1; ++ ++ if (direction.x < 0) { ++ // negative ++ startX = (chunkX << 4) - 1; ++ } else { ++ startX = (chunkX << 4) + 16; ++ } ++ startZ = chunkZ << 4; ++ } else { ++ // z direction ++ incX = 1; ++ incZ = 0; ++ ++ if (neighbourOffZ < 0) { ++ // negative ++ startZ = (chunkZ << 4) - 1; ++ } else { ++ startZ = (chunkZ << 4) + 16; ++ } ++ startX = chunkX << 4; ++ } ++ ++ final long propagateDirection = 1L << direction.getOpposite().ordinal(); // we only want to check in this direction towards this chunk ++ final int encodeOffset = this.coordinateOffset; ++ ++ for (int currY = currSectionY << 4, maxY = currY | 15; currY <= maxY; ++currY) { ++ for (int i = 0, currX = startX, currZ = startZ; i < 16; ++i, currX += incX, currZ += incZ) { ++ final int level = neighbourNibble.getUpdating( ++ (currX & 15) ++ | ((currZ & 15) << 4) ++ | ((currY & 15) << 8) ++ ); ++ ++ if (level <= 1) { ++ // nothing to propagate ++ continue; ++ } ++ ++ this.appendToIncreaseQueue( ++ ((currX + (currZ << 6) + (currY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((level & 0xFL) << (6 + 6 + 16)) ++ | (propagateDirection << (6 + 6 + 16 + 4)) ++ | FLAG_HAS_SIDED_TRANSPARENT_BLOCKS // don't know if the current block is transparent, must check. ++ ); ++ } ++ } ++ } ++ } ++ } ++ ++ public static Boolean[] getEmptySectionsForChunk(final ChunkAccess chunk) { ++ final LevelChunkSection[] sections = chunk.getSections(); ++ final Boolean[] ret = new Boolean[sections.length]; ++ ++ for (int i = 0; i < sections.length; ++i) { ++ if (sections[i] == null || sections[i].isEmpty()) { ++ ret[i] = Boolean.TRUE; ++ } else { ++ ret[i] = Boolean.FALSE; ++ } ++ } ++ ++ return ret; ++ } ++ ++ public final void forceHandleEmptySectionChanges(final LightChunkGetter lightAccess, final ChunkAccess chunk, final Boolean[] emptinessChanges) { ++ final int chunkX = chunk.getPos().x; ++ final int chunkZ = chunk.getPos().z; ++ this.setupCaches(lightAccess, chunkX * 16 + 7, 128, chunkZ * 16 + 7, true, true); ++ try { ++ // force current chunk into cache ++ this.setChunkInCache(chunkX, chunkZ, chunk); ++ this.setBlocksForChunkInCache(chunkX, chunkZ, chunk.getSections()); ++ this.setNibblesForChunkInCache(chunkX, chunkZ, this.getNibblesOnChunk(chunk)); ++ this.setEmptinessMapCache(chunkX, chunkZ, this.getEmptinessMap(chunk)); ++ ++ final boolean[] ret = this.handleEmptySectionChanges(lightAccess, chunk, emptinessChanges, false); ++ if (ret != null) { ++ this.setEmptinessMap(chunk, ret); ++ } ++ this.updateVisible(lightAccess); ++ } finally { ++ this.destroyCaches(); ++ } ++ } ++ ++ public final void handleEmptySectionChanges(final LightChunkGetter lightAccess, final int chunkX, final int chunkZ, ++ final Boolean[] emptinessChanges) { ++ this.setupCaches(lightAccess, chunkX * 16 + 7, 128, chunkZ * 16 + 7, true, true); ++ try { ++ final ChunkAccess chunk = this.getChunkInCache(chunkX, chunkZ); ++ if (chunk == null) { ++ return; ++ } ++ final boolean[] ret = this.handleEmptySectionChanges(lightAccess, chunk, emptinessChanges, false); ++ if (ret != null) { ++ this.setEmptinessMap(chunk, ret); ++ } ++ this.updateVisible(lightAccess); ++ } finally { ++ this.destroyCaches(); ++ } ++ } ++ ++ protected abstract void initNibble(final int chunkX, final int chunkY, final int chunkZ, final boolean extrude, final boolean initRemovedNibbles); ++ ++ protected abstract void setNibbleNull(final int chunkX, final int chunkY, final int chunkZ); ++ ++ // subclasses should not initialise caches, as this will always be done by the super call ++ // subclasses should not invoke updateVisible, as this will always be done by the super call ++ // subclasses are guaranteed that this is always called before a changed block set ++ // newChunk specifies whether the changes describe a "first load" of a chunk or changes to existing, already loaded chunks ++ // rets non-null when the emptiness map changed and needs to be updated ++ protected final boolean[] handleEmptySectionChanges(final LightChunkGetter lightAccess, final ChunkAccess chunk, ++ final Boolean[] emptinessChanges, final boolean unlit) { ++ final Level world = (Level)lightAccess.getLevel(); ++ final int chunkX = chunk.getPos().x; ++ final int chunkZ = chunk.getPos().z; ++ ++ boolean[] chunkEmptinessMap = this.getEmptinessMap(chunkX, chunkZ); ++ boolean[] ret = null; ++ final boolean needsInit = unlit || chunkEmptinessMap == null; ++ if (needsInit) { ++ this.setEmptinessMapCache(chunkX, chunkZ, ret = chunkEmptinessMap = new boolean[WorldUtil.getTotalSections(world)]); ++ } ++ ++ // update emptiness map ++ for (int sectionIndex = (emptinessChanges.length - 1); sectionIndex >= 0; --sectionIndex) { ++ final Boolean valueBoxed = emptinessChanges[sectionIndex]; ++ if (valueBoxed == null) { ++ if (needsInit) { ++ throw new IllegalStateException("Current chunk has not initialised emptiness map yet supplied emptiness map isn't filled?"); ++ } ++ continue; ++ } ++ chunkEmptinessMap[sectionIndex] = valueBoxed.booleanValue(); ++ } ++ ++ // now init neighbour nibbles ++ for (int sectionIndex = (emptinessChanges.length - 1); sectionIndex >= 0; --sectionIndex) { ++ final Boolean valueBoxed = emptinessChanges[sectionIndex]; ++ final int sectionY = sectionIndex + this.minSection; ++ if (valueBoxed == null) { ++ continue; ++ } ++ ++ final boolean empty = valueBoxed.booleanValue(); ++ ++ if (empty) { ++ continue; ++ } ++ ++ for (int dz = -1; dz <= 1; ++dz) { ++ for (int dx = -1; dx <= 1; ++dx) { ++ // if we're not empty, we also need to initialise nibbles ++ // note: if we're unlit, we absolutely do not want to extrude, as light data isn't set up ++ final boolean extrude = (dx | dz) != 0 || !unlit; ++ for (int dy = 1; dy >= -1; --dy) { ++ this.initNibble(dx + chunkX, dy + sectionY, dz + chunkZ, extrude, false); ++ } ++ } ++ } ++ } ++ ++ // check for de-init and lazy-init ++ // lazy init is when chunks are being lit, so at the time they weren't loaded when their neighbours were running ++ // init checks. ++ for (int dz = -1; dz <= 1; ++dz) { ++ for (int dx = -1; dx <= 1; ++dx) { ++ // does this neighbour have 1 radius loaded? ++ boolean neighboursLoaded = true; ++ neighbour_loaded_search: ++ for (int dz2 = -1; dz2 <= 1; ++dz2) { ++ for (int dx2 = -1; dx2 <= 1; ++dx2) { ++ if (this.getEmptinessMap(dx + dx2 + chunkX, dz + dz2 + chunkZ) == null) { ++ neighboursLoaded = false; ++ break neighbour_loaded_search; ++ } ++ } ++ } ++ ++ for (int sectionY = this.maxLightSection; sectionY >= this.minLightSection; --sectionY) { ++ // check neighbours to see if we need to de-init this one ++ boolean allEmpty = true; ++ neighbour_search: ++ for (int dy2 = -1; dy2 <= 1; ++dy2) { ++ for (int dz2 = -1; dz2 <= 1; ++dz2) { ++ for (int dx2 = -1; dx2 <= 1; ++dx2) { ++ final int y = sectionY + dy2; ++ if (y < this.minSection || y > this.maxSection) { ++ // empty ++ continue; ++ } ++ final boolean[] emptinessMap = this.getEmptinessMap(dx + dx2 + chunkX, dz + dz2 + chunkZ); ++ if (emptinessMap != null) { ++ if (!emptinessMap[y - this.minSection]) { ++ allEmpty = false; ++ break neighbour_search; ++ } ++ } else { ++ final LevelChunkSection section = this.getChunkSection(dx + dx2 + chunkX, y, dz + dz2 + chunkZ); ++ if (section != null && section != EMPTY_CHUNK_SECTION) { ++ allEmpty = false; ++ break neighbour_search; ++ } ++ } ++ } ++ } ++ } ++ ++ if (allEmpty & neighboursLoaded) { ++ // can only de-init when neighbours are loaded ++ // de-init is fine to delay, as de-init is just an optimisation - it's not required for lighting ++ // to be correct ++ ++ // all were empty, so de-init ++ this.setNibbleNull(dx + chunkX, sectionY, dz + chunkZ); ++ } else if (!allEmpty) { ++ // must init ++ final boolean extrude = (dx | dz) != 0 || !unlit; ++ this.initNibble(dx + chunkX, sectionY, dz + chunkZ, extrude, false); ++ } ++ } ++ } ++ } ++ ++ return ret; ++ } ++ ++ public final void checkChunkEdges(final LightChunkGetter lightAccess, final int chunkX, final int chunkZ) { ++ this.setupCaches(lightAccess, chunkX * 16 + 7, 128, chunkZ * 16 + 7, true, false); ++ try { ++ final ChunkAccess chunk = this.getChunkInCache(chunkX, chunkZ); ++ if (chunk == null) { ++ return; ++ } ++ this.checkChunkEdges(lightAccess, chunk, this.minLightSection, this.maxLightSection); ++ this.updateVisible(lightAccess); ++ } finally { ++ this.destroyCaches(); ++ } ++ } ++ ++ public final void checkChunkEdges(final LightChunkGetter lightAccess, final int chunkX, final int chunkZ, final ShortCollection sections) { ++ this.setupCaches(lightAccess, chunkX * 16 + 7, 128, chunkZ * 16 + 7, true, false); ++ try { ++ final ChunkAccess chunk = this.getChunkInCache(chunkX, chunkZ); ++ if (chunk == null) { ++ return; ++ } ++ this.checkChunkEdges(lightAccess, chunk, sections); ++ this.updateVisible(lightAccess); ++ } finally { ++ this.destroyCaches(); ++ } ++ } ++ ++ // subclasses should not initialise caches, as this will always be done by the super call ++ // subclasses should not invoke updateVisible, as this will always be done by the super call ++ // needsEdgeChecks applies when possibly loading vanilla data, which means we need to validate the current ++ // chunks light values with respect to neighbours ++ // subclasses should note that the emptiness changes are propagated BEFORE this is called, so this function ++ // does not need to detect empty chunks itself (and it should do no handling for them either!) ++ protected abstract void lightChunk(final LightChunkGetter lightAccess, final ChunkAccess chunk, final boolean needsEdgeChecks); ++ ++ public final void light(final LightChunkGetter lightAccess, final ChunkAccess chunk, final Boolean[] emptySections) { ++ final int chunkX = chunk.getPos().x; ++ final int chunkZ = chunk.getPos().z; ++ this.setupCaches(lightAccess, chunkX * 16 + 7, 128, chunkZ * 16 + 7, true, true); ++ ++ try { ++ final SWMRNibbleArray[] nibbles = getFilledEmptyLight(this.maxLightSection - this.minLightSection + 1); ++ // force current chunk into cache ++ this.setChunkInCache(chunkX, chunkZ, chunk); ++ this.setBlocksForChunkInCache(chunkX, chunkZ, chunk.getSections()); ++ this.setNibblesForChunkInCache(chunkX, chunkZ, nibbles); ++ this.setEmptinessMapCache(chunkX, chunkZ, this.getEmptinessMap(chunk)); ++ ++ final boolean[] ret = this.handleEmptySectionChanges(lightAccess, chunk, emptySections, true); ++ if (ret != null) { ++ this.setEmptinessMap(chunk, ret); ++ } ++ this.lightChunk(lightAccess, chunk, true); ++ this.setNibbles(chunk, nibbles); ++ this.updateVisible(lightAccess); ++ } finally { ++ this.destroyCaches(); ++ } ++ } ++ ++ public final void relightChunks(final LightChunkGetter lightAccess, final Set chunks, ++ final Consumer chunkLightCallback, final IntConsumer onComplete) { ++ // it's recommended for maximum performance that the set is ordered according to a BFS from the center of ++ // the region of chunks to relight ++ // it's required that tickets are added for each chunk to keep them loaded ++ final Long2ObjectOpenHashMap nibblesByChunk = new Long2ObjectOpenHashMap<>(); ++ final Long2ObjectOpenHashMap emptinessMapByChunk = new Long2ObjectOpenHashMap<>(); ++ ++ final int[] neighbourLightOrder = new int[] { ++ // d = 0 ++ 0, 0, ++ // d = 1 ++ -1, 0, ++ 0, -1, ++ 1, 0, ++ 0, 1, ++ // d = 2 ++ -1, 1, ++ 1, 1, ++ -1, -1, ++ 1, -1, ++ }; ++ ++ int lightCalls = 0; ++ ++ for (final ChunkPos chunkPos : chunks) { ++ final int chunkX = chunkPos.x; ++ final int chunkZ = chunkPos.z; ++ final ChunkAccess chunk = (ChunkAccess)lightAccess.getChunkForLighting(chunkX, chunkZ); ++ if (chunk == null || !this.canUseChunk(chunk)) { ++ throw new IllegalStateException(); ++ } ++ ++ for (int i = 0, len = neighbourLightOrder.length; i < len; i += 2) { ++ final int dx = neighbourLightOrder[i]; ++ final int dz = neighbourLightOrder[i + 1]; ++ final int neighbourX = dx + chunkX; ++ final int neighbourZ = dz + chunkZ; ++ ++ final ChunkAccess neighbour = (ChunkAccess)lightAccess.getChunkForLighting(neighbourX, neighbourZ); ++ if (neighbour == null || !this.canUseChunk(neighbour)) { ++ continue; ++ } ++ ++ if (nibblesByChunk.get(CoordinateUtils.getChunkKey(neighbourX, neighbourZ)) != null) { ++ // lit already called for neighbour, no need to light it now ++ continue; ++ } ++ ++ // light neighbour chunk ++ this.setupEncodeOffset(neighbourX * 16 + 7, 128, neighbourZ * 16 + 7); ++ try { ++ // insert all neighbouring chunks for this neighbour that we have data for ++ for (int dz2 = -1; dz2 <= 1; ++dz2) { ++ for (int dx2 = -1; dx2 <= 1; ++dx2) { ++ final int neighbourX2 = neighbourX + dx2; ++ final int neighbourZ2 = neighbourZ + dz2; ++ final long key = CoordinateUtils.getChunkKey(neighbourX2, neighbourZ2); ++ final ChunkAccess neighbour2 = (ChunkAccess)lightAccess.getChunkForLighting(neighbourX2, neighbourZ2); ++ if (neighbour2 == null || !this.canUseChunk(neighbour2)) { ++ continue; ++ } ++ ++ final SWMRNibbleArray[] nibbles = nibblesByChunk.get(key); ++ if (nibbles == null) { ++ // we haven't lit this chunk ++ continue; ++ } ++ ++ this.setChunkInCache(neighbourX2, neighbourZ2, neighbour2); ++ this.setBlocksForChunkInCache(neighbourX2, neighbourZ2, neighbour2.getSections()); ++ this.setNibblesForChunkInCache(neighbourX2, neighbourZ2, nibbles); ++ this.setEmptinessMapCache(neighbourX2, neighbourZ2, emptinessMapByChunk.get(key)); ++ } ++ } ++ ++ final long key = CoordinateUtils.getChunkKey(neighbourX, neighbourZ); ++ ++ // now insert the neighbour chunk and light it ++ final SWMRNibbleArray[] nibbles = getFilledEmptyLight(this.world); ++ nibblesByChunk.put(key, nibbles); ++ ++ this.setChunkInCache(neighbourX, neighbourZ, neighbour); ++ this.setBlocksForChunkInCache(neighbourX, neighbourZ, neighbour.getSections()); ++ this.setNibblesForChunkInCache(neighbourX, neighbourZ, nibbles); ++ ++ final boolean[] neighbourEmptiness = this.handleEmptySectionChanges(lightAccess, neighbour, getEmptySectionsForChunk(neighbour), true); ++ emptinessMapByChunk.put(key, neighbourEmptiness); ++ if (chunks.contains(new ChunkPos(neighbourX, neighbourZ))) { ++ this.setEmptinessMap(neighbour, neighbourEmptiness); ++ } ++ ++ this.lightChunk(lightAccess, neighbour, false); ++ } finally { ++ this.destroyCaches(); ++ } ++ } ++ ++ // done lighting all neighbours, so the chunk is now fully lit ++ ++ // make sure nibbles are fully updated before calling back ++ final SWMRNibbleArray[] nibbles = nibblesByChunk.get(CoordinateUtils.getChunkKey(chunkX, chunkZ)); ++ for (final SWMRNibbleArray nibble : nibbles) { ++ nibble.updateVisible(); ++ } ++ ++ this.setNibbles(chunk, nibbles); ++ ++ for (int y = this.minLightSection; y <= this.maxLightSection; ++y) { ++ lightAccess.onLightUpdate(this.skylightPropagator ? LightLayer.SKY : LightLayer.BLOCK, SectionPos.of(chunkX, y, chunkX)); ++ } ++ ++ // now do callback ++ if (chunkLightCallback != null) { ++ chunkLightCallback.accept(chunkPos); ++ } ++ ++lightCalls; ++ } ++ ++ if (onComplete != null) { ++ onComplete.accept(lightCalls); ++ } ++ } ++ ++ // contains: ++ // lower (6 + 6 + 16) = 28 bits: encoded coordinate position (x | (z << 6) | (y << (6 + 6)))) ++ // next 4 bits: propagated light level (0, 15] ++ // next 6 bits: propagation direction bitset ++ // next 24 bits: unused ++ // last 3 bits: state flags ++ // state flags: ++ // whether the increase propagator needs to write the propagated level to the position, used to avoid cascading light ++ // updates for block sources ++ protected static final long FLAG_WRITE_LEVEL = Long.MIN_VALUE >>> 2; ++ // whether the propagation needs to check if its current level is equal to the expected level ++ // used only in increase propagation ++ protected static final long FLAG_RECHECK_LEVEL = Long.MIN_VALUE >>> 1; ++ // whether the propagation needs to consider if its block is conditionally transparent ++ protected static final long FLAG_HAS_SIDED_TRANSPARENT_BLOCKS = Long.MIN_VALUE; ++ ++ protected long[] increaseQueue = new long[16 * 16 * 16]; ++ protected int increaseQueueInitialLength; ++ protected long[] decreaseQueue = new long[16 * 16 * 16]; ++ protected int decreaseQueueInitialLength; ++ ++ protected final long[] resizeIncreaseQueue() { ++ return this.increaseQueue = Arrays.copyOf(this.increaseQueue, this.increaseQueue.length * 2); ++ } ++ ++ protected final long[] resizeDecreaseQueue() { ++ return this.decreaseQueue = Arrays.copyOf(this.decreaseQueue, this.decreaseQueue.length * 2); ++ } ++ ++ protected final void appendToIncreaseQueue(final long value) { ++ final int idx = this.increaseQueueInitialLength++; ++ long[] queue = this.increaseQueue; ++ if (idx >= queue.length) { ++ queue = this.resizeIncreaseQueue(); ++ queue[idx] = value; ++ } else { ++ queue[idx] = value; ++ } ++ } ++ ++ protected final void appendToDecreaseQueue(final long value) { ++ final int idx = this.decreaseQueueInitialLength++; ++ long[] queue = this.decreaseQueue; ++ if (idx >= queue.length) { ++ queue = this.resizeDecreaseQueue(); ++ queue[idx] = value; ++ } else { ++ queue[idx] = value; ++ } ++ } ++ ++ protected static final AxisDirection[][] OLD_CHECK_DIRECTIONS = new AxisDirection[1 << 6][]; ++ protected static final int ALL_DIRECTIONS_BITSET = (1 << 6) - 1; ++ static { ++ for (int i = 0; i < OLD_CHECK_DIRECTIONS.length; ++i) { ++ final List directions = new ArrayList<>(); ++ for (int bitset = i, len = Integer.bitCount(i), index = 0; index < len; ++index, bitset ^= IntegerUtil.getTrailingBit(bitset)) { ++ directions.add(AXIS_DIRECTIONS[IntegerUtil.trailingZeros(bitset)]); ++ } ++ OLD_CHECK_DIRECTIONS[i] = directions.toArray(new AxisDirection[0]); ++ } ++ } ++ ++ protected final void performLightIncrease(final LightChunkGetter lightAccess) { ++ final BlockGetter world = lightAccess.getLevel(); ++ long[] queue = this.increaseQueue; ++ int queueReadIndex = 0; ++ int queueLength = this.increaseQueueInitialLength; ++ this.increaseQueueInitialLength = 0; ++ final int decodeOffsetX = -this.encodeOffsetX; ++ final int decodeOffsetY = -this.encodeOffsetY; ++ final int decodeOffsetZ = -this.encodeOffsetZ; ++ final int encodeOffset = this.coordinateOffset; ++ final int sectionOffset = this.chunkSectionIndexOffset; ++ ++ while (queueReadIndex < queueLength) { ++ final long queueValue = queue[queueReadIndex++]; ++ ++ final int posX = ((int)queueValue & 63) + decodeOffsetX; ++ final int posZ = (((int)queueValue >>> 6) & 63) + decodeOffsetZ; ++ final int posY = (((int)queueValue >>> 12) & ((1 << 16) - 1)) + decodeOffsetY; ++ final int propagatedLightLevel = (int)((queueValue >>> (6 + 6 + 16)) & 0xFL); ++ final AxisDirection[] checkDirections = OLD_CHECK_DIRECTIONS[(int)((queueValue >>> (6 + 6 + 16 + 4)) & 63L)]; ++ ++ if ((queueValue & FLAG_RECHECK_LEVEL) != 0L) { ++ if (this.getLightLevel(posX, posY, posZ) != propagatedLightLevel) { ++ // not at the level we expect, so something changed. ++ continue; ++ } ++ } else if ((queueValue & FLAG_WRITE_LEVEL) != 0L) { ++ // these are used to restore block sources after a propagation decrease ++ this.setLightLevel(posX, posY, posZ, propagatedLightLevel); ++ } ++ ++ if ((queueValue & FLAG_HAS_SIDED_TRANSPARENT_BLOCKS) == 0L) { ++ // we don't need to worry about our state here. ++ for (final AxisDirection propagate : checkDirections) { ++ final int offX = posX + propagate.x; ++ final int offY = posY + propagate.y; ++ final int offZ = posZ + propagate.z; ++ ++ final int sectionIndex = (offX >> 4) + 5 * (offZ >> 4) + (5 * 5) * (offY >> 4) + sectionOffset; ++ final int localIndex = (offX & 15) | ((offZ & 15) << 4) | ((offY & 15) << 8); ++ ++ final SWMRNibbleArray currentNibble = this.nibbleCache[sectionIndex]; ++ final int currentLevel; ++ if (currentNibble == null || (currentLevel = currentNibble.getUpdating(localIndex)) >= (propagatedLightLevel - 1)) { ++ continue; // already at the level we want or unloaded ++ } ++ ++ final BlockState blockState = this.getBlockState(sectionIndex, localIndex); ++ if (blockState == null) { ++ continue; ++ } ++ final int opacityCached = blockState.getOpacityIfCached(); ++ if (opacityCached != -1) { ++ final int targetLevel = propagatedLightLevel - Math.max(1, opacityCached); ++ if (targetLevel > currentLevel) { ++ currentNibble.set(localIndex, targetLevel); ++ this.postLightUpdate(offX, offY, offZ); ++ ++ if (targetLevel > 1) { ++ if (queueLength >= queue.length) { ++ queue = this.resizeIncreaseQueue(); ++ } ++ queue[queueLength++] = ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((targetLevel & 0xFL) << (6 + 6 + 16)) ++ | (propagate.everythingButTheOppositeDirection << (6 + 6 + 16 + 4)); ++ continue; ++ } ++ } ++ continue; ++ } else { ++ this.mutablePos1.set(offX, offY, offZ); ++ long flags = 0; ++ if (blockState.isConditionallyFullOpaque()) { ++ final VoxelShape cullingFace = blockState.getFaceOcclusionShape(world, this.mutablePos1, propagate.getOpposite().nms); ++ ++ if (Shapes.faceShapeOccludes(Shapes.empty(), cullingFace)) { ++ continue; ++ } ++ flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS; ++ } ++ ++ final int opacity = blockState.getLightBlock(world, this.mutablePos1); ++ final int targetLevel = propagatedLightLevel - Math.max(1, opacity); ++ if (targetLevel <= currentLevel) { ++ continue; ++ } ++ ++ currentNibble.set(localIndex, targetLevel); ++ this.postLightUpdate(offX, offY, offZ); ++ ++ if (targetLevel > 1) { ++ if (queueLength >= queue.length) { ++ queue = this.resizeIncreaseQueue(); ++ } ++ queue[queueLength++] = ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((targetLevel & 0xFL) << (6 + 6 + 16)) ++ | (propagate.everythingButTheOppositeDirection << (6 + 6 + 16 + 4)) ++ | (flags); ++ } ++ continue; ++ } ++ } ++ } else { ++ // we actually need to worry about our state here ++ final BlockState fromBlock = this.getBlockState(posX, posY, posZ); ++ this.mutablePos2.set(posX, posY, posZ); ++ for (final AxisDirection propagate : checkDirections) { ++ final int offX = posX + propagate.x; ++ final int offY = posY + propagate.y; ++ final int offZ = posZ + propagate.z; ++ ++ final VoxelShape fromShape = fromBlock.isConditionallyFullOpaque() ? fromBlock.getFaceOcclusionShape(world, this.mutablePos2, propagate.nms) : Shapes.empty(); ++ ++ if (fromShape != Shapes.empty() && Shapes.faceShapeOccludes(Shapes.empty(), fromShape)) { ++ continue; ++ } ++ ++ final int sectionIndex = (offX >> 4) + 5 * (offZ >> 4) + (5 * 5) * (offY >> 4) + sectionOffset; ++ final int localIndex = (offX & 15) | ((offZ & 15) << 4) | ((offY & 15) << 8); ++ ++ final SWMRNibbleArray currentNibble = this.nibbleCache[sectionIndex]; ++ final int currentLevel; ++ ++ if (currentNibble == null || (currentLevel = currentNibble.getUpdating(localIndex)) >= (propagatedLightLevel - 1)) { ++ continue; // already at the level we want ++ } ++ ++ final BlockState blockState = this.getBlockState(sectionIndex, localIndex); ++ if (blockState == null) { ++ continue; ++ } ++ final int opacityCached = blockState.getOpacityIfCached(); ++ if (opacityCached != -1) { ++ final int targetLevel = propagatedLightLevel - Math.max(1, opacityCached); ++ if (targetLevel > currentLevel) { ++ currentNibble.set(localIndex, targetLevel); ++ this.postLightUpdate(offX, offY, offZ); ++ ++ if (targetLevel > 1) { ++ if (queueLength >= queue.length) { ++ queue = this.resizeIncreaseQueue(); ++ } ++ queue[queueLength++] = ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((targetLevel & 0xFL) << (6 + 6 + 16)) ++ | (propagate.everythingButTheOppositeDirection << (6 + 6 + 16 + 4)); ++ continue; ++ } ++ } ++ continue; ++ } else { ++ this.mutablePos1.set(offX, offY, offZ); ++ long flags = 0; ++ if (blockState.isConditionallyFullOpaque()) { ++ final VoxelShape cullingFace = blockState.getFaceOcclusionShape(world, this.mutablePos1, propagate.getOpposite().nms); ++ ++ if (Shapes.faceShapeOccludes(fromShape, cullingFace)) { ++ continue; ++ } ++ flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS; ++ } ++ ++ final int opacity = blockState.getLightBlock(world, this.mutablePos1); ++ final int targetLevel = propagatedLightLevel - Math.max(1, opacity); ++ if (targetLevel <= currentLevel) { ++ continue; ++ } ++ ++ currentNibble.set(localIndex, targetLevel); ++ this.postLightUpdate(offX, offY, offZ); ++ ++ if (targetLevel > 1) { ++ if (queueLength >= queue.length) { ++ queue = this.resizeIncreaseQueue(); ++ } ++ queue[queueLength++] = ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((targetLevel & 0xFL) << (6 + 6 + 16)) ++ | (propagate.everythingButTheOppositeDirection << (6 + 6 + 16 + 4)) ++ | (flags); ++ } ++ continue; ++ } ++ } ++ } ++ } ++ } ++ ++ protected final void performLightDecrease(final LightChunkGetter lightAccess) { ++ final BlockGetter world = lightAccess.getLevel(); ++ long[] queue = this.decreaseQueue; ++ long[] increaseQueue = this.increaseQueue; ++ int queueReadIndex = 0; ++ int queueLength = this.decreaseQueueInitialLength; ++ this.decreaseQueueInitialLength = 0; ++ int increaseQueueLength = this.increaseQueueInitialLength; ++ final int decodeOffsetX = -this.encodeOffsetX; ++ final int decodeOffsetY = -this.encodeOffsetY; ++ final int decodeOffsetZ = -this.encodeOffsetZ; ++ final int encodeOffset = this.coordinateOffset; ++ final int sectionOffset = this.chunkSectionIndexOffset; ++ final int emittedMask = this.emittedLightMask; ++ ++ while (queueReadIndex < queueLength) { ++ final long queueValue = queue[queueReadIndex++]; ++ ++ final int posX = ((int)queueValue & 63) + decodeOffsetX; ++ final int posZ = (((int)queueValue >>> 6) & 63) + decodeOffsetZ; ++ final int posY = (((int)queueValue >>> 12) & ((1 << 16) - 1)) + decodeOffsetY; ++ final int propagatedLightLevel = (int)((queueValue >>> (6 + 6 + 16)) & 0xF); ++ final AxisDirection[] checkDirections = OLD_CHECK_DIRECTIONS[(int)((queueValue >>> (6 + 6 + 16 + 4)) & 63)]; ++ ++ if ((queueValue & FLAG_HAS_SIDED_TRANSPARENT_BLOCKS) == 0L) { ++ // we don't need to worry about our state here. ++ for (final AxisDirection propagate : checkDirections) { ++ final int offX = posX + propagate.x; ++ final int offY = posY + propagate.y; ++ final int offZ = posZ + propagate.z; ++ ++ final int sectionIndex = (offX >> 4) + 5 * (offZ >> 4) + (5 * 5) * (offY >> 4) + sectionOffset; ++ final int localIndex = (offX & 15) | ((offZ & 15) << 4) | ((offY & 15) << 8); ++ ++ final SWMRNibbleArray currentNibble = this.nibbleCache[sectionIndex]; ++ final int lightLevel; ++ ++ if (currentNibble == null || (lightLevel = currentNibble.getUpdating(localIndex)) == 0) { ++ // already at lowest (or unloaded), nothing we can do ++ continue; ++ } ++ ++ final BlockState blockState = this.getBlockState(sectionIndex, localIndex); ++ if (blockState == null) { ++ continue; ++ } ++ final int opacityCached = blockState.getOpacityIfCached(); ++ if (opacityCached != -1) { ++ final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacityCached)); ++ if (lightLevel > targetLevel) { ++ // it looks like another source propagated here, so re-propagate it ++ if (increaseQueueLength >= increaseQueue.length) { ++ increaseQueue = this.resizeIncreaseQueue(); ++ } ++ increaseQueue[increaseQueueLength++] = ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((lightLevel & 0xFL) << (6 + 6 + 16)) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) ++ | FLAG_RECHECK_LEVEL; ++ continue; ++ } ++ final int emittedLight = blockState.getLightEmission() & emittedMask; ++ if (emittedLight != 0) { ++ // re-propagate source ++ // note: do not set recheck level, or else the propagation will fail ++ if (increaseQueueLength >= increaseQueue.length) { ++ increaseQueue = this.resizeIncreaseQueue(); ++ } ++ increaseQueue[increaseQueueLength++] = ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((emittedLight & 0xFL) << (6 + 6 + 16)) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) ++ | (blockState.isConditionallyFullOpaque() ? (FLAG_WRITE_LEVEL | FLAG_HAS_SIDED_TRANSPARENT_BLOCKS) : FLAG_WRITE_LEVEL); ++ } ++ ++ currentNibble.set(localIndex, 0); ++ this.postLightUpdate(offX, offY, offZ); ++ ++ if (targetLevel > 0) { // we actually need to propagate 0 just in case we find a neighbour... ++ if (queueLength >= queue.length) { ++ queue = this.resizeDecreaseQueue(); ++ } ++ queue[queueLength++] = ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((targetLevel & 0xFL) << (6 + 6 + 16)) ++ | ((propagate.everythingButTheOppositeDirection) << (6 + 6 + 16 + 4)); ++ continue; ++ } ++ continue; ++ } else { ++ this.mutablePos1.set(offX, offY, offZ); ++ long flags = 0; ++ if (blockState.isConditionallyFullOpaque()) { ++ final VoxelShape cullingFace = blockState.getFaceOcclusionShape(world, this.mutablePos1, propagate.getOpposite().nms); ++ ++ if (Shapes.faceShapeOccludes(Shapes.empty(), cullingFace)) { ++ continue; ++ } ++ flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS; ++ } ++ ++ final int opacity = blockState.getLightBlock(world, this.mutablePos1); ++ final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacity)); ++ if (lightLevel > targetLevel) { ++ // it looks like another source propagated here, so re-propagate it ++ if (increaseQueueLength >= increaseQueue.length) { ++ increaseQueue = this.resizeIncreaseQueue(); ++ } ++ increaseQueue[increaseQueueLength++] = ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((lightLevel & 0xFL) << (6 + 6 + 16)) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) ++ | (FLAG_RECHECK_LEVEL | flags); ++ continue; ++ } ++ final int emittedLight = blockState.getLightEmission() & emittedMask; ++ if (emittedLight != 0) { ++ // re-propagate source ++ // note: do not set recheck level, or else the propagation will fail ++ if (increaseQueueLength >= increaseQueue.length) { ++ increaseQueue = this.resizeIncreaseQueue(); ++ } ++ increaseQueue[increaseQueueLength++] = ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((emittedLight & 0xFL) << (6 + 6 + 16)) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) ++ | (flags | FLAG_WRITE_LEVEL); ++ } ++ ++ currentNibble.set(localIndex, 0); ++ this.postLightUpdate(offX, offY, offZ); ++ ++ if (targetLevel > 0) { ++ if (queueLength >= queue.length) { ++ queue = this.resizeDecreaseQueue(); ++ } ++ queue[queueLength++] = ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((targetLevel & 0xFL) << (6 + 6 + 16)) ++ | ((propagate.everythingButTheOppositeDirection) << (6 + 6 + 16 + 4)) ++ | flags; ++ } ++ continue; ++ } ++ } ++ } else { ++ // we actually need to worry about our state here ++ final BlockState fromBlock = this.getBlockState(posX, posY, posZ); ++ this.mutablePos2.set(posX, posY, posZ); ++ for (final AxisDirection propagate : checkDirections) { ++ final int offX = posX + propagate.x; ++ final int offY = posY + propagate.y; ++ final int offZ = posZ + propagate.z; ++ ++ final int sectionIndex = (offX >> 4) + 5 * (offZ >> 4) + (5 * 5) * (offY >> 4) + sectionOffset; ++ final int localIndex = (offX & 15) | ((offZ & 15) << 4) | ((offY & 15) << 8); ++ ++ final VoxelShape fromShape = fromBlock.isConditionallyFullOpaque() ? fromBlock.getFaceOcclusionShape(world, this.mutablePos2, propagate.nms) : Shapes.empty(); ++ ++ if (fromShape != Shapes.empty() && Shapes.faceShapeOccludes(Shapes.empty(), fromShape)) { ++ continue; ++ } ++ ++ final SWMRNibbleArray currentNibble = this.nibbleCache[sectionIndex]; ++ final int lightLevel; ++ ++ if (currentNibble == null || (lightLevel = currentNibble.getUpdating(localIndex)) == 0) { ++ // already at lowest (or unloaded), nothing we can do ++ continue; ++ } ++ ++ final BlockState blockState = this.getBlockState(sectionIndex, localIndex); ++ if (blockState == null) { ++ continue; ++ } ++ final int opacityCached = blockState.getOpacityIfCached(); ++ if (opacityCached != -1) { ++ final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacityCached)); ++ if (lightLevel > targetLevel) { ++ // it looks like another source propagated here, so re-propagate it ++ if (increaseQueueLength >= increaseQueue.length) { ++ increaseQueue = this.resizeIncreaseQueue(); ++ } ++ increaseQueue[increaseQueueLength++] = ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((lightLevel & 0xFL) << (6 + 6 + 16)) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) ++ | FLAG_RECHECK_LEVEL; ++ continue; ++ } ++ final int emittedLight = blockState.getLightEmission() & emittedMask; ++ if (emittedLight != 0) { ++ // re-propagate source ++ // note: do not set recheck level, or else the propagation will fail ++ if (increaseQueueLength >= increaseQueue.length) { ++ increaseQueue = this.resizeIncreaseQueue(); ++ } ++ increaseQueue[increaseQueueLength++] = ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((emittedLight & 0xFL) << (6 + 6 + 16)) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) ++ | (blockState.isConditionallyFullOpaque() ? (FLAG_WRITE_LEVEL | FLAG_HAS_SIDED_TRANSPARENT_BLOCKS) : FLAG_WRITE_LEVEL); ++ } ++ ++ currentNibble.set(localIndex, 0); ++ this.postLightUpdate(offX, offY, offZ); ++ ++ if (targetLevel > 0) { // we actually need to propagate 0 just in case we find a neighbour... ++ if (queueLength >= queue.length) { ++ queue = this.resizeDecreaseQueue(); ++ } ++ queue[queueLength++] = ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((targetLevel & 0xFL) << (6 + 6 + 16)) ++ | ((propagate.everythingButTheOppositeDirection) << (6 + 6 + 16 + 4)); ++ continue; ++ } ++ continue; ++ } else { ++ this.mutablePos1.set(offX, offY, offZ); ++ long flags = 0; ++ if (blockState.isConditionallyFullOpaque()) { ++ final VoxelShape cullingFace = blockState.getFaceOcclusionShape(world, this.mutablePos1, propagate.getOpposite().nms); ++ ++ if (Shapes.faceShapeOccludes(fromShape, cullingFace)) { ++ continue; ++ } ++ flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS; ++ } ++ ++ final int opacity = blockState.getLightBlock(world, this.mutablePos1); ++ final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacity)); ++ if (lightLevel > targetLevel) { ++ // it looks like another source propagated here, so re-propagate it ++ if (increaseQueueLength >= increaseQueue.length) { ++ increaseQueue = this.resizeIncreaseQueue(); ++ } ++ increaseQueue[increaseQueueLength++] = ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((lightLevel & 0xFL) << (6 + 6 + 16)) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) ++ | (FLAG_RECHECK_LEVEL | flags); ++ continue; ++ } ++ final int emittedLight = blockState.getLightEmission() & emittedMask; ++ if (emittedLight != 0) { ++ // re-propagate source ++ // note: do not set recheck level, or else the propagation will fail ++ if (increaseQueueLength >= increaseQueue.length) { ++ increaseQueue = this.resizeIncreaseQueue(); ++ } ++ increaseQueue[increaseQueueLength++] = ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((emittedLight & 0xFL) << (6 + 6 + 16)) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) ++ | (flags | FLAG_WRITE_LEVEL); ++ } ++ ++ currentNibble.set(localIndex, 0); ++ this.postLightUpdate(offX, offY, offZ); ++ ++ if (targetLevel > 0) { // we actually need to propagate 0 just in case we find a neighbour... ++ if (queueLength >= queue.length) { ++ queue = this.resizeDecreaseQueue(); ++ } ++ queue[queueLength++] = ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((targetLevel & 0xFL) << (6 + 6 + 16)) ++ | ((propagate.everythingButTheOppositeDirection) << (6 + 6 + 16 + 4)) ++ | flags; ++ } ++ continue; ++ } ++ } ++ } ++ } ++ ++ // propagate sources we clobbered ++ this.increaseQueueInitialLength = increaseQueueLength; ++ this.performLightIncrease(lightAccess); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/starlight/light/StarLightInterface.java b/src/main/java/ca/spottedleaf/starlight/light/StarLightInterface.java +new file mode 100644 +index 0000000000000000000000000000000000000000..9b8412f50d161471166cdf5c9effc2d58915faa1 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/starlight/light/StarLightInterface.java +@@ -0,0 +1,635 @@ ++package ca.spottedleaf.starlight.light; ++ ++import io.papermc.paper.util.CoordinateUtils; ++import io.papermc.paper.util.WorldUtil; ++import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; ++import it.unimi.dsi.fastutil.shorts.ShortCollection; ++import it.unimi.dsi.fastutil.shorts.ShortOpenHashSet; ++import net.minecraft.core.BlockPos; ++import net.minecraft.core.SectionPos; ++import net.minecraft.server.level.ServerLevel; ++import net.minecraft.server.level.TicketType; ++import net.minecraft.world.level.ChunkPos; ++import net.minecraft.world.level.Level; ++import net.minecraft.world.level.chunk.ChunkAccess; ++import net.minecraft.world.level.chunk.ChunkStatus; ++import net.minecraft.world.level.chunk.DataLayer; ++import net.minecraft.world.level.chunk.LightChunkGetter; ++import net.minecraft.world.level.lighting.LayerLightEventListener; ++import net.minecraft.world.level.lighting.LevelLightEngine; ++import java.util.*; ++import java.util.concurrent.CompletableFuture; ++import java.util.function.Consumer; ++import java.util.function.IntConsumer; ++ ++public final class StarLightInterface { ++ ++ public static final TicketType CHUNK_WORK_TICKET = TicketType.create("starlight_chunk_work_ticket", (p1, p2) -> Long.compare(p1.toLong(), p2.toLong())); ++ ++ /** ++ * Can be {@code null}, indicating the light is all empty. ++ */ ++ protected final Level world; ++ protected final LightChunkGetter lightAccess; ++ ++ protected final ArrayDeque cachedSkyPropagators; ++ protected final ArrayDeque cachedBlockPropagators; ++ ++ protected final LightQueue lightQueue = new LightQueue(this); ++ ++ protected final LayerLightEventListener skyReader; ++ protected final LayerLightEventListener blockReader; ++ protected final boolean isClientSide; ++ ++ protected final int minSection; ++ protected final int maxSection; ++ protected final int minLightSection; ++ protected final int maxLightSection; ++ ++ public final LevelLightEngine lightEngine; ++ ++ public StarLightInterface(final LightChunkGetter lightAccess, final boolean hasSkyLight, final boolean hasBlockLight, final LevelLightEngine lightEngine) { ++ this.lightAccess = lightAccess; ++ this.world = lightAccess == null ? null : (Level)lightAccess.getLevel(); ++ this.cachedSkyPropagators = hasSkyLight && lightAccess != null ? new ArrayDeque<>() : null; ++ this.cachedBlockPropagators = hasBlockLight && lightAccess != null ? new ArrayDeque<>() : null; ++ this.isClientSide = !(this.world instanceof ServerLevel); ++ if (this.world == null) { ++ this.minSection = 0; ++ this.maxSection = 15; ++ this.minLightSection = -1; ++ this.maxLightSection = 16; ++ } else { ++ this.minSection = WorldUtil.getMinSection(this.world); ++ this.maxSection = WorldUtil.getMaxSection(this.world); ++ this.minLightSection = WorldUtil.getMinLightSection(this.world); ++ this.maxLightSection = WorldUtil.getMaxLightSection(this.world); ++ } ++ this.lightEngine = lightEngine; ++ this.skyReader = !hasSkyLight ? LayerLightEventListener.DummyLightLayerEventListener.INSTANCE : new LayerLightEventListener() { ++ @Override ++ public void checkBlock(final BlockPos blockPos) { ++ StarLightInterface.this.lightEngine.checkBlock(blockPos.immutable()); ++ } ++ ++ @Override ++ public void onBlockEmissionIncrease(final BlockPos blockPos, final int i) { ++ // skylight doesn't care ++ } ++ ++ @Override ++ public boolean hasLightWork() { ++ // not really correct... ++ return StarLightInterface.this.hasUpdates(); ++ } ++ ++ @Override ++ public int runUpdates(final int i, final boolean bl, final boolean bl2) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void enableLightSources(final ChunkPos chunkPos, final boolean bl) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public DataLayer getDataLayerData(final SectionPos pos) { ++ final ChunkAccess chunk = StarLightInterface.this.getAnyChunkNow(pos.getX(), pos.getZ()); ++ if (chunk == null || (!StarLightInterface.this.isClientSide && !chunk.isLightCorrect()) || !chunk.getStatus().isOrAfter(ChunkStatus.LIGHT)) { ++ return null; ++ } ++ ++ final int sectionY = pos.getY(); ++ ++ if (sectionY > StarLightInterface.this.maxLightSection || sectionY < StarLightInterface.this.minLightSection) { ++ return null; ++ } ++ ++ if (chunk.getSkyEmptinessMap() == null) { ++ return null; ++ } ++ ++ return chunk.getSkyNibbles()[sectionY - StarLightInterface.this.minLightSection].toVanillaNibble(); ++ } ++ ++ @Override ++ public int getLightValue(final BlockPos blockPos) { ++ final int x = blockPos.getX(); ++ int y = blockPos.getY(); ++ final int z = blockPos.getZ(); ++ ++ final ChunkAccess chunk = StarLightInterface.this.getAnyChunkNow(x >> 4, z >> 4); ++ if (chunk == null || (!StarLightInterface.this.isClientSide && !chunk.isLightCorrect()) || !chunk.getStatus().isOrAfter(ChunkStatus.LIGHT)) { ++ return 15; ++ } ++ ++ int sectionY = y >> 4; ++ ++ if (sectionY > StarLightInterface.this.maxLightSection) { ++ return 15; ++ } ++ ++ if (sectionY < StarLightInterface.this.minLightSection) { ++ sectionY = StarLightInterface.this.minLightSection; ++ y = sectionY << 4; ++ } ++ ++ final SWMRNibbleArray[] nibbles = chunk.getSkyNibbles(); ++ final SWMRNibbleArray immediate = nibbles[sectionY - StarLightInterface.this.minLightSection]; ++ ++ if (StarLightInterface.this.isClientSide) { ++ if (!immediate.isNullNibbleUpdating()) { ++ return immediate.getUpdating(x, y, z); ++ } ++ } else { ++ if (!immediate.isNullNibbleVisible()) { ++ return immediate.getVisible(x, y, z); ++ } ++ } ++ ++ final boolean[] emptinessMap = chunk.getSkyEmptinessMap(); ++ ++ if (emptinessMap == null) { ++ return 15; ++ } ++ ++ // are we above this chunk's lowest empty section? ++ int lowestY = StarLightInterface.this.minLightSection - 1; ++ for (int currY = StarLightInterface.this.maxSection; currY >= StarLightInterface.this.minSection; --currY) { ++ if (emptinessMap[currY - StarLightInterface.this.minSection]) { ++ continue; ++ } ++ ++ // should always be full lit here ++ lowestY = currY; ++ break; ++ } ++ ++ if (sectionY > lowestY) { ++ return 15; ++ } ++ ++ // this nibble is going to depend solely on the skylight data above it ++ // find first non-null data above (there does exist one, as we just found it above) ++ for (int currY = sectionY + 1; currY <= StarLightInterface.this.maxLightSection; ++currY) { ++ final SWMRNibbleArray nibble = nibbles[currY - StarLightInterface.this.minLightSection]; ++ if (StarLightInterface.this.isClientSide) { ++ if (!nibble.isNullNibbleUpdating()) { ++ return nibble.getUpdating(x, 0, z); ++ } ++ } else { ++ if (!nibble.isNullNibbleVisible()) { ++ return nibble.getVisible(x, 0, z); ++ } ++ } ++ } ++ ++ // should never reach here ++ return 15; ++ } ++ ++ @Override ++ public void updateSectionStatus(final SectionPos pos, final boolean notReady) { ++ StarLightInterface.this.sectionChange(pos, notReady); ++ } ++ }; ++ this.blockReader = !hasBlockLight ? LayerLightEventListener.DummyLightLayerEventListener.INSTANCE : new LayerLightEventListener() { ++ @Override ++ public void checkBlock(final BlockPos blockPos) { ++ StarLightInterface.this.lightEngine.checkBlock(blockPos.immutable()); ++ } ++ ++ @Override ++ public void onBlockEmissionIncrease(final BlockPos blockPos, final int i) { ++ this.checkBlock(blockPos); ++ } ++ ++ @Override ++ public boolean hasLightWork() { ++ // not really correct... ++ return StarLightInterface.this.hasUpdates(); ++ } ++ ++ @Override ++ public int runUpdates(final int i, final boolean bl, final boolean bl2) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void enableLightSources(final ChunkPos chunkPos, final boolean bl) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public DataLayer getDataLayerData(final SectionPos pos) { ++ final ChunkAccess chunk = StarLightInterface.this.getAnyChunkNow(pos.getX(), pos.getZ()); ++ ++ if (chunk == null || pos.getY() < StarLightInterface.this.minLightSection || pos.getY() > StarLightInterface.this.maxLightSection) { ++ return null; ++ } ++ ++ return chunk.getBlockNibbles()[pos.getY() - StarLightInterface.this.minLightSection].toVanillaNibble(); ++ } ++ ++ @Override ++ public int getLightValue(final BlockPos blockPos) { ++ final int cx = blockPos.getX() >> 4; ++ final int cy = blockPos.getY() >> 4; ++ final int cz = blockPos.getZ() >> 4; ++ ++ if (cy < StarLightInterface.this.minLightSection || cy > StarLightInterface.this.maxLightSection) { ++ return 0; ++ } ++ ++ final ChunkAccess chunk = StarLightInterface.this.getAnyChunkNow(cx, cz); ++ ++ if (chunk == null) { ++ return 0; ++ } ++ ++ final SWMRNibbleArray nibble = chunk.getBlockNibbles()[cy - StarLightInterface.this.minLightSection]; ++ if (StarLightInterface.this.isClientSide) { ++ return nibble.getUpdating(blockPos.getX(), blockPos.getY(), blockPos.getZ()); ++ } else { ++ return nibble.getVisible(blockPos.getX(), blockPos.getY(), blockPos.getZ()); ++ } ++ } ++ ++ @Override ++ public void updateSectionStatus(final SectionPos pos, final boolean notReady) { ++ StarLightInterface.this.sectionChange(pos, notReady); ++ } ++ }; ++ } ++ ++ public LayerLightEventListener getSkyReader() { ++ return this.skyReader; ++ } ++ ++ public LayerLightEventListener getBlockReader() { ++ return this.blockReader; ++ } ++ ++ public boolean isClientSide() { ++ return this.isClientSide; ++ } ++ ++ public ChunkAccess getAnyChunkNow(final int chunkX, final int chunkZ) { ++ if (this.world == null) { ++ // empty world ++ return null; ++ } ++ return ((ServerLevel)this.world).getChunkSource().getChunkAtImmediately(chunkX, chunkZ); ++ } ++ ++ public boolean hasUpdates() { ++ return !this.lightQueue.isEmpty(); ++ } ++ ++ public Level getWorld() { ++ return this.world; ++ } ++ ++ public LightChunkGetter getLightAccess() { ++ return this.lightAccess; ++ } ++ ++ protected final SkyStarLightEngine getSkyLightEngine() { ++ if (this.cachedSkyPropagators == null) { ++ return null; ++ } ++ final SkyStarLightEngine ret; ++ synchronized (this.cachedSkyPropagators) { ++ ret = this.cachedSkyPropagators.pollFirst(); ++ } ++ ++ if (ret == null) { ++ return new SkyStarLightEngine(this.world); ++ } ++ return ret; ++ } ++ ++ protected final void releaseSkyLightEngine(final SkyStarLightEngine engine) { ++ if (this.cachedSkyPropagators == null) { ++ return; ++ } ++ synchronized (this.cachedSkyPropagators) { ++ this.cachedSkyPropagators.addFirst(engine); ++ } ++ } ++ ++ protected final BlockStarLightEngine getBlockLightEngine() { ++ if (this.cachedBlockPropagators == null) { ++ return null; ++ } ++ final BlockStarLightEngine ret; ++ synchronized (this.cachedBlockPropagators) { ++ ret = this.cachedBlockPropagators.pollFirst(); ++ } ++ ++ if (ret == null) { ++ return new BlockStarLightEngine(this.world); ++ } ++ return ret; ++ } ++ ++ protected final void releaseBlockLightEngine(final BlockStarLightEngine engine) { ++ if (this.cachedBlockPropagators == null) { ++ return; ++ } ++ synchronized (this.cachedBlockPropagators) { ++ this.cachedBlockPropagators.addFirst(engine); ++ } ++ } ++ ++ public CompletableFuture blockChange(final BlockPos pos) { ++ if (this.world == null || pos.getY() < WorldUtil.getMinBlockY(this.world) || pos.getY() > WorldUtil.getMaxBlockY(this.world)) { // empty world ++ return null; ++ } ++ ++ return this.lightQueue.queueBlockChange(pos); ++ } ++ ++ public CompletableFuture sectionChange(final SectionPos pos, final boolean newEmptyValue) { ++ if (this.world == null) { // empty world ++ return null; ++ } ++ ++ return this.lightQueue.queueSectionChange(pos, newEmptyValue); ++ } ++ ++ public void forceLoadInChunk(final ChunkAccess chunk, final Boolean[] emptySections) { ++ final SkyStarLightEngine skyEngine = this.getSkyLightEngine(); ++ final BlockStarLightEngine blockEngine = this.getBlockLightEngine(); ++ ++ try { ++ if (skyEngine != null) { ++ skyEngine.forceHandleEmptySectionChanges(this.lightAccess, chunk, emptySections); ++ } ++ if (blockEngine != null) { ++ blockEngine.forceHandleEmptySectionChanges(this.lightAccess, chunk, emptySections); ++ } ++ } finally { ++ this.releaseSkyLightEngine(skyEngine); ++ this.releaseBlockLightEngine(blockEngine); ++ } ++ } ++ ++ public void loadInChunk(final int chunkX, final int chunkZ, final Boolean[] emptySections) { ++ final SkyStarLightEngine skyEngine = this.getSkyLightEngine(); ++ final BlockStarLightEngine blockEngine = this.getBlockLightEngine(); ++ ++ try { ++ if (skyEngine != null) { ++ skyEngine.handleEmptySectionChanges(this.lightAccess, chunkX, chunkZ, emptySections); ++ } ++ if (blockEngine != null) { ++ blockEngine.handleEmptySectionChanges(this.lightAccess, chunkX, chunkZ, emptySections); ++ } ++ } finally { ++ this.releaseSkyLightEngine(skyEngine); ++ this.releaseBlockLightEngine(blockEngine); ++ } ++ } ++ ++ public void lightChunk(final ChunkAccess chunk, final Boolean[] emptySections) { ++ final SkyStarLightEngine skyEngine = this.getSkyLightEngine(); ++ final BlockStarLightEngine blockEngine = this.getBlockLightEngine(); ++ ++ try { ++ if (skyEngine != null) { ++ skyEngine.light(this.lightAccess, chunk, emptySections); ++ } ++ if (blockEngine != null) { ++ blockEngine.light(this.lightAccess, chunk, emptySections); ++ } ++ } finally { ++ this.releaseSkyLightEngine(skyEngine); ++ this.releaseBlockLightEngine(blockEngine); ++ } ++ } ++ ++ public void relightChunks(final Set chunks, final Consumer chunkLightCallback, ++ final IntConsumer onComplete) { ++ final SkyStarLightEngine skyEngine = this.getSkyLightEngine(); ++ final BlockStarLightEngine blockEngine = this.getBlockLightEngine(); ++ ++ try { ++ if (skyEngine != null) { ++ skyEngine.relightChunks(this.lightAccess, chunks, blockEngine == null ? chunkLightCallback : null, ++ blockEngine == null ? onComplete : null); ++ } ++ if (blockEngine != null) { ++ blockEngine.relightChunks(this.lightAccess, chunks, chunkLightCallback, onComplete); ++ } ++ } finally { ++ this.releaseSkyLightEngine(skyEngine); ++ this.releaseBlockLightEngine(blockEngine); ++ } ++ } ++ ++ public void checkChunkEdges(final int chunkX, final int chunkZ) { ++ this.checkSkyEdges(chunkX, chunkZ); ++ this.checkBlockEdges(chunkX, chunkZ); ++ } ++ ++ public void checkSkyEdges(final int chunkX, final int chunkZ) { ++ final SkyStarLightEngine skyEngine = this.getSkyLightEngine(); ++ ++ try { ++ if (skyEngine != null) { ++ skyEngine.checkChunkEdges(this.lightAccess, chunkX, chunkZ); ++ } ++ } finally { ++ this.releaseSkyLightEngine(skyEngine); ++ } ++ } ++ ++ public void checkBlockEdges(final int chunkX, final int chunkZ) { ++ final BlockStarLightEngine blockEngine = this.getBlockLightEngine(); ++ try { ++ if (blockEngine != null) { ++ blockEngine.checkChunkEdges(this.lightAccess, chunkX, chunkZ); ++ } ++ } finally { ++ this.releaseBlockLightEngine(blockEngine); ++ } ++ } ++ ++ public void checkSkyEdges(final int chunkX, final int chunkZ, final ShortCollection sections) { ++ final SkyStarLightEngine skyEngine = this.getSkyLightEngine(); ++ ++ try { ++ if (skyEngine != null) { ++ skyEngine.checkChunkEdges(this.lightAccess, chunkX, chunkZ, sections); ++ } ++ } finally { ++ this.releaseSkyLightEngine(skyEngine); ++ } ++ } ++ ++ public void checkBlockEdges(final int chunkX, final int chunkZ, final ShortCollection sections) { ++ final BlockStarLightEngine blockEngine = this.getBlockLightEngine(); ++ try { ++ if (blockEngine != null) { ++ blockEngine.checkChunkEdges(this.lightAccess, chunkX, chunkZ, sections); ++ } ++ } finally { ++ this.releaseBlockLightEngine(blockEngine); ++ } ++ } ++ ++ public void scheduleChunkLight(final ChunkPos pos, final Runnable run) { ++ this.lightQueue.queueChunkLighting(pos, run); ++ } ++ ++ public void removeChunkTasks(final ChunkPos pos) { ++ this.lightQueue.removeChunk(pos); ++ } ++ ++ public void propagateChanges() { ++ if (this.lightQueue.isEmpty()) { ++ return; ++ } ++ ++ final SkyStarLightEngine skyEngine = this.getSkyLightEngine(); ++ final BlockStarLightEngine blockEngine = this.getBlockLightEngine(); ++ ++ try { ++ LightQueue.ChunkTasks task; ++ while ((task = this.lightQueue.removeFirstTask()) != null) { ++ if (task.lightTasks != null) { ++ for (final Runnable run : task.lightTasks) { ++ run.run(); ++ } ++ } ++ ++ final long coordinate = task.chunkCoordinate; ++ final int chunkX = CoordinateUtils.getChunkX(coordinate); ++ final int chunkZ = CoordinateUtils.getChunkZ(coordinate); ++ ++ final Set positions = task.changedPositions; ++ final Boolean[] sectionChanges = task.changedSectionSet; ++ ++ if (skyEngine != null && (!positions.isEmpty() || sectionChanges != null)) { ++ skyEngine.blocksChangedInChunk(this.lightAccess, chunkX, chunkZ, positions, sectionChanges); ++ } ++ if (blockEngine != null && (!positions.isEmpty() || sectionChanges != null)) { ++ blockEngine.blocksChangedInChunk(this.lightAccess, chunkX, chunkZ, positions, sectionChanges); ++ } ++ ++ if (skyEngine != null && task.queuedEdgeChecksSky != null) { ++ skyEngine.checkChunkEdges(this.lightAccess, chunkX, chunkZ, task.queuedEdgeChecksSky); ++ } ++ if (blockEngine != null && task.queuedEdgeChecksBlock != null) { ++ blockEngine.checkChunkEdges(this.lightAccess, chunkX, chunkZ, task.queuedEdgeChecksBlock); ++ } ++ ++ task.onComplete.complete(null); ++ } ++ } finally { ++ this.releaseSkyLightEngine(skyEngine); ++ this.releaseBlockLightEngine(blockEngine); ++ } ++ } ++ ++ protected static final class LightQueue { ++ ++ protected final Long2ObjectLinkedOpenHashMap chunkTasks = new Long2ObjectLinkedOpenHashMap<>(); ++ protected final StarLightInterface manager; ++ ++ public LightQueue(final StarLightInterface manager) { ++ this.manager = manager; ++ } ++ ++ public synchronized boolean isEmpty() { ++ return this.chunkTasks.isEmpty(); ++ } ++ ++ public synchronized CompletableFuture queueBlockChange(final BlockPos pos) { ++ final ChunkTasks tasks = this.chunkTasks.computeIfAbsent(CoordinateUtils.getChunkKey(pos), ChunkTasks::new); ++ tasks.changedPositions.add(pos.immutable()); ++ return tasks.onComplete; ++ } ++ ++ public synchronized CompletableFuture queueSectionChange(final SectionPos pos, final boolean newEmptyValue) { ++ final ChunkTasks tasks = this.chunkTasks.computeIfAbsent(CoordinateUtils.getChunkKey(pos), ChunkTasks::new); ++ ++ if (tasks.changedSectionSet == null) { ++ tasks.changedSectionSet = new Boolean[this.manager.maxSection - this.manager.minSection + 1]; ++ } ++ tasks.changedSectionSet[pos.getY() - this.manager.minSection] = Boolean.valueOf(newEmptyValue); ++ ++ return tasks.onComplete; ++ } ++ ++ public synchronized CompletableFuture queueChunkLighting(final ChunkPos pos, final Runnable lightTask) { ++ final ChunkTasks tasks = this.chunkTasks.computeIfAbsent(CoordinateUtils.getChunkKey(pos), ChunkTasks::new); ++ if (tasks.lightTasks == null) { ++ tasks.lightTasks = new ArrayList<>(); ++ } ++ tasks.lightTasks.add(lightTask); ++ ++ return tasks.onComplete; ++ } ++ ++ public synchronized CompletableFuture queueChunkSkylightEdgeCheck(final SectionPos pos, final ShortCollection sections) { ++ final ChunkTasks tasks = this.chunkTasks.computeIfAbsent(CoordinateUtils.getChunkKey(pos), ChunkTasks::new); ++ ++ ShortOpenHashSet queuedEdges = tasks.queuedEdgeChecksSky; ++ if (queuedEdges == null) { ++ queuedEdges = tasks.queuedEdgeChecksSky = new ShortOpenHashSet(); ++ } ++ queuedEdges.addAll(sections); ++ ++ return tasks.onComplete; ++ } ++ ++ public synchronized CompletableFuture queueChunkBlocklightEdgeCheck(final SectionPos pos, final ShortCollection sections) { ++ final ChunkTasks tasks = this.chunkTasks.computeIfAbsent(CoordinateUtils.getChunkKey(pos), ChunkTasks::new); ++ ++ ShortOpenHashSet queuedEdges = tasks.queuedEdgeChecksBlock; ++ if (queuedEdges == null) { ++ queuedEdges = tasks.queuedEdgeChecksBlock = new ShortOpenHashSet(); ++ } ++ queuedEdges.addAll(sections); ++ ++ return tasks.onComplete; ++ } ++ ++ public void removeChunk(final ChunkPos pos) { ++ final ChunkTasks tasks; ++ synchronized (this) { ++ tasks = this.chunkTasks.remove(CoordinateUtils.getChunkKey(pos)); ++ } ++ if (tasks != null) { ++ tasks.onComplete.complete(null); ++ } ++ } ++ ++ public synchronized ChunkTasks removeFirstTask() { ++ if (this.chunkTasks.isEmpty()) { ++ return null; ++ } ++ return this.chunkTasks.removeFirst(); ++ } ++ ++ protected static final class ChunkTasks { ++ ++ public final Set changedPositions = new HashSet<>(); ++ public Boolean[] changedSectionSet; ++ public ShortOpenHashSet queuedEdgeChecksSky; ++ public ShortOpenHashSet queuedEdgeChecksBlock; ++ public List lightTasks; ++ ++ public final CompletableFuture onComplete = new CompletableFuture<>(); ++ ++ public final long chunkCoordinate; ++ ++ public ChunkTasks(final long chunkCoordinate) { ++ this.chunkCoordinate = chunkCoordinate; ++ } ++ } ++ } ++} +diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java +index f436ab35798c9b6e6cb2eb60d2c02cbf9b742e69..807bbe54f6516f794bdcb735bb7b8d6812e3ef01 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java ++++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java +@@ -501,6 +501,46 @@ public class PaperCommand extends Command { + } + } + ++ // Paper start - rewrite light engine ++ private void starlightFixLight(ServerPlayer sender, ServerLevel world, ThreadedLevelLightEngine lightengine, int radius) { ++ long start = System.nanoTime(); ++ java.util.LinkedHashSet chunks = new java.util.LinkedHashSet<>(MCUtil.getSpiralOutChunks(sender.blockPosition(), radius)); // getChunkCoordinates is actually just bad mappings, this function rets position as blockpos ++ ++ int[] pending = new int[1]; ++ for (java.util.Iterator iterator = chunks.iterator(); iterator.hasNext();) { ++ final ChunkPos chunkPos = iterator.next(); ++ ++ final net.minecraft.world.level.chunk.ChunkAccess chunk = world.getChunkSource().getChunkAtImmediately(chunkPos.x, chunkPos.z); ++ if (chunk == null || !chunk.isLightCorrect() || !chunk.getStatus().isOrAfter(net.minecraft.world.level.chunk.ChunkStatus.LIGHT)) { ++ // cannot relight this chunk ++ iterator.remove(); ++ continue; ++ } ++ ++ ++pending[0]; ++ } ++ ++ int[] relitChunks = new int[1]; ++ lightengine.relight(chunks, ++ (ChunkPos chunkPos) -> { ++ ++relitChunks[0]; ++ sender.getBukkitEntity().sendMessage( ++ ChatColor.BLUE + "Relit chunk " + ChatColor.DARK_AQUA + chunkPos + ChatColor.BLUE + ++ ", progress: " + ChatColor.DARK_AQUA + (int)(Math.round(100.0 * (double)(relitChunks[0])/(double)pending[0])) + "%" ++ ); ++ }, ++ (int totalRelit) -> { ++ final long end = System.nanoTime(); ++ final long diff = Math.round(1.0e-6*(end - start)); ++ sender.getBukkitEntity().sendMessage( ++ ChatColor.BLUE + "Relit " + ChatColor.DARK_AQUA + totalRelit + ChatColor.BLUE + " chunks. Took " + ++ ChatColor.DARK_AQUA + diff + "ms" ++ ); ++ }); ++ sender.getBukkitEntity().sendMessage(ChatColor.BLUE + "Relighting " + ChatColor.DARK_AQUA + pending[0] + ChatColor.BLUE + " chunks"); ++ } ++ // Paper end - rewrite light engine ++ + private void doFixLight(CommandSender sender, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage("Only players can use this command"); +@@ -509,7 +549,7 @@ public class PaperCommand extends Command { + int radius = 2; + if (args.length > 1) { + try { +- radius = Math.min(5, Integer.parseInt(args[1])); ++ radius = Math.min(32, Integer.parseInt(args[1])); // Paper - MOOOOOORE + } catch (Exception e) { + sender.sendMessage("Not a number"); + return; +@@ -522,6 +562,13 @@ public class PaperCommand extends Command { + ServerLevel world = (ServerLevel) handle.level; + ThreadedLevelLightEngine lightengine = world.getChunkSource().getLightEngine(); + ++ // Paper start - rewrite light engine ++ if (true) { ++ this.starlightFixLight(handle, world, lightengine, radius); ++ return; ++ } ++ // Paper end - rewrite light engine ++ + net.minecraft.core.BlockPos center = MCUtil.toBlockPosition(player.getLocation()); + Deque queue = new ArrayDeque<>(MCUtil.getSpiralOutChunks(center, radius)); + updateLight(sender, world, lightengine, queue); +diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLightUpdatePacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLightUpdatePacket.java +index d8be2ad889f46491e50404916fb4ae0de5f42098..29ba097a7b230ef67d4d1b5f4ebe20cd5228d214 100644 +--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLightUpdatePacket.java ++++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLightUpdatePacket.java +@@ -32,25 +32,17 @@ public class ClientboundLightUpdatePacket implements Packet { +- if (remainingSends.get() == 0) { +- cleaner1.run(); +- cleaner2.run(); +- } +- }, "Light Packet Release"); +- } ++ // Paper - rewrite light engine + } + + @Override + public boolean hasFinishListener() { +- return true; ++ return false; // Paper - rewrite light engine + } + + // Paper end +@@ -63,8 +55,8 @@ public class ClientboundLightUpdatePacket implements Packet> fullChunkFuture; private int fullChunkCreateCount; private volatile boolean isFullChunkReady; // Paper - cache chunk ticking stage + private volatile CompletableFuture> tickingChunkFuture; private volatile boolean isTickingReady; // Paper - cache chunk ticking stage + private volatile CompletableFuture> entityTickingChunkFuture; private volatile boolean isEntityTickingReady; // Paper - cache chunk ticking stage +- private CompletableFuture chunkToSave; ++ public CompletableFuture chunkToSave; // Paper - public + @Nullable + private final DebugBuffer chunkToSaveHistory; + public int oldTicketLevel; +diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java +index a2895a48556558ee83ab0c53137da9e5c6208a83..313a591dda5ef3962b4eab377abdce7c7ad08ec7 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -121,7 +121,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + public final LongSet entitiesInLevel; + public final ServerLevel level; + private final ThreadedLevelLightEngine lightEngine; +- private final BlockableEventLoop mainThreadExecutor; ++ public final BlockableEventLoop mainThreadExecutor; // Paper - public + final java.util.concurrent.Executor mainInvokingExecutor; // Paper + public final ChunkGenerator generator; + public final Supplier overworldDataStorage; +diff --git a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java +index 833b6b2193cf08e123aabb344f2283730aed1bcd..136ca55ac38b709e086f8e2e68dc1d0db2670ae4 100644 +--- a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java ++++ b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java +@@ -25,6 +25,17 @@ import net.minecraft.world.level.lighting.LevelLightEngine; + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// Paper start ++import ca.spottedleaf.starlight.light.StarLightEngine; ++import io.papermc.paper.util.CoordinateUtils; ++import java.util.function.Supplier; ++import net.minecraft.world.level.lighting.LayerLightEventListener; ++import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; ++import it.unimi.dsi.fastutil.longs.LongArrayList; ++import it.unimi.dsi.fastutil.longs.LongIterator; ++import net.minecraft.world.level.chunk.ChunkStatus; ++// Paper end ++ + public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCloseable { + private static final Logger LOGGER = LogManager.getLogger(); + private final ProcessorMailbox taskMailbox; +@@ -159,13 +170,166 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl + private volatile int taskPerBatch = 5; + private final AtomicBoolean scheduled = new AtomicBoolean(); + ++ // Paper start - replace light engine impl ++ protected final ca.spottedleaf.starlight.light.StarLightInterface theLightEngine; ++ public final boolean hasBlockLight; ++ public final boolean hasSkyLight; ++ // Paper end - replace light engine impl ++ + public ThreadedLevelLightEngine(LightChunkGetter chunkProvider, ChunkMap chunkStorage, boolean hasBlockLight, ProcessorMailbox processor, ProcessorHandle> executor) { +- super(chunkProvider, true, hasBlockLight); ++ super(chunkProvider, false, false); // Paper - destroy vanilla light engine state + this.chunkMap = chunkStorage; this.playerChunkMap = chunkMap; // Paper + this.sorterMailbox = executor; + this.taskMailbox = processor; ++ // Paper start - replace light engine impl ++ this.hasBlockLight = true; ++ this.hasSkyLight = hasBlockLight; // Nice variable name. ++ this.theLightEngine = new ca.spottedleaf.starlight.light.StarLightInterface(chunkProvider, this.hasSkyLight, this.hasBlockLight, this); ++ // Paper end - replace light engine impl ++ } ++ ++ // Paper start - replace light engine impl ++ protected final ChunkAccess getChunk(final int chunkX, final int chunkZ) { ++ return ((ServerLevel)this.theLightEngine.getWorld()).getChunkSource().getChunkAtImmediately(chunkX, chunkZ); ++ } ++ ++ protected long relightCounter; ++ ++ public int relight(java.util.Set chunks_param, ++ java.util.function.Consumer chunkLightCallback, ++ java.util.function.IntConsumer onComplete) { ++ if (!org.bukkit.Bukkit.isPrimaryThread()) { ++ throw new IllegalStateException("Must only be called on the main thread"); ++ } ++ ++ java.util.Set chunks = new java.util.LinkedHashSet<>(chunks_param); ++ // add tickets ++ java.util.Map ticketIds = new java.util.HashMap<>(); ++ int totalChunks = 0; ++ for (java.util.Iterator iterator = chunks.iterator(); iterator.hasNext();) { ++ final ChunkPos chunkPos = iterator.next(); ++ ++ final ChunkAccess chunk = ((ServerLevel)this.theLightEngine.getWorld()).getChunkSource().getChunkAtImmediately(chunkPos.x, chunkPos.z); ++ if (chunk == null || !chunk.isLightCorrect() || !chunk.getStatus().isOrAfter(ChunkStatus.LIGHT)) { ++ // cannot relight this chunk ++ iterator.remove(); ++ continue; ++ } ++ ++ final Long id = Long.valueOf(this.relightCounter++); ++ ++ ((ServerLevel)this.theLightEngine.getWorld()).getChunkSource().addTicketAtLevel(TicketType.CHUNK_RELIGHT, chunkPos, net.minecraft.server.MCUtil.getTicketLevelFor(ChunkStatus.LIGHT), id); ++ ticketIds.put(chunkPos, id); ++ ++ ++totalChunks; ++ } ++ ++ this.taskMailbox.tell(() -> { ++ this.theLightEngine.relightChunks(chunks, (ChunkPos chunkPos) -> { ++ chunkLightCallback.accept(chunkPos); ++ ((java.util.concurrent.Executor)((ServerLevel)this.theLightEngine.getWorld()).getChunkSource().mainThreadProcessor).execute(() -> { ++ ((ServerLevel)this.theLightEngine.getWorld()).getChunkSource().chunkMap.getUpdatingChunkIfPresent(chunkPos.toLong()).broadcast(new net.minecraft.network.protocol.game.ClientboundLightUpdatePacket(chunkPos, ThreadedLevelLightEngine.this, null, null, true), false); ++ ((ServerLevel)this.theLightEngine.getWorld()).getChunkSource().removeTicketAtLevel(TicketType.CHUNK_RELIGHT, chunkPos, net.minecraft.server.MCUtil.getTicketLevelFor(ChunkStatus.LIGHT), ticketIds.get(chunkPos)); ++ }); ++ }, onComplete); ++ }); ++ this.tryScheduleUpdate(); ++ ++ return totalChunks; ++ } ++ ++ private final Long2IntOpenHashMap chunksBeingWorkedOn = new Long2IntOpenHashMap(); ++ ++ private void queueTaskForSection(final int chunkX, final int chunkY, final int chunkZ, final Supplier> runnable) { ++ final ServerLevel world = (ServerLevel)this.theLightEngine.getWorld(); ++ ++ final ChunkAccess center = this.theLightEngine.getAnyChunkNow(chunkX, chunkZ); ++ if (center == null || !center.getStatus().isOrAfter(ChunkStatus.LIGHT)) { ++ // do not accept updates in unlit chunks, unless we might be generating a chunk. thanks to the amazing ++ // chunk scheduling, we could be lighting and generating a chunk at the same time ++ return; ++ } ++ ++ if (center.getStatus() != ChunkStatus.FULL) { ++ // do not keep chunk loaded, we are probably in a gen thread ++ // if we proceed to add a ticket the chunk will be loaded, which is not what we want (avoid cascading gen) ++ runnable.get(); ++ return; ++ } ++ ++ if (!world.getChunkSource().chunkMap.mainThreadExecutor.isSameThread()) { ++ // ticket logic is not safe to run off-main, re-schedule ++ world.getChunkSource().chunkMap.mainThreadExecutor.execute(() -> { ++ this.queueTaskForSection(chunkX, chunkY, chunkZ, runnable); ++ }); ++ return; ++ } ++ ++ final long key = CoordinateUtils.getChunkKey(chunkX, chunkZ); ++ ++ final CompletableFuture updateFuture = runnable.get(); ++ ++ if (updateFuture == null) { ++ // not scheduled ++ return; ++ } ++ ++ final int references = this.chunksBeingWorkedOn.addTo(key, 1); ++ if (references == 0) { ++ final ChunkPos pos = new ChunkPos(chunkX, chunkZ); ++ world.getChunkSource().addRegionTicket(ca.spottedleaf.starlight.light.StarLightInterface.CHUNK_WORK_TICKET, pos, 0, pos); ++ } ++ ++ // append future to this chunk and 1 radius neighbours chunk save futures ++ // this prevents us from saving the world without first waiting for the light engine ++ ++ for (int dx = -1; dx <= 1; ++dx) { ++ for (int dz = -1; dz <= 1; ++dz) { ++ ChunkHolder neighbour = world.getChunkSource().chunkMap.getUpdatingChunkIfPresent(CoordinateUtils.getChunkKey(dx + chunkX, dz + chunkZ)); ++ if (neighbour != null) { ++ neighbour.chunkToSave = neighbour.chunkToSave.thenCombine(updateFuture, (final ChunkAccess curr, final Void ignore) -> { ++ return curr; ++ }); ++ } ++ } ++ } ++ ++ updateFuture.thenAcceptAsync((final Void ignore) -> { ++ final int newReferences = this.chunksBeingWorkedOn.get(key); ++ if (newReferences == 1) { ++ this.chunksBeingWorkedOn.remove(key); ++ final ChunkPos pos = new ChunkPos(chunkX, chunkZ); ++ world.getChunkSource().removeRegionTicket(ca.spottedleaf.starlight.light.StarLightInterface.CHUNK_WORK_TICKET, pos, 0, pos); ++ } else { ++ this.chunksBeingWorkedOn.put(key, newReferences - 1); ++ } ++ }, world.getChunkSource().chunkMap.mainThreadExecutor).whenComplete((final Void ignore, final Throwable thr) -> { ++ if (thr != null) { ++ LOGGER.fatal("Failed to remove ticket level for post chunk task " + new ChunkPos(chunkX, chunkZ), thr); ++ } ++ }); ++ } ++ ++ @Override ++ public boolean hasLightWork() { ++ // route to new light engine ++ return this.theLightEngine.hasUpdates() || !this.queue.isEmpty(); + } + ++ @Override ++ public LayerLightEventListener getLayerListener(final LightLayer lightType) { ++ return lightType == LightLayer.BLOCK ? this.theLightEngine.getBlockReader() : this.theLightEngine.getSkyReader(); ++ } ++ ++ @Override ++ public int getRawBrightness(final BlockPos pos, final int ambientDarkness) { ++ // need to use new light hooks for this ++ final int sky = this.theLightEngine.getSkyReader().getLightValue(pos) - ambientDarkness; ++ final int block = this.theLightEngine.getBlockReader().getLightValue(pos); ++ return Math.max(sky, block); ++ } ++ // Paper end - replace light engine impl ++ + @Override + public void close() { + } +@@ -182,15 +346,16 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl + + @Override + public void checkBlock(BlockPos pos) { +- BlockPos blockPos = pos.immutable(); +- this.addTask(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()), ThreadedLevelLightEngine.TaskType.POST_UPDATE, Util.name(() -> { +- super.checkBlock(blockPos); +- }, () -> { +- return "checkBlock " + blockPos; +- })); ++ // Paper start - replace light engine impl ++ final BlockPos posCopy = pos.immutable(); ++ this.queueTaskForSection(posCopy.getX() >> 4, posCopy.getY() >> 4, posCopy.getZ() >> 4, () -> { ++ return this.theLightEngine.blockChange(posCopy); ++ }); ++ // Paper end - replace light engine impl + } + + protected void updateChunkStatus(ChunkPos pos) { ++ if (true) return; // Paper - replace light engine impl + this.addTask(pos.x, pos.z, () -> { + return 0; + }, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> { +@@ -213,17 +378,16 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl + + @Override + public void updateSectionStatus(SectionPos pos, boolean notReady) { +- this.addTask(pos.x(), pos.z(), () -> { +- return 0; +- }, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> { +- super.updateSectionStatus(pos, notReady); +- }, () -> { +- return "updateSectionStatus " + pos + " " + notReady; +- })); ++ // Paper start - replace light engine impl ++ this.queueTaskForSection(pos.getX(), pos.getY(), pos.getZ(), () -> { ++ return this.theLightEngine.sectionChange(pos, notReady); ++ }); ++ // Paper end - replace light engine impl + } + + @Override + public void enableLightSources(ChunkPos chunkPos, boolean bl) { ++ if (true) return; // Paper - replace light engine impl + this.addTask(chunkPos.x, chunkPos.z, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> { + super.enableLightSources(chunkPos, bl); + }, () -> { +@@ -233,6 +397,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl + + @Override + public void queueSectionData(LightLayer lightType, SectionPos pos, @Nullable DataLayer nibbles, boolean bl) { ++ if (true) return; // Paper - replace light engine impl + this.addTask(pos.x(), pos.z(), () -> { + return 0; + }, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> { +@@ -254,6 +419,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl + + @Override + public void retainData(ChunkPos pos, boolean retainData) { ++ if (true) return; // Paper - replace light engine impl + this.addTask(pos.x, pos.z, () -> { + return 0; + }, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> { +@@ -264,6 +430,37 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl + } + + public CompletableFuture lightChunk(ChunkAccess chunk, boolean excludeBlocks) { ++ // Paper start - replace light engine impl ++ if (true) { ++ boolean lit = excludeBlocks; ++ final ChunkPos chunkPos = chunk.getPos(); ++ ++ return CompletableFuture.supplyAsync(() -> { ++ final Boolean[] emptySections = StarLightEngine.getEmptySectionsForChunk(chunk); ++ if (!lit) { ++ chunk.setLightCorrect(false); ++ this.theLightEngine.lightChunk(chunk, emptySections); ++ chunk.setLightCorrect(true); ++ } else { ++ this.theLightEngine.forceLoadInChunk(chunk, emptySections); ++ // can't really force the chunk to be edged checked, as we need neighbouring chunks - but we don't have ++ // them, so if it's not loaded then i guess we can't do edge checks. later loads of the chunk should ++ // catch what we miss here. ++ this.theLightEngine.checkChunkEdges(chunkPos.x, chunkPos.z); ++ } ++ ++ this.chunkMap.releaseLightTicket(chunkPos); ++ return chunk; ++ }, (runnable) -> { ++ this.theLightEngine.scheduleChunkLight(chunkPos, runnable); ++ this.tryScheduleUpdate(); ++ }).whenComplete((final ChunkAccess c, final Throwable throwable) -> { ++ if (throwable != null) { ++ LOGGER.fatal("Failed to light chunk " + chunkPos, throwable); ++ } ++ }); ++ } ++ // Paper end - replace light engine impl + ChunkPos chunkPos = chunk.getPos(); + // Paper start + //ichunkaccess.b(false); // Don't need to disable this +@@ -306,7 +503,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl + } + + public void tryScheduleUpdate() { +- if ((!this.queue.isEmpty() || super.hasLightWork()) && this.scheduled.compareAndSet(false, true)) { // Paper ++ if (this.hasLightWork() && this.scheduled.compareAndSet(false, true)) { // Paper // Paper - rewrite light engine + this.taskMailbox.tell(() -> { + this.runUpdate(); + this.scheduled.set(false); +@@ -323,12 +520,12 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl + if (queue.poll(pre, post)) { + pre.forEach(Runnable::run); + pre.clear(); +- super.runUpdates(Integer.MAX_VALUE, true, true); ++ this.theLightEngine.propagateChanges(); // Paper - rewrite light engine + post.forEach(Runnable::run); + post.clear(); + } else { + // might have level updates to go still +- super.runUpdates(Integer.MAX_VALUE, true, true); ++ this.theLightEngine.propagateChanges(); // Paper - rewrite light engine + } + // Paper end + } +diff --git a/src/main/java/net/minecraft/server/level/TicketType.java b/src/main/java/net/minecraft/server/level/TicketType.java +index 41ddcf6775f99c56cf4b13b284420061e5dd6bdc..ae46429264e6a7e5c88b6b6a41a6df4db7b3e70d 100644 +--- a/src/main/java/net/minecraft/server/level/TicketType.java ++++ b/src/main/java/net/minecraft/server/level/TicketType.java +@@ -32,6 +32,7 @@ public class TicketType { + public static final TicketType PLUGIN_TICKET = TicketType.create("plugin_ticket", (plugin1, plugin2) -> plugin1.getClass().getName().compareTo(plugin2.getClass().getName())); // CraftBukkit + public static final TicketType DELAY_UNLOAD = create("delay_unload", Long::compareTo, 300); // Paper + public static final TicketType REQUIRED_LOAD = create("required_load", Long::compareTo); // Paper - make sure getChunkAt does not fail ++ public static final TicketType CHUNK_RELIGHT = create("light_update", Long::compareTo); // Paper - ensure chunks stay loaded for lighting + + public static TicketType create(String name, Comparator argumentComparator) { + return new TicketType<>(name, argumentComparator, 0L); +diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java +index b534fd9e5d2a17926282cf40c9d66a2143a37bfe..4a7fdea6a5f966db444dc41f7215faa99e3820b3 100644 +--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java ++++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java +@@ -643,6 +643,7 @@ public abstract class BlockBehaviour { + this.isViewBlocking = blockbase_info.isViewBlocking; + this.hasPostProcess = blockbase_info.hasPostProcess; + this.emissiveRendering = blockbase_info.emissiveRendering; ++ this.conditionallyFullOpaque = this.isOpaque() & this.isTransparentOnSomeFaces(); // Paper + } + // Paper start - impl cached craft block data, lazy load to fix issue with loading at the wrong time + private org.bukkit.craftbukkit.block.data.CraftBlockData cachedCraftBlockData; +@@ -663,6 +664,18 @@ public abstract class BlockBehaviour { + protected boolean isTicking; + protected FluidState fluid; + // Paper end ++ // Paper start ++ protected int opacityIfCached = -1; ++ // ret -1 if opacity is dynamic, or -1 if the block is conditionally full opaque, else return opacity in [0, 15] ++ public final int getOpacityIfCached() { ++ return this.opacityIfCached; ++ } ++ ++ protected final boolean conditionallyFullOpaque; ++ public final boolean isConditionallyFullOpaque() { ++ return this.conditionallyFullOpaque; ++ } ++ // Paper end + + public void initCache() { + this.fluid = this.getBlock().getFluidState(this.asState()); // Paper - moved from getFluid() +@@ -671,6 +684,7 @@ public abstract class BlockBehaviour { + this.cache = new BlockBehaviour.BlockStateBase.Cache(this.asState()); + } + this.shapeExceedsCube = this.cache == null || this.cache.largeCollisionShape; // Paper - moved from actual method to here ++ this.opacityIfCached = this.cache == null || this.isConditionallyFullOpaque() ? -1 : this.cache.lightBlock; // Paper - cache opacity for light + + } + +diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java +index 8393950a0b38ec7897d7643803d5accdb1f983f3..ae2050da03ea2ed82b5b0dadbe4e9d37162e8fdb 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java ++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java +@@ -41,6 +41,36 @@ public interface ChunkAccess extends BlockGetter, FeatureAccess { + net.minecraft.world.level.Level getLevel(); + // Paper end + ++ // Paper start ++ default ca.spottedleaf.starlight.light.SWMRNibbleArray[] getBlockNibbles() { ++ throw new UnsupportedOperationException(this.getClass().getName()); ++ } ++ default void setBlockNibbles(ca.spottedleaf.starlight.light.SWMRNibbleArray[] nibbles) { ++ throw new UnsupportedOperationException(this.getClass().getName()); ++ } ++ ++ default ca.spottedleaf.starlight.light.SWMRNibbleArray[] getSkyNibbles() { ++ throw new UnsupportedOperationException(this.getClass().getName()); ++ } ++ default void setSkyNibbles(ca.spottedleaf.starlight.light.SWMRNibbleArray[] nibbles) { ++ throw new UnsupportedOperationException(this.getClass().getName()); ++ } ++ public default boolean[] getSkyEmptinessMap() { ++ throw new UnsupportedOperationException(this.getClass().getName()); ++ } ++ public default void setSkyEmptinessMap(final boolean[] emptinessMap) { ++ throw new UnsupportedOperationException(this.getClass().getName()); ++ } ++ ++ public default boolean[] getBlockEmptinessMap() { ++ throw new UnsupportedOperationException(this.getClass().getName()); ++ } ++ ++ public default void setBlockEmptinessMap(final boolean[] emptinessMap) { ++ throw new UnsupportedOperationException(this.getClass().getName()); ++ } ++ // Paper end ++ + BlockState getType(final int x, final int y, final int z); // Paper + @Nullable + BlockState setBlockState(BlockPos pos, BlockState state, boolean moved); +diff --git a/src/main/java/net/minecraft/world/level/chunk/DataLayer.java b/src/main/java/net/minecraft/world/level/chunk/DataLayer.java +index c561d69b4b903cd3625468b239cb1ace3e317700..08c1be9c9735132f383b9be9fda6f8af4a502107 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/DataLayer.java ++++ b/src/main/java/net/minecraft/world/level/chunk/DataLayer.java +@@ -12,7 +12,7 @@ public class DataLayer { + public static final int SIZE = 2048; + private static final int NIBBLE_SIZE = 4; + @Nullable +- protected byte[] data; ++ protected byte[] data; public final byte[] getDataRaw() { return this.data; } // Paper - provide accessor + // Paper start + public static byte[] EMPTY_NIBBLE = new byte[2048]; + private static final int nibbleBucketSizeMultiplier = Integer.getInteger("Paper.nibbleBucketSize", 3072); +@@ -55,6 +55,7 @@ public class DataLayer { + boolean poolSafe = false; + public java.lang.Runnable cleaner; + private void registerCleaner() { ++ if (true) return; // Paper - purge cleaner usage + if (!poolSafe) { + cleaner = net.minecraft.server.MCUtil.registerCleaner(this, this.data, DataLayer::releaseBytes); + } else { +@@ -69,7 +70,7 @@ public class DataLayer { + } + public DataLayer(byte[] bytes, boolean isSafe) { + this.data = bytes; +- if (!isSafe) this.data = getCloneIfSet(); // Paper - clone for safety ++ // Paper - purge cleaner usage + registerCleaner(); + // Paper end + if (bytes.length != 2048) { +@@ -155,7 +156,7 @@ public class DataLayer { + } + // Paper end + public DataLayer copy() { +- return this.data == null ? new DataLayer() : new DataLayer(this.data); // Paper - clone in ctor ++ return this.data == null ? new DataLayer() : new DataLayer(this.data.clone()); // Paper - clone in ctor // Paper - no longer clone in constructor + } + + public String toString() { +diff --git a/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java b/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java +index 8245c5834ec69beb8e3b95fb3900601009a9273f..5f6b611dc99cc04cd553b9e01dba19ec6d8b250e 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java ++++ b/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java +@@ -1,5 +1,6 @@ + package net.minecraft.world.level.chunk; + ++import ca.spottedleaf.starlight.light.SWMRNibbleArray; + import it.unimi.dsi.fastutil.longs.LongSet; + import java.util.BitSet; + import java.util.Map; +@@ -29,6 +30,48 @@ public class ImposterProtoChunk extends ProtoChunk { + this.wrapped = wrapped; + } + ++ // Paper start - rewrite light engine ++ @Override ++ public SWMRNibbleArray[] getBlockNibbles() { ++ return this.getWrapped().getBlockNibbles(); ++ } ++ ++ @Override ++ public void setBlockNibbles(SWMRNibbleArray[] nibbles) { ++ this.getWrapped().setBlockNibbles(nibbles); ++ } ++ ++ @Override ++ public SWMRNibbleArray[] getSkyNibbles() { ++ return this.getWrapped().getSkyNibbles(); ++ } ++ ++ @Override ++ public void setSkyNibbles(SWMRNibbleArray[] nibbles) { ++ this.getWrapped().setSkyNibbles(nibbles); ++ } ++ ++ @Override ++ public boolean[] getSkyEmptinessMap() { ++ return this.getWrapped().getSkyEmptinessMap(); ++ } ++ ++ @Override ++ public void setSkyEmptinessMap(boolean[] emptinessMap) { ++ this.getWrapped().setSkyEmptinessMap(emptinessMap); ++ } ++ ++ @Override ++ public boolean[] getBlockEmptinessMap() { ++ return this.getWrapped().getBlockEmptinessMap(); ++ } ++ ++ @Override ++ public void setBlockEmptinessMap(boolean[] emptinessMap) { ++ this.getWrapped().setBlockEmptinessMap(emptinessMap); ++ } ++ // Paper end - rewrite light engine ++ + @Nullable + @Override + public BlockEntity getBlockEntity(BlockPos pos) { +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 d580f0375cce5e995c024f1b0cd4843b5718121c..f0c43f9f636a5dd1f0dfbae604dfa1f4ff9ebd4e 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +@@ -1,5 +1,7 @@ + package net.minecraft.world.level.chunk; + ++import ca.spottedleaf.starlight.light.SWMRNibbleArray; ++import ca.spottedleaf.starlight.light.StarLightEngine; + import com.google.common.collect.ImmutableList; + import com.destroystokyo.paper.exception.ServerInternalException; + import com.google.common.collect.Maps; +@@ -17,7 +19,6 @@ import java.util.Collections; + import java.util.Iterator; + import java.util.Map; + import java.util.Map.Entry; +-import java.util.Objects; + import java.util.Set; + import java.util.function.Consumer; + import java.util.function.Supplier; +@@ -28,7 +29,6 @@ import net.minecraft.CrashReport; + import net.minecraft.CrashReportCategory; + import net.minecraft.ReportedException; + import net.minecraft.core.BlockPos; +-import net.minecraft.core.DefaultedRegistry; + import net.minecraft.core.Registry; + import net.minecraft.core.SectionPos; + import net.minecraft.nbt.CompoundTag; +@@ -125,11 +125,62 @@ public class LevelChunk implements ChunkAccess { + private volatile boolean isLightCorrect; + private final Int2ObjectMap gameEventDispatcherSections; + ++ // Paper start - rewrite light engine ++ protected volatile SWMRNibbleArray[] blockNibbles; ++ protected volatile SWMRNibbleArray[] skyNibbles; ++ protected volatile boolean[] skyEmptinessMap; ++ protected volatile boolean[] blockEmptinessMap; ++ ++ @Override ++ public SWMRNibbleArray[] getBlockNibbles() { ++ return this.blockNibbles; ++ } ++ ++ @Override ++ public void setBlockNibbles(SWMRNibbleArray[] nibbles) { ++ this.blockNibbles = nibbles; ++ } ++ ++ @Override ++ public SWMRNibbleArray[] getSkyNibbles() { ++ return this.skyNibbles; ++ } ++ ++ @Override ++ public void setSkyNibbles(SWMRNibbleArray[] nibbles) { ++ this.skyNibbles = nibbles; ++ } ++ ++ @Override ++ public boolean[] getSkyEmptinessMap() { ++ return this.skyEmptinessMap; ++ } ++ ++ @Override ++ public void setSkyEmptinessMap(boolean[] emptinessMap) { ++ this.skyEmptinessMap = emptinessMap; ++ } ++ ++ @Override ++ public boolean[] getBlockEmptinessMap() { ++ return this.blockEmptinessMap; ++ } ++ ++ @Override ++ public void setBlockEmptinessMap(boolean[] emptinessMap) { ++ this.blockEmptinessMap = emptinessMap; ++ } ++ // Paper end - rewrite light engine ++ + public LevelChunk(Level world, ChunkPos pos, ChunkBiomeContainer biomes) { + this(world, pos, biomes, UpgradeData.EMPTY, EmptyTickList.empty(), EmptyTickList.empty(), 0L, (LevelChunkSection[]) null, (Consumer) null); + } + + public LevelChunk(Level world, ChunkPos pos, ChunkBiomeContainer biomes, UpgradeData upgradeData, TickList blockTickScheduler, TickList fluidTickScheduler, long inhabitedTime, @Nullable LevelChunkSection[] sections, @Nullable Consumer loadToWorldConsumer) { ++ // Paper start ++ this.blockNibbles = StarLightEngine.getFilledEmptyLight(world); ++ this.skyNibbles = StarLightEngine.getFilledEmptyLight(world); ++ // Paper end + this.pendingBlockEntities = Maps.newHashMap(); + this.tickersInLevel = Maps.newHashMap(); + this.heightmaps = Maps.newEnumMap(Heightmap.Types.class); +@@ -333,6 +384,12 @@ public class LevelChunk implements ChunkAccess { + + 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); ++ // Paper start - copy over protochunk light ++ this.setBlockNibbles(protoChunk.getBlockNibbles()); ++ this.setSkyNibbles(protoChunk.getSkyNibbles()); ++ this.setSkyEmptinessMap(protoChunk.getSkyEmptinessMap()); ++ this.setBlockEmptinessMap(protoChunk.getBlockEmptinessMap()); ++ // Paper end - copy over protochunk light + Iterator iterator = protoChunk.getBlockEntities().values().iterator(); + + while (iterator.hasNext()) { +diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java +index cdac1f7b30e4c043dcb12ac9e29af926df8170bd..c1c95ac9deb134a0cf5c7763090ac5f3cddf24cc 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java ++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java +@@ -18,7 +18,7 @@ public class LevelChunkSection { + short nonEmptyBlockCount; // Paper - package-private + private short tickingBlockCount; + private short tickingFluidCount; +- final PalettedContainer states; // Paper - package-private ++ public final PalettedContainer states; // Paper - package-private // Paper - public + + // Paper start - Anti-Xray - Add parameters + @Deprecated public LevelChunkSection(int yOffset) { this(yOffset, null, null, true); } // Notice for updates: Please make sure this constructor isn't used anywhere +diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java +index 554474d4b2e57d8a005b3c3b9b23f32a62243058..79fd7a6e8a6eb1f699d03801910d97066677311c 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java ++++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java +@@ -174,7 +174,7 @@ public class PalettedContainer implements PaletteResize { + return this.get(y << 8 | z << 4 | x); // Paper - inline + } + +- protected T get(int index) { ++ public T get(int index) { // Paper - public + T object = this.palette.valueFor(this.storage.get(index)); + return (T)(object == null ? this.defaultValue : object); + } +diff --git a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java +index 7dc3d806a680150c6a2fffa1436fd63bbdc31eb3..e1b32b644bc976ff66258ed706f4d1e8de99420d 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java ++++ b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java +@@ -1,5 +1,7 @@ + package net.minecraft.world.level.chunk; + ++import ca.spottedleaf.starlight.light.SWMRNibbleArray; ++import ca.spottedleaf.starlight.light.StarLightEngine; + import com.google.common.collect.Lists; + import com.google.common.collect.Maps; + import com.google.common.collect.Sets; +@@ -72,6 +74,53 @@ public class ProtoChunk implements ChunkAccess { + // Paper end + private static boolean PRINTED_OUTDATED_CTOR_MSG = false; // Paper - Add level + ++ // Paper start - rewrite light engine ++ protected volatile SWMRNibbleArray[] blockNibbles; ++ protected volatile SWMRNibbleArray[] skyNibbles; ++ protected volatile boolean[] skyEmptinessMap; ++ protected volatile boolean[] blockEmptinessMap; ++ ++ @Override ++ public SWMRNibbleArray[] getBlockNibbles() { ++ return this.blockNibbles; ++ } ++ ++ @Override ++ public void setBlockNibbles(SWMRNibbleArray[] nibbles) { ++ this.blockNibbles = nibbles; ++ } ++ ++ @Override ++ public SWMRNibbleArray[] getSkyNibbles() { ++ return this.skyNibbles; ++ } ++ ++ @Override ++ public void setSkyNibbles(SWMRNibbleArray[] nibbles) { ++ this.skyNibbles = nibbles; ++ } ++ ++ @Override ++ public boolean[] getSkyEmptinessMap() { ++ return this.skyEmptinessMap; ++ } ++ ++ @Override ++ public void setSkyEmptinessMap(boolean[] emptinessMap) { ++ this.skyEmptinessMap = emptinessMap; ++ } ++ ++ @Override ++ public boolean[] getBlockEmptinessMap() { ++ return this.blockEmptinessMap; ++ } ++ ++ @Override ++ public void setBlockEmptinessMap(boolean[] emptinessMap) { ++ this.blockEmptinessMap = emptinessMap; ++ } ++ // Paper end - rewrite light engine ++ + @Deprecated // Paper start - add level + public ProtoChunk(ChunkPos pos, UpgradeData upgradeData, LevelHeightAccessor world) { + // Paper start +@@ -100,6 +149,10 @@ public class ProtoChunk implements ChunkAccess { + } + } + public ProtoChunk(ChunkPos pos, UpgradeData upgradeData, @Nullable LevelChunkSection[] levelChunkSections, ProtoTickList blockTickScheduler, ProtoTickList fluidTickScheduler, LevelHeightAccessor world, net.minecraft.server.level.ServerLevel level) { ++ // Paper start ++ this.blockNibbles = StarLightEngine.getFilledEmptyLight(world); ++ this.skyNibbles = StarLightEngine.getFilledEmptyLight(world); ++ // Paper end + this.level = level; + // Paper end + this.chunkPos = pos; +@@ -197,7 +250,7 @@ public class ProtoChunk implements ChunkAccess { + + LevelChunkSection levelChunkSection = this.getOrCreateSection(l); + BlockState blockState = levelChunkSection.setBlockState(i & 15, j & 15, k & 15, state); +- if (this.status.isOrAfter(ChunkStatus.FEATURES) && state != blockState && (state.getLightBlock(this, pos) != blockState.getLightBlock(this, pos) || state.getLightEmission() != blockState.getLightEmission() || state.useShapeForLightOcclusion() || blockState.useShapeForLightOcclusion())) { ++ if (this.status.isOrAfter(ChunkStatus.LIGHT) && state != blockState && (state.getLightBlock(this, pos) != blockState.getLightBlock(this, pos) || state.getLightEmission() != blockState.getLightEmission() || state.useShapeForLightOcclusion() || blockState.useShapeForLightOcclusion())) { // Paper - move block updates to only happen after lighting occurs (or during, thanks chunk system) + this.lightEngine.checkBlock(pos); + } + +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 80b9f3547bc30cb470d272132e96fcce188efd91..c81392f5b4a6dcef9c1864c1b2c268914b904561 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 +@@ -63,6 +63,14 @@ import org.apache.logging.log4j.Logger; + + public class ChunkSerializer { + ++ // Paper start - replace light engine impl ++ private static final int STARLIGHT_LIGHT_VERSION = 5; ++ ++ private static final String BLOCKLIGHT_STATE_TAG = "starlight.blocklight_state"; ++ private static final String SKYLIGHT_STATE_TAG = "starlight.skylight_state"; ++ private static final String STARLIGHT_VERSION_TAG = "starlight.light_version"; ++ // Paper end - replace light engine impl ++ + private static final Logger LOGGER = LogManager.getLogger(); + public static final String TAG_UPGRADE_DATA = "UpgradeData"; + +@@ -131,13 +139,20 @@ public class ChunkSerializer { + ProtoTickList protochunkticklist1 = new ProtoTickList<>((fluidtype) -> { + return fluidtype == null || fluidtype == Fluids.EMPTY; + }, pos, nbttagcompound1.getList("LiquidsToBeTicked", 9), world); +- boolean flag = nbttagcompound1.getBoolean("isLightOn"); ++ boolean flag = getStatus(nbt).isOrAfter(ChunkStatus.LIGHT) && nbttagcompound1.get("isLightOn") != null && nbttagcompound1.getInt(STARLIGHT_VERSION_TAG) == STARLIGHT_LIGHT_VERSION; // Paper + ListTag nbttaglist = nbttagcompound1.getList("Sections", 10); + int i = world.getSectionsCount(); + LevelChunkSection[] achunksection = new LevelChunkSection[i]; + boolean flag1 = world.dimensionType().hasSkyLight(); + ServerChunkCache chunkproviderserver = world.getChunkSource(); + LevelLightEngine lightengine = chunkproviderserver.getLightEngine(); ++ // Paper start ++ ca.spottedleaf.starlight.light.SWMRNibbleArray[] blockNibbles = ca.spottedleaf.starlight.light.StarLightEngine.getFilledEmptyLight(world); // Paper - replace light impl ++ ca.spottedleaf.starlight.light.SWMRNibbleArray[] skyNibbles = ca.spottedleaf.starlight.light.StarLightEngine.getFilledEmptyLight(world); // Paper - replace light impl ++ final int minSection = io.papermc.paper.util.WorldUtil.getMinLightSection(world); ++ final int maxSection = io.papermc.paper.util.WorldUtil.getMaxLightSection(world); ++ boolean canReadSky = world.dimensionType().hasSkyLight(); ++ // Paper end + + if (flag) { + tasksToExecuteOnMain.add(() -> { // Paper - delay this task since we're executing off-main +@@ -146,7 +161,7 @@ public class ChunkSerializer { + } + + for (int j = 0; j < nbttaglist.size(); ++j) { +- CompoundTag nbttagcompound2 = nbttaglist.getCompound(j); ++ CompoundTag nbttagcompound2 = nbttaglist.getCompound(j); CompoundTag sectionData = nbttagcompound2; // Paper + byte b0 = nbttagcompound2.getByte("Y"); + + if (nbttagcompound2.contains("Palette", 9) && nbttagcompound2.contains("BlockStates", 12)) { +@@ -164,23 +179,29 @@ public class ChunkSerializer { + } + + if (flag) { +- if (nbttagcompound2.contains("BlockLight", 7)) { +- // Paper start - delay this task since we're executing off-main +- DataLayer blockLight = new DataLayer(nbttagcompound2.getByteArray("BlockLight")); +- tasksToExecuteOnMain.add(() -> { +- lightengine.queueSectionData(LightLayer.BLOCK, SectionPos.of(chunkcoordintpair1, b0), blockLight, true); +- }); +- // Paper end - delay this task since we're executing off-main ++ // Paper start - rewrite light engine ++ int y = sectionData.getByte("Y"); ++ ++ if (sectionData.contains("BlockLight", 7)) { ++ // this is where our diff is ++ blockNibbles[y - minSection] = new ca.spottedleaf.starlight.light.SWMRNibbleArray(sectionData.getByteArray("BlockLight").clone(), sectionData.getInt(BLOCKLIGHT_STATE_TAG)); // clone for data safety ++ } else { ++ blockNibbles[y - minSection] = new ca.spottedleaf.starlight.light.SWMRNibbleArray(null, sectionData.getInt(BLOCKLIGHT_STATE_TAG)); + } + +- if (flag1 && nbttagcompound2.contains("SkyLight", 7)) { +- // Paper start - delay this task since we're executing off-main +- DataLayer skyLight = new DataLayer(nbttagcompound2.getByteArray("SkyLight")); +- tasksToExecuteOnMain.add(() -> { +- lightengine.queueSectionData(LightLayer.SKY, SectionPos.of(chunkcoordintpair1, b0), skyLight, true); +- }); +- // Paper end - delay this task since we're executing off-main ++ if (canReadSky) { ++ if (sectionData.contains("SkyLight", 7)) { ++ // we store under the same key so mod programs editing nbt ++ // can still read the data, hopefully. ++ // however, for compatibility we store chunks as unlit so vanilla ++ // is forced to re-light them if it encounters our data. It's too much of a burden ++ // to try and maintain compatibility with a broken and inferior skylight management system. ++ skyNibbles[y - minSection] = new ca.spottedleaf.starlight.light.SWMRNibbleArray(sectionData.getByteArray("SkyLight").clone(), sectionData.getInt(SKYLIGHT_STATE_TAG)); // clone for data safety ++ } else { ++ skyNibbles[y - minSection] = new ca.spottedleaf.starlight.light.SWMRNibbleArray(null, sectionData.getInt(SKYLIGHT_STATE_TAG)); ++ } + } ++ // Paper end - rewrite light engine + } + } + +@@ -224,8 +245,12 @@ public class ChunkSerializer { + object = new LevelChunk(world.getLevel(), pos, biomestorage, chunkconverter, (TickList) object1, (TickList) object2, k, achunksection, // Paper start - fix massive nbt memory leak due to lambda. move lambda into a container method to not leak scope. Only clone needed NBT keys. + createLoadEntitiesConsumer(new SafeNBTCopy(nbttagcompound1, "TileEntities", "Entities", "ChunkBukkitValues")) // Paper - move CB Chunk PDC into here + );// Paper end ++ ((LevelChunk)object).setBlockNibbles(blockNibbles); // Paper - replace light impl ++ ((LevelChunk)object).setSkyNibbles(skyNibbles); // Paper - replace light impl + } else { + ProtoChunk protochunk = new ProtoChunk(pos, chunkconverter, achunksection, protochunkticklist, protochunkticklist1, world, world); // Paper - add level ++ protochunk.setBlockNibbles(blockNibbles); // Paper - replace light impl ++ protochunk.setSkyNibbles(skyNibbles); // Paper - replace light impl + + protochunk.setBiomes(biomestorage); + object = protochunk; +@@ -406,7 +431,7 @@ public class ChunkSerializer { + DataLayer[] blockLight = new DataLayer[lightenginethreaded.getMaxLightSection() - lightenginethreaded.getMinLightSection()]; + DataLayer[] skyLight = new DataLayer[lightenginethreaded.getMaxLightSection() - lightenginethreaded.getMinLightSection()]; + +- for (int i = lightenginethreaded.getMinLightSection(); i < lightenginethreaded.getMaxLightSection(); ++i) { ++ for (int i = lightenginethreaded.getMinLightSection(); false && i < lightenginethreaded.getMaxLightSection(); ++i) { // Paper - don't run loop, we don't need to - light data is per chunk now + DataLayer blockArray = lightenginethreaded.getLayerListener(LightLayer.BLOCK).getDataLayerData(SectionPos.of(chunkPos, i)); + DataLayer skyArray = lightenginethreaded.getLayerListener(LightLayer.SKY).getDataLayerData(SectionPos.of(chunkPos, i)); + +@@ -455,6 +480,12 @@ public class ChunkSerializer { + return saveChunk(world, chunk, null); + } + public static CompoundTag saveChunk(ServerLevel world, ChunkAccess chunk, AsyncSaveData asyncsavedata) { ++ // Paper start - rewrite light impl ++ final int minSection = io.papermc.paper.util.WorldUtil.getMinLightSection(world); ++ final int maxSection = io.papermc.paper.util.WorldUtil.getMaxLightSection(world); ++ ca.spottedleaf.starlight.light.SWMRNibbleArray[] blockNibbles = chunk.getBlockNibbles(); ++ ca.spottedleaf.starlight.light.SWMRNibbleArray[] skyNibbles = chunk.getSkyNibbles(); ++ // Paper end - rewrite light impl + // Paper end + ChunkPos chunkcoordintpair = chunk.getPos(); + CompoundTag nbttagcompound = new CompoundTag(); +@@ -483,32 +514,33 @@ public class ChunkSerializer { + LevelChunkSection chunksection = (LevelChunkSection) Arrays.stream(achunksection).filter((chunksection1) -> { + return chunksection1 != null && SectionPos.blockToSectionCoord(chunksection1.bottomBlockY()) == finalI; // CraftBukkit - decompile errors + }).findFirst().orElse(LevelChunk.EMPTY_SECTION); +- // Paper start - async chunk save for unload +- DataLayer nibblearray; // block light +- DataLayer nibblearray1; // sky light +- if (asyncsavedata == null) { +- nibblearray = lightenginethreaded.getLayerListener(LightLayer.BLOCK).getDataLayerData(SectionPos.of(chunkcoordintpair, i)); /// Paper - diff on method change (see getAsyncSaveData) +- nibblearray1 = lightenginethreaded.getLayerListener(LightLayer.SKY).getDataLayerData(SectionPos.of(chunkcoordintpair, i)); // Paper - diff on method change (see getAsyncSaveData) +- } else { +- nibblearray = asyncsavedata.blockLight[i - lightenginethreaded.getMinLightSection()]; +- nibblearray1 = asyncsavedata.skyLight[i - lightenginethreaded.getMinLightSection()]; +- } +- // Paper end +- if (chunksection != LevelChunk.EMPTY_SECTION || nibblearray != null || nibblearray1 != null) { +- CompoundTag nbttagcompound2 = new CompoundTag(); ++ // Paper start - replace light engine ++ ca.spottedleaf.starlight.light.SWMRNibbleArray.SaveState blockNibble = blockNibbles[i - minSection].getSaveState(); ++ ca.spottedleaf.starlight.light.SWMRNibbleArray.SaveState skyNibble = skyNibbles[i - minSection].getSaveState(); ++ if (chunksection != LevelChunk.EMPTY_SECTION || blockNibble != null || skyNibble != null) { ++ // Paper end - replace light engine ++ CompoundTag nbttagcompound2 = new CompoundTag(); CompoundTag section = nbttagcompound2; // Paper + + nbttagcompound2.putByte("Y", (byte) (i & 255)); + if (chunksection != LevelChunk.EMPTY_SECTION) { + chunksection.getStates().write(nbttagcompound2, "Palette", "BlockStates"); + } + +- if (nibblearray != null && !nibblearray.isEmpty()) { +- nbttagcompound2.putByteArray("BlockLight", nibblearray.asBytesPoolSafe().clone()); // Paper ++ // Paper start - replace light engine ++ if (blockNibble != null) { ++ if (blockNibble.data != null) { ++ section.putByteArray("BlockLight", blockNibble.data); ++ } ++ section.putInt(BLOCKLIGHT_STATE_TAG, blockNibble.state); + } + +- if (nibblearray1 != null && !nibblearray1.isEmpty()) { +- nbttagcompound2.putByteArray("SkyLight", nibblearray1.asBytesPoolSafe().clone()); // Paper ++ if (skyNibble != null) { ++ if (skyNibble.data != null) { ++ section.putByteArray("SkyLight", skyNibble.data); ++ } ++ section.putInt(SKYLIGHT_STATE_TAG, skyNibble.state); + } ++ // Paper end - replace light engine + + nbttaglist.add(nbttagcompound2); + } +@@ -516,7 +548,8 @@ public class ChunkSerializer { + + nbttagcompound1.put("Sections", nbttaglist); + if (flag) { +- nbttagcompound1.putBoolean("isLightOn", true); ++ nbttagcompound1.putInt(STARLIGHT_VERSION_TAG, STARLIGHT_LIGHT_VERSION); // Paper ++ nbttagcompound1.putBoolean("isLightOn", false); // Paper - set to false but still store, this allows us to detect --eraseCache (as eraseCache _removes_) + } + + ChunkBiomeContainer biomestorage = chunk.getBiomes(); diff --git a/patches/server/0765-Rewrite-entity-bounding-box-lookup-calls.patch b/patches/server/0765-Rewrite-entity-bounding-box-lookup-calls.patch new file mode 100644 index 0000000000..8b31dbdd18 --- /dev/null +++ b/patches/server/0765-Rewrite-entity-bounding-box-lookup-calls.patch @@ -0,0 +1,1320 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Thu, 17 Jun 2021 19:55:02 -0700 +Subject: [PATCH] Rewrite entity bounding box lookup calls + +For whatever reason, Mojang thought it was OK to make this system +scale logn relative to the number of entity sections loaded. +On top of that, they do a hashtable lookup per section - before +this was just a basic array access. + +This patch brings back entity slices for lookup only. + +diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java +new file mode 100644 +index 0000000000000000000000000000000000000000..47b5f75d9f27cf3ab947fd1f69cbd609fb9f2749 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java +@@ -0,0 +1,500 @@ ++package io.papermc.paper.world; ++ ++import com.destroystokyo.paper.util.maplist.EntityList; ++import it.unimi.dsi.fastutil.objects.Reference2ObjectMap; ++import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; ++import net.minecraft.server.level.ChunkHolder; ++import net.minecraft.server.level.ServerLevel; ++import net.minecraft.util.Mth; ++import net.minecraft.world.entity.Entity; ++import net.minecraft.world.entity.EntityType; ++import net.minecraft.world.entity.boss.EnderDragonPart; ++import net.minecraft.world.entity.boss.enderdragon.EnderDragon; ++import net.minecraft.world.phys.AABB; ++import java.util.Arrays; ++import java.util.Iterator; ++import java.util.List; ++import java.util.function.Predicate; ++ ++public final class ChunkEntitySlices { ++ ++ protected final int minSection; ++ protected final int maxSection; ++ protected final int chunkX; ++ protected final int chunkZ; ++ protected final ServerLevel world; ++ ++ protected final EntityCollectionBySection allEntities; ++ protected final EntityCollectionBySection hardCollidingEntities; ++ protected final Reference2ObjectOpenHashMap, EntityCollectionBySection> entitiesByClass; ++ protected final EntityList entities = new EntityList(); ++ ++ public ChunkHolder.FullChunkStatus status; ++ ++ // TODO implement container search optimisations ++ ++ public ChunkEntitySlices(final ServerLevel world, final int chunkX, final int chunkZ, final ChunkHolder.FullChunkStatus status, ++ final int minSection, final int maxSection) { // inclusive, inclusive ++ this.minSection = minSection; ++ this.maxSection = maxSection; ++ this.chunkX = chunkX; ++ this.chunkZ = chunkZ; ++ this.world = world; ++ ++ this.allEntities = new EntityCollectionBySection(this); ++ this.hardCollidingEntities = new EntityCollectionBySection(this); ++ this.entitiesByClass = new Reference2ObjectOpenHashMap<>(); ++ ++ this.status = status; ++ } ++ ++ // Paper start - optimise CraftChunk#getEntities ++ public org.bukkit.entity.Entity[] getChunkEntities() { ++ List ret = new java.util.ArrayList<>(); ++ final Entity[] entities = this.entities.getRawData(); ++ for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) { ++ final Entity entity = entities[i]; ++ if (entity == null) { ++ continue; ++ } ++ final org.bukkit.entity.Entity bukkit = entity.getBukkitEntity(); ++ if (bukkit != null && bukkit.isValid()) { ++ ret.add(bukkit); ++ } ++ } ++ ++ return ret.toArray(new org.bukkit.entity.Entity[0]); ++ } ++ // Paper end - optimise CraftChunk#getEntities ++ ++ public boolean isEmpty() { ++ return this.entities.size() == 0; ++ } ++ ++ private void updateTicketLevels() { ++ final Entity[] entities = this.entities.getRawData(); ++ for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) { ++ final Entity entity = entities[i]; ++ entity.chunkStatus = this.status; ++ } ++ } ++ ++ public synchronized void updateStatus(final ChunkHolder.FullChunkStatus status) { ++ this.status = status; ++ this.updateTicketLevels(); ++ } ++ ++ public synchronized void addEntity(final Entity entity, final int chunkSection) { ++ if (!this.entities.add(entity)) { ++ return; ++ } ++ entity.chunkStatus = this.status; ++ final int sectionIndex = chunkSection - this.minSection; ++ ++ this.allEntities.addEntity(entity, sectionIndex); ++ ++ if (entity.hardCollides()) { ++ this.hardCollidingEntities.addEntity(entity, sectionIndex); ++ } ++ ++ for (final Iterator, EntityCollectionBySection>> iterator = ++ this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) { ++ final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); ++ ++ if (entry.getKey().isInstance(entity)) { ++ entry.getValue().addEntity(entity, sectionIndex); ++ } ++ } ++ } ++ ++ public synchronized void removeEntity(final Entity entity, final int chunkSection) { ++ if (!this.entities.remove(entity)) { ++ return; ++ } ++ entity.chunkStatus = ChunkHolder.FullChunkStatus.INACCESSIBLE; ++ final int sectionIndex = chunkSection - this.minSection; ++ ++ this.allEntities.removeEntity(entity, sectionIndex); ++ ++ if (entity.hardCollides()) { ++ this.hardCollidingEntities.removeEntity(entity, sectionIndex); ++ } ++ ++ for (final Iterator, EntityCollectionBySection>> iterator = ++ this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) { ++ final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); ++ ++ if (entry.getKey().isInstance(entity)) { ++ entry.getValue().removeEntity(entity, sectionIndex); ++ } ++ } ++ } ++ ++ public void getHardCollidingEntities(final Entity except, final AABB box, final List into, final Predicate predicate) { ++ this.hardCollidingEntities.getEntities(except, box, into, predicate); ++ } ++ ++ public void getEntities(final Entity except, final AABB box, final List into, final Predicate predicate) { ++ this.allEntities.getEntitiesWithEnderDragonParts(except, box, into, predicate); ++ } ++ ++ public void getEntities(final EntityType type, final AABB box, final List into, ++ final Predicate predicate) { ++ this.allEntities.getEntities(type, box, (List)into, (Predicate)predicate); ++ } ++ ++ protected EntityCollectionBySection initClass(final Class clazz) { ++ final EntityCollectionBySection ret = new EntityCollectionBySection(this); ++ ++ for (int sectionIndex = 0; sectionIndex < this.allEntities.entitiesBySection.length; ++sectionIndex) { ++ final BasicEntityList sectionEntities = this.allEntities.entitiesBySection[sectionIndex]; ++ if (sectionEntities == null) { ++ continue; ++ } ++ ++ final Entity[] storage = sectionEntities.storage; ++ ++ for (int i = 0, len = Math.min(storage.length, sectionEntities.size()); i < len; ++i) { ++ final Entity entity = storage[i]; ++ ++ if (clazz.isInstance(entity)) { ++ ret.addEntity(entity, sectionIndex); ++ } ++ } ++ } ++ ++ return ret; ++ } ++ ++ public void getEntities(final Class clazz, final Entity except, final AABB box, final List into, ++ final Predicate predicate) { ++ EntityCollectionBySection collection = this.entitiesByClass.get(clazz); ++ if (collection != null) { ++ collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List)into, (Predicate)predicate); ++ } else { ++ synchronized (this) { ++ this.entitiesByClass.putIfAbsent(clazz, collection = this.initClass(clazz)); ++ } ++ collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List)into, (Predicate)predicate); ++ } ++ } ++ ++ public synchronized void updateEntity(final Entity entity) { ++ /*// TODO ++ if (prev aabb != entity.getBoundingBox()) { ++ this.entityMap.delete(entity, prev aabb); ++ this.entityMap.insert(entity, prev aabb = entity.getBoundingBox()); ++ }*/ ++ } ++ ++ protected static final class BasicEntityList { ++ ++ protected static final Entity[] EMPTY = new Entity[0]; ++ protected static final int DEFAULT_CAPACITY = 4; ++ ++ protected E[] storage; ++ protected int size; ++ ++ public BasicEntityList() { ++ this(0); ++ } ++ ++ public BasicEntityList(final int cap) { ++ this.storage = (E[])(cap <= 0 ? EMPTY : new Entity[cap]); ++ } ++ ++ public boolean isEmpty() { ++ return this.size == 0; ++ } ++ ++ public int size() { ++ return this.size; ++ } ++ ++ private void resize() { ++ if (this.storage == EMPTY) { ++ this.storage = (E[])new Entity[DEFAULT_CAPACITY]; ++ } else { ++ this.storage = Arrays.copyOf(this.storage, this.storage.length * 2); ++ } ++ } ++ ++ public void add(final E entity) { ++ final int idx = this.size++; ++ if (idx >= this.storage.length) { ++ this.resize(); ++ this.storage[idx] = entity; ++ } else { ++ this.storage[idx] = entity; ++ } ++ } ++ ++ public int indexOf(final E entity) { ++ final E[] storage = this.storage; ++ ++ for (int i = 0, len = Math.min(this.storage.length, this.size); i < len; ++i) { ++ if (storage[i] == entity) { ++ return i; ++ } ++ } ++ ++ return -1; ++ } ++ ++ public boolean remove(final E entity) { ++ final int idx = this.indexOf(entity); ++ if (idx == -1) { ++ return false; ++ } ++ ++ final int size = --this.size; ++ final E[] storage = this.storage; ++ if (idx != size) { ++ System.arraycopy(storage, idx + 1, storage, idx, size - idx); ++ } ++ ++ storage[size] = null; ++ ++ return true; ++ } ++ ++ public boolean has(final E entity) { ++ return this.indexOf(entity) != -1; ++ } ++ } ++ ++ protected static final class EntityCollectionBySection { ++ ++ protected final ChunkEntitySlices manager; ++ protected final long[] nonEmptyBitset; ++ protected final BasicEntityList[] entitiesBySection; ++ protected int count; ++ ++ public EntityCollectionBySection(final ChunkEntitySlices manager) { ++ this.manager = manager; ++ ++ final int sectionCount = manager.maxSection - manager.minSection + 1; ++ ++ this.nonEmptyBitset = new long[(sectionCount + (Long.SIZE - 1)) >>> 6]; // (sectionCount + (Long.SIZE - 1)) / Long.SIZE ++ this.entitiesBySection = new BasicEntityList[sectionCount]; ++ } ++ ++ public void addEntity(final Entity entity, final int sectionIndex) { ++ BasicEntityList list = this.entitiesBySection[sectionIndex]; ++ ++ if (list != null && list.has(entity)) { ++ return; ++ } ++ ++ if (list == null) { ++ this.entitiesBySection[sectionIndex] = list = new BasicEntityList<>(); ++ this.nonEmptyBitset[sectionIndex >>> 6] |= (1L << (sectionIndex & (Long.SIZE - 1))); ++ } ++ ++ list.add(entity); ++ ++this.count; ++ } ++ ++ public void removeEntity(final Entity entity, final int sectionIndex) { ++ final BasicEntityList list = this.entitiesBySection[sectionIndex]; ++ ++ if (list == null || !list.remove(entity)) { ++ return; ++ } ++ ++ --this.count; ++ ++ if (list.isEmpty()) { ++ this.entitiesBySection[sectionIndex] = null; ++ this.nonEmptyBitset[sectionIndex >>> 6] ^= (1L << (sectionIndex & (Long.SIZE - 1))); ++ } ++ } ++ ++ public void getEntities(final Entity except, final AABB box, final List into, final Predicate predicate) { ++ if (this.count == 0) { ++ return; ++ } ++ ++ final int minSection = this.manager.minSection; ++ final int maxSection = this.manager.maxSection; ++ ++ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); ++ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); ++ ++ // TODO use the bitset ++ ++ final BasicEntityList[] entitiesBySection = this.entitiesBySection; ++ ++ for (int section = min; section <= max; ++section) { ++ final BasicEntityList list = entitiesBySection[section - minSection]; ++ ++ if (list == null) { ++ continue; ++ } ++ ++ final Entity[] storage = list.storage; ++ ++ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { ++ final Entity entity = storage[i]; ++ ++ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { ++ continue; ++ } ++ ++ if (predicate != null && !predicate.test(entity)) { ++ continue; ++ } ++ ++ into.add(entity); ++ } ++ } ++ } ++ ++ public void getEntitiesWithEnderDragonParts(final Entity except, final AABB box, final List into, ++ final Predicate predicate) { ++ if (this.count == 0) { ++ return; ++ } ++ ++ final int minSection = this.manager.minSection; ++ final int maxSection = this.manager.maxSection; ++ ++ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); ++ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); ++ ++ // TODO use the bitset ++ ++ final BasicEntityList[] entitiesBySection = this.entitiesBySection; ++ ++ for (int section = min; section <= max; ++section) { ++ final BasicEntityList list = entitiesBySection[section - minSection]; ++ ++ if (list == null) { ++ continue; ++ } ++ ++ final Entity[] storage = list.storage; ++ ++ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { ++ final Entity entity = storage[i]; ++ ++ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { ++ continue; ++ } ++ ++ if (predicate == null || predicate.test(entity)) { ++ into.add(entity); ++ } // else: continue to test the ender dragon parts ++ ++ if (entity instanceof EnderDragon) { ++ for (final EnderDragonPart part : ((EnderDragon)entity).subEntities) { ++ if (part == except || !part.getBoundingBox().intersects(box)) { ++ continue; ++ } ++ ++ if (predicate != null && !predicate.test(part)) { ++ continue; ++ } ++ ++ into.add(part); ++ } ++ } ++ } ++ } ++ } ++ ++ public void getEntitiesWithEnderDragonParts(final Entity except, final Class clazz, final AABB box, final List into, ++ final Predicate predicate) { ++ if (this.count == 0) { ++ return; ++ } ++ ++ final int minSection = this.manager.minSection; ++ final int maxSection = this.manager.maxSection; ++ ++ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); ++ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); ++ ++ // TODO use the bitset ++ ++ final BasicEntityList[] entitiesBySection = this.entitiesBySection; ++ ++ for (int section = min; section <= max; ++section) { ++ final BasicEntityList list = entitiesBySection[section - minSection]; ++ ++ if (list == null) { ++ continue; ++ } ++ ++ final Entity[] storage = list.storage; ++ ++ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { ++ final Entity entity = storage[i]; ++ ++ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { ++ continue; ++ } ++ ++ if (predicate == null || predicate.test(entity)) { ++ into.add(entity); ++ } // else: continue to test the ender dragon parts ++ ++ if (entity instanceof EnderDragon) { ++ for (final EnderDragonPart part : ((EnderDragon)entity).subEntities) { ++ if (part == except || !part.getBoundingBox().intersects(box) || !clazz.isInstance(part)) { ++ continue; ++ } ++ ++ if (predicate != null && !predicate.test(part)) { ++ continue; ++ } ++ ++ into.add(part); ++ } ++ } ++ } ++ } ++ } ++ ++ public void getEntities(final EntityType type, final AABB box, final List into, ++ final Predicate predicate) { ++ if (this.count == 0) { ++ return; ++ } ++ ++ final int minSection = this.manager.minSection; ++ final int maxSection = this.manager.maxSection; ++ ++ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); ++ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); ++ ++ // TODO use the bitset ++ ++ final BasicEntityList[] entitiesBySection = this.entitiesBySection; ++ ++ for (int section = min; section <= max; ++section) { ++ final BasicEntityList list = entitiesBySection[section - minSection]; ++ ++ if (list == null) { ++ continue; ++ } ++ ++ final Entity[] storage = list.storage; ++ ++ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) { ++ final Entity entity = storage[i]; ++ ++ if (entity == null || (type != null && entity.getType() != type) || !entity.getBoundingBox().intersects(box)) { ++ continue; ++ } ++ ++ if (predicate != null && !predicate.test((T)entity)) { ++ continue; ++ } ++ ++ into.add((T)entity); ++ } ++ } ++ } ++ } ++} +diff --git a/src/main/java/io/papermc/paper/world/EntitySliceManager.java b/src/main/java/io/papermc/paper/world/EntitySliceManager.java +new file mode 100644 +index 0000000000000000000000000000000000000000..3ba094e640d7fe7803e2bbdab8ff3beb6f50e8a0 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/world/EntitySliceManager.java +@@ -0,0 +1,391 @@ ++package io.papermc.paper.world; ++ ++import io.papermc.paper.util.CoordinateUtils; ++import io.papermc.paper.util.WorldUtil; ++import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; ++import net.minecraft.core.BlockPos; ++import net.minecraft.server.level.ChunkHolder; ++import net.minecraft.server.level.ServerLevel; ++import net.minecraft.util.Mth; ++import net.minecraft.world.entity.Entity; ++import net.minecraft.world.entity.EntityType; ++import net.minecraft.world.phys.AABB; ++import java.util.List; ++import java.util.concurrent.locks.StampedLock; ++import java.util.function.Predicate; ++ ++public final class EntitySliceManager { ++ ++ protected static final int REGION_SHIFT = 5; ++ protected static final int REGION_MASK = (1 << REGION_SHIFT) - 1; ++ protected static final int REGION_SIZE = 1 << REGION_SHIFT; ++ ++ public final ServerLevel world; ++ ++ private final StampedLock stateLock = new StampedLock(); ++ protected final Long2ObjectOpenHashMap regions = new Long2ObjectOpenHashMap<>(64, 0.7f); ++ ++ private final int minSection; // inclusive ++ private final int maxSection; // inclusive ++ ++ protected final Long2ObjectOpenHashMap statusMap = new Long2ObjectOpenHashMap<>(); ++ { ++ this.statusMap.defaultReturnValue(ChunkHolder.FullChunkStatus.INACCESSIBLE); ++ } ++ ++ public EntitySliceManager(final ServerLevel world) { ++ this.world = world; ++ this.minSection = WorldUtil.getMinSection(world); ++ this.maxSection = WorldUtil.getMaxSection(world); ++ } ++ ++ public void chunkStatusChange(final int x, final int z, final ChunkHolder.FullChunkStatus newStatus) { ++ if (newStatus == ChunkHolder.FullChunkStatus.INACCESSIBLE) { ++ this.statusMap.remove(CoordinateUtils.getChunkKey(x, z)); ++ } else { ++ this.statusMap.put(CoordinateUtils.getChunkKey(x, z), newStatus); ++ final ChunkEntitySlices slices = this.getChunk(x, z); ++ if (slices != null) { ++ slices.updateStatus(newStatus); ++ } ++ } ++ } ++ ++ public synchronized void addEntity(final Entity entity) { ++ final BlockPos pos = entity.blockPosition(); ++ final int sectionX = pos.getX() >> 4; ++ final int sectionY = Mth.clamp(pos.getY() >> 4, this.minSection, this.maxSection); ++ final int sectionZ = pos.getZ() >> 4; ++ final ChunkEntitySlices slices = this.getOrCreateChunk(sectionX, sectionZ); ++ slices.addEntity(entity, sectionY); ++ ++ entity.sectionX = sectionX; ++ entity.sectionY = sectionY; ++ entity.sectionZ = sectionZ; ++ } ++ ++ public synchronized void removeEntity(final Entity entity) { ++ final ChunkEntitySlices slices = this.getChunk(entity.sectionX, entity.sectionZ); ++ slices.removeEntity(entity, entity.sectionY); ++ if (slices.isEmpty()) { ++ this.removeChunk(entity.sectionX, entity.sectionZ); ++ } ++ } ++ ++ public void moveEntity(final Entity entity) { ++ final BlockPos newPos = entity.blockPosition(); ++ final int newSectionX = newPos.getX() >> 4; ++ final int newSectionY = Mth.clamp(newPos.getY() >> 4, this.minSection, this.maxSection); ++ final int newSectionZ = newPos.getZ() >> 4; ++ ++ if (newSectionX == entity.sectionX && newSectionY == entity.sectionY && newSectionZ == entity.sectionZ) { ++ return; ++ } ++ ++ synchronized (this) { ++ // are we changing chunks? ++ if (newSectionX != entity.sectionX || newSectionZ != entity.sectionZ) { ++ final ChunkEntitySlices slices = this.getOrCreateChunk(newSectionX, newSectionZ); ++ final ChunkEntitySlices old = this.getChunk(entity.sectionX, entity.sectionZ); ++ synchronized (old) { ++ old.removeEntity(entity, entity.sectionY); ++ if (old.isEmpty()) { ++ this.removeChunk(entity.sectionX, entity.sectionZ); ++ } ++ } ++ ++ synchronized (slices) { ++ slices.addEntity(entity, newSectionY); ++ ++ entity.sectionX = newSectionX; ++ entity.sectionY = newSectionY; ++ entity.sectionZ = newSectionZ; ++ } ++ } else { ++ final ChunkEntitySlices slices = this.getChunk(newSectionX, newSectionZ); ++ // same chunk ++ synchronized (slices) { ++ slices.removeEntity(entity, entity.sectionY); ++ slices.addEntity(entity, newSectionY); ++ } ++ entity.sectionY = newSectionY; ++ } ++ } ++ ++ } ++ ++ public void getEntities(final Entity except, final AABB box, final List into, final Predicate predicate) { ++ final int minChunkX = (Mth.floor(box.minX) - 2) >> 4; ++ final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4; ++ final int maxChunkX = (Mth.floor(box.maxX) + 2) >> 4; ++ final int maxChunkZ = (Mth.floor(box.maxZ) + 2) >> 4; ++ ++ final int minRegionX = minChunkX >> REGION_SHIFT; ++ final int minRegionZ = minChunkZ >> REGION_SHIFT; ++ final int maxRegionX = maxChunkX >> REGION_SHIFT; ++ final int maxRegionZ = maxChunkZ >> REGION_SHIFT; ++ ++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { ++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; ++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; ++ ++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { ++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); ++ ++ if (region == null) { ++ continue; ++ } ++ ++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; ++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; ++ ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ for (int currX = minX; currX <= maxX; ++currX) { ++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); ++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { ++ continue; ++ } ++ ++ chunk.getEntities(except, box, into, predicate); ++ } ++ } ++ } ++ } ++ } ++ ++ public void getHardCollidingEntities(final Entity except, final AABB box, final List into, final Predicate predicate) { ++ final int minChunkX = (Mth.floor(box.minX) - 2) >> 4; ++ final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4; ++ final int maxChunkX = (Mth.floor(box.maxX) + 2) >> 4; ++ final int maxChunkZ = (Mth.floor(box.maxZ) + 2) >> 4; ++ ++ final int minRegionX = minChunkX >> REGION_SHIFT; ++ final int minRegionZ = minChunkZ >> REGION_SHIFT; ++ final int maxRegionX = maxChunkX >> REGION_SHIFT; ++ final int maxRegionZ = maxChunkZ >> REGION_SHIFT; ++ ++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { ++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; ++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; ++ ++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { ++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); ++ ++ if (region == null) { ++ continue; ++ } ++ ++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; ++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; ++ ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ for (int currX = minX; currX <= maxX; ++currX) { ++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); ++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { ++ continue; ++ } ++ ++ chunk.getHardCollidingEntities(except, box, into, predicate); ++ } ++ } ++ } ++ } ++ } ++ ++ public void getEntities(final EntityType type, final AABB box, final List into, ++ final Predicate predicate) { ++ final int minChunkX = (Mth.floor(box.minX) - 2) >> 4; ++ final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4; ++ final int maxChunkX = (Mth.floor(box.maxX) + 2) >> 4; ++ final int maxChunkZ = (Mth.floor(box.maxZ) + 2) >> 4; ++ ++ final int minRegionX = minChunkX >> REGION_SHIFT; ++ final int minRegionZ = minChunkZ >> REGION_SHIFT; ++ final int maxRegionX = maxChunkX >> REGION_SHIFT; ++ final int maxRegionZ = maxChunkZ >> REGION_SHIFT; ++ ++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { ++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; ++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; ++ ++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { ++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); ++ ++ if (region == null) { ++ continue; ++ } ++ ++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; ++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; ++ ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ for (int currX = minX; currX <= maxX; ++currX) { ++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); ++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { ++ continue; ++ } ++ ++ chunk.getEntities(type, box, (List)into, (Predicate)predicate); ++ } ++ } ++ } ++ } ++ } ++ ++ public void getEntities(final Class clazz, final Entity except, final AABB box, final List into, ++ final Predicate predicate) { ++ final int minChunkX = (Mth.floor(box.minX) - 2) >> 4; ++ final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4; ++ final int maxChunkX = (Mth.floor(box.maxX) + 2) >> 4; ++ final int maxChunkZ = (Mth.floor(box.maxZ) + 2) >> 4; ++ ++ final int minRegionX = minChunkX >> REGION_SHIFT; ++ final int minRegionZ = minChunkZ >> REGION_SHIFT; ++ final int maxRegionX = maxChunkX >> REGION_SHIFT; ++ final int maxRegionZ = maxChunkZ >> REGION_SHIFT; ++ ++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { ++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; ++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; ++ ++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { ++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); ++ ++ if (region == null) { ++ continue; ++ } ++ ++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; ++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; ++ ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ for (int currX = minX; currX <= maxX; ++currX) { ++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); ++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { ++ continue; ++ } ++ ++ chunk.getEntities(clazz, except, box, into, predicate); ++ } ++ } ++ } ++ } ++ } ++ ++ public ChunkEntitySlices getChunk(final int chunkX, final int chunkZ) { ++ final ChunkSlicesRegion region = this.getRegion(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT); ++ if (region == null) { ++ return null; ++ } ++ ++ return region.get((chunkX & REGION_MASK) | ((chunkZ & REGION_MASK) << REGION_SHIFT)); ++ } ++ ++ public ChunkEntitySlices getOrCreateChunk(final int chunkX, final int chunkZ) { ++ final ChunkSlicesRegion region = this.getRegion(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT); ++ ChunkEntitySlices ret; ++ if (region == null || (ret = region.get((chunkX & REGION_MASK) | ((chunkZ & REGION_MASK) << REGION_SHIFT))) == null) { ++ ret = new ChunkEntitySlices(this.world, chunkX, chunkZ, this.statusMap.get(CoordinateUtils.getChunkKey(chunkX, chunkZ)), ++ WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world)); ++ ++ this.addChunk(chunkX, chunkZ, ret); ++ ++ return ret; ++ } ++ ++ return ret; ++ } ++ ++ public ChunkSlicesRegion getRegion(final int regionX, final int regionZ) { ++ final long key = CoordinateUtils.getChunkKey(regionX, regionZ); ++ final long attempt = this.stateLock.tryOptimisticRead(); ++ if (attempt != 0L) { ++ try { ++ final ChunkSlicesRegion ret = this.regions.get(key); ++ ++ if (this.stateLock.validate(attempt)) { ++ return ret; ++ } ++ } catch (final Error error) { ++ throw error; ++ } catch (final Throwable thr) { ++ // ignore ++ } ++ } ++ ++ this.stateLock.readLock(); ++ try { ++ return this.regions.get(key); ++ } finally { ++ this.stateLock.tryUnlockRead(); ++ } ++ } ++ ++ public synchronized void removeChunk(final int chunkX, final int chunkZ) { ++ final long key = CoordinateUtils.getChunkKey(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT); ++ final int relIndex = (chunkX & REGION_MASK) | ((chunkZ & REGION_MASK) << REGION_SHIFT); ++ ++ final ChunkSlicesRegion region = this.regions.get(key); ++ final int remaining = region.remove(relIndex); ++ ++ if (remaining == 0) { ++ this.stateLock.writeLock(); ++ try { ++ this.regions.remove(key); ++ } finally { ++ this.stateLock.tryUnlockWrite(); ++ } ++ } ++ } ++ ++ public synchronized void addChunk(final int chunkX, final int chunkZ, final ChunkEntitySlices slices) { ++ final long key = CoordinateUtils.getChunkKey(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT); ++ final int relIndex = (chunkX & REGION_MASK) | ((chunkZ & REGION_MASK) << REGION_SHIFT); ++ ++ ChunkSlicesRegion region = this.regions.get(key); ++ if (region != null) { ++ region.add(relIndex, slices); ++ } else { ++ region = new ChunkSlicesRegion(); ++ region.add(relIndex, slices); ++ this.stateLock.writeLock(); ++ try { ++ this.regions.put(key, region); ++ } finally { ++ this.stateLock.tryUnlockWrite(); ++ } ++ } ++ } ++ ++ public static final class ChunkSlicesRegion { ++ ++ protected final ChunkEntitySlices[] slices = new ChunkEntitySlices[REGION_SIZE * REGION_SIZE]; ++ protected int sliceCount; ++ ++ public ChunkEntitySlices get(final int index) { ++ return this.slices[index]; ++ } ++ ++ public int remove(final int index) { ++ final ChunkEntitySlices slices = this.slices[index]; ++ if (slices == null) { ++ throw new IllegalStateException(); ++ } ++ ++ this.slices[index] = null; ++ ++ return --this.sliceCount; ++ } ++ ++ public void add(final int index, final ChunkEntitySlices slices) { ++ final ChunkEntitySlices curr = this.slices[index]; ++ if (curr != null) { ++ throw new IllegalStateException(); ++ } ++ ++ this.slices[index] = slices; ++ ++ ++this.sliceCount; ++ } ++ } ++} +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 7000909d2910e3e0e87e05f0a8bda8269f8d7e05..7fd55f26ad278aae27c102638ad8ab6776f27c7d 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -437,7 +437,7 @@ public class ServerLevel extends Level implements WorldGenLevel { + DataFixer datafixer = minecraftserver.getFixerUpper(); + EntityPersistentStorage entitypersistentstorage = new EntityStorage(this, new File(convertable_conversionsession.getDimensionPath(resourcekey), "entities"), datafixer, flag2, minecraftserver); + +- this.entityManager = new PersistentEntitySectionManager<>(Entity.class, new ServerLevel.EntityCallbacks(), entitypersistentstorage); ++ this.entityManager = new PersistentEntitySectionManager<>(Entity.class, new ServerLevel.EntityCallbacks(), entitypersistentstorage, this.entitySliceManager); // Paper + StructureManager definedstructuremanager = minecraftserver.getStructureManager(); + int j = this.spigotConfig.viewDistance; // Spigot + PersistentEntitySectionManager persistententitysectionmanager = this.entityManager; +diff --git a/src/main/java/net/minecraft/server/level/WorldGenRegion.java b/src/main/java/net/minecraft/server/level/WorldGenRegion.java +index 0f6b534a4c789a2f09f6c4624e5d58b99c7ed0e6..21d1e0c9c471e9e556b5bd70166a769b46105c7a 100644 +--- a/src/main/java/net/minecraft/server/level/WorldGenRegion.java ++++ b/src/main/java/net/minecraft/server/level/WorldGenRegion.java +@@ -77,6 +77,23 @@ public class WorldGenRegion implements WorldGenLevel { + @Nullable + private Supplier currentlyGenerating; + ++ // Paper start ++ // No-op, this class doesn't provide entity access ++ @Override ++ public List getHardCollidingEntities(Entity except, AABB box, Predicate predicate) { ++ return Collections.emptyList(); ++ } ++ ++ @Override ++ public void getEntities(Entity except, AABB box, Predicate predicate, List into) {} ++ ++ @Override ++ public void getHardCollidingEntities(Entity except, AABB box, Predicate predicate, List into) {} ++ ++ @Override ++ public void getEntitiesByClass(Class clazz, Entity except, AABB box, List into, Predicate predicate) {} ++ // Paper end ++ + public WorldGenRegion(ServerLevel world, List list, ChunkStatus chunkstatus, int i) { + this.generatingStatus = chunkstatus; + this.writeRadiusCutoff = i; +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index b7c0574aa8e3aec3bc13064b59c5f7efb075cf13..74fcc1b45a9e57280da82f7c181530d4183872a5 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -417,6 +417,56 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n + } + // Paper end - make end portalling safe + ++ // Paper start ++ /** ++ * Overriding this field will cause memory leaks. ++ */ ++ private final boolean hardCollides; ++ ++ private static final java.util.Map, Boolean> cachedOverrides = java.util.Collections.synchronizedMap(new java.util.WeakHashMap<>()); ++ { ++ /* // Goodbye, broken on reobf... ++ Boolean hardCollides = cachedOverrides.get(this.getClass()); ++ if (hardCollides == null) { ++ try { ++ java.lang.reflect.Method getHardCollisionBoxEntityMethod = Entity.class.getMethod("canCollideWith", Entity.class); ++ java.lang.reflect.Method hasHardCollisionBoxMethod = Entity.class.getMethod("canBeCollidedWith"); ++ if (!this.getClass().getMethod(hasHardCollisionBoxMethod.getName(), hasHardCollisionBoxMethod.getParameterTypes()).equals(hasHardCollisionBoxMethod) ++ || !this.getClass().getMethod(getHardCollisionBoxEntityMethod.getName(), getHardCollisionBoxEntityMethod.getParameterTypes()).equals(getHardCollisionBoxEntityMethod)) { ++ hardCollides = Boolean.TRUE; ++ } else { ++ hardCollides = Boolean.FALSE; ++ } ++ cachedOverrides.put(this.getClass(), hardCollides); ++ } ++ catch (ThreadDeath thr) { throw thr; } ++ catch (Throwable thr) { ++ // shouldn't happen, just explode ++ throw new RuntimeException(thr); ++ } ++ } */ ++ this.hardCollides = this instanceof Boat ++ || this instanceof net.minecraft.world.entity.monster.Shulker ++ || this instanceof net.minecraft.world.entity.vehicle.AbstractMinecart ++ || this.shouldHardCollide(); ++ } ++ ++ // plugins can override ++ protected boolean shouldHardCollide() { ++ return false; ++ } ++ ++ public final boolean hardCollides() { ++ return this.hardCollides; ++ } ++ ++ public net.minecraft.server.level.ChunkHolder.FullChunkStatus chunkStatus; ++ ++ public int sectionX = Integer.MIN_VALUE; ++ public int sectionY = Integer.MIN_VALUE; ++ public int sectionZ = Integer.MIN_VALUE; ++ // Paper end ++ + public Entity(EntityType type, Level world) { + this.id = Entity.ENTITY_COUNTER.incrementAndGet(); + this.passengers = ImmutableList.of(); +@@ -2280,11 +2330,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n + return InteractionResult.PASS; + } + +- public boolean canCollideWith(Entity other) { ++ public boolean canCollideWith(Entity other) { // Paper - diff on change, hard colliding entities override this - TODO CHECK ON UPDATE - AbstractMinecart/Boat override + return other.canBeCollidedWith() && !this.isPassengerOfSameVehicle(other); + } + +- public boolean canBeCollidedWith() { ++ public boolean canBeCollidedWith() { // Paper - diff on change, hard colliding entities override this TODO CHECK ON UPDATE - Boat/Shulker override + return false; + } + +diff --git a/src/main/java/net/minecraft/world/level/EntityGetter.java b/src/main/java/net/minecraft/world/level/EntityGetter.java +index 325e244c46ec208a2e7e18d71ccbbfcc25fc1bce..94130509e3a7980c378cc95c46821cf0fc753ce6 100644 +--- a/src/main/java/net/minecraft/world/level/EntityGetter.java ++++ b/src/main/java/net/minecraft/world/level/EntityGetter.java +@@ -18,6 +18,18 @@ import net.minecraft.world.phys.shapes.Shapes; + import net.minecraft.world.phys.shapes.VoxelShape; + + public interface EntityGetter { ++ ++ // Paper start ++ List getHardCollidingEntities(Entity except, AABB box, Predicate predicate); ++ ++ void getEntities(Entity except, AABB box, Predicate predicate, List into); ++ ++ void getHardCollidingEntities(Entity except, AABB box, Predicate predicate, List into); ++ ++ void getEntitiesByClass(Class clazz, Entity except, final AABB box, List into, ++ Predicate predicate); ++ // Paper end ++ + List getEntities(@Nullable Entity except, AABB box, Predicate predicate); + + List getEntities(EntityTypeTest filter, AABB box, Predicate predicate); +@@ -55,8 +67,8 @@ public interface EntityGetter { + return Stream.empty(); + } else { + AABB aABB = box.inflate(1.0E-7D); +- return this.getEntities(entity, aABB, predicate.and((entityx) -> { +- if (entityx.getBoundingBox().intersects(aABB)) { ++ Predicate hardCollides = (entityx) -> { // Paper - optimise entity hard collisions ++ if (true || entityx.getBoundingBox().intersects(aABB)) { // Paper - always true + if (entity == null) { + if (entityx.canBeCollidedWith()) { + return true; +@@ -67,7 +79,11 @@ public interface EntityGetter { + } + + return false; +- })).stream().map(Entity::getBoundingBox).map(Shapes::create); ++ }; // Paper start - optimise entity hard collisions ++ predicate = predicate == null ? hardCollides : hardCollides.and(predicate); ++ return (entity != null && entity.hardCollides() ? this.getEntities(entity, aABB, predicate) : this.getHardCollidingEntities(entity, aABB, predicate)) ++ .stream().map(Entity::getBoundingBox).map(Shapes::create); ++ // Paper end - optimise entity hard collisions + } + } + +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index a7485746417e0dddb392c89a5a1d467c0bc83fbe..82da0f2226f71a5081e24c17a8a39be22e3c5316 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -203,6 +203,50 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + return this.typeKey; + } + ++ // Paper start ++ protected final io.papermc.paper.world.EntitySliceManager entitySliceManager; ++ ++ // Paper start - optimise CraftChunk#getEntities ++ public org.bukkit.entity.Entity[] getChunkEntities(int chunkX, int chunkZ) { ++ io.papermc.paper.world.ChunkEntitySlices slices = this.entitySliceManager.getChunk(chunkX, chunkZ); ++ if (slices == null) { ++ return new org.bukkit.entity.Entity[0]; ++ } ++ return slices.getChunkEntities(); ++ } ++ // Paper end - optimise CraftChunk#getEntities ++ ++ @Override ++ public List getHardCollidingEntities(Entity except, AABB box, Predicate predicate) { ++ List ret = new java.util.ArrayList<>(); ++ this.entitySliceManager.getEntities(except, box, ret, predicate); ++ return ret; ++ } ++ ++ @Override ++ public void getEntities(Entity except, AABB box, Predicate predicate, List into) { ++ this.entitySliceManager.getEntities(except, box, into, predicate); ++ } ++ ++ @Override ++ public void getHardCollidingEntities(Entity except, AABB box, Predicate predicate, List into) { ++ this.entitySliceManager.getHardCollidingEntities(except, box, into, predicate); ++ } ++ ++ @Override ++ public void getEntitiesByClass(Class clazz, Entity except, final AABB box, List into, ++ Predicate predicate) { ++ this.entitySliceManager.getEntities((Class)clazz, except, box, (List)into, (Predicate)predicate); ++ } ++ ++ @Override ++ public List getEntitiesOfClass(Class entityClass, AABB box, Predicate predicate) { ++ List ret = new java.util.ArrayList<>(); ++ this.entitySliceManager.getEntities(entityClass, null, box, ret, predicate); ++ return ret; ++ } ++ // Paper end ++ + 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 + this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot + this.paperConfig = new com.destroystokyo.paper.PaperWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName(), this.spigotConfig); // Paper +@@ -279,6 +323,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + this.chunkPacketBlockController = this.paperConfig.antiXray ? + new com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray(this, executor) + : com.destroystokyo.paper.antixray.ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray ++ this.entitySliceManager = new io.papermc.paper.world.EntitySliceManager((ServerLevel)this); // Paper + } + + // Paper start +@@ -996,26 +1041,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + public List getEntities(@Nullable Entity except, AABB box, Predicate predicate) { + this.getProfiler().incrementCounter("getEntities"); + List list = Lists.newArrayList(); +- +- this.getEntities().get(box, (entity1) -> { +- if (entity1 != except && predicate.test(entity1)) { +- list.add(entity1); +- } +- +- if (entity1 instanceof EnderDragon) { +- EnderDragonPart[] aentitycomplexpart = ((EnderDragon) entity1).getSubEntities(); +- int i = aentitycomplexpart.length; +- +- for (int j = 0; j < i; ++j) { +- EnderDragonPart entitycomplexpart = aentitycomplexpart[j]; +- +- if (entity1 != except && predicate.test(entitycomplexpart)) { +- list.add(entitycomplexpart); +- } +- } +- } +- +- }, predicate == net.minecraft.world.entity.EntitySelector.CONTAINER_ENTITY_SELECTOR); // Paper ++ this.entitySliceManager.getEntities(except, box, list, predicate); // Paper - optimise this call + return list; + } + +@@ -1024,26 +1050,22 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + this.getProfiler().incrementCounter("getEntities"); + List list = Lists.newArrayList(); + +- this.getEntities().get(filter, box, (entity) -> { +- if (predicate.test(entity)) { +- list.add(entity); +- } +- +- if (entity instanceof EnderDragon) { +- EnderDragonPart[] aentitycomplexpart = ((EnderDragon) entity).getSubEntities(); +- int i = aentitycomplexpart.length; +- +- for (int j = 0; j < i; ++j) { +- EnderDragonPart entitycomplexpart = aentitycomplexpart[j]; +- T t0 = filter.tryCast(entitycomplexpart); +- +- if (t0 != null && predicate.test(t0)) { +- list.add(t0); +- } +- } ++ // Paper start - optimise this call ++ if (filter instanceof net.minecraft.world.entity.EntityType) { ++ this.entitySliceManager.getEntities((net.minecraft.world.entity.EntityType)filter, box, list, predicate); ++ } else { ++ Predicate test = (obj) -> { ++ return filter.tryCast(obj) != null; ++ }; ++ predicate = predicate == null ? test : test.and((Predicate)predicate); ++ Class base; ++ if (filter == null || (base = filter.getBaseClass()) == null || base == Entity.class) { ++ this.entitySliceManager.getEntities((Entity) null, box, (List)list, (Predicate)predicate); ++ } else { ++ this.entitySliceManager.getEntities(base, null, box, (List)list, (Predicate)predicate); // Paper - optimise this call + } +- +- }); ++ } ++ // Paper end - optimise this call + return list; + } + +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 e4e4b48250d32d9e6c034d62e13ac2277bffda8f..a0dd49d17e1a83edbb0bf5cfbaba3f8fb665f75a 100644 +--- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java ++++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java +@@ -45,8 +45,10 @@ public class PersistentEntitySectionManager implements A + private final Long2ObjectMap chunkLoadStatuses = new Long2ObjectOpenHashMap(); + private final LongSet chunksToUnload = new LongOpenHashSet(); + private final Queue> loadingInbox = Queues.newConcurrentLinkedQueue(); ++ public final io.papermc.paper.world.EntitySliceManager entitySliceManager; // Paper + +- public PersistentEntitySectionManager(Class entityClass, LevelCallback handler, EntityPersistentStorage dataAccess) { ++ public PersistentEntitySectionManager(Class entityClass, LevelCallback handler, EntityPersistentStorage dataAccess, io.papermc.paper.world.EntitySliceManager entitySliceManager) { // Paper ++ this.entitySliceManager = entitySliceManager; // Paper + this.sectionStorage = new EntitySectionStorage<>(entityClass, this.chunkVisibility); + this.chunkVisibility.defaultReturnValue(Visibility.HIDDEN); + this.chunkLoadStatuses.defaultReturnValue(PersistentEntitySectionManager.ChunkLoadStatus.FRESH); +@@ -97,6 +99,7 @@ public class PersistentEntitySectionManager implements A + EntitySection entitysection = this.sectionStorage.getOrCreateSection(i); + + entitysection.add(entity); // CraftBukkit - decompile error ++ this.entitySliceManager.addEntity((Entity)entity); // Paper + entity.setLevelCallback(new PersistentEntitySectionManager.Callback(entity, i, entitysection)); + if (!existing) { + this.callbacks.onCreated(entity); +@@ -154,6 +157,7 @@ public class PersistentEntitySectionManager implements A + io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous chunk ticking status update"); // Paper + Visibility visibility = Visibility.fromFullChunkStatus(levelType); + ++ this.entitySliceManager.chunkStatusChange(chunkPos.x, chunkPos.z, levelType); // Paper + this.updateChunkStatus(chunkPos, visibility); + } + +@@ -434,6 +438,7 @@ public class PersistentEntitySectionManager implements A + long i = SectionPos.asLong(blockposition); + + if (i != this.currentSectionKey) { ++ PersistentEntitySectionManager.this.entitySliceManager.moveEntity((Entity)this.entity); // Paper + Visibility visibility = this.currentSection.getStatus(); + + if (!this.currentSection.remove(this.entity)) { +@@ -482,6 +487,7 @@ public class PersistentEntitySectionManager implements A + if (!this.currentSection.remove(this.entity)) { + PersistentEntitySectionManager.LOGGER.warn("Entity {} wasn't found in section {} (destroying due to {})", this.entity, SectionPos.of(this.currentSectionKey), reason); + } ++ PersistentEntitySectionManager.this.entitySliceManager.removeEntity((Entity)this.entity); // Paper + + Visibility visibility = PersistentEntitySectionManager.getEffectiveStatus(this.entity, this.currentSection.getStatus()); + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +index 591a66dcdb717ad041120ab27de8f2f3a1975358..b27945cc7beb36a10bb9f3503b060f6c2ad18b77 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +@@ -110,11 +110,7 @@ public class CraftChunk implements Chunk { + this.getWorld().getChunkAt(x, z); // Transient load for this tick + } + +- Location location = new Location(null, 0, 0, 0); +- return this.getWorld().getEntities().stream().filter((entity) -> { +- entity.getLocation(location); +- return location.getBlockX() >> 4 == this.x && location.getBlockZ() >> 4 == this.z; +- }).toArray(Entity[]::new); ++ return ((CraftWorld)this.getWorld()).getHandle().getChunkEntities(this.x, this.z); // Paper - optimise this + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java b/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java +index c5d132dcf70a55de041c0187ff8fb889248bdfee..950d4381459d31d02acf55c4aef4f5e33367748b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java +@@ -254,4 +254,20 @@ public class DummyGeneratorAccess implements WorldGenLevel { + public Stream> startsForFeature(SectionPos pos, StructureFeature feature) { + throw new UnsupportedOperationException("Not supported yet."); + } ++ ++ // Paper start ++ @Override ++ public List getHardCollidingEntities(Entity except, AABB box, Predicate predicate) { ++ return java.util.Collections.emptyList(); ++ } ++ ++ @Override ++ public void getEntities(Entity except, AABB box, Predicate predicate, List into) {} ++ ++ @Override ++ public void getHardCollidingEntities(Entity except, AABB box, Predicate predicate, List into) {} ++ ++ @Override ++ public void getEntitiesByClass(Class clazz, Entity except, AABB box, List into, Predicate predicate) {} ++ // Paper end + } +diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java +index 951e8ba1e05436aa0afcd0a72358550422afa791..b5da2f39ff6e2e7cb519c5d22be6ae4d77dc60ab 100644 +--- a/src/main/java/org/spigotmc/ActivationRange.java ++++ b/src/main/java/org/spigotmc/ActivationRange.java +@@ -205,7 +205,13 @@ public class ActivationRange + ActivationType.VILLAGER.boundingBox = player.getBoundingBox().inflate( villagerActivationRange, 256, waterActivationRange ); + // Paper end + +- world.getEntities().get(maxBB, ActivationRange::activateEntity); ++ // Paper start ++ java.util.List entities = world.getEntities((Entity)null, maxBB, null); ++ for (int i = 0; i < entities.size(); i++) { ++ Entity entity = entities.get(i); ++ ActivationRange.activateEntity(entity); ++ } ++ // Paper end + } + MinecraftTimings.entityActivationCheckTimer.stopTiming(); + } diff --git a/patches/server/0766-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch b/patches/server/0766-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch new file mode 100644 index 0000000000..abb2ace683 --- /dev/null +++ b/patches/server/0766-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch @@ -0,0 +1,1675 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Mon, 4 May 2020 10:06:24 -0700 +Subject: [PATCH] Highly optimise single and multi-AABB VoxelShapes and + collisions + + +diff --git a/src/main/java/io/papermc/paper/util/CachedLists.java b/src/main/java/io/papermc/paper/util/CachedLists.java +index be668387f65a633c6ac497fca632a4767a1bf3a2..e08f4e39db4ee3fed62e37364d17dcc5c5683504 100644 +--- a/src/main/java/io/papermc/paper/util/CachedLists.java ++++ b/src/main/java/io/papermc/paper/util/CachedLists.java +@@ -1,8 +1,57 @@ + package io.papermc.paper.util; + ++import net.minecraft.world.entity.Entity; ++import net.minecraft.world.phys.AABB; ++import org.bukkit.Bukkit; ++import org.bukkit.craftbukkit.util.UnsafeList; ++import java.util.List; ++ + public final class CachedLists { + +- public static void reset() { ++ // Paper start - optimise collisions ++ static final UnsafeList TEMP_COLLISION_LIST = new UnsafeList<>(1024); ++ static boolean tempCollisionListInUse; ++ ++ public static UnsafeList getTempCollisionList() { ++ if (!Bukkit.isPrimaryThread() || tempCollisionListInUse) { ++ return new UnsafeList<>(16); ++ } ++ tempCollisionListInUse = true; ++ return TEMP_COLLISION_LIST; ++ } ++ ++ public static void returnTempCollisionList(List list) { ++ if (list != TEMP_COLLISION_LIST) { ++ return; ++ } ++ ((UnsafeList)list).setSize(0); ++ tempCollisionListInUse = false; ++ } + ++ static final UnsafeList TEMP_GET_ENTITIES_LIST = new UnsafeList<>(1024); ++ static boolean tempGetEntitiesListInUse; ++ ++ public static UnsafeList getTempGetEntitiesList() { ++ if (!Bukkit.isPrimaryThread() || tempGetEntitiesListInUse) { ++ return new UnsafeList<>(16); ++ } ++ tempGetEntitiesListInUse = true; ++ return TEMP_GET_ENTITIES_LIST; ++ } ++ ++ public static void returnTempGetEntitiesList(List list) { ++ if (list != TEMP_GET_ENTITIES_LIST) { ++ return; ++ } ++ ((UnsafeList)list).setSize(0); ++ tempGetEntitiesListInUse = false; ++ } ++ // Paper end - optimise collisions ++ ++ public static void reset() { ++ // Paper start - optimise collisions ++ TEMP_COLLISION_LIST.completeReset(); ++ TEMP_GET_ENTITIES_LIST.completeReset(); ++ // Paper end - optimise collisions + } + } +diff --git a/src/main/java/io/papermc/paper/util/CollisionUtil.java b/src/main/java/io/papermc/paper/util/CollisionUtil.java +new file mode 100644 +index 0000000000000000000000000000000000000000..98ca1199a823cdf55b913396ce0a24554e85f116 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/CollisionUtil.java +@@ -0,0 +1,645 @@ ++package io.papermc.paper.util; ++ ++import io.papermc.paper.voxel.AABBVoxelShape; ++import net.minecraft.core.BlockPos; ++import net.minecraft.server.level.ServerChunkCache; ++import net.minecraft.server.level.ServerLevel; ++import net.minecraft.server.level.WorldGenRegion; ++import net.minecraft.util.Mth; ++import net.minecraft.world.entity.Entity; ++import net.minecraft.world.item.Item; ++import net.minecraft.world.level.CollisionGetter; ++import net.minecraft.world.level.EntityGetter; ++import net.minecraft.world.level.block.Blocks; ++import net.minecraft.world.level.block.state.BlockState; ++import net.minecraft.world.level.border.WorldBorder; ++import net.minecraft.world.level.chunk.ChunkAccess; ++import net.minecraft.world.level.chunk.LevelChunkSection; ++import net.minecraft.world.level.material.FlowingFluid; ++import net.minecraft.world.level.material.FluidState; ++import net.minecraft.world.phys.AABB; ++import net.minecraft.world.phys.Vec3; ++import net.minecraft.world.phys.shapes.ArrayVoxelShape; ++import net.minecraft.world.phys.shapes.CollisionContext; ++import net.minecraft.world.phys.shapes.EntityCollisionContext; ++import net.minecraft.world.phys.shapes.Shapes; ++import net.minecraft.world.phys.shapes.VoxelShape; ++import java.util.List; ++import java.util.Optional; ++import java.util.function.BiPredicate; ++import java.util.function.Predicate; ++ ++public final class CollisionUtil { ++ ++ public static final double COLLISION_EPSILON = 1.0E-7; ++ ++ public static boolean isEmpty(final AABB aabb) { ++ return (aabb.maxX - aabb.minX) < COLLISION_EPSILON && (aabb.maxY - aabb.minY) < COLLISION_EPSILON && (aabb.maxZ - aabb.minZ) < COLLISION_EPSILON; ++ } ++ ++ public static boolean isEmpty(final double minX, final double minY, final double minZ, ++ final double maxX, final double maxY, final double maxZ) { ++ return (maxX - minX) < COLLISION_EPSILON && (maxY - minY) < COLLISION_EPSILON && (maxZ - minZ) < COLLISION_EPSILON; ++ } ++ ++ public static AABB getBoxForChunk(final int chunkX, final int chunkZ) { ++ double x = (double)(chunkX << 4); ++ double z = (double)(chunkZ << 4); ++ // use a bounding box bigger than the chunk to prevent entities from entering it on move ++ return new AABB(x - 3*COLLISION_EPSILON, Double.NEGATIVE_INFINITY, z - 3*COLLISION_EPSILON, ++ x + (16.0 + 3*COLLISION_EPSILON), Double.POSITIVE_INFINITY, z + (16.0 + 3*COLLISION_EPSILON), false); ++ } ++ ++ /* ++ A couple of rules for VoxelShape collisions: ++ Two shapes only intersect if they are actually more than EPSILON units into each other. This also applies to movement ++ checks. ++ If the two shapes strictly collide, then the return value of a collide call will return a value in the opposite ++ direction of the source move. However, this value will not be greater in magnitude than EPSILON. Collision code ++ will automatically round it to 0. ++ */ ++ ++ public static boolean voxelShapeIntersect(final double minX1, final double minY1, final double minZ1, final double maxX1, ++ final double maxY1, final double maxZ1, final double minX2, final double minY2, ++ final double minZ2, final double maxX2, final double maxY2, final double maxZ2) { ++ return (minX1 - maxX2) < -COLLISION_EPSILON && (maxX1 - minX2) > COLLISION_EPSILON && ++ (minY1 - maxY2) < -COLLISION_EPSILON && (maxY1 - minY2) > COLLISION_EPSILON && ++ (minZ1 - maxZ2) < -COLLISION_EPSILON && (maxZ1 - minZ2) > COLLISION_EPSILON; ++ } ++ ++ public static boolean voxelShapeIntersect(final AABB box, final double minX, final double minY, final double minZ, ++ final double maxX, final double maxY, final double maxZ) { ++ return (box.minX - maxX) < -COLLISION_EPSILON && (box.maxX - minX) > COLLISION_EPSILON && ++ (box.minY - maxY) < -COLLISION_EPSILON && (box.maxY - minY) > COLLISION_EPSILON && ++ (box.minZ - maxZ) < -COLLISION_EPSILON && (box.maxZ - minZ) > COLLISION_EPSILON; ++ } ++ ++ public static boolean voxelShapeIntersect(final AABB box1, final AABB box2) { ++ return (box1.minX - box2.maxX) < -COLLISION_EPSILON && (box1.maxX - box2.minX) > COLLISION_EPSILON && ++ (box1.minY - box2.maxY) < -COLLISION_EPSILON && (box1.maxY - box2.minY) > COLLISION_EPSILON && ++ (box1.minZ - box2.maxZ) < -COLLISION_EPSILON && (box1.maxZ - box2.minZ) > COLLISION_EPSILON; ++ } ++ ++ public static double collideX(final AABB target, final AABB source, final double source_move) { ++ if (source_move == 0.0) { ++ return 0.0; ++ } ++ ++ if ((source.minY - target.maxY) < -COLLISION_EPSILON && (source.maxY - target.minY) > COLLISION_EPSILON && ++ (source.minZ - target.maxZ) < -COLLISION_EPSILON && (source.maxZ - target.minZ) > COLLISION_EPSILON) { ++ if (source_move >= 0.0) { ++ final double max_move = target.minX - source.maxX; // < 0.0 if no strict collision ++ if (max_move < -COLLISION_EPSILON) { ++ return source_move; ++ } ++ return Math.min(max_move, source_move); ++ } else { ++ final double max_move = target.maxX - source.minX; // > 0.0 if no strict collision ++ if (max_move > COLLISION_EPSILON) { ++ return source_move; ++ } ++ return Math.max(max_move, source_move); ++ } ++ } ++ return source_move; ++ } ++ ++ public static double collideY(final AABB target, final AABB source, final double source_move) { ++ if (source_move == 0.0) { ++ return 0.0; ++ } ++ ++ if ((source.minX - target.maxX) < -COLLISION_EPSILON && (source.maxX - target.minX) > COLLISION_EPSILON && ++ (source.minZ - target.maxZ) < -COLLISION_EPSILON && (source.maxZ - target.minZ) > COLLISION_EPSILON) { ++ if (source_move >= 0.0) { ++ final double max_move = target.minY - source.maxY; // < 0.0 if no strict collision ++ if (max_move < -COLLISION_EPSILON) { ++ return source_move; ++ } ++ return Math.min(max_move, source_move); ++ } else { ++ final double max_move = target.maxY - source.minY; // > 0.0 if no strict collision ++ if (max_move > COLLISION_EPSILON) { ++ return source_move; ++ } ++ return Math.max(max_move, source_move); ++ } ++ } ++ return source_move; ++ } ++ ++ public static double collideZ(final AABB target, final AABB source, final double source_move) { ++ if (source_move == 0.0) { ++ return 0.0; ++ } ++ ++ if ((source.minX - target.maxX) < -COLLISION_EPSILON && (source.maxX - target.minX) > COLLISION_EPSILON && ++ (source.minY - target.maxY) < -COLLISION_EPSILON && (source.maxY - target.minY) > COLLISION_EPSILON) { ++ if (source_move >= 0.0) { ++ final double max_move = target.minZ - source.maxZ; // < 0.0 if no strict collision ++ if (max_move < -COLLISION_EPSILON) { ++ return source_move; ++ } ++ return Math.min(max_move, source_move); ++ } else { ++ final double max_move = target.maxZ - source.minZ; // > 0.0 if no strict collision ++ if (max_move > COLLISION_EPSILON) { ++ return source_move; ++ } ++ return Math.max(max_move, source_move); ++ } ++ } ++ return source_move; ++ } ++ ++ public static AABB offsetX(final AABB box, final double dx) { ++ return new AABB(box.minX + dx, box.minY, box.minZ, box.maxX + dx, box.maxY, box.maxZ, false); ++ } ++ ++ public static AABB offsetY(final AABB box, final double dy) { ++ return new AABB(box.minX, box.minY + dy, box.minZ, box.maxX, box.maxY + dy, box.maxZ, false); ++ } ++ ++ public static AABB offsetZ(final AABB box, final double dz) { ++ return new AABB(box.minX, box.minY, box.minZ + dz, box.maxX, box.maxY, box.maxZ + dz, false); ++ } ++ ++ public static AABB expandRight(final AABB box, final double dx) { // dx > 0.0 ++ return new AABB(box.minX, box.minY, box.minZ, box.maxX + dx, box.maxY, box.maxZ, false); ++ } ++ ++ public static AABB expandLeft(final AABB box, final double dx) { // dx < 0.0 ++ return new AABB(box.minX - dx, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ, false); ++ } ++ ++ public static AABB expandUpwards(final AABB box, final double dy) { // dy > 0.0 ++ return new AABB(box.minX, box.minY, box.minZ, box.maxX, box.maxY + dy, box.maxZ, false); ++ } ++ ++ public static AABB expandDownwards(final AABB box, final double dy) { // dy < 0.0 ++ return new AABB(box.minX, box.minY - dy, box.minZ, box.maxX, box.maxY, box.maxZ, false); ++ } ++ ++ public static AABB expandForwards(final AABB box, final double dz) { // dz > 0.0 ++ return new AABB(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ + dz, false); ++ } ++ ++ public static AABB expandBackwards(final AABB box, final double dz) { // dz < 0.0 ++ return new AABB(box.minX, box.minY, box.minZ - dz, box.maxX, box.maxY, box.maxZ, false); ++ } ++ ++ public static AABB cutRight(final AABB box, final double dx) { // dx > 0.0 ++ return new AABB(box.maxX, box.minY, box.minZ, box.maxX + dx, box.maxY, box.maxZ, false); ++ } ++ ++ public static AABB cutLeft(final AABB box, final double dx) { // dx < 0.0 ++ return new AABB(box.minX + dx, box.minY, box.minZ, box.minX, box.maxY, box.maxZ, false); ++ } ++ ++ public static AABB cutUpwards(final AABB box, final double dy) { // dy > 0.0 ++ return new AABB(box.minX, box.maxY, box.minZ, box.maxX, box.maxY + dy, box.maxZ, false); ++ } ++ ++ public static AABB cutDownwards(final AABB box, final double dy) { // dy < 0.0 ++ return new AABB(box.minX, box.minY + dy, box.minZ, box.maxX, box.minY, box.maxZ, false); ++ } ++ ++ public static AABB cutForwards(final AABB box, final double dz) { // dz > 0.0 ++ return new AABB(box.minX, box.minY, box.maxZ, box.maxX, box.maxY, box.maxZ + dz, false); ++ } ++ ++ public static AABB cutBackwards(final AABB box, final double dz) { // dz < 0.0 ++ return new AABB(box.minX, box.minY, box.minZ + dz, box.maxX, box.maxY, box.minZ, false); ++ } ++ ++ public static double performCollisionsX(final AABB currentBoundingBox, double value, final List potentialCollisions) { ++ for (int i = 0, len = potentialCollisions.size(); i < len; ++i) { ++ final AABB target = potentialCollisions.get(i); ++ value = collideX(target, currentBoundingBox, value); ++ } ++ ++ return value; ++ } ++ ++ public static double performCollisionsY(final AABB currentBoundingBox, double value, final List potentialCollisions) { ++ for (int i = 0, len = potentialCollisions.size(); i < len; ++i) { ++ final AABB target = potentialCollisions.get(i); ++ value = collideY(target, currentBoundingBox, value); ++ } ++ ++ return value; ++ } ++ ++ public static double performCollisionsZ(final AABB currentBoundingBox, double value, final List potentialCollisions) { ++ for (int i = 0, len = potentialCollisions.size(); i < len; ++i) { ++ final AABB target = potentialCollisions.get(i); ++ value = collideZ(target, currentBoundingBox, value); ++ } ++ ++ return value; ++ } ++ ++ public static Vec3 performCollisions(final Vec3 moveVector, AABB axisalignedbb, final List potentialCollisions) { ++ double x = moveVector.x; ++ double y = moveVector.y; ++ double z = moveVector.z; ++ ++ if (y != 0.0) { ++ y = performCollisionsY(axisalignedbb, y, potentialCollisions); ++ if (y != 0.0) { ++ axisalignedbb = offsetY(axisalignedbb, y); ++ } ++ } ++ ++ final boolean xSmaller = Math.abs(x) < Math.abs(z); ++ ++ if (xSmaller && z != 0.0) { ++ z = performCollisionsZ(axisalignedbb, z, potentialCollisions); ++ if (z != 0.0) { ++ axisalignedbb = offsetZ(axisalignedbb, z); ++ } ++ } ++ ++ if (x != 0.0) { ++ x = performCollisionsX(axisalignedbb, x, potentialCollisions); ++ if (!xSmaller && x != 0.0) { ++ axisalignedbb = offsetX(axisalignedbb, x); ++ } ++ } ++ ++ if (!xSmaller && z != 0.0) { ++ z = performCollisionsZ(axisalignedbb, z, potentialCollisions); ++ } ++ ++ return new Vec3(x, y, z); ++ } ++ ++ public static boolean addBoxesToIfIntersects(final VoxelShape shape, final AABB aabb, final List list) { ++ if (shape instanceof AABBVoxelShape) { ++ final AABBVoxelShape shapeCasted = (AABBVoxelShape)shape; ++ if (voxelShapeIntersect(shapeCasted.aabb, aabb) && !isEmpty(shapeCasted.aabb)) { ++ list.add(shapeCasted.aabb); ++ return true; ++ } ++ return false; ++ } else if (shape instanceof ArrayVoxelShape) { ++ final ArrayVoxelShape shapeCasted = (ArrayVoxelShape)shape; ++ // this can be optimised by checking an "overall shape" first, but not needed ++ ++ final double offX = shapeCasted.getOffsetX(); ++ final double offY = shapeCasted.getOffsetY(); ++ final double offZ = shapeCasted.getOffsetZ(); ++ ++ boolean ret = false; ++ ++ for (final AABB boundingBox : shapeCasted.getBoundingBoxesRepresentation()) { ++ final double minX, minY, minZ, maxX, maxY, maxZ; ++ if (voxelShapeIntersect(aabb, minX = boundingBox.minX + offX, minY = boundingBox.minY + offY, minZ = boundingBox.minZ + offZ, ++ maxX = boundingBox.maxX + offX, maxY = boundingBox.maxY + offY, maxZ = boundingBox.maxZ + offZ) ++ && !isEmpty(minX, minY, minZ, maxX, maxY, maxZ)) { ++ list.add(new AABB(minX, minY, minZ, maxX, maxY, maxZ, false)); ++ ret = true; ++ } ++ } ++ ++ return ret; ++ } else { ++ final List boxes = shape.toAabbs(); ++ ++ boolean ret = false; ++ ++ for (int i = 0, len = boxes.size(); i < len; ++i) { ++ final AABB box = boxes.get(i); ++ if (voxelShapeIntersect(box, aabb) && !isEmpty(box)) { ++ list.add(box); ++ ret = true; ++ } ++ } ++ ++ return ret; ++ } ++ } ++ ++ public static void addBoxesTo(final VoxelShape shape, final List list) { ++ if (shape instanceof AABBVoxelShape) { ++ final AABBVoxelShape shapeCasted = (AABBVoxelShape)shape; ++ if (!isEmpty(shapeCasted.aabb)) { ++ list.add(shapeCasted.aabb); ++ } ++ } else if (shape instanceof ArrayVoxelShape) { ++ final ArrayVoxelShape shapeCasted = (ArrayVoxelShape)shape; ++ ++ final double offX = shapeCasted.getOffsetX(); ++ final double offY = shapeCasted.getOffsetY(); ++ final double offZ = shapeCasted.getOffsetZ(); ++ ++ for (final AABB boundingBox : shapeCasted.getBoundingBoxesRepresentation()) { ++ final AABB box = boundingBox.move(offX, offY, offZ); ++ if (!isEmpty(box)) { ++ list.add(box); ++ } ++ } ++ } else { ++ final List boxes = shape.toAabbs(); ++ for (int i = 0, len = boxes.size(); i < len; ++i) { ++ final AABB box = boxes.get(i); ++ if (!isEmpty(box)) { ++ list.add(box); ++ } ++ } ++ } ++ } ++ ++ public static boolean isAlmostCollidingOnBorder(final WorldBorder worldborder, final AABB boundingBox) { ++ return isAlmostCollidingOnBorder(worldborder, boundingBox.minX, boundingBox.maxX, boundingBox.minZ, boundingBox.maxZ); ++ } ++ ++ public static boolean isAlmostCollidingOnBorder(final WorldBorder worldborder, final double boxMinX, final double boxMaxX, ++ final double boxMinZ, final double boxMaxZ) { ++ final double borderMinX = worldborder.getMinX(); // -X ++ final double borderMaxX = worldborder.getMaxX(); // +X ++ ++ final double borderMinZ = worldborder.getMinZ(); // -Z ++ final double borderMaxZ = worldborder.getMaxZ(); // +Z ++ ++ return ++ // Not intersecting if we're smaller ++ !voxelShapeIntersect( ++ boxMinX + COLLISION_EPSILON, Double.NEGATIVE_INFINITY, boxMinZ + COLLISION_EPSILON, ++ boxMaxX - COLLISION_EPSILON, Double.POSITIVE_INFINITY, boxMaxZ - COLLISION_EPSILON, ++ borderMinX, Double.NEGATIVE_INFINITY, borderMinZ, borderMaxX, Double.POSITIVE_INFINITY, borderMaxZ ++ ) ++ && ++ ++ // Are intersecting if we're larger ++ voxelShapeIntersect( ++ boxMinX - COLLISION_EPSILON, Double.NEGATIVE_INFINITY, boxMinZ - COLLISION_EPSILON, ++ boxMaxX + COLLISION_EPSILON, Double.POSITIVE_INFINITY, boxMaxZ + COLLISION_EPSILON, ++ borderMinX, Double.NEGATIVE_INFINITY, borderMinZ, borderMaxX, Double.POSITIVE_INFINITY, borderMaxZ ++ ); ++ } ++ ++ public static boolean isCollidingWithBorderEdge(final WorldBorder worldborder, final AABB boundingBox) { ++ return isCollidingWithBorderEdge(worldborder, boundingBox.minX, boundingBox.maxX, boundingBox.minZ, boundingBox.maxZ); ++ } ++ ++ public static boolean isCollidingWithBorderEdge(final WorldBorder worldborder, final double boxMinX, final double boxMaxX, ++ final double boxMinZ, final double boxMaxZ) { ++ final double borderMinX = worldborder.getMinX() + COLLISION_EPSILON; // -X ++ final double borderMaxX = worldborder.getMaxX() - COLLISION_EPSILON; // +X ++ ++ final double borderMinZ = worldborder.getMinZ() + COLLISION_EPSILON; // -Z ++ final double borderMaxZ = worldborder.getMaxZ() - COLLISION_EPSILON; // +Z ++ ++ return boxMinX < borderMinX || boxMaxX > borderMaxX || boxMinZ < borderMinZ || boxMaxZ > borderMaxZ; ++ } ++ ++ public static boolean getCollisionsForBlocksOrWorldBorder(final CollisionGetter getter, final Entity entity, final AABB aabb, ++ final List into, final boolean loadChunks, final boolean collidesWithUnloaded, ++ final boolean checkBorder, final boolean checkOnly, final BiPredicate predicate) { ++ boolean ret = false; ++ ++ if (checkBorder) { ++ if (CollisionUtil.isAlmostCollidingOnBorder(getter.getWorldBorder(), aabb)) { ++ if (checkOnly) { ++ return true; ++ } else { ++ CollisionUtil.addBoxesTo(getter.getWorldBorder().getCollisionShape(), into); ++ ret = true; ++ } ++ } ++ } ++ ++ int minBlockX = Mth.floor(aabb.minX - COLLISION_EPSILON) - 1; ++ int maxBlockX = Mth.floor(aabb.maxX + COLLISION_EPSILON) + 1; ++ ++ int minBlockY = Mth.floor(aabb.minY - COLLISION_EPSILON) - 1; ++ int maxBlockY = Mth.floor(aabb.maxY + COLLISION_EPSILON) + 1; ++ ++ int minBlockZ = Mth.floor(aabb.minZ - COLLISION_EPSILON) - 1; ++ int maxBlockZ = Mth.floor(aabb.maxZ + COLLISION_EPSILON) + 1; ++ ++ final int minSection = WorldUtil.getMinSection(getter); ++ final int maxSection = WorldUtil.getMaxSection(getter); ++ final int minBlock = minSection << 4; ++ final int maxBlock = (maxSection << 4) | 15; ++ ++ BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos(); ++ CollisionContext collisionShape = null; ++ ++ // special cases: ++ if (minBlockY > maxBlock || maxBlockY < minBlock) { ++ // no point in checking ++ return ret; ++ } ++ ++ int minYIterate = Math.max(minBlock, minBlockY); ++ int maxYIterate = Math.min(maxBlock, maxBlockY); ++ ++ int minChunkX = minBlockX >> 4; ++ int maxChunkX = maxBlockX >> 4; ++ ++ int minChunkZ = minBlockZ >> 4; ++ int maxChunkZ = maxBlockZ >> 4; ++ ++ ServerChunkCache chunkProvider; ++ if (getter instanceof WorldGenRegion) { ++ chunkProvider = null; ++ } else if (getter instanceof ServerLevel) { ++ chunkProvider = ((ServerLevel)getter).getChunkSource(); ++ } else { ++ chunkProvider = null; ++ } ++ // TODO special case single chunk? ++ ++ for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) { ++ int minZ = currChunkZ == minChunkZ ? minBlockZ & 15 : 0; // coordinate in chunk ++ int maxZ = currChunkZ == maxChunkZ ? maxBlockZ & 15 : 15; // coordinate in chunk ++ ++ for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) { ++ int minX = currChunkX == minChunkX ? minBlockX & 15 : 0; // coordinate in chunk ++ int maxX = currChunkX == maxChunkX ? maxBlockX & 15 : 15; // coordinate in chunk ++ ++ int chunkXGlobalPos = currChunkX << 4; ++ int chunkZGlobalPos = currChunkZ << 4; ++ ChunkAccess chunk; ++ if (chunkProvider == null) { ++ chunk = (ChunkAccess)getter.getChunkForCollisions(currChunkX, currChunkZ); ++ } else { ++ chunk = loadChunks ? chunkProvider.getChunk(currChunkX, currChunkZ, true) : chunkProvider.getChunkAtIfLoadedImmediately(currChunkX, currChunkZ); ++ } ++ ++ ++ if (chunk == null) { ++ if (collidesWithUnloaded) { ++ if (checkOnly) { ++ return true; ++ } else { ++ into.add(getBoxForChunk(currChunkX, currChunkZ)); ++ ret = true; ++ } ++ } ++ continue; ++ } ++ ++ LevelChunkSection[] sections = chunk.getSections(); ++ ++ // bound y ++ ++ for (int currY = minYIterate; currY <= maxYIterate; ++currY) { ++ LevelChunkSection section = sections[(currY >> 4) - minSection]; ++ if (section == null || section.isEmpty()) { ++ // empty ++ // skip to next section ++ currY = (currY & ~(15)) + 15; // increment by 15: iterator loop increments by the extra one ++ continue; ++ } ++ ++ net.minecraft.world.level.chunk.PalettedContainer blocks = section.states; ++ ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ for (int currX = minX; currX <= maxX; ++currX) { ++ int localBlockIndex = (currX) | (currZ << 4) | ((currY & 15) << 8); ++ int blockX = currX | chunkXGlobalPos; ++ int blockY = currY; ++ int blockZ = currZ | chunkZGlobalPos; ++ ++ int edgeCount = ((blockX == minBlockX || blockX == maxBlockX) ? 1 : 0) + ++ ((blockY == minBlockY || blockY == maxBlockY) ? 1 : 0) + ++ ((blockZ == minBlockZ || blockZ == maxBlockZ) ? 1 : 0); ++ if (edgeCount == 3) { ++ continue; ++ } ++ ++ BlockState blockData = blocks.get(localBlockIndex); ++ if (blockData.isAir()) { ++ continue; ++ } ++ ++ if ((edgeCount != 1 || blockData.shapeExceedsCube()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON)) { ++ mutablePos.set(blockX, blockY, blockZ); ++ if (collisionShape == null) { ++ collisionShape = new LazyEntityCollisionContext(entity); ++ } ++ VoxelShape voxelshape2 = blockData.getCollisionShape(getter, mutablePos, collisionShape); ++ if (voxelshape2 != Shapes.empty()) { ++ VoxelShape voxelshape3 = voxelshape2.move((double)blockX, (double)blockY, (double)blockZ); ++ ++ if (predicate != null && !predicate.test(blockData, mutablePos)) { ++ continue; ++ } ++ ++ if (checkOnly) { ++ if (voxelshape3.intersects(aabb)) { ++ return true; ++ } ++ } else { ++ ret |= addBoxesToIfIntersects(voxelshape3, aabb, into); ++ } ++ } ++ } ++ } ++ } ++ } ++ } ++ } ++ ++ return ret; ++ } ++ ++ public static boolean getEntityHardCollisions(final CollisionGetter getter, final Entity entity, AABB aabb, ++ final List into, final boolean checkOnly, final Predicate predicate) { ++ if (isEmpty(aabb) || !(getter instanceof EntityGetter entityGetter)) { ++ return false; ++ } ++ ++ boolean ret = false; ++ ++ // to comply with vanilla intersection rules, expand by -epsilon so we only get stuff we definitely collide with. ++ // Vanilla for hard collisions has this backwards, and they expand by +epsilon but this causes terrible problems ++ // specifically with boat collisions. ++ aabb = aabb.inflate(-COLLISION_EPSILON, -COLLISION_EPSILON, -COLLISION_EPSILON); ++ final List entities = CachedLists.getTempGetEntitiesList(); ++ try { ++ if (entity != null && entity.hardCollides()) { ++ entityGetter.getEntities(entity, aabb, predicate, entities); ++ } else { ++ entityGetter.getHardCollidingEntities(entity, aabb, predicate, entities); ++ } ++ ++ for (int i = 0, len = entities.size(); i < len; ++i) { ++ final Entity otherEntity = entities.get(i); ++ ++ if ((entity == null && otherEntity.canBeCollidedWith()) || (entity != null && entity.canCollideWith(otherEntity))) { ++ if (checkOnly) { ++ return true; ++ } else { ++ into.add(otherEntity.getBoundingBox()); ++ ret = true; ++ } ++ } ++ } ++ } finally { ++ CachedLists.returnTempGetEntitiesList(entities); ++ } ++ ++ return ret; ++ } ++ ++ public static boolean getCollisions(final CollisionGetter view, final Entity entity, final AABB aabb, ++ final List into, final boolean loadChunks, final boolean collidesWithUnloadedChunks, ++ final boolean checkBorder, final boolean checkOnly, final BiPredicate blockPredicate, ++ final Predicate entityPredicate) { ++ if (checkOnly) { ++ return getCollisionsForBlocksOrWorldBorder(view, entity, aabb, into, loadChunks, collidesWithUnloadedChunks, checkBorder, checkOnly, blockPredicate) ++ || getEntityHardCollisions(view, entity, aabb, into, checkOnly, entityPredicate); ++ } else { ++ return getCollisionsForBlocksOrWorldBorder(view, entity, aabb, into, loadChunks, collidesWithUnloadedChunks, checkBorder, checkOnly, blockPredicate) ++ | getEntityHardCollisions(view, entity, aabb, into, checkOnly, entityPredicate); ++ } ++ } ++ ++ public static final class LazyEntityCollisionContext extends EntityCollisionContext { ++ ++ private CollisionContext delegate; ++ private final Entity entity; ++ ++ public LazyEntityCollisionContext(final Entity entity) { ++ super(false, 0.0, null, null, null, Optional.ofNullable(entity)); ++ this.entity = entity; ++ } ++ ++ public CollisionContext getDelegate() { ++ return this.delegate == null ? this.delegate = (this.entity == null ? CollisionContext.empty() : CollisionContext.of(this.entity)) : this.delegate; ++ } ++ ++ @Override ++ public boolean isDescending() { ++ return this.getDelegate().isDescending(); ++ } ++ ++ @Override ++ public boolean isAbove(final VoxelShape shape, final BlockPos pos, final boolean defaultValue) { ++ return this.getDelegate().isAbove(shape, pos, defaultValue); ++ } ++ ++ @Override ++ public boolean hasItemOnFeet(final Item item) { ++ return this.getDelegate().hasItemOnFeet(item); ++ } ++ ++ @Override ++ public boolean isHoldingItem(final Item item) { ++ return this.getDelegate().isHoldingItem(item); ++ } ++ ++ @Override ++ public boolean canStandOnFluid(final FluidState state, final FlowingFluid fluid) { ++ return this.getDelegate().canStandOnFluid(state, fluid); ++ } ++ } ++ ++ private CollisionUtil() { ++ throw new RuntimeException(); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/voxel/AABBVoxelShape.java b/src/main/java/io/papermc/paper/voxel/AABBVoxelShape.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d67a40e7be030142443680c89e1763fc9ecdfe0a +--- /dev/null ++++ b/src/main/java/io/papermc/paper/voxel/AABBVoxelShape.java +@@ -0,0 +1,200 @@ ++package io.papermc.paper.voxel; ++ ++import io.papermc.paper.util.CollisionUtil; ++import it.unimi.dsi.fastutil.doubles.DoubleArrayList; ++import it.unimi.dsi.fastutil.doubles.DoubleList; ++import net.minecraft.core.Direction; ++import net.minecraft.world.phys.AABB; ++import net.minecraft.world.phys.shapes.Shapes; ++import net.minecraft.world.phys.shapes.VoxelShape; ++import java.util.ArrayList; ++import java.util.List; ++ ++public final class AABBVoxelShape extends VoxelShape { ++ ++ public final AABB aabb; ++ ++ public AABBVoxelShape(AABB aabb) { ++ super(Shapes.getFullUnoptimisedCube().shape); ++ this.aabb = aabb; ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return CollisionUtil.isEmpty(this.aabb); ++ } ++ ++ @Override ++ public double min(Direction.Axis enumdirection_enumaxis) { ++ switch (enumdirection_enumaxis.ordinal()) { ++ case 0: ++ return this.aabb.minX; ++ case 1: ++ return this.aabb.minY; ++ case 2: ++ return this.aabb.minZ; ++ default: ++ throw new IllegalStateException("Unknown axis requested"); ++ } ++ } ++ ++ @Override ++ public double max(Direction.Axis enumdirection_enumaxis) { ++ switch (enumdirection_enumaxis.ordinal()) { ++ case 0: ++ return this.aabb.maxX; ++ case 1: ++ return this.aabb.maxY; ++ case 2: ++ return this.aabb.maxZ; ++ default: ++ throw new IllegalStateException("Unknown axis requested"); ++ } ++ } ++ ++ @Override ++ public AABB bounds() { ++ return this.aabb; ++ } ++ ++ // enum direction axis is from 0 -> 2, so we keep the lower bits for direction axis. ++ @Override ++ protected double get(Direction.Axis enumdirection_enumaxis, int i) { ++ switch (enumdirection_enumaxis.ordinal() | (i << 2)) { ++ case (0 | (0 << 2)): ++ return this.aabb.minX; ++ case (1 | (0 << 2)): ++ return this.aabb.minY; ++ case (2 | (0 << 2)): ++ return this.aabb.minZ; ++ case (0 | (1 << 2)): ++ return this.aabb.maxX; ++ case (1 | (1 << 2)): ++ return this.aabb.maxY; ++ case (2 | (1 << 2)): ++ return this.aabb.maxZ; ++ default: ++ throw new IllegalStateException("Unknown axis requested"); ++ } ++ } ++ ++ private DoubleList cachedListX; ++ private DoubleList cachedListY; ++ private DoubleList cachedListZ; ++ ++ @Override ++ protected DoubleList getCoords(Direction.Axis enumdirection_enumaxis) { ++ switch (enumdirection_enumaxis.ordinal()) { ++ case 0: ++ return this.cachedListX == null ? this.cachedListX = DoubleArrayList.wrap(new double[] { this.aabb.minX, this.aabb.maxX }) : this.cachedListX; ++ case 1: ++ return this.cachedListY == null ? this.cachedListY = DoubleArrayList.wrap(new double[] { this.aabb.minY, this.aabb.maxY }) : this.cachedListY; ++ case 2: ++ return this.cachedListZ == null ? this.cachedListZ = DoubleArrayList.wrap(new double[] { this.aabb.minZ, this.aabb.maxZ }) : this.cachedListZ; ++ default: ++ throw new IllegalStateException("Unknown axis requested"); ++ } ++ } ++ ++ @Override ++ public VoxelShape move(double d0, double d1, double d2) { ++ return new AABBVoxelShape(this.aabb.move(d0, d1, d2)); ++ } ++ ++ @Override ++ public VoxelShape optimize() { ++ if (this.isEmpty()) { ++ return Shapes.empty(); ++ } else if (this == Shapes.BLOCK_OPTIMISED || this.aabb.equals(Shapes.BLOCK_OPTIMISED.aabb)) { ++ return Shapes.BLOCK_OPTIMISED; ++ } ++ return this; ++ } ++ ++ @Override ++ public void forAllBoxes(Shapes.DoubleLineConsumer voxelshapes_a) { ++ voxelshapes_a.consume(this.aabb.minX, this.aabb.minY, this.aabb.minZ, this.aabb.maxX, this.aabb.maxY, this.aabb.maxZ); ++ } ++ ++ @Override ++ public List toAabbs() { // getAABBs ++ List ret = new ArrayList<>(1); ++ ret.add(this.aabb); ++ return ret; ++ } ++ ++ @Override ++ protected int findIndex(Direction.Axis enumdirection_enumaxis, double d0) { // findPointIndexAfterOffset ++ switch (enumdirection_enumaxis.ordinal()) { ++ case 0: ++ return d0 < this.aabb.maxX ? (d0 < this.aabb.minX ? -1 : 0) : 1; ++ case 1: ++ return d0 < this.aabb.maxY ? (d0 < this.aabb.minY ? -1 : 0) : 1; ++ case 2: ++ return d0 < this.aabb.maxZ ? (d0 < this.aabb.minZ ? -1 : 0) : 1; ++ default: ++ throw new IllegalStateException("Unknown axis requested"); ++ } ++ } ++ ++ @Override ++ protected VoxelShape calculateFace(Direction direction) { ++ if (this.isEmpty()) { ++ return Shapes.empty(); ++ } ++ if (this == Shapes.BLOCK_OPTIMISED) { ++ return this; ++ } ++ switch (direction) { ++ case EAST: // +X ++ case WEST: { // -X ++ final double from = direction == Direction.EAST ? 1.0 - CollisionUtil.COLLISION_EPSILON : CollisionUtil.COLLISION_EPSILON; ++ if (from > this.aabb.maxX || this.aabb.minX > from) { ++ return Shapes.empty(); ++ } ++ return new AABBVoxelShape(new AABB(0.0, this.aabb.minY, this.aabb.minZ, 1.0, this.aabb.maxY, this.aabb.maxZ)).optimize(); ++ } ++ case UP: // +Y ++ case DOWN: { // -Y ++ final double from = direction == Direction.UP ? 1.0 - CollisionUtil.COLLISION_EPSILON : CollisionUtil.COLLISION_EPSILON; ++ if (from > this.aabb.maxY || this.aabb.minY > from) { ++ return Shapes.empty(); ++ } ++ return new AABBVoxelShape(new AABB(this.aabb.minX, 0.0, this.aabb.minZ, this.aabb.maxX, 1.0, this.aabb.maxZ)).optimize(); ++ } ++ case SOUTH: // +Z ++ case NORTH: { // -Z ++ final double from = direction == Direction.SOUTH ? 1.0 - CollisionUtil.COLLISION_EPSILON : CollisionUtil.COLLISION_EPSILON; ++ if (from > this.aabb.maxZ || this.aabb.minZ > from) { ++ return Shapes.empty(); ++ } ++ return new AABBVoxelShape(new AABB(this.aabb.minX, this.aabb.minY, 0.0, this.aabb.maxX, this.aabb.maxY, 1.0)).optimize(); ++ } ++ default: { ++ throw new IllegalStateException("Unknown axis requested"); ++ } ++ } ++ } ++ ++ @Override ++ public double collide(Direction.Axis enumdirection_enumaxis, AABB axisalignedbb, double d0) { ++ if (CollisionUtil.isEmpty(this.aabb) || CollisionUtil.isEmpty(axisalignedbb)) { ++ return d0; ++ } ++ switch (enumdirection_enumaxis.ordinal()) { ++ case 0: ++ return CollisionUtil.collideX(this.aabb, axisalignedbb, d0); ++ case 1: ++ return CollisionUtil.collideY(this.aabb, axisalignedbb, d0); ++ case 2: ++ return CollisionUtil.collideZ(this.aabb, axisalignedbb, d0); ++ default: ++ throw new IllegalStateException("Unknown axis requested"); ++ } ++ } ++ ++ @Override ++ public boolean intersects(AABB axisalingedbb) { ++ return CollisionUtil.voxelShapeIntersect(this.aabb, axisalingedbb); ++ } ++} +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index 44b2429d9cc20bdebe58203f973382a4f7ea4c6c..4345c0e1cf00cb8cd0cacc93ebc0a69009be7eff 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -419,7 +419,7 @@ public class ServerPlayer extends Player { + + if (blockposition1 != null) { + this.moveTo(blockposition1, 0.0F, 0.0F); +- if (world.noCollision(this)) { ++ if (world.noCollision(this, this.getBoundingBox(), null, true)) { // Paper - make sure this loads chunks, we default to NOT loading now + break; + } + } +@@ -427,7 +427,7 @@ public class ServerPlayer extends Player { + } else { + this.moveTo(blockposition, 0.0F, 0.0F); + +- while (!world.noCollision(this) && this.getY() < (double) (world.getMaxBuildHeight() - 1)) { ++ while (!world.noCollision(this, this.getBoundingBox(), null, true) && this.getY() < (double) (world.getMaxBuildHeight() - 1)) { // Paper - make sure this loads chunks, we default to NOT loading now + this.setPos(this.getX(), this.getY() + 1.0D, this.getZ()); + } + } +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index cf8ed790e09987528178519ba99376f27b15245f..4c8f6d2d72194d313e7383b5a499c8ca1a84e1da 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -934,7 +934,7 @@ public abstract class PlayerList { + + worldserver1.getChunkSource().addRegionTicket(net.minecraft.server.level.TicketType.POST_TELEPORT, new net.minecraft.world.level.ChunkPos(location.getBlockX() >> 4, location.getBlockZ() >> 4), 1, entityplayer.getId()); // Paper + entityplayer1.forceCheckHighPriority(); // Player - Chunk priority +- while (avoidSuffocation && !worldserver1.noCollision(entityplayer1) && entityplayer1.getY() < (double) worldserver1.getMaxBuildHeight()) { ++ while (avoidSuffocation && !worldserver1.noCollision(entityplayer1, entityplayer1.getBoundingBox(), null, true) && entityplayer1.getY() < (double) worldserver1.getMaxBuildHeight()) { // Paper - make sure this loads chunks, we default to NOT loading now + entityplayer1.setPos(entityplayer1.getX(), entityplayer1.getY() + 1.0D, entityplayer1.getZ()); + } + // CraftBukkit start +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 74fcc1b45a9e57280da82f7c181530d4183872a5..481e84fda6dccfaf684c922a12fa19ed35c87b3c 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -1025,9 +1025,44 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n + float f2 = this.getBlockSpeedFactor(); + + this.setDeltaMovement(this.getDeltaMovement().multiply((double) f2, 1.0D, (double) f2)); +- if (this.level.getBlockStatesIfLoaded(this.getBoundingBox().deflate(1.0E-6D)).noneMatch((iblockdata1) -> { +- return iblockdata1.is((Tag) BlockTags.FIRE) || iblockdata1.is(Blocks.LAVA); +- })) { ++ // Paper start - remove expensive streams from here ++ boolean noneMatch = true; ++ AABB fireSearchBox = this.getBoundingBox().deflate(1.0E-6D); ++ { ++ int minX = Mth.floor(fireSearchBox.minX); ++ int minY = Mth.floor(fireSearchBox.minY); ++ int minZ = Mth.floor(fireSearchBox.minZ); ++ int maxX = Mth.floor(fireSearchBox.maxX); ++ int maxY = Mth.floor(fireSearchBox.maxY); ++ int maxZ = Mth.floor(fireSearchBox.maxZ); ++ fire_search_loop: ++ for (int fz = minZ; fz <= maxZ; ++fz) { ++ for (int fx = minX; fx <= maxX; ++fx) { ++ for (int fy = minY; fy <= maxY; ++fy) { ++ net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk)this.level.getChunkIfLoadedImmediately(fx >> 4, fz >> 4); ++ if (chunk == null) { ++ // Vanilla rets an empty stream if all the chunks are not loaded, so noneMatch will be true ++ // even if we're in lava/fire ++ noneMatch = true; ++ break fire_search_loop; ++ } ++ if (!noneMatch) { ++ // don't do get type, we already know we're in fire - we just need to check the chunks ++ // loaded state ++ continue; ++ } ++ ++ BlockState type = chunk.getType(fx, fy, fz); ++ if (type.is((Tag) BlockTags.FIRE) || type.is(Blocks.LAVA)) { ++ noneMatch = false; ++ // can't break, we need to retain vanilla behavior by ensuring ALL chunks are loaded ++ } ++ } ++ } ++ } ++ } ++ if (noneMatch) { ++ // Paper end - remove expensive streams from here + if (this.remainingFireTicks <= 0) { + this.setRemainingFireTicks(-this.getFireImmuneTicks()); + } +@@ -1149,39 +1184,79 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n + return offsetFactor; + } + +- private Vec3 collide(Vec3 movement) { +- AABB axisalignedbb = this.getBoundingBox(); +- CollisionContext voxelshapecollision = CollisionContext.of(this); +- VoxelShape voxelshape = this.level.getWorldBorder().getCollisionShape(); +- Stream stream = !this.level.getWorldBorder().isWithinBounds(axisalignedbb) ? Stream.empty() : Stream.of(voxelshape); // Paper +- Stream stream1 = this.level.getEntityCollisions(this, axisalignedbb.expandTowards(movement), (entity) -> { +- return true; +- }); +- RewindableStream streamaccumulator = new RewindableStream<>(Stream.concat(stream1, stream)); +- Vec3 vec3d1 = movement.lengthSqr() == 0.0D ? movement : Entity.collideBoundingBoxHeuristically(this, movement, axisalignedbb, this.level, voxelshapecollision, streamaccumulator); +- boolean flag = movement.x != vec3d1.x; +- boolean flag1 = movement.y != vec3d1.y; +- boolean flag2 = movement.z != vec3d1.z; +- boolean flag3 = this.onGround || flag1 && movement.y < 0.0D; +- +- if (this.maxUpStep > 0.0F && flag3 && (flag || flag2)) { +- Vec3 vec3d2 = Entity.collideBoundingBoxHeuristically(this, new Vec3(movement.x, (double) this.maxUpStep, movement.z), axisalignedbb, this.level, voxelshapecollision, streamaccumulator); +- Vec3 vec3d3 = Entity.collideBoundingBoxHeuristically(this, new Vec3(0.0D, (double) this.maxUpStep, 0.0D), axisalignedbb.expandTowards(movement.x, 0.0D, movement.z), this.level, voxelshapecollision, streamaccumulator); +- +- if (vec3d3.y < (double) this.maxUpStep) { +- Vec3 vec3d4 = Entity.collideBoundingBoxHeuristically(this, new Vec3(movement.x, 0.0D, movement.z), axisalignedbb.move(vec3d3), this.level, voxelshapecollision, streamaccumulator).add(vec3d3); +- +- if (vec3d4.horizontalDistanceSqr() > vec3d2.horizontalDistanceSqr()) { +- vec3d2 = vec3d4; ++ private Vec3 collide(Vec3 moveVector) { ++ // Paper start - optimise collisions ++ // This is a copy of vanilla's except that it uses strictly AABB math ++ if (moveVector.x == 0.0 && moveVector.y == 0.0 && moveVector.z == 0.0) { ++ return moveVector; ++ } ++ ++ final Level world = this.level; ++ final AABB currBoundingBox = this.getBoundingBox(); ++ ++ if (io.papermc.paper.util.CollisionUtil.isEmpty(currBoundingBox)) { ++ return moveVector; ++ } ++ ++ final List potentialCollisions = io.papermc.paper.util.CachedLists.getTempCollisionList(); ++ try { ++ final double stepHeight = (double)this.maxUpStep; ++ final AABB collisionBox; ++ ++ if (moveVector.x == 0.0 && moveVector.z == 0.0 && moveVector.y != 0.0) { ++ if (moveVector.y > 0.0) { ++ collisionBox = io.papermc.paper.util.CollisionUtil.cutUpwards(currBoundingBox, moveVector.y); ++ } else { ++ collisionBox = io.papermc.paper.util.CollisionUtil.cutDownwards(currBoundingBox, moveVector.y); ++ } ++ } else { ++ if (stepHeight > 0.0 && (this.onGround || (moveVector.y < 0.0)) && (moveVector.x != 0.0 || moveVector.z != 0.0)) { ++ // don't bother getting the collisions if we don't need them. ++ if (moveVector.y <= 0.0) { ++ collisionBox = io.papermc.paper.util.CollisionUtil.expandUpwards(currBoundingBox.expandTowards(moveVector.x, moveVector.y, moveVector.z), stepHeight); ++ } else { ++ collisionBox = currBoundingBox.expandTowards(moveVector.x, Math.max(stepHeight, moveVector.y), moveVector.z); ++ } ++ } else { ++ collisionBox = currBoundingBox.expandTowards(moveVector.x, moveVector.y, moveVector.z); + } + } + +- if (vec3d2.horizontalDistanceSqr() > vec3d1.horizontalDistanceSqr()) { +- return vec3d2.add(Entity.collideBoundingBoxHeuristically(this, new Vec3(0.0D, -vec3d2.y + movement.y, 0.0D), axisalignedbb.move(vec3d2), this.level, voxelshapecollision, streamaccumulator)); ++ io.papermc.paper.util.CollisionUtil.getCollisions(world, this, collisionBox, potentialCollisions, false, true, ++ false, false, null, null); ++ ++ if (io.papermc.paper.util.CollisionUtil.isCollidingWithBorderEdge(world.getWorldBorder(), collisionBox)) { ++ io.papermc.paper.util.CollisionUtil.addBoxesToIfIntersects(world.getWorldBorder().getCollisionShape(), collisionBox, potentialCollisions); + } +- } + +- return vec3d1; ++ final Vec3 limitedMoveVector = io.papermc.paper.util.CollisionUtil.performCollisions(moveVector, currBoundingBox, potentialCollisions); ++ ++ if (stepHeight > 0.0 ++ && (this.onGround || (limitedMoveVector.y != moveVector.y && moveVector.y < 0.0)) ++ && (limitedMoveVector.x != moveVector.x || limitedMoveVector.z != moveVector.z)) { ++ Vec3 vec3d2 = io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(moveVector.x, stepHeight, moveVector.z), currBoundingBox, potentialCollisions); ++ final Vec3 vec3d3 = io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(0.0, stepHeight, 0.0), currBoundingBox.expandTowards(moveVector.x, 0.0, moveVector.z), potentialCollisions); ++ ++ if (vec3d3.y < stepHeight) { ++ final Vec3 vec3d4 = io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(moveVector.x, 0.0D, moveVector.z), currBoundingBox.move(vec3d3), potentialCollisions).add(vec3d3); ++ ++ if (vec3d4.horizontalDistanceSqr() > vec3d2.horizontalDistanceSqr()) { ++ vec3d2 = vec3d4; ++ } ++ } ++ ++ if (vec3d2.horizontalDistanceSqr() > limitedMoveVector.horizontalDistanceSqr()) { ++ return vec3d2.add(io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(0.0D, -vec3d2.y + moveVector.y, 0.0D), currBoundingBox.move(vec3d2), potentialCollisions)); ++ } ++ ++ return limitedMoveVector; ++ } else { ++ return limitedMoveVector; ++ } ++ } finally { ++ io.papermc.paper.util.CachedLists.returnTempCollisionList(potentialCollisions); ++ } ++ // Paper end - optimise collisions + } + + public static Vec3 collideBoundingBoxHeuristically(@Nullable Entity entity, Vec3 movement, AABB entityBoundingBox, Level world, CollisionContext context, RewindableStream collisions) { +@@ -2320,9 +2395,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n + float f = this.dimensions.width * 0.8F; + AABB axisalignedbb = AABB.ofSize(this.getEyePosition(), (double) f, 1.0E-6D, (double) f); + +- return this.level.getBlockCollisions(this, axisalignedbb, (iblockdata, blockposition) -> { +- return iblockdata.isSuffocating(this.level, blockposition); +- }).findAny().isPresent(); ++ // Paper start ++ return io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this.level, this, axisalignedbb, null, ++ false, false, false, true, (iblockdata, blockposition) -> { ++ return iblockdata.isSuffocating(this.level, blockposition); ++ }); ++ // Paper end + } + } + +diff --git a/src/main/java/net/minecraft/world/level/CollisionGetter.java b/src/main/java/net/minecraft/world/level/CollisionGetter.java +index 2a784a8342e708e0813c7076a2ca8e429446ffd3..f38cd7e2c341ae2fbbe9dd26cf872da9571b416a 100644 +--- a/src/main/java/net/minecraft/world/level/CollisionGetter.java ++++ b/src/main/java/net/minecraft/world/level/CollisionGetter.java +@@ -36,28 +36,40 @@ public interface CollisionGetter extends BlockGetter { + return this.isUnobstructed(entity, Shapes.create(entity.getBoundingBox())); + } + ++ // Paper start - optimise collisions ++ default boolean noCollision(Entity entity, AABB box, Predicate filter, boolean loadChunks) { ++ return !io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this, entity, box, null, loadChunks, false, entity != null, true, null) ++ && !io.papermc.paper.util.CollisionUtil.getEntityHardCollisions(this, entity, box, null, true, filter); ++ } ++ // Paper end - optimise collisions ++ + default boolean noCollision(AABB box) { +- return this.noCollision((Entity)null, box, (e) -> { +- return true; +- }); ++ // Paper start - optimise collisions ++ return !io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this, null, box, null, false, false, false, true, null) ++ && !io.papermc.paper.util.CollisionUtil.getEntityHardCollisions(this, null, box, null, true, null); ++ // Paper end - optimise collisions + } + + default boolean noCollision(Entity entity) { +- return this.noCollision(entity, entity.getBoundingBox(), (e) -> { +- return true; +- }); ++ // Paper start - optimise collisions ++ AABB box = entity.getBoundingBox(); ++ return !io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this, entity, box, null, false, false, entity != null, true, null) ++ && !io.papermc.paper.util.CollisionUtil.getEntityHardCollisions(this, entity, box, null, true, null); ++ // Paper end - optimise collisions + } + + default boolean noCollision(Entity entity, AABB box) { +- return this.noCollision(entity, box, (e) -> { +- return true; +- }); ++ // Paper start - optimise collisions ++ return !io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this, entity, box, null, false, false, entity != null, true, null) ++ && !io.papermc.paper.util.CollisionUtil.getEntityHardCollisions(this, entity, box, null, true, null); ++ // Paper end - optimise collisions + } + + default boolean noCollision(@Nullable Entity entity, AABB box, Predicate filter) { +- try { if (entity != null) entity.collisionLoadChunks = true; // Paper +- return this.getCollisions(entity, box, filter).allMatch(VoxelShape::isEmpty); +- } finally { if (entity != null) entity.collisionLoadChunks = false; } // Paper ++ // Paper start - optimise collisions ++ return !io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this, entity, box, null, false, false, entity != null, true, null) ++ && !io.papermc.paper.util.CollisionUtil.getEntityHardCollisions(this, entity, box, null, true, filter); ++ // Paper end - optimise collisions + } + + Stream getEntityCollisions(@Nullable Entity entity, AABB box, Predicate predicate); +diff --git a/src/main/java/net/minecraft/world/level/CollisionSpliterator.java b/src/main/java/net/minecraft/world/level/CollisionSpliterator.java +index 6124e3a32325e8c74bf839010a79d7c82c49aaff..cffb08bcb63f7ee2d8a163d865d87a9031f19407 100644 +--- a/src/main/java/net/minecraft/world/level/CollisionSpliterator.java ++++ b/src/main/java/net/minecraft/world/level/CollisionSpliterator.java +@@ -106,7 +106,7 @@ public class CollisionSpliterator extends AbstractSpliterator { + + VoxelShape voxelShape = blockState.getCollisionShape(this.collisionGetter, this.pos, this.context); + if (voxelShape == Shapes.block()) { +- if (!this.box.intersects((double)i, (double)j, (double)k, (double)i + 1.0D, (double)j + 1.0D, (double)k + 1.0D)) { ++ if (!io.papermc.paper.util.CollisionUtil.voxelShapeIntersect(this.box, (double)i, (double)j, (double)k, (double)i + 1.0D, (double)j + 1.0D, (double)k + 1.0D)) { // Paper - keep vanilla behavior for voxelshape intersection - See comment in CollisionUtil + continue; + } + +diff --git a/src/main/java/net/minecraft/world/level/EntityGetter.java b/src/main/java/net/minecraft/world/level/EntityGetter.java +index 94130509e3a7980c378cc95c46821cf0fc753ce6..7224c56e8a68870364c6538c82c04f371b74aabd 100644 +--- a/src/main/java/net/minecraft/world/level/EntityGetter.java ++++ b/src/main/java/net/minecraft/world/level/EntityGetter.java +@@ -49,7 +49,7 @@ public interface EntityGetter { + return true; + } else { + for(Entity entity2 : this.getEntities(entity, shape.bounds())) { +- if (!entity2.isRemoved() && entity2.blocksBuilding && (entity == null || !entity2.isPassengerOfSameVehicle(entity)) && Shapes.joinIsNotEmpty(shape, Shapes.create(entity2.getBoundingBox()), BooleanOp.AND)) { ++ if (!entity2.isRemoved() && entity2.blocksBuilding && (entity == null || !entity2.isPassengerOfSameVehicle(entity)) && shape.intersects(entity2.getBoundingBox())) { // Paper + return false; + } + } +@@ -66,7 +66,7 @@ public interface EntityGetter { + if (box.getSize() < 1.0E-7D) { + return Stream.empty(); + } else { +- AABB aABB = box.inflate(1.0E-7D); ++ AABB aABB = box.inflate(-1.0E-7D); // Paper - needs to be negated, or else we get things we don't collide with + Predicate hardCollides = (entityx) -> { // Paper - optimise entity hard collisions + if (true || entityx.getBoundingBox().intersects(aABB)) { // Paper - always true + if (entity == null) { +diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java +index 4a7fdea6a5f966db444dc41f7215faa99e3820b3..d87f8d106834678364f8720447d671de60c2454e 100644 +--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java ++++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java +@@ -685,7 +685,7 @@ public abstract class BlockBehaviour { + } + this.shapeExceedsCube = this.cache == null || this.cache.largeCollisionShape; // Paper - moved from actual method to here + this.opacityIfCached = this.cache == null || this.isConditionallyFullOpaque() ? -1 : this.cache.lightBlock; // Paper - cache opacity for light +- ++ // TODO optimise light + } + + public Block getBlock() { +diff --git a/src/main/java/net/minecraft/world/phys/AABB.java b/src/main/java/net/minecraft/world/phys/AABB.java +index 120498a39b7ca7aee9763084507508d4a1c425aa..68cc6f2a78a06293a29317fda72ab3ee79b3533a 100644 +--- a/src/main/java/net/minecraft/world/phys/AABB.java ++++ b/src/main/java/net/minecraft/world/phys/AABB.java +@@ -25,6 +25,17 @@ public class AABB { + this.maxZ = Math.max(z1, z2); + } + ++ // Paper start ++ public AABB(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, boolean dummy) { ++ this.minX = minX; ++ this.minY = minY; ++ this.minZ = minZ; ++ this.maxX = maxX; ++ this.maxY = maxY; ++ this.maxZ = maxZ; ++ } ++ // Paper end ++ + public AABB(BlockPos pos) { + this((double)pos.getX(), (double)pos.getY(), (double)pos.getZ(), (double)(pos.getX() + 1), (double)(pos.getY() + 1), (double)(pos.getZ() + 1)); + } +diff --git a/src/main/java/net/minecraft/world/phys/shapes/ArrayVoxelShape.java b/src/main/java/net/minecraft/world/phys/shapes/ArrayVoxelShape.java +index 99427b6130895ddecee8bcf77db72d809c24c375..61f5339042290eeaea54964cd3838d0bf4646cb2 100644 +--- a/src/main/java/net/minecraft/world/phys/shapes/ArrayVoxelShape.java ++++ b/src/main/java/net/minecraft/world/phys/shapes/ArrayVoxelShape.java +@@ -6,6 +6,9 @@ import java.util.Arrays; + import net.minecraft.Util; + import net.minecraft.core.Direction; + ++// Paper start ++import it.unimi.dsi.fastutil.doubles.AbstractDoubleList; ++// Paper end + public class ArrayVoxelShape extends VoxelShape { + private final DoubleList xs; + private final DoubleList ys; +@@ -16,6 +19,11 @@ public class ArrayVoxelShape extends VoxelShape { + } + + ArrayVoxelShape(DiscreteVoxelShape shape, DoubleList xPoints, DoubleList yPoints, DoubleList zPoints) { ++ // Paper start - optimise multi-aabb shapes ++ this(shape, xPoints, yPoints, zPoints, null, 0.0, 0.0, 0.0); ++ } ++ ArrayVoxelShape(DiscreteVoxelShape shape, DoubleList xPoints, DoubleList yPoints, DoubleList zPoints, net.minecraft.world.phys.AABB[] boundingBoxesRepresentation, double offsetX, double offsetY, double offsetZ) { ++ // Paper end - optimise multi-aabb shapes + super(shape); + int i = shape.getXSize() + 1; + int j = shape.getYSize() + 1; +@@ -27,6 +35,12 @@ public class ArrayVoxelShape extends VoxelShape { + } else { + throw (IllegalArgumentException)Util.pauseInIde(new IllegalArgumentException("Lengths of point arrays must be consistent with the size of the VoxelShape.")); + } ++ // Paper start - optimise multi-aabb shapes ++ this.boundingBoxesRepresentation = boundingBoxesRepresentation == null ? this.toAabbs().toArray(EMPTY) : boundingBoxesRepresentation; ++ this.offsetX = offsetX; ++ this.offsetY = offsetY; ++ this.offsetZ = offsetZ; ++ // Paper end - optimise multi-aabb shapes + } + + @Override +@@ -42,4 +56,152 @@ public class ArrayVoxelShape extends VoxelShape { + throw new IllegalArgumentException(); + } + } ++ ++ // Paper start ++ public static final class DoubleListOffsetExposed extends AbstractDoubleList { ++ ++ public final DoubleArrayList list; ++ public final double offset; ++ ++ public DoubleListOffsetExposed(final DoubleArrayList list, final double offset) { ++ this.list = list; ++ this.offset = offset; ++ } ++ ++ @Override ++ public double getDouble(final int index) { ++ return this.list.getDouble(index) + this.offset; ++ } ++ ++ @Override ++ public int size() { ++ return this.list.size(); ++ } ++ } ++ ++ static final net.minecraft.world.phys.AABB[] EMPTY = new net.minecraft.world.phys.AABB[0]; ++ final net.minecraft.world.phys.AABB[] boundingBoxesRepresentation; ++ ++ final double offsetX; ++ final double offsetY; ++ final double offsetZ; ++ ++ public final net.minecraft.world.phys.AABB[] getBoundingBoxesRepresentation() { ++ return this.boundingBoxesRepresentation; ++ } ++ ++ public final double getOffsetX() { ++ return this.offsetX; ++ } ++ ++ public final double getOffsetY() { ++ return this.offsetY; ++ } ++ ++ public final double getOffsetZ() { ++ return this.offsetZ; ++ } ++ ++ @Override ++ public java.util.List toAabbs() { ++ if (this.boundingBoxesRepresentation == null) { ++ return super.toAabbs(); ++ } ++ java.util.List ret = new java.util.ArrayList<>(this.boundingBoxesRepresentation.length); ++ ++ double offX = this.offsetX; ++ double offY = this.offsetY; ++ double offZ = this.offsetZ; ++ ++ for (net.minecraft.world.phys.AABB boundingBox : this.boundingBoxesRepresentation) { ++ ret.add(boundingBox.move(offX, offY, offZ)); ++ } ++ ++ return ret; ++ } ++ ++ protected static DoubleArrayList getList(DoubleList from) { ++ if (from instanceof DoubleArrayList) { ++ return (DoubleArrayList)from; ++ } else { ++ return DoubleArrayList.wrap(from.toDoubleArray()); ++ } ++ } ++ ++ @Override ++ public VoxelShape move(double x, double y, double z) { ++ if (x == 0.0 && y == 0.0 && z == 0.0) { ++ return this; ++ } ++ DoubleListOffsetExposed xPoints, yPoints, zPoints; ++ double offsetX, offsetY, offsetZ; ++ ++ if (this.xs instanceof DoubleListOffsetExposed) { ++ xPoints = new DoubleListOffsetExposed(((DoubleListOffsetExposed)this.xs).list, offsetX = this.offsetX + x); ++ yPoints = new DoubleListOffsetExposed(((DoubleListOffsetExposed)this.ys).list, offsetY = this.offsetY + y); ++ zPoints = new DoubleListOffsetExposed(((DoubleListOffsetExposed)this.zs).list, offsetZ = this.offsetZ + z); ++ } else { ++ xPoints = new DoubleListOffsetExposed(getList(this.xs), offsetX = x); ++ yPoints = new DoubleListOffsetExposed(getList(this.ys), offsetY = y); ++ zPoints = new DoubleListOffsetExposed(getList(this.zs), offsetZ = z); ++ } ++ ++ return new ArrayVoxelShape(this.shape, xPoints, yPoints, zPoints, this.boundingBoxesRepresentation, offsetX, offsetY, offsetZ); ++ } ++ ++ @Override ++ public final boolean intersects(net.minecraft.world.phys.AABB axisalingedbb) { ++ // this can be optimised by checking an "overall shape" first, but not needed ++ double offX = this.offsetX; ++ double offY = this.offsetY; ++ double offZ = this.offsetZ; ++ ++ for (net.minecraft.world.phys.AABB boundingBox : this.boundingBoxesRepresentation) { ++ if (io.papermc.paper.util.CollisionUtil.voxelShapeIntersect(axisalingedbb, boundingBox.minX + offX, boundingBox.minY + offY, boundingBox.minZ + offZ, ++ boundingBox.maxX + offX, boundingBox.maxY + offY, boundingBox.maxZ + offZ)) { ++ return true; ++ } ++ } ++ ++ return false; ++ } ++ ++ @Override ++ public void forAllBoxes(Shapes.DoubleLineConsumer doubleLineConsumer) { ++ if (this.boundingBoxesRepresentation == null) { ++ super.forAllBoxes(doubleLineConsumer); ++ return; ++ } ++ for (final net.minecraft.world.phys.AABB boundingBox : this.boundingBoxesRepresentation) { ++ doubleLineConsumer.consume(boundingBox.minX + this.offsetX, boundingBox.minY + this.offsetY, boundingBox.minZ + this.offsetZ, ++ boundingBox.maxX + this.offsetX, boundingBox.maxY + this.offsetY, boundingBox.maxZ + this.offsetZ); ++ } ++ } ++ ++ @Override ++ public VoxelShape optimize() { ++ if (this == Shapes.empty() || this.boundingBoxesRepresentation.length == 0) { ++ return this; ++ } ++ ++ VoxelShape simplified = Shapes.empty(); ++ for (final net.minecraft.world.phys.AABB boundingBox : this.boundingBoxesRepresentation) { ++ simplified = Shapes.joinUnoptimized(simplified, Shapes.box(boundingBox.minX + this.offsetX, boundingBox.minY + this.offsetY, boundingBox.minZ + this.offsetZ, ++ boundingBox.maxX + this.offsetX, boundingBox.maxY + this.offsetY, boundingBox.maxZ + this.offsetZ), BooleanOp.OR); ++ } ++ ++ if (!(simplified instanceof ArrayVoxelShape)) { ++ return simplified; ++ } ++ ++ final net.minecraft.world.phys.AABB[] boundingBoxesRepresentation = ((ArrayVoxelShape)simplified).getBoundingBoxesRepresentation(); ++ ++ if (boundingBoxesRepresentation.length == 1) { ++ return new io.papermc.paper.voxel.AABBVoxelShape(boundingBoxesRepresentation[0]).optimize(); ++ } ++ ++ return simplified; ++ } ++ // Paper end ++ + } +diff --git a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java +index 16bc18cacbf7a23fb744c8a12e7fd8da699b2fea..2fb416bd1d7a9879c13907b7e3c6b857fb1bf0ed 100644 +--- a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java ++++ b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java +@@ -26,16 +26,17 @@ public final class Shapes { + DiscreteVoxelShape discreteVoxelShape = new BitSetDiscreteVoxelShape(1, 1, 1); + discreteVoxelShape.fill(0, 0, 0); + return new CubeVoxelShape(discreteVoxelShape); +- }); ++ }); public static VoxelShape getFullUnoptimisedCube() { return BLOCK; } // Paper - OBFHELPER + public static final VoxelShape INFINITY = box(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); + private static final VoxelShape EMPTY = new ArrayVoxelShape(new BitSetDiscreteVoxelShape(0, 0, 0), (DoubleList)(new DoubleArrayList(new double[]{0.0D})), (DoubleList)(new DoubleArrayList(new double[]{0.0D})), (DoubleList)(new DoubleArrayList(new double[]{0.0D}))); ++ public static final io.papermc.paper.voxel.AABBVoxelShape BLOCK_OPTIMISED = new io.papermc.paper.voxel.AABBVoxelShape(new AABB(0.0, 0.0, 0.0, 1.0, 1.0, 1.0)); // Paper + + public static VoxelShape empty() { + return EMPTY; + } + + public static VoxelShape block() { +- return BLOCK; ++ return BLOCK_OPTIMISED; // Paper + } + + public static VoxelShape box(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { +@@ -47,30 +48,11 @@ public final class Shapes { + } + + public static VoxelShape create(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { +- if (!(maxX - minX < 1.0E-7D) && !(maxY - minY < 1.0E-7D) && !(maxZ - minZ < 1.0E-7D)) { +- int i = findBits(minX, maxX); +- int j = findBits(minY, maxY); +- int k = findBits(minZ, maxZ); +- if (i >= 0 && j >= 0 && k >= 0) { +- if (i == 0 && j == 0 && k == 0) { +- return block(); +- } else { +- int l = 1 << i; +- int m = 1 << j; +- int n = 1 << k; +- BitSetDiscreteVoxelShape bitSetDiscreteVoxelShape = BitSetDiscreteVoxelShape.withFilledBounds(l, m, n, (int)Math.round(minX * (double)l), (int)Math.round(minY * (double)m), (int)Math.round(minZ * (double)n), (int)Math.round(maxX * (double)l), (int)Math.round(maxY * (double)m), (int)Math.round(maxZ * (double)n)); +- return new CubeVoxelShape(bitSetDiscreteVoxelShape); +- } +- } else { +- return new ArrayVoxelShape(BLOCK.shape, (DoubleList)DoubleArrayList.wrap(new double[]{minX, maxX}), (DoubleList)DoubleArrayList.wrap(new double[]{minY, maxY}), (DoubleList)DoubleArrayList.wrap(new double[]{minZ, maxZ})); +- } +- } else { +- return empty(); +- } ++ return new io.papermc.paper.voxel.AABBVoxelShape(new AABB(minX, minY, minZ, maxX, maxY, maxZ)); // Paper + } + + public static VoxelShape create(AABB box) { +- return create(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ); ++ return new io.papermc.paper.voxel.AABBVoxelShape(box); // Paper + } + + @VisibleForTesting +@@ -132,6 +114,20 @@ public final class Shapes { + } + + public static boolean joinIsNotEmpty(VoxelShape shape1, VoxelShape shape2, BooleanOp predicate) { ++ // Paper start - optimise voxelshape ++ if (predicate == BooleanOp.AND) { ++ if (shape1 instanceof io.papermc.paper.voxel.AABBVoxelShape && shape2 instanceof io.papermc.paper.voxel.AABBVoxelShape) { ++ return io.papermc.paper.util.CollisionUtil.voxelShapeIntersect(((io.papermc.paper.voxel.AABBVoxelShape)shape1).aabb, ((io.papermc.paper.voxel.AABBVoxelShape)shape2).aabb); ++ } else if (shape1 instanceof io.papermc.paper.voxel.AABBVoxelShape && shape2 instanceof ArrayVoxelShape) { ++ return ((ArrayVoxelShape)shape2).intersects(((io.papermc.paper.voxel.AABBVoxelShape)shape1).aabb); ++ } else if (shape2 instanceof io.papermc.paper.voxel.AABBVoxelShape && shape1 instanceof ArrayVoxelShape) { ++ return ((ArrayVoxelShape)shape1).intersects(((io.papermc.paper.voxel.AABBVoxelShape)shape2).aabb); ++ } ++ } ++ return joinIsNotEmptyVanilla(shape1, shape2, predicate); ++ } ++ public static boolean joinIsNotEmptyVanilla(VoxelShape shape1, VoxelShape shape2, BooleanOp predicate) { ++ // Paper end - optimise voxelshape + if (predicate.apply(false, false)) { + throw (IllegalArgumentException)Util.pauseInIde(new IllegalArgumentException()); + } else { +@@ -285,6 +281,43 @@ public final class Shapes { + } + + public static VoxelShape getFaceShape(VoxelShape shape, Direction direction) { ++ // Paper start - optimise shape creation here for lighting, as this shape is going to be used ++ // for transparency checks ++ if (shape == BLOCK || shape == BLOCK_OPTIMISED) { ++ return BLOCK_OPTIMISED; ++ } else if (shape == empty()) { ++ return empty(); ++ } ++ ++ if (shape instanceof io.papermc.paper.voxel.AABBVoxelShape) { ++ final AABB box = ((io.papermc.paper.voxel.AABBVoxelShape)shape).aabb; ++ switch (direction) { ++ case WEST: // -X ++ case EAST: { // +X ++ final boolean useEmpty = direction == Direction.EAST ? !DoubleMath.fuzzyEquals(box.maxX, 1.0, io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON) : ++ !DoubleMath.fuzzyEquals(box.minX, 0.0, io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON); ++ return useEmpty ? empty() : new io.papermc.paper.voxel.AABBVoxelShape(new AABB(0.0, box.minY, box.minZ, 1.0, box.maxY, box.maxZ)).optimize(); ++ } ++ case DOWN: // -Y ++ case UP: { // +Y ++ final boolean useEmpty = direction == Direction.UP ? !DoubleMath.fuzzyEquals(box.maxY, 1.0, io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON) : ++ !DoubleMath.fuzzyEquals(box.minY, 0.0, io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON); ++ return useEmpty ? empty() : new io.papermc.paper.voxel.AABBVoxelShape(new AABB(box.minX, 0.0, box.minZ, box.maxX, 1.0, box.maxZ)).optimize(); ++ } ++ case NORTH: // -Z ++ case SOUTH: { // +Z ++ final boolean useEmpty = direction == Direction.SOUTH ? !DoubleMath.fuzzyEquals(box.maxZ, 1.0, io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON) : ++ !DoubleMath.fuzzyEquals(box.minZ,0.0, io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON); ++ return useEmpty ? empty() : new io.papermc.paper.voxel.AABBVoxelShape(new AABB(box.minX, box.minY, 0.0, box.maxX, box.maxY, 1.0)).optimize(); ++ } ++ } ++ } ++ ++ // fall back to vanilla ++ return getFaceShapeVanilla(shape, direction); ++ } ++ public static VoxelShape getFaceShapeVanilla(VoxelShape shape, Direction direction) { ++ // Paper end + if (shape == block()) { + return block(); + } else { +@@ -299,7 +332,7 @@ public final class Shapes { + i = 0; + } + +- return (VoxelShape)(!bl ? empty() : new SliceShape(shape, axis, i)); ++ return (VoxelShape)(!bl ? empty() : new SliceShape(shape, axis, i).optimize().optimize()); // Paper - first optimize converts to ArrayVoxelShape, second optimize could convert to AABBVoxelShape + } + } + +@@ -324,6 +357,53 @@ public final class Shapes { + } + + public static boolean faceShapeOccludes(VoxelShape one, VoxelShape two) { ++ // Paper start - try to optimise for the case where the shapes do _not_ occlude ++ // which is _most_ of the time in lighting ++ if (one == getFullUnoptimisedCube() || one == BLOCK_OPTIMISED ++ || two == getFullUnoptimisedCube() || two == BLOCK_OPTIMISED) { ++ return true; ++ } ++ boolean v1Empty = one == empty(); ++ boolean v2Empty = two == empty(); ++ if (v1Empty && v2Empty) { ++ return false; ++ } ++ if ((one instanceof io.papermc.paper.voxel.AABBVoxelShape || v1Empty) ++ && (two instanceof io.papermc.paper.voxel.AABBVoxelShape || v2Empty)) { ++ if (!v1Empty && !v2Empty && (one != two)) { ++ AABB boundingBox1 = ((io.papermc.paper.voxel.AABBVoxelShape)one).aabb; ++ AABB boundingBox2 = ((io.papermc.paper.voxel.AABBVoxelShape)two).aabb; ++ // can call it here in some cases ++ ++ // check overall bounding box ++ double minY = Math.min(boundingBox1.minY, boundingBox2.minY); ++ double maxY = Math.max(boundingBox1.maxY, boundingBox2.maxY); ++ if (minY > io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON || maxY < (1 - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)) { ++ return false; ++ } ++ double minX = Math.min(boundingBox1.minX, boundingBox2.minX); ++ double maxX = Math.max(boundingBox1.maxX, boundingBox2.maxX); ++ if (minX > io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON || maxX < (1 - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)) { ++ return false; ++ } ++ double minZ = Math.min(boundingBox1.minZ, boundingBox2.minZ); ++ double maxZ = Math.max(boundingBox1.maxZ, boundingBox2.maxZ); ++ if (minZ > io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON || maxZ < (1 - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)) { ++ return false; ++ } ++ // fall through to full merge check ++ } else { ++ AABB boundingBox = v1Empty ? ((io.papermc.paper.voxel.AABBVoxelShape)two).aabb : ((io.papermc.paper.voxel.AABBVoxelShape)one).aabb; ++ // check if the bounding box encloses the full cube ++ return (boundingBox.minY <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && boundingBox.maxY >= (1 - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)) && ++ (boundingBox.minX <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && boundingBox.maxX >= (1 - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)) && ++ (boundingBox.minZ <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && boundingBox.maxZ >= (1 - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)); ++ } ++ } ++ return faceShapeOccludesVanilla(one, two); ++ } ++ public static boolean faceShapeOccludesVanilla(VoxelShape one, VoxelShape two) { ++ // Paper end + if (one != block() && two != block()) { + if (one.isEmpty() && two.isEmpty()) { + return false; +diff --git a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java +index f325d76c79d63629200262a77eab7cdcc9beedfa..0ab742f38f79c150630bb9ba153d92d864aface1 100644 +--- a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java ++++ b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java +@@ -16,11 +16,17 @@ import net.minecraft.world.phys.BlockHitResult; + import net.minecraft.world.phys.Vec3; + + public abstract class VoxelShape { +- protected final DiscreteVoxelShape shape; ++ public final DiscreteVoxelShape shape; // Paper - public + @Nullable + private VoxelShape[] faces; + +- VoxelShape(DiscreteVoxelShape voxels) { ++ // Paper start ++ public boolean intersects(AABB shape) { ++ return Shapes.joinIsNotEmpty(this, new io.papermc.paper.voxel.AABBVoxelShape(shape), BooleanOp.AND); ++ } ++ // Paper end ++ ++ protected VoxelShape(DiscreteVoxelShape voxels) { // Paper - protected + this.shape = voxels; + } + +@@ -163,7 +169,7 @@ public abstract class VoxelShape { + } + } + +- private VoxelShape calculateFace(Direction direction) { ++ protected VoxelShape calculateFace(Direction direction) { // Paper + Direction.Axis axis = direction.getAxis(); + DoubleList doubleList = this.getCoords(axis); + if (doubleList.size() == 2 && DoubleMath.fuzzyEquals(doubleList.getDouble(0), 0.0D, 1.0E-7D) && DoubleMath.fuzzyEquals(doubleList.getDouble(1), 1.0D, 1.0E-7D)) { diff --git a/patches/server/0767-Optimise-chunk-tick-iteration.patch b/patches/server/0767-Optimise-chunk-tick-iteration.patch new file mode 100644 index 0000000000..fd2b4dd0f9 --- /dev/null +++ b/patches/server/0767-Optimise-chunk-tick-iteration.patch @@ -0,0 +1,94 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Thu, 7 May 2020 05:48:54 -0700 +Subject: [PATCH] Optimise chunk tick iteration + +Use a dedicated list of entity ticking chunks to reduce the cost + +diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +index 9196be25ff874a0c81868906c5b86bfe4e2968f5..7ea86cbeb72f08d751c14006f428fe5921916061 100644 +--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java ++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +@@ -1006,19 +1006,35 @@ public class ServerChunkCache extends ChunkSource { + + this.lastSpawnState = spawnercreature_d; + this.level.getProfiler().pop(); +- List list = Lists.newArrayList(this.chunkMap.getChunks()); +- +- Collections.shuffle(list); ++ // Paper - moved down, enabled if per-player = false + // Paper - moved natural spawn event up + this.level.timings.chunkTicks.startTiming(); // Paper +- list.forEach((playerchunk) -> { +- Optional optional = ((Either) playerchunk.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left(); +- +- if (optional.isPresent()) { +- LevelChunk chunk = (LevelChunk) optional.get(); ++ // Paper start ++ java.util.Iterator iterator; ++ if (this.level.paperConfig.perPlayerMobSpawns) { ++ iterator = this.entityTickingChunks.iterator(); ++ } else { ++ iterator = this.entityTickingChunks.unsafeIterator(); ++ List shuffled = new java.util.ArrayList<>(this.entityTickingChunks.size()); ++ while (iterator.hasNext()) { ++ shuffled.add(iterator.next()); ++ } ++ Collections.shuffle(shuffled); ++ iterator = shuffled.iterator(); ++ } ++ try { while (iterator.hasNext()) { ++ LevelChunk chunk = iterator.next(); ++ ChunkHolder playerchunk = chunk.playerChunk; ++ if (playerchunk != null) { ++ this.level.getProfiler().push("broadcast"); ++ this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timings ++ playerchunk.broadcastChanges(chunk); ++ this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timings ++ this.level.getProfiler().pop(); ++ // Paper end + ChunkPos chunkcoordintpair = chunk.getPos(); + +- if (this.level.isPositionEntityTicking(chunkcoordintpair) && !this.chunkMap.isOutsideOfRange(playerchunk, chunkcoordintpair, false)) { // Paper - optimise isOutsideOfRange ++ if ((true || this.level.isPositionEntityTicking(chunkcoordintpair)) && !this.chunkMap.isOutsideOfRange(playerchunk, chunkcoordintpair, false)) { // Paper - optimise isOutsideOfRange // Paper - we only iterate entity ticking chunks + chunk.setInhabitedTime(chunk.getInhabitedTime() + j); + if (flag1 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunk.getPos()) && !this.chunkMap.isOutsideOfRange(playerchunk, chunkcoordintpair, true)) { // Spigot // Paper - optimise isOutsideOfRange + NaturalSpawner.spawnForChunk(this.level, chunk, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag2); +@@ -1029,7 +1045,13 @@ public class ServerChunkCache extends ChunkSource { + // this.level.timings.doTickTiles.stopTiming(); // Spigot // Paper + } + } +- }); ++ } // Paper start - optimise chunk tick iteration ++ } finally { ++ if (iterator instanceof io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator) { ++ ((io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator)iterator).finishedIterating(); ++ } ++ } ++ // Paper end - optimise chunk tick iteration + this.level.timings.chunkTicks.stopTiming(); // Paper + this.level.getProfiler().push("customSpawners"); + if (flag1) { +@@ -1038,21 +1060,7 @@ public class ServerChunkCache extends ChunkSource { + } // Paper - timings + } + +- this.level.getProfiler().popPush("broadcast"); +- this.chunkMap.getChunks().forEach((playerchunk) -> { // Paper - no... just no... +- Optional optional = ((Either) playerchunk.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left(); // CraftBukkit - decompile error +- +- Objects.requireNonNull(playerchunk); +- +- // Paper start - timings +- optional.ifPresent(chunk -> { +- this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timings +- playerchunk.broadcastChanges(chunk); +- this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timings +- }); +- // Paper end +- }); +- this.level.getProfiler().pop(); ++ // Paper - no, iterating just ONCE is expensive enough! Don't do it TWICE! Code moved up + this.level.getProfiler().pop(); + } + diff --git a/patches/server/0768-Execute-chunk-tasks-mid-tick.patch b/patches/server/0768-Execute-chunk-tasks-mid-tick.patch new file mode 100644 index 0000000000..672f07a035 --- /dev/null +++ b/patches/server/0768-Execute-chunk-tasks-mid-tick.patch @@ -0,0 +1,168 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Mon, 6 Apr 2020 04:20:44 -0700 +Subject: [PATCH] Execute chunk tasks mid-tick + +This will help the server load chunks if tick times are high. + +diff --git a/src/main/java/co/aikar/timings/MinecraftTimings.java b/src/main/java/co/aikar/timings/MinecraftTimings.java +index b27021a42cbed3f0648a8d0903d00d03922ae221..eada966d7f108a6081be7a848f5c1dfcb1eed676 100644 +--- a/src/main/java/co/aikar/timings/MinecraftTimings.java ++++ b/src/main/java/co/aikar/timings/MinecraftTimings.java +@@ -45,6 +45,8 @@ public final class MinecraftTimings { + public static final Timing antiXrayUpdateTimer = Timings.ofSafe("anti-xray - update"); + public static final Timing antiXrayObfuscateTimer = Timings.ofSafe("anti-xray - obfuscate"); + ++ public static final Timing midTickChunkTasks = Timings.ofSafe("Mid Tick Chunk Tasks"); ++ + private static final Map, String> taskNameCache = new MapMaker().weakKeys().makeMap(); + + private MinecraftTimings() {} +diff --git a/src/main/java/com/destroystokyo/paper/server/ticklist/PaperTickList.java b/src/main/java/com/destroystokyo/paper/server/ticklist/PaperTickList.java +index 5fdaefc128956581be4bb9b34199fd6410563991..b7edc1121797bc1c57e25f540ed0124fa8b36b7a 100644 +--- a/src/main/java/com/destroystokyo/paper/server/ticklist/PaperTickList.java ++++ b/src/main/java/com/destroystokyo/paper/server/ticklist/PaperTickList.java +@@ -312,6 +312,7 @@ public final class PaperTickList extends ServerTickList { // extend to avo + toTick.tickState = STATE_SCHEDULED; + this.addToNotTickingReady(toTick); + } ++ MinecraftServer.getServer().executeMidTickTasks(); // Paper - exec chunk tasks during world tick + } catch (final Throwable thr) { + // start copy from TickListServer // TODO check on update + CrashReport crashreport = CrashReport.forThrowable(thr, "Exception while ticking"); +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 154b0dd9bcfefbd7e7eae8c2f28bbabcd0fde531..099db3a627c1d3c08f3b923bb02a1cb8a0a268ab 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -330,6 +330,76 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= MAX_CHUNK_EXEC_TIME) { ++ if (!moreTasks) { ++ lastMidTickExecuteFailure = currTime; ++ } ++ ++ // note: negative values reduce the time ++ long overuse = diff - MAX_CHUNK_EXEC_TIME; ++ if (overuse >= (10L * 1000L * 1000L)) { // 10ms ++ // make sure something like a GC or dumb plugin doesn't screw us over... ++ overuse = 10L * 1000L * 1000L; // 10ms ++ } ++ ++ double overuseCount = (double)overuse/(double)MAX_CHUNK_EXEC_TIME; ++ long extraSleep = (long)Math.round(overuseCount*CHUNK_TASK_QUEUE_BACKOFF_MIN_TIME); ++ ++ lastMidTickExecute = currTime + extraSleep; ++ return; ++ } ++ } ++ } finally { ++ co.aikar.timings.MinecraftTimings.midTickChunkTasks.stopTiming(); ++ } ++ } ++ // Paper end - execute chunk tasks mid tick ++ + public MinecraftServer(OptionSet options, DataPackConfig datapackconfiguration, Thread thread, RegistryAccess.RegistryHolder iregistrycustom_dimension, LevelStorageSource.LevelStorageAccess convertable_conversionsession, WorldData savedata, PackRepository resourcepackrepository, Proxy proxy, DataFixer datafixer, ServerResources datapackresources, @Nullable MinecraftSessionService minecraftsessionservice, @Nullable GameProfileRepository gameprofilerepository, @Nullable GameProfileCache usercache, ChunkProgressListenerFactory worldloadlistenerfactory) { + super("Server"); + SERVER = this; // Paper - better singleton +@@ -1324,6 +1394,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop dragonParts; + private final StructureFeatureManager structureFeatureManager; + private final boolean tickTime; +- ++ // Paper start - execute chunk tasks mid tick ++ public long lastMidTickExecuteFailure; ++ // Paper end - execute chunk tasks mid tick + + // CraftBukkit start + private int tickPosition; +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index 82da0f2226f71a5081e24c17a8a39be22e3c5316..6ca0134c76adc335bb5125831e332fd709f09de7 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -912,6 +912,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + public void guardEntityTick(Consumer tickConsumer, T entity) { + try { + tickConsumer.accept(entity); ++ MinecraftServer.getServer().executeMidTickTasks(); // Paper - execute chunk tasks mid tick + } catch (Throwable throwable) { + if (throwable instanceof ThreadDeath) throw throwable; // Paper + // Paper start - Prevent tile entity and entity crashes diff --git a/patches/server/0769-Don-t-read-neighbour-chunk-data-off-disk-when-conver.patch b/patches/server/0769-Don-t-read-neighbour-chunk-data-off-disk-when-conver.patch new file mode 100644 index 0000000000..dbf1b0db95 --- /dev/null +++ b/patches/server/0769-Don-t-read-neighbour-chunk-data-off-disk-when-conver.patch @@ -0,0 +1,21 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sun, 11 Apr 2021 02:58:48 -0700 +Subject: [PATCH] Don't read neighbour chunk data off disk when converting + chunks + +Lighting is purged on update anyways, so let's not add more +into the conversion process + +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 176610b31f66b890afe61f4de46c412382bb8d22..037bbd562e2f35e17c324cd200c55c5e6cb5d768 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 +@@ -40,6 +40,7 @@ public class ChunkStorage implements AutoCloseable { + + // CraftBukkit start + private boolean check(ServerChunkCache cps, int x, int z) throws IOException { ++ if (true) return true; // Paper - this isn't even needed anymore, light is purged updating to 1.14+, why are we holding up the conversion process reading chunk data off disk - return true, we need to set light populated to true so the converter recognizes the chunk as being "full" + ChunkPos pos = new ChunkPos(x, z); + if (cps != null) { + //com.google.common.base.Preconditions.checkState(org.bukkit.Bukkit.isPrimaryThread(), "primary thread"); // Paper - this function is now MT-Safe diff --git a/patches/server/0770-Do-not-copy-visible-chunks.patch b/patches/server/0770-Do-not-copy-visible-chunks.patch new file mode 100644 index 0000000000..f63bf3fee5 --- /dev/null +++ b/patches/server/0770-Do-not-copy-visible-chunks.patch @@ -0,0 +1,140 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sun, 21 Mar 2021 11:22:10 -0700 +Subject: [PATCH] Do not copy visible chunks + +For servers with a lot of chunk holders, copying for each +tickDistanceManager call can take up quite a bit in +the function. I saw approximately 1/3rd of the function +on the copy. + +diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java +index 807bbe54f6516f794bdcb735bb7b8d6812e3ef01..2ef4b4c2ff81d0fa33d4630593266066d8e6a6f3 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java ++++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java +@@ -277,7 +277,7 @@ public class PaperCommand extends Command { + int ticking = 0; + int entityTicking = 0; + +- for (ChunkHolder chunk : world.getChunkSource().chunkMap.updatingChunkMap.values()) { ++ for (ChunkHolder chunk : world.getChunkSource().chunkMap.updatingChunks.getUpdatingMap().values()) { // Paper - change updating chunks map + if (chunk.getFullChunkUnchecked() == null) { + continue; + } +diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java +index 313a591dda5ef3962b4eab377abdce7c7ad08ec7..364caf495bb8df867885f9df8dfd0c0f1b4673a6 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -114,6 +114,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + private static final int MIN_VIEW_DISTANCE = 3; + public static final int MAX_VIEW_DISTANCE = 33; + public static final int MAX_CHUNK_DISTANCE = 33 + ChunkStatus.maxDistance(); ++ // Paper start - Don't copy ++ public final com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object updatingChunks = new com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<>(); ++ // Paper end - Don't copy + public static final int FORCED_TICKET_LEVEL = 31; + public final Long2ObjectLinkedOpenHashMap updatingChunkMap = new Long2ObjectLinkedOpenHashMap(); + public volatile Long2ObjectLinkedOpenHashMap visibleChunkMap; +@@ -676,12 +679,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + + @Nullable + public ChunkHolder getUpdatingChunkIfPresent(long pos) { +- return (ChunkHolder) this.updatingChunkMap.get(pos); ++ return this.updatingChunks.getUpdating(pos); // Paper - Don't copy + } + + @Nullable + public ChunkHolder getVisibleChunkIfPresent(long pos) { +- return (ChunkHolder) this.visibleChunkMap.get(pos); ++ // Paper start - Don't copy ++ if (Thread.currentThread() == this.level.thread) { ++ return this.updatingChunks.getVisible(pos); ++ } ++ return this.updatingChunks.getVisibleAsync(pos); ++ // Paper end - Don't copy + } + + protected IntSupplier getChunkQueueLevel(long pos) { +@@ -833,7 +841,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + // Paper end + } + +- this.updatingChunkMap.put(pos, holder); ++ this.updatingChunks.queueUpdate(pos, holder); // Paper - Don't copy + this.modified = true; + } + +@@ -986,7 +994,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + while (longiterator.hasNext()) { // Spigot + long j = longiterator.nextLong(); + longiterator.remove(); // Spigot +- ChunkHolder playerchunk = (ChunkHolder) this.updatingChunkMap.remove(j); ++ ChunkHolder playerchunk = this.updatingChunks.queueRemove(j); // Paper - Don't copy + + if (playerchunk != null) { + this.pendingUnloads.put(j, playerchunk); +@@ -1121,7 +1129,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + if (!this.modified) { + return false; + } else { +- this.visibleChunkMap = this.updatingChunkMap.clone(); ++ // Paper start - Don't copy ++ synchronized (this.updatingChunks) { ++ this.updatingChunks.performUpdates(); ++ } ++ // Paper end - Don't copy ++ + this.modified = false; + return true; + } +@@ -1587,7 +1600,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + } + + public int size() { +- return this.visibleChunkMap.size(); ++ return this.updatingChunks.getVisibleMap().size(); // Paper - Don't copy + } + + protected DistanceManager getDistanceManager() { +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index 6dd6008e3e540d48939f7f3f0c03f7fd920d7d4a..40463344f364618dd2e7330cb0168ff69a5fa58b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -147,7 +147,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { + @Override + public int getTileEntityCount() { + // We don't use the full world tile entity list, so we must iterate chunks +- Long2ObjectLinkedOpenHashMap chunks = world.getChunkSource().chunkMap.visibleChunkMap; ++ Long2ObjectLinkedOpenHashMap chunks = world.getChunkSource().chunkMap.updatingChunks.getVisibleMap(); // Paper - change updating chunks map + int size = 0; + for (ChunkHolder playerchunk : chunks.values()) { + net.minecraft.world.level.chunk.LevelChunk chunk = playerchunk.getTickingChunk(); +@@ -168,7 +168,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { + public int getChunkCount() { + int ret = 0; + +- for (ChunkHolder chunkHolder : world.getChunkSource().chunkMap.visibleChunkMap.values()) { ++ for (ChunkHolder chunkHolder : world.getChunkSource().chunkMap.updatingChunks.getVisibleMap().values()) { // Paper - change updating chunks map + if (chunkHolder.getTickingChunk() != null) { + ++ret; + } +@@ -341,7 +341,18 @@ public class CraftWorld extends CraftRegionAccessor implements World { + + @Override + public Chunk[] getLoadedChunks() { +- Long2ObjectLinkedOpenHashMap chunks = this.world.getChunkSource().chunkMap.visibleChunkMap; ++ // Paper start ++ if (Thread.currentThread() != world.getLevel().thread) { ++ // Paper start - change updating chunks map ++ Long2ObjectLinkedOpenHashMap chunks; ++ synchronized (world.getChunkSource().chunkMap.updatingChunks) { ++ chunks = world.getChunkSource().chunkMap.updatingChunks.getVisibleMap().clone(); ++ } ++ return chunks.values().stream().map(ChunkHolder::getFullChunk).filter(Objects::nonNull).map(net.minecraft.world.level.chunk.LevelChunk::getBukkitChunk).toArray(Chunk[]::new); ++ // Paper end - change updating chunks map ++ } ++ // Paper end ++ Long2ObjectLinkedOpenHashMap chunks = world.getChunkSource().chunkMap.updatingChunks.getVisibleMap(); // Paper - change updating chunks map + return chunks.values().stream().map(ChunkHolder::getFullChunk).filter(Objects::nonNull).map(net.minecraft.world.level.chunk.LevelChunk::getBukkitChunk).toArray(Chunk[]::new); + } + diff --git a/patches/server/0771-Replace-player-chunk-loader-system.patch b/patches/server/0771-Replace-player-chunk-loader-system.patch new file mode 100644 index 0000000000..de6d3562e5 --- /dev/null +++ b/patches/server/0771-Replace-player-chunk-loader-system.patch @@ -0,0 +1,1815 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sun, 24 Jan 2021 20:27:32 -0800 +Subject: [PATCH] Replace player chunk loader system + +The old one has undebuggable problems. Rewriting seems +the most sensible option. + +This new player chunk manager will also strictly rate limit +chunk sends so that netty threads do not get overloaded, whether +it be from the anti-xray logic or the compression itself. + +Chunk loading is also rate limited in the same manner, so this +will result in a maximum responsiveness for change. + +Config: +``` +chunk-loading: + min-load-radius: 2 + max-concurrent-sends: 2 + autoconfig-send-distance: true + target-player-chunk-send-rate: 100.0 + global-max-chunk-send-rate: -1 + enable-frustum-priority: false + global-max-chunk-load-rate: 300.0 + player-max-concurrent-loads: 4.0 + global-max-concurrent-loads: 500.0 +``` + +min-load-radius - The radius of chunks around a player that +are not throttled for loading. The number of chunks +affected is actually the configured value plus one as this +config controls the chunks the client will be able to render. + +max-concurrent-sends - The maximum number of chunks that +can be queued to send at any given time. Low values +are generally going to solve server-sided networking +bottlenecks like anti-xray and chunk compression. Client +side networking is unlikely to be helped (i.e this wont help +people running off McDonald's wifi). + +autoconfig-send-distance - Whether to try to use the client's +view distance for the send view distance in the server. In the +case that no plugin has explicitly set the send distance and +the client view distance is less-than the server's send distance, +the client's view distance will be used. This will not affect +tick view distance or no-tick view distance. + +target-player-chunk-send-rate - The maximum chunk send rate +an individual player will have. -1 means no limit + +global-max-chunk-send-rate - The maximum chunk send rate for +the whole server. -1 means no limit + +enable-frustum-priority - Whether chunks in front of a player +are prioritised to load/send first. Disabled by default +because the client can bug out due to the out of order +chunk sending. + +global-max-chunk-load-rate - The maximum chunk load rate +for the whole server. + +player-max-concurrent-loads and global-max-concurrent-loads +The maximum number of concurrent loads for the server is +determined by the number of players on the server multiplied by the +`player-max-concurrent-loads`. It is then limited to +whatever `global-max-concurrent-loads` is configured to. + +diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java +index cfe293881f68c8db337c3a48948362bb7b3e3522..7d44abcb4fff9717a1af55879deb7eb9c2d9e7e9 100644 +--- a/src/main/java/co/aikar/timings/TimingsExport.java ++++ b/src/main/java/co/aikar/timings/TimingsExport.java +@@ -153,7 +153,7 @@ public class TimingsExport extends Thread { + return pair(rule, world.getWorld().getGameRuleValue(rule)); + })), + pair("ticking-distance", world.getChunkSource().chunkMap.getEffectiveViewDistance()), +- pair("notick-viewdistance", world.getChunkSource().chunkMap.getEffectiveNoTickViewDistance()) ++ pair("notick-viewdistance", world.getChunkSource().chunkMap.playerChunkManager.getTargetNoTickViewDistance()) // Paper - replace old player chunk management + )); + })); + +diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java +index 9d31f1f1a224d1442cb7915df514c0522a4b876b..fc07a8454f80c22bc3776a42b2c31f8793543f5c 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java +@@ -523,4 +523,26 @@ public class PaperConfig { + itemValidationBookAuthorLength = getInt("settings.item-validation.book.author", itemValidationBookAuthorLength); + itemValidationBookPageLength = getInt("settings.item-validation.book.page", itemValidationBookPageLength); + } ++ ++ public static int playerMinChunkLoadRadius; ++ public static boolean playerAutoConfigureSendViewDistance; ++ public static int playerMaxConcurrentChunkSends; ++ public static double playerTargetChunkSendRate; ++ public static double globalMaxChunkSendRate; ++ public static boolean playerFrustumPrioritisation; ++ public static double globalMaxChunkLoadRate; ++ public static double playerMaxConcurrentChunkLoads; ++ public static double globalMaxConcurrentChunkLoads; ++ ++ private static void newPlayerChunkManagement() { ++ playerMinChunkLoadRadius = getInt("settings.chunk-loading.min-load-radius", 2); ++ playerMaxConcurrentChunkSends = getInt("settings.chunk-loading.max-concurrent-sends", 2); ++ playerAutoConfigureSendViewDistance = getBoolean("settings.chunk-loading.autoconfig-send-distance", true); ++ playerTargetChunkSendRate = getDouble("settings.chunk-loading.target-player-chunk-send-rate", 100.0); ++ globalMaxChunkSendRate = getDouble("settings.chunk-loading.global-max-chunk-send-rate", -1.0); ++ playerFrustumPrioritisation = getBoolean("settings.chunk-loading.enable-frustum-priority", false); ++ globalMaxChunkLoadRate = getDouble("settings.chunk-loading.global-max-chunk-load-rate", 300.0); ++ playerMaxConcurrentChunkLoads = getDouble("settings.chunk-loading.player-max-concurrent-loads", 4.0); ++ globalMaxConcurrentChunkLoads = getDouble("settings.chunk-loading.global-max-concurrent-loads", 500.0); ++ } + } +diff --git a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java +new file mode 100644 +index 0000000000000000000000000000000000000000..b3da5ec6701448a4b273c86aff9c64e3d75e5885 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java +@@ -0,0 +1,989 @@ ++package io.papermc.paper.chunk; ++ ++import com.destroystokyo.paper.PaperConfig; ++import com.destroystokyo.paper.util.misc.PlayerAreaMap; ++import com.destroystokyo.paper.util.misc.PooledLinkedHashSets; ++import io.papermc.paper.util.CoordinateUtils; ++import io.papermc.paper.util.IntervalledCounter; ++import io.papermc.paper.util.TickThread; ++import it.unimi.dsi.fastutil.longs.LongOpenHashSet; ++import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; ++import it.unimi.dsi.fastutil.objects.Reference2ObjectLinkedOpenHashMap; ++import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet; ++import net.minecraft.network.protocol.Packet; ++import net.minecraft.network.protocol.game.ClientboundSetChunkCacheCenterPacket; ++import net.minecraft.network.protocol.game.ClientboundSetChunkCacheRadiusPacket; ++import net.minecraft.server.MCUtil; ++import net.minecraft.server.MinecraftServer; ++import net.minecraft.server.level.ChunkHolder; ++import net.minecraft.server.level.ChunkMap; ++import net.minecraft.server.level.ServerPlayer; ++import net.minecraft.server.level.TicketType; ++import net.minecraft.util.Mth; ++import net.minecraft.world.level.ChunkPos; ++import net.minecraft.world.level.chunk.LevelChunk; ++import java.util.ArrayDeque; ++import java.util.ArrayList; ++import java.util.List; ++import java.util.TreeSet; ++import java.util.concurrent.atomic.AtomicInteger; ++ ++public final class PlayerChunkLoader { ++ ++ public static final int MIN_VIEW_DISTANCE = 2; ++ public static final int MAX_VIEW_DISTANCE = 32; ++ ++ public static final int TICK_TICKET_LEVEL = 31; ++ public static final int LOADED_TICKET_LEVEL = 33; ++ ++ protected final ChunkMap chunkMap; ++ protected final Reference2ObjectLinkedOpenHashMap playerMap = new Reference2ObjectLinkedOpenHashMap<>(512, 0.7f); ++ protected final ReferenceLinkedOpenHashSet chunkSendQueue = new ReferenceLinkedOpenHashSet<>(512, 0.7f); ++ ++ protected final TreeSet chunkLoadQueue = new TreeSet<>((final PlayerLoaderData p1, final PlayerLoaderData p2) -> { ++ if (p1 == p2) { ++ return 0; ++ } ++ ++ final ChunkPriorityHolder holder1 = p1.loadQueue.peekFirst(); ++ final ChunkPriorityHolder holder2 = p2.loadQueue.peekFirst(); ++ ++ final int priorityCompare = Double.compare(holder1 == null ? Double.MAX_VALUE : holder1.priority, holder2 == null ? Double.MAX_VALUE : holder2.priority); ++ ++ if (priorityCompare != 0) { ++ return priorityCompare; ++ } ++ ++ final int idCompare = Integer.compare(p1.player.getId(), p2.player.getId()); ++ ++ if (idCompare != 0) { ++ return idCompare; ++ } ++ ++ // last resort ++ return Integer.compare(System.identityHashCode(p1), System.identityHashCode(p2)); ++ }); ++ ++ protected final TreeSet chunkSendWaitQueue = new TreeSet<>((final PlayerLoaderData p1, final PlayerLoaderData p2) -> { ++ if (p1 == p2) { ++ return 0; ++ } ++ ++ final int timeCompare = Long.compare(p1.nextChunkSendTarget, p2.nextChunkSendTarget); ++ if (timeCompare != 0) { ++ return timeCompare; ++ } ++ ++ final int idCompare = Integer.compare(p1.player.getId(), p2.player.getId()); ++ ++ if (idCompare != 0) { ++ return idCompare; ++ } ++ ++ // last resort ++ return Integer.compare(System.identityHashCode(p1), System.identityHashCode(p2)); ++ }); ++ ++ ++ // no throttling is applied below this VD for loading ++ ++ /** ++ * The chunks to be sent to players, provided they're send-ready. Send-ready means the chunk and its 1 radius neighbours are loaded. ++ */ ++ public final PlayerAreaMap broadcastMap; ++ ++ /** ++ * The chunks to be brought up to send-ready status. Send-ready means the chunk and its 1 radius neighbours are loaded. ++ */ ++ public final PlayerAreaMap loadMap; ++ ++ /** ++ * Areamap used only to remove tickets for send-ready chunks. View distance is always + 1 of load view distance. Thus, ++ * this map is always representing the chunks we are actually going to load. ++ */ ++ public final PlayerAreaMap loadTicketCleanup; ++ ++ /** ++ * The chunks to brought to ticking level. Each chunk must have 2 radius neighbours loaded before this can happen. ++ */ ++ public final PlayerAreaMap tickMap; ++ ++ /** ++ * -1 if defaulting to [load distance], else always in [2, load distance] ++ */ ++ protected int rawSendDistance = -1; ++ ++ /** ++ * -1 if defaulting to [tick view distance + 1], else always in [tick view distance + 1, 32 + 1] ++ */ ++ protected int rawLoadDistance = -1; ++ ++ /** ++ * Never -1, always in [2, 32] ++ */ ++ protected int rawTickDistance = -1; ++ ++ // methods to bridge for API ++ ++ public int getTargetViewDistance() { ++ return this.getTickDistance(); ++ } ++ ++ public void setTargetViewDistance(final int distance) { ++ this.setTickDistance(distance); ++ } ++ ++ public int getTargetNoTickViewDistance() { ++ return this.getLoadDistance() - 1; ++ } ++ ++ public void setTargetNoTickViewDistance(final int distance) { ++ this.setLoadDistance(distance == -1 ? -1 : distance + 1); ++ } ++ ++ public int getTargetSendDistance() { ++ return this.rawSendDistance == -1 ? this.getLoadDistance() : this.rawSendDistance; ++ } ++ ++ public void setTargetSendDistance(final int distance) { ++ this.setSendDistance(distance); ++ } ++ ++ // internal methods ++ ++ public int getSendDistance() { ++ final int loadDistance = this.getLoadDistance(); ++ return this.rawSendDistance == -1 ? loadDistance : Math.min(this.rawSendDistance, loadDistance); ++ } ++ ++ public void setSendDistance(final int distance) { ++ if (distance != -1 && (distance < MIN_VIEW_DISTANCE || distance > MAX_VIEW_DISTANCE + 1)) { ++ throw new IllegalArgumentException(Integer.toString(distance)); ++ } ++ this.rawSendDistance = distance; ++ } ++ ++ public int getLoadDistance() { ++ final int tickDistance = this.getTickDistance(); ++ return this.rawLoadDistance == -1 ? tickDistance + 1 : Math.max(tickDistance + 1, this.rawLoadDistance); ++ } ++ ++ public void setLoadDistance(final int distance) { ++ if (distance != -1 && (distance < MIN_VIEW_DISTANCE || distance > MAX_VIEW_DISTANCE + 1)) { ++ throw new IllegalArgumentException(Integer.toString(distance)); ++ } ++ this.rawLoadDistance = distance; ++ } ++ ++ public int getTickDistance() { ++ return this.rawTickDistance; ++ } ++ ++ public void setTickDistance(final int distance) { ++ if (distance < MIN_VIEW_DISTANCE || distance > MAX_VIEW_DISTANCE) { ++ throw new IllegalArgumentException(Integer.toString(distance)); ++ } ++ this.rawTickDistance = distance; ++ } ++ ++ /* ++ Players have 3 different types of view distance: ++ 1. Sending view distance ++ 2. Loading view distance ++ 3. Ticking view distance ++ ++ But for configuration purposes (and API) there are: ++ 1. No-tick view distance ++ 2. Tick view distance ++ 3. Broadcast view distance ++ ++ These aren't always the same as the types we represent internally. ++ ++ Loading view distance is always max(no-tick + 1, tick + 1) ++ - no-tick has 1 added because clients need an extra radius to render chunks ++ - tick has 1 added because it needs an extra radius of chunks to load before they can be marked ticking ++ ++ Loading view distance is defined as the radius of chunks that will be brought to send-ready status, which means ++ it loads chunks in radius load-view-distance + 1. ++ ++ The maximum value for send view distance is the load view distance. API can set it lower. ++ */ ++ ++ public PlayerChunkLoader(final ChunkMap chunkMap, final PooledLinkedHashSets pooledHashSets) { ++ this.chunkMap = chunkMap; ++ this.broadcastMap = new PlayerAreaMap(pooledHashSets, ++ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { ++ if (player.needsChunkCenterUpdate) { ++ player.needsChunkCenterUpdate = false; ++ player.connection.send(new ClientboundSetChunkCacheCenterPacket(currPosX, currPosZ)); ++ } ++ PlayerChunkLoader.this.onChunkEnter(player, rangeX, rangeZ); ++ }, ++ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { ++ PlayerChunkLoader.this.onChunkLeave(player, rangeX, rangeZ); ++ }); ++ this.loadMap = new PlayerAreaMap(pooledHashSets, ++ null, ++ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { ++ if (newState != null) { ++ return; ++ } ++ PlayerChunkLoader.this.isTargetedForPlayerLoad.remove(CoordinateUtils.getChunkKey(rangeX, rangeZ)); ++ }); ++ this.loadTicketCleanup = new PlayerAreaMap(pooledHashSets, ++ null, ++ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { ++ if (newState != null) { ++ return; ++ } ++ ChunkPos chunkPos = new ChunkPos(rangeX, rangeZ); ++ PlayerChunkLoader.this.chunkMap.level.getChunkSource().removeTicketAtLevel(TicketType.PLAYER, chunkPos, LOADED_TICKET_LEVEL, chunkPos); ++ if (PlayerChunkLoader.this.chunkTicketTracker.remove(chunkPos.toLong())) { ++ --PlayerChunkLoader.this.concurrentChunkLoads; ++ } ++ }); ++ this.tickMap = new PlayerAreaMap(pooledHashSets, ++ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { ++ if (newState.size() != 1) { ++ return; ++ } ++ LevelChunk chunk = PlayerChunkLoader.this.chunkMap.level.getChunkSource().getChunkAtIfLoadedMainThreadNoCache(rangeX, rangeZ); ++ if (chunk == null || !chunk.areNeighboursLoaded(2)) { ++ return; ++ } ++ ++ ChunkPos chunkPos = new ChunkPos(rangeX, rangeZ); ++ PlayerChunkLoader.this.chunkMap.level.getChunkSource().addTicketAtLevel(TicketType.PLAYER, chunkPos, TICK_TICKET_LEVEL, chunkPos); ++ }, ++ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { ++ if (newState != null) { ++ return; ++ } ++ ChunkPos chunkPos = new ChunkPos(rangeX, rangeZ); ++ PlayerChunkLoader.this.chunkMap.level.getChunkSource().removeTicketAtLevel(TicketType.PLAYER, chunkPos, TICK_TICKET_LEVEL, chunkPos); ++ }); ++ } ++ ++ protected final LongOpenHashSet isTargetedForPlayerLoad = new LongOpenHashSet(); ++ protected final LongOpenHashSet chunkTicketTracker = new LongOpenHashSet(); ++ ++ // rets whether the chunk is at a loaded stage that is ready to be sent to players ++ public boolean isChunkPlayerLoaded(final int chunkX, final int chunkZ) { ++ final long key = CoordinateUtils.getChunkKey(chunkX, chunkZ); ++ final ChunkHolder chunk = this.chunkMap.getVisibleChunkIfPresent(key); ++ ++ if (chunk == null) { ++ return false; ++ } ++ ++ return chunk.getSendingChunk() != null && this.isTargetedForPlayerLoad.contains(key); ++ } ++ ++ public boolean isChunkSent(final ServerPlayer player, final int chunkX, final int chunkZ) { ++ final PlayerLoaderData data = this.playerMap.get(player); ++ if (data == null) { ++ return false; ++ } ++ ++ return data.hasSentChunk(chunkX, chunkZ); ++ } ++ ++ protected int getMaxConcurrentChunkSends() { ++ return PaperConfig.playerMaxConcurrentChunkSends; ++ } ++ ++ protected int getMaxChunkLoads() { ++ double config = PaperConfig.playerMaxConcurrentChunkLoads; ++ double max = PaperConfig.globalMaxConcurrentChunkLoads; ++ return (int)Math.ceil(Math.min(config * MinecraftServer.getServer().getPlayerCount(), max <= 1.0 ? Double.MAX_VALUE : max)); ++ } ++ ++ protected long getTargetSendPerPlayerAddend() { ++ return PaperConfig.playerTargetChunkSendRate <= 1.0 ? 0L : (long)Math.round(1.0e9 / PaperConfig.playerTargetChunkSendRate); ++ } ++ ++ protected long getMaxSendAddend() { ++ return PaperConfig.globalMaxChunkSendRate <= 1.0 ? 0L : (long)Math.round(1.0e9 / PaperConfig.globalMaxChunkSendRate); ++ } ++ ++ public void onChunkPlayerTickReady(final int chunkX, final int chunkZ) { ++ final ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); ++ this.chunkMap.level.getChunkSource().addTicketAtLevel(TicketType.PLAYER, chunkPos, TICK_TICKET_LEVEL, chunkPos); ++ } ++ ++ public void onChunkSendReady(final int chunkX, final int chunkZ) { ++ final PooledLinkedHashSets.PooledObjectLinkedOpenHashSet playersInSendRange = this.broadcastMap.getObjectsInRange(chunkX, chunkZ); ++ ++ if (playersInSendRange == null) { ++ return; ++ } ++ ++ final Object[] rawData = playersInSendRange.getBackingSet(); ++ for (int i = 0, len = rawData.length; i < len; ++i) { ++ final Object raw = rawData[i]; ++ ++ if (!(raw instanceof ServerPlayer)) { ++ continue; ++ } ++ this.onChunkEnter((ServerPlayer)raw, chunkX, chunkZ); ++ } ++ ++ // now let's try and queue mid tick logic again ++ } ++ ++ public void onChunkEnter(final ServerPlayer player, final int chunkX, final int chunkZ) { ++ final PlayerLoaderData data = this.playerMap.get(player); ++ ++ if (data == null) { ++ return; ++ } ++ ++ if (data.hasSentChunk(chunkX, chunkZ) || !this.isChunkPlayerLoaded(chunkX, chunkZ)) { ++ // if we don't have player tickets, then the load logic will pick this up and queue to send ++ return; ++ } ++ ++ final long playerPos = this.broadcastMap.getLastCoordinate(player); ++ final int playerChunkX = CoordinateUtils.getChunkX(playerPos); ++ final int playerChunkZ = CoordinateUtils.getChunkZ(playerPos); ++ final int manhattanDistance = Math.abs(playerChunkX - chunkX) + Math.abs(playerChunkZ - chunkZ); ++ ++ final ChunkPriorityHolder holder = new ChunkPriorityHolder(chunkX, chunkZ, manhattanDistance, 0.0); ++ data.sendQueue.add(holder); ++ } ++ ++ public void onChunkLoad(final int chunkX, final int chunkZ) { ++ if (this.chunkTicketTracker.remove(CoordinateUtils.getChunkKey(chunkX, chunkZ))) { ++ --this.concurrentChunkLoads; ++ } ++ } ++ ++ public void onChunkLeave(final ServerPlayer player, final int chunkX, final int chunkZ) { ++ final PlayerLoaderData data = this.playerMap.get(player); ++ ++ if (data == null) { ++ return; ++ } ++ ++ data.unloadChunk(chunkX, chunkZ); ++ } ++ ++ public void addPlayer(final ServerPlayer player) { ++ TickThread.ensureTickThread("Cannot add player async"); ++ if (!player.isRealPlayer) { ++ return; ++ } ++ final PlayerLoaderData data = new PlayerLoaderData(player, this); ++ if (this.playerMap.putIfAbsent(player, data) == null) { ++ data.update(); ++ } ++ } ++ ++ public void removePlayer(final ServerPlayer player) { ++ TickThread.ensureTickThread("Cannot remove player async"); ++ if (!player.isRealPlayer) { ++ return; ++ } ++ ++ final PlayerLoaderData loaderData = this.playerMap.remove(player); ++ if (loaderData == null) { ++ return; ++ } ++ loaderData.remove(); ++ this.chunkLoadQueue.remove(loaderData); ++ this.chunkSendQueue.remove(loaderData); ++ this.chunkSendWaitQueue.remove(loaderData); ++ synchronized (this.sendingChunkCounts) { ++ final int count = this.sendingChunkCounts.removeInt(loaderData); ++ if (count != 0) { ++ concurrentChunkSends.getAndAdd(-count); ++ } ++ } ++ } ++ ++ public void updatePlayer(final ServerPlayer player) { ++ TickThread.ensureTickThread("Cannot update player async"); ++ if (!player.isRealPlayer) { ++ return; ++ } ++ final PlayerLoaderData loaderData = this.playerMap.get(player); ++ if (loaderData != null) { ++ loaderData.update(); ++ } ++ } ++ ++ public PlayerLoaderData getData(final ServerPlayer player) { ++ return this.playerMap.get(player); ++ } ++ ++ public void tick() { ++ TickThread.ensureTickThread("Cannot tick async"); ++ for (final PlayerLoaderData data : this.playerMap.values()) { ++ data.update(); ++ } ++ this.tickMidTick(); ++ } ++ ++ protected static final AtomicInteger concurrentChunkSends = new AtomicInteger(); ++ protected final Reference2IntOpenHashMap sendingChunkCounts = new Reference2IntOpenHashMap<>(); ++ private static long nextChunkSend; ++ private void trySendChunks() { ++ final long time = System.nanoTime(); ++ if (time < nextChunkSend) { ++ return; ++ } ++ // drain entries from wait queue ++ while (!this.chunkSendWaitQueue.isEmpty()) { ++ final PlayerLoaderData data = this.chunkSendWaitQueue.first(); ++ ++ if (data.nextChunkSendTarget > time) { ++ break; ++ } ++ ++ this.chunkSendWaitQueue.pollFirst(); ++ ++ this.chunkSendQueue.add(data); ++ } ++ ++ if (this.chunkSendQueue.isEmpty()) { ++ return; ++ } ++ ++ final int maxSends = this.getMaxConcurrentChunkSends(); ++ final long nextPlayerDeadline = this.getTargetSendPerPlayerAddend() + time; ++ for (;;) { ++ if (this.chunkSendQueue.isEmpty()) { ++ break; ++ } ++ final int currSends = concurrentChunkSends.get(); ++ if (currSends >= maxSends) { ++ break; ++ } ++ ++ if (!concurrentChunkSends.compareAndSet(currSends, currSends + 1)) { ++ continue; ++ } ++ ++ // send chunk ++ ++ final PlayerLoaderData data = this.chunkSendQueue.removeFirst(); ++ ++ final ChunkPriorityHolder queuedSend = data.sendQueue.pollFirst(); ++ if (queuedSend == null) { ++ concurrentChunkSends.getAndDecrement(); // we never sent, so decrease ++ // stop iterating over players who have nothing to send ++ if (this.chunkSendQueue.isEmpty()) { ++ // nothing left ++ break; ++ } ++ continue; ++ } ++ ++ if (!this.isChunkPlayerLoaded(queuedSend.chunkX, queuedSend.chunkZ)) { ++ throw new IllegalStateException(); ++ } ++ ++ data.nextChunkSendTarget = nextPlayerDeadline; ++ this.chunkSendWaitQueue.add(data); ++ ++ synchronized (this.sendingChunkCounts) { ++ this.sendingChunkCounts.addTo(data, 1); ++ } ++ ++ data.sendChunk(queuedSend.chunkX, queuedSend.chunkZ, () -> { ++ synchronized (this.sendingChunkCounts) { ++ final int count = this.sendingChunkCounts.getInt(data); ++ if (count == 0) { ++ // disconnected, so we don't need to decrement: it will be decremented for us ++ return; ++ } ++ if (count == 1) { ++ this.sendingChunkCounts.removeInt(data); ++ } else { ++ this.sendingChunkCounts.put(data, count - 1); ++ } ++ } ++ ++ concurrentChunkSends.getAndDecrement(); ++ }); ++ ++ nextChunkSend = this.getMaxSendAddend() + time; ++ if (time < nextChunkSend) { ++ break; ++ } ++ } ++ } ++ ++ protected int concurrentChunkLoads; ++ // this interval prevents bursting a lot of chunk loads ++ protected static final IntervalledCounter TICKET_ADDITION_COUNTER_SHORT = new IntervalledCounter((long)(1.0e6 * 50.0)); // 50ms ++ // this interval ensures the rate is kept between ticks correctly ++ protected static final IntervalledCounter TICKET_ADDITION_COUNTER_LONG = new IntervalledCounter((long)(1.0e6 * 1000.0)); // 1000ms ++ private void tryLoadChunks() { ++ if (this.chunkLoadQueue.isEmpty()) { ++ return; ++ } ++ ++ final int maxLoads = this.getMaxChunkLoads(); ++ final long time = System.nanoTime(); ++ boolean updatedCounters = false; ++ for (;;) { ++ final PlayerLoaderData data = this.chunkLoadQueue.pollFirst(); ++ ++ final ChunkPriorityHolder queuedLoad = data.loadQueue.peekFirst(); ++ if (queuedLoad == null) { ++ if (this.chunkLoadQueue.isEmpty()) { ++ break; ++ } ++ continue; ++ } ++ ++ if (!updatedCounters) { ++ updatedCounters = true; ++ TICKET_ADDITION_COUNTER_SHORT.updateCurrentTime(time); ++ TICKET_ADDITION_COUNTER_LONG.updateCurrentTime(time); ++ } ++ ++ if (this.isChunkPlayerLoaded(queuedLoad.chunkX, queuedLoad.chunkZ)) { ++ // already loaded! ++ data.loadQueue.pollFirst(); // already loaded so we just skip ++ this.chunkLoadQueue.add(data); ++ ++ // ensure the chunk is queued to send ++ this.onChunkSendReady(queuedLoad.chunkX, queuedLoad.chunkZ); ++ continue; ++ } ++ ++ final long chunkKey = CoordinateUtils.getChunkKey(queuedLoad.chunkX, queuedLoad.chunkZ); ++ ++ final double priority = queuedLoad.priority; ++ // while we do need to rate limit chunk loads, the logic for sending chunks requires that tickets are present. ++ // when chunks are loaded (i.e spawn) but do not have this player's tickets, they have to wait behind the ++ // load queue. To avoid this problem, we check early here if tickets are required to load the chunk - if they ++ // aren't required, it bypasses the limiter system. ++ boolean unloadedTargetChunk = false; ++ unloaded_check: ++ for (int dz = -1; dz <= 1; ++dz) { ++ for (int dx = -1; dx <= 1; ++dx) { ++ final int offX = queuedLoad.chunkX + dx; ++ final int offZ = queuedLoad.chunkZ + dz; ++ if (this.chunkMap.level.getChunkSource().getChunkAtIfLoadedMainThreadNoCache(offX, offZ) == null) { ++ unloadedTargetChunk = true; ++ break unloaded_check; ++ } ++ } ++ } ++ if (unloadedTargetChunk && priority > 0.0) { ++ // priority > 0.0 implies rate limited chunks ++ ++ final int currentChunkLoads = this.concurrentChunkLoads; ++ if (currentChunkLoads >= maxLoads || (TICKET_ADDITION_COUNTER_SHORT.getRate() >= PaperConfig.globalMaxChunkLoadRate || TICKET_ADDITION_COUNTER_LONG.getRate() >= PaperConfig.globalMaxChunkLoadRate)) { ++ // don't poll, we didn't load it ++ this.chunkLoadQueue.add(data); ++ break; ++ } ++ } ++ ++ // can only poll after we decide to load ++ data.loadQueue.pollFirst(); ++ ++ // now that we've polled we can re-add to load queue ++ this.chunkLoadQueue.add(data); ++ ++ // add necessary tickets to load chunk up to send-ready ++ for (int dz = -1; dz <= 1; ++dz) { ++ for (int dx = -1; dx <= 1; ++dx) { ++ final int offX = queuedLoad.chunkX + dx; ++ final int offZ = queuedLoad.chunkZ + dz; ++ final ChunkPos chunkPos = new ChunkPos(offX, offZ); ++ ++ this.chunkMap.level.getChunkSource().addTicketAtLevel(TicketType.PLAYER, chunkPos, LOADED_TICKET_LEVEL, chunkPos); ++ if (this.chunkMap.level.getChunkSource().getChunkAtIfLoadedMainThreadNoCache(offX, offZ) != null) { ++ continue; ++ } ++ ++ if (priority > 0.0 && this.chunkTicketTracker.add(CoordinateUtils.getChunkKey(offX, offZ))) { ++ // won't reach here if unloadedTargetChunk is false ++ ++this.concurrentChunkLoads; ++ TICKET_ADDITION_COUNTER_SHORT.addTime(time); ++ TICKET_ADDITION_COUNTER_LONG.addTime(time); ++ } ++ } ++ } ++ ++ // mark that we've added tickets here ++ this.isTargetedForPlayerLoad.add(chunkKey); ++ ++ // it's possible all we needed was the player tickets to queue up the send. ++ if (this.isChunkPlayerLoaded(queuedLoad.chunkX, queuedLoad.chunkZ)) { ++ // yup, all we needed. ++ this.onChunkSendReady(queuedLoad.chunkX, queuedLoad.chunkZ); ++ } ++ } ++ } ++ ++ public void tickMidTick() { ++ // try to send more chunks ++ this.trySendChunks(); ++ ++ // try to queue more chunks to load ++ this.tryLoadChunks(); ++ } ++ ++ static final class ChunkPriorityHolder { ++ public final int chunkX; ++ public final int chunkZ; ++ public final int manhattanDistanceToPlayer; ++ public final double priority; ++ ++ public ChunkPriorityHolder(final int chunkX, final int chunkZ, final int manhattanDistanceToPlayer, final double priority) { ++ this.chunkX = chunkX; ++ this.chunkZ = chunkZ; ++ this.manhattanDistanceToPlayer = manhattanDistanceToPlayer; ++ this.priority = priority; ++ } ++ } ++ ++ public static final class PlayerLoaderData { ++ ++ protected static final float FOV = 110.0f; ++ protected static final double PRIORITISED_DISTANCE = 12.0 * 16.0; ++ ++ // Player max sprint speed is approximately 8m/s ++ protected static final double LOOK_PRIORITY_SPEED_THRESHOLD = (10.0/20.0) * (10.0/20.0); ++ protected static final double LOOK_PRIORITY_YAW_DELTA_RECALC_THRESHOLD = 3.0f; ++ ++ protected double lastLocX = Double.NEGATIVE_INFINITY; ++ protected double lastLocZ = Double.NEGATIVE_INFINITY; ++ ++ protected int lastChunkX; ++ protected int lastChunkZ; ++ ++ // this is corrected so that 0 is along the positive x-axis ++ protected float lastYaw = Float.NEGATIVE_INFINITY; ++ ++ protected int lastSendDistance = Integer.MIN_VALUE; ++ protected int lastLoadDistance = Integer.MIN_VALUE; ++ protected int lastTickDistance = Integer.MIN_VALUE; ++ protected boolean usingLookingPriority; ++ ++ protected final ServerPlayer player; ++ protected final PlayerChunkLoader loader; ++ ++ // warning: modifications of this field must be aware that the loadQueue inside PlayerChunkLoader uses this field ++ // in a comparator! ++ protected final ArrayDeque loadQueue = new ArrayDeque<>(); ++ protected final LongOpenHashSet sentChunks = new LongOpenHashSet(); ++ ++ protected final TreeSet sendQueue = new TreeSet<>((final ChunkPriorityHolder p1, final ChunkPriorityHolder p2) -> { ++ final int distanceCompare = Integer.compare(p1.manhattanDistanceToPlayer, p2.manhattanDistanceToPlayer); ++ if (distanceCompare != 0) { ++ return distanceCompare; ++ } ++ ++ final int coordinateXCompare = Integer.compare(p1.chunkX, p2.chunkX); ++ if (coordinateXCompare != 0) { ++ return coordinateXCompare; ++ } ++ ++ return Integer.compare(p1.chunkZ, p2.chunkZ); ++ }); ++ ++ protected int sendViewDistance = -1; ++ protected int loadViewDistance = -1; ++ protected int tickViewDistance = -1; ++ ++ protected long nextChunkSendTarget; ++ ++ public PlayerLoaderData(final ServerPlayer player, final PlayerChunkLoader loader) { ++ this.player = player; ++ this.loader = loader; ++ } ++ ++ // these view distance methods are for api ++ public int getTargetSendViewDistance() { ++ final int tickViewDistance = this.tickViewDistance == -1 ? this.loader.getTickDistance() : this.tickViewDistance; ++ final int loadViewDistance = Math.max(tickViewDistance + 1, this.loadViewDistance == -1 ? this.loader.getLoadDistance() : this.loadViewDistance); ++ final int clientViewDistance = this.getClientViewDistance(); ++ final int sendViewDistance = Math.min(loadViewDistance, this.sendViewDistance == -1 ? (!PaperConfig.playerAutoConfigureSendViewDistance || clientViewDistance == -1 ? this.loader.getSendDistance() : clientViewDistance + 1) : this.sendViewDistance); ++ return sendViewDistance; ++ } ++ ++ public void setTargetSendViewDistance(final int distance) { ++ if (distance != -1 && (distance < MIN_VIEW_DISTANCE || distance > MAX_VIEW_DISTANCE + 1)) { ++ throw new IllegalArgumentException(Integer.toString(distance)); ++ } ++ this.sendViewDistance = distance; ++ } ++ ++ public int getTargetNoTickViewDistance() { ++ return (this.loadViewDistance == -1 ? this.getLoadDistance() : this.loadViewDistance) - 1; ++ } ++ ++ public void setTargetNoTickViewDistance(final int distance) { ++ if (distance != -1 && (distance < MIN_VIEW_DISTANCE || distance > MAX_VIEW_DISTANCE)) { ++ throw new IllegalArgumentException(Integer.toString(distance)); ++ } ++ this.loadViewDistance = distance == -1 ? -1 : distance + 1; ++ } ++ ++ public int getTargetTickViewDistance() { ++ return this.tickViewDistance == -1 ? this.loader.getTickDistance() : this.tickViewDistance; ++ } ++ ++ public void setTargetTickViewDistance(final int distance) { ++ if (distance < MIN_VIEW_DISTANCE || distance > MAX_VIEW_DISTANCE) { ++ throw new IllegalArgumentException(Integer.toString(distance)); ++ } ++ this.tickViewDistance = distance; ++ } ++ ++ protected int getLoadDistance() { ++ final int tickViewDistance = this.tickViewDistance == -1 ? this.loader.getTickDistance() : this.tickViewDistance; ++ ++ return Math.max(tickViewDistance + 1, this.loadViewDistance == -1 ? this.loader.getLoadDistance() : this.loadViewDistance); ++ } ++ ++ public boolean hasSentChunk(final int chunkX, final int chunkZ) { ++ return this.sentChunks.contains(CoordinateUtils.getChunkKey(chunkX, chunkZ)); ++ } ++ ++ public void sendChunk(final int chunkX, final int chunkZ, final Runnable onChunkSend) { ++ if (this.sentChunks.add(CoordinateUtils.getChunkKey(chunkX, chunkZ))) { ++ this.player.getLevel().getChunkSource().chunkMap.updateChunkTracking(this.player, ++ new ChunkPos(chunkX, chunkZ), new Packet[2], false, true); // unloaded, loaded ++ this.player.connection.connection.execute(onChunkSend); ++ } else { ++ throw new IllegalStateException(); ++ } ++ } ++ ++ public void unloadChunk(final int chunkX, final int chunkZ) { ++ if (this.sentChunks.remove(CoordinateUtils.getChunkKey(chunkX, chunkZ))) { ++ this.player.getLevel().getChunkSource().chunkMap.updateChunkTracking(this.player, ++ new ChunkPos(chunkX, chunkZ), null, true, false); // unloaded, loaded ++ } ++ } ++ ++ protected static boolean triangleIntersects(final double p1x, final double p1z, // triangle point ++ final double p2x, final double p2z, // triangle point ++ final double p3x, final double p3z, // triangle point ++ ++ final double targetX, final double targetZ) { // point ++ // from barycentric coordinates: ++ // targetX = a*p1x + b*p2x + c*p3x ++ // targetZ = a*p1z + b*p2z + c*p3z ++ // 1.0 = a*1.0 + b*1.0 + c*1.0 ++ // where a, b, c >= 0.0 ++ // so, if any of a, b, c are less-than zero then there is no intersection. ++ ++ // d = ((p2z - p3z)(p1x - p3x) + (p3x - p2x)(p1z - p3z)) ++ // a = ((p2z - p3z)(targetX - p3x) + (p3x - p2x)(targetZ - p3z)) / d ++ // b = ((p3z - p1z)(targetX - p3x) + (p1x - p3x)(targetZ - p3z)) / d ++ // c = 1.0 - a - b ++ ++ final double d = (p2z - p3z)*(p1x - p3x) + (p3x - p2x)*(p1z - p3z); ++ final double a = ((p2z - p3z)*(targetX - p3x) + (p3x - p2x)*(targetZ - p3z)) / d; ++ ++ if (a < 0.0 || a > 1.0) { ++ return false; ++ } ++ ++ final double b = ((p3z - p1z)*(targetX - p3x) + (p1x - p3x)*(targetZ - p3z)) / d; ++ if (b < 0.0 || b > 1.0) { ++ return false; ++ } ++ ++ final double c = 1.0 - a - b; ++ ++ return c >= 0.0 && c <= 1.0; ++ } ++ ++ public void remove() { ++ this.loader.broadcastMap.remove(this.player); ++ this.loader.loadMap.remove(this.player); ++ this.loader.loadTicketCleanup.remove(this.player); ++ this.loader.tickMap.remove(this.player); ++ } ++ ++ protected int getClientViewDistance() { ++ return this.player.clientViewDistance == null ? -1 : this.player.clientViewDistance.intValue(); ++ } ++ ++ public void update() { ++ final int tickViewDistance = this.tickViewDistance == -1 ? this.loader.getTickDistance() : this.tickViewDistance; ++ // load view cannot be less-than tick view + 1 ++ final int loadViewDistance = Math.max(tickViewDistance + 1, this.loadViewDistance == -1 ? this.loader.getLoadDistance() : this.loadViewDistance); ++ // send view cannot be greater-than load view ++ final int clientViewDistance = this.getClientViewDistance(); ++ final int sendViewDistance = Math.min(loadViewDistance, this.sendViewDistance == -1 ? (!PaperConfig.playerAutoConfigureSendViewDistance || clientViewDistance == -1 ? this.loader.getSendDistance() : clientViewDistance + 1) : this.sendViewDistance); ++ ++ final double posX = this.player.getX(); ++ final double posZ = this.player.getZ(); ++ final float yaw = MCUtil.normalizeYaw(this.player.yRot + 90.0f); // mc yaw 0 is along the positive z axis, but obviously this is really dumb - offset so we are at positive x-axis ++ ++ // in general, we really only want to prioritise chunks in front if we know we're moving pretty fast into them. ++ final boolean useLookPriority = PaperConfig.playerFrustumPrioritisation && (this.player.getDeltaMovement().horizontalDistanceSqr() > LOOK_PRIORITY_SPEED_THRESHOLD || ++ this.player.getAbilities().flying); ++ ++ // make sure we're in the send queue ++ this.loader.chunkSendWaitQueue.add(this); ++ ++ if ( ++ // has view distance stayed the same? ++ sendViewDistance == this.lastSendDistance ++ && loadViewDistance == this.lastLoadDistance ++ && tickViewDistance == this.lastTickDistance ++ ++ && (this.usingLookingPriority ? ( ++ // has our block stayed the same (this also accounts for chunk change)? ++ Mth.floor(this.lastLocX) == Mth.floor(posX) ++ && Mth.floor(this.lastLocZ) == Mth.floor(posZ) ++ ) : ( ++ // has our chunk stayed the same ++ (Mth.floor(this.lastLocX) >> 4) == (Mth.floor(posX) >> 4) ++ && (Mth.floor(this.lastLocZ) >> 4) == (Mth.floor(posZ) >> 4) ++ )) ++ ++ // has our decision about look priority changed? ++ && this.usingLookingPriority == useLookPriority ++ ++ // if we are currently using look priority, has our yaw stayed within recalc threshold? ++ && (!this.usingLookingPriority || Math.abs(yaw - this.lastYaw) <= LOOK_PRIORITY_YAW_DELTA_RECALC_THRESHOLD) ++ ) { ++ // nothing we care about changed, so we're not re-calculating ++ return; ++ } ++ ++ final int centerChunkX = Mth.floor(posX) >> 4; ++ final int centerChunkZ = Mth.floor(posZ) >> 4; ++ ++ this.player.needsChunkCenterUpdate = true; ++ this.loader.broadcastMap.addOrUpdate(this.player, centerChunkX, centerChunkZ, sendViewDistance); ++ this.player.needsChunkCenterUpdate = false; ++ this.loader.loadMap.addOrUpdate(this.player, centerChunkX, centerChunkZ, loadViewDistance); ++ this.loader.loadTicketCleanup.addOrUpdate(this.player, centerChunkX, centerChunkZ, loadViewDistance + 1); ++ this.loader.tickMap.addOrUpdate(this.player, centerChunkX, centerChunkZ, tickViewDistance); ++ ++ if (sendViewDistance != this.lastSendDistance) { ++ // update the view radius for client ++ // note that this should be after the map calls because the client wont expect unload calls not in its VD ++ // and it's possible we decreased VD here ++ this.player.connection.send(new ClientboundSetChunkCacheRadiusPacket(sendViewDistance - 1)); // client already expects the 1 radius neighbours, so subtract 1. ++ } ++ ++ this.lastLocX = posX; ++ this.lastLocZ = posZ; ++ this.lastYaw = yaw; ++ this.lastSendDistance = sendViewDistance; ++ this.lastLoadDistance = loadViewDistance; ++ this.lastTickDistance = tickViewDistance; ++ this.usingLookingPriority = useLookPriority; ++ ++ this.lastChunkX = centerChunkX; ++ this.lastChunkZ = centerChunkZ; ++ ++ // points for player "view" triangle: ++ ++ // obviously, the player pos is a vertex ++ final double p1x = posX; ++ final double p1z = posZ; ++ ++ // to the left of the looking direction ++ final double p2x = PRIORITISED_DISTANCE * Math.cos(Math.toRadians(yaw + (double)(FOV / 2.0))) // calculate rotated vector ++ + p1x; // offset vector ++ final double p2z = PRIORITISED_DISTANCE * Math.sin(Math.toRadians(yaw + (double)(FOV / 2.0))) // calculate rotated vector ++ + p1z; // offset vector ++ ++ // to the right of the looking direction ++ final double p3x = PRIORITISED_DISTANCE * Math.cos(Math.toRadians(yaw - (double)(FOV / 2.0))) // calculate rotated vector ++ + p1x; // offset vector ++ final double p3z = PRIORITISED_DISTANCE * Math.sin(Math.toRadians(yaw - (double)(FOV / 2.0))) // calculate rotated vector ++ + p1z; // offset vector ++ ++ // now that we have all of our points, we can recalculate the load queue ++ ++ final List loadQueue = new ArrayList<>(); ++ ++ // clear send queue, we are re-sorting ++ this.sendQueue.clear(); ++ ++ final int searchViewDistance = Math.max(loadViewDistance, sendViewDistance); ++ ++ for (int dx = -searchViewDistance; dx <= searchViewDistance; ++dx) { ++ for (int dz = -searchViewDistance; dz <= searchViewDistance; ++dz) { ++ final int chunkX = dx + centerChunkX; ++ final int chunkZ = dz + centerChunkZ; ++ final int squareDistance = Math.max(Math.abs(dx), Math.abs(dz)); ++ ++ if (this.hasSentChunk(chunkX, chunkZ)) { ++ // already sent (which means it is also loaded) ++ continue; ++ } ++ ++ final boolean loadChunk = squareDistance <= loadViewDistance; ++ final boolean sendChunk = squareDistance <= sendViewDistance; ++ ++ final boolean prioritised = useLookPriority && triangleIntersects( ++ // prioritisation triangle ++ p1x, p1z, p2x, p2z, p3x, p3z, ++ ++ // center of chunk ++ (double)((chunkX << 4) | 8), (double)((chunkZ << 4) | 8) ++ ); ++ ++ ++ final int manhattanDistance = (Math.abs(dx) + Math.abs(dz)); ++ ++ final double priority; ++ ++ if (squareDistance <= PaperConfig.playerMinChunkLoadRadius) { ++ // priority should be negative, and we also want to order it from center outwards ++ // so we want (0,0) to be the smallest, and (minLoadedRadius,minLoadedRadius) to be the greatest ++ priority = -((2 * PaperConfig.playerMinChunkLoadRadius + 1) - (dx + dz)); ++ } else { ++ if (prioritised) { ++ // we don't prioritise these chunks above others because we also want to make sure some chunks ++ // will be loaded if the player changes direction ++ priority = (double)manhattanDistance / 6.0; ++ } else { ++ priority = (double)manhattanDistance; ++ } ++ } ++ ++ final ChunkPriorityHolder holder = new ChunkPriorityHolder(chunkX, chunkZ, manhattanDistance, priority); ++ ++ if (!this.loader.isChunkPlayerLoaded(chunkX, chunkZ)) { ++ if (loadChunk) { ++ loadQueue.add(holder); ++ } ++ } else { ++ // loaded but not sent: so queue it! ++ if (sendChunk) { ++ this.sendQueue.add(holder); ++ } ++ } ++ } ++ } ++ ++ loadQueue.sort((final ChunkPriorityHolder p1, final ChunkPriorityHolder p2) -> { ++ return Double.compare(p1.priority, p2.priority); ++ }); ++ ++ // we're modifying loadQueue, must remove ++ this.loader.chunkLoadQueue.remove(this); ++ ++ this.loadQueue.clear(); ++ this.loadQueue.addAll(loadQueue); ++ ++ // must re-add ++ this.loader.chunkLoadQueue.add(this); ++ } ++ } ++} +diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java +index 103657ad936f1a75ffbb92ca5eafb816e977e363..23757f466da571aec35a373112dcbba0d1f46dcb 100644 +--- a/src/main/java/net/minecraft/network/Connection.java ++++ b/src/main/java/net/minecraft/network/Connection.java +@@ -93,6 +93,28 @@ public class Connection extends SimpleChannelInboundHandler> { + public boolean queueImmunity = false; + public ConnectionProtocol protocol; + // Paper end ++ // Paper start - add pending task queue ++ private final Queue pendingTasks = new java.util.concurrent.ConcurrentLinkedQueue<>(); ++ public void execute(final Runnable run) { ++ if (this.channel == null || !this.channel.isRegistered()) { ++ run.run(); ++ return; ++ } ++ final boolean queue = !this.queue.isEmpty(); ++ if (!queue) { ++ this.channel.eventLoop().execute(run); ++ } else { ++ this.pendingTasks.add(run); ++ if (this.queue.isEmpty()) { ++ // something flushed async, dump tasks now ++ Runnable r; ++ while ((r = this.pendingTasks.poll()) != null) { ++ this.channel.eventLoop().execute(r); ++ } ++ } ++ } ++ } ++ // Paper end - add pending task queue + + // Paper start - allow controlled flushing + volatile boolean canFlush = true; +@@ -399,6 +421,7 @@ public class Connection extends SimpleChannelInboundHandler> { + return false; + } + private boolean processQueue() { ++ try { // Paper - add pending task queue + if (this.queue.isEmpty()) return true; + // Paper start - make only one flush call per sendPacketQueue() call + final boolean needsFlush = this.canFlush; +@@ -430,6 +453,12 @@ public class Connection extends SimpleChannelInboundHandler> { + } + } + return true; ++ } finally { // Paper start - add pending task queue ++ Runnable r; ++ while ((r = this.pendingTasks.poll()) != null) { ++ this.channel.eventLoop().execute(r); ++ } ++ } // Paper end - add pending task queue + } + // Paper end + +diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java +index 379ba589b0423284d63782d951c64770b160cf2d..86d1bb22665bf46c7744ef653eda0caefc2a4337 100644 +--- a/src/main/java/net/minecraft/server/MCUtil.java ++++ b/src/main/java/net/minecraft/server/MCUtil.java +@@ -637,7 +637,7 @@ public final class MCUtil { + + worldData.addProperty("name", world.getWorld().getName()); + worldData.addProperty("view-distance", world.getChunkSource().chunkMap.getEffectiveViewDistance()); +- worldData.addProperty("no-view-distance", world.getChunkSource().chunkMap.getRawNoTickViewDistance()); ++ worldData.addProperty("no-view-distance", world.getChunkSource().chunkMap.playerChunkManager.getTargetNoTickViewDistance()); // Paper - replace old player chunk management + worldData.addProperty("keep-spawn-loaded", world.keepSpawnInMemory); + worldData.addProperty("keep-spawn-loaded-range", world.paperConfig.keepLoadedRange); + worldData.addProperty("visible-chunk-count", visibleChunks.size()); +diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java +index 5c138478fd1bdbf84626dbcca3e8c31729e41a9f..a36f3b37cb36f0bcc3e45ff66e02e30ce5ff0f96 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java ++++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java +@@ -491,7 +491,7 @@ public class ChunkHolder { + // Paper start - per player view distance + // there can be potential desync with player's last mapped section and the view distance map, so use the + // view distance map here. +- com.destroystokyo.paper.util.misc.PlayerAreaMap viewDistanceMap = this.chunkMap.playerViewDistanceBroadcastMap; ++ com.destroystokyo.paper.util.misc.PlayerAreaMap viewDistanceMap = this.chunkMap.playerChunkManager.broadcastMap; // Paper - replace old player chunk manager + com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet players = viewDistanceMap.getObjectsInRange(this.pos); + if (players == null) { + return; +@@ -508,6 +508,7 @@ public class ChunkHolder { + + int viewDistance = viewDistanceMap.getLastViewDistance(player); + long lastPosition = viewDistanceMap.getLastCoordinate(player); ++ if (!this.chunkMap.playerChunkManager.isChunkSent(player, this.pos.x, this.pos.z)) continue; // Paper - replace player chunk management + + int distX = Math.abs(net.minecraft.server.MCUtil.getCoordinateX(lastPosition) - this.pos.x); + int distZ = Math.abs(net.minecraft.server.MCUtil.getCoordinateZ(lastPosition) - this.pos.z); +@@ -524,6 +525,7 @@ public class ChunkHolder { + continue; + } + ServerPlayer player = (ServerPlayer)temp; ++ if (!this.chunkMap.playerChunkManager.isChunkSent(player, this.pos.x, this.pos.z)) continue; // Paper - replace player chunk management + player.connection.send(packet); + } + } +diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java +index 364caf495bb8df867885f9df8dfd0c0f1b4673a6..45b668095459c16d2a723870d7533c69463bfde8 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -188,22 +188,7 @@ 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 - no-tick view distance +- int noTickViewDistance; +- public final int getRawNoTickViewDistance() { +- return this.noTickViewDistance; +- } +- public final int getEffectiveNoTickViewDistance() { +- return this.noTickViewDistance == -1 ? this.getEffectiveViewDistance() : this.noTickViewDistance; +- } +- public final int getLoadViewDistance() { +- return Math.max(this.getEffectiveViewDistance(), this.getEffectiveNoTickViewDistance()); +- } +- +- public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerViewDistanceBroadcastMap; +- public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerViewDistanceTickMap; +- public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerViewDistanceNoTickMap; +- // Paper end - no-tick view distance ++ public final io.papermc.paper.chunk.PlayerChunkLoader playerChunkManager = new io.papermc.paper.chunk.PlayerChunkLoader(this, this.pooledLinkedPlayerHashSets); // Paper - replace chunk loader + // Paper start - use distance map to optimise tracker + public static boolean isLegacyTrackingEntity(Entity entity) { + return entity.isLegacyTrackingEntity; +@@ -242,7 +227,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + com.destroystokyo.paper.util.misc.PlayerAreaMap trackMap = this.playerEntityTrackerTrackMaps[i]; + int trackRange = this.entityTrackerTrackRanges[i]; + +- trackMap.add(player, chunkX, chunkZ, Math.min(trackRange, this.getEffectiveViewDistance())); ++ trackMap.add(player, chunkX, chunkZ, Math.min(trackRange, player.getBukkitEntity().getViewDistance())); // Paper - per player view distances + } + // Paper end - use distance map to optimise entity tracker + // Paper start - optimise PlayerChunkMap#isOutsideRange +@@ -251,19 +236,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + // Paper start - optimise PlayerChunkMap#isOutsideRange + this.playerChunkTickRangeMap.add(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); + // Paper end - optimise PlayerChunkMap#isOutsideRange +- // Paper start - no-tick view distance +- int effectiveTickViewDistance = this.getEffectiveViewDistance(); +- int effectiveNoTickViewDistance = Math.max(this.getEffectiveNoTickViewDistance(), effectiveTickViewDistance); +- +- if (!this.skipPlayer(player)) { +- this.playerViewDistanceTickMap.add(player, chunkX, chunkZ, effectiveTickViewDistance); +- this.playerViewDistanceNoTickMap.add(player, chunkX, chunkZ, effectiveNoTickViewDistance + 2); // clients need chunk 1 neighbour, and we need another 1 for sending those extra neighbours (as we require neighbours to send) +- } +- +- player.needsChunkCenterUpdate = true; +- this.playerViewDistanceBroadcastMap.add(player, chunkX, chunkZ, effectiveNoTickViewDistance + 1); // clients need an extra neighbour to render the full view distance configured +- player.needsChunkCenterUpdate = false; +- // Paper end - no-tick view distance ++ this.playerChunkManager.addPlayer(player); // Paper - replace chunk loader + } + + void removePlayerFromDistanceMaps(ServerPlayer player) { +@@ -276,11 +249,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + this.playerMobSpawnMap.remove(player); + this.playerChunkTickRangeMap.remove(player); + // Paper end - optimise PlayerChunkMap#isOutsideRange +- // Paper start - no-tick view distance +- this.playerViewDistanceBroadcastMap.remove(player); +- this.playerViewDistanceTickMap.remove(player); +- this.playerViewDistanceNoTickMap.remove(player); +- // Paper end - no-tick view distance ++ this.playerChunkManager.removePlayer(player); // Paper - replace chunk loader + } + + void updateMaps(ServerPlayer player) { +@@ -292,25 +261,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + com.destroystokyo.paper.util.misc.PlayerAreaMap trackMap = this.playerEntityTrackerTrackMaps[i]; + int trackRange = this.entityTrackerTrackRanges[i]; + +- trackMap.update(player, chunkX, chunkZ, Math.min(trackRange, this.getEffectiveViewDistance())); ++ trackMap.update(player, chunkX, chunkZ, Math.min(trackRange, player.getBukkitEntity().getViewDistance())); // Paper - per player view distances + } + // Paper end - use distance map to optimise entity tracker + // Paper start - optimise PlayerChunkMap#isOutsideRange + this.playerChunkTickRangeMap.update(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); + // Paper end - optimise PlayerChunkMap#isOutsideRange +- // Paper start - no-tick view distance +- int effectiveTickViewDistance = this.getEffectiveViewDistance(); +- int effectiveNoTickViewDistance = Math.max(this.getEffectiveNoTickViewDistance(), effectiveTickViewDistance); +- +- if (!this.skipPlayer(player)) { +- this.playerViewDistanceTickMap.update(player, chunkX, chunkZ, effectiveTickViewDistance); +- this.playerViewDistanceNoTickMap.update(player, chunkX, chunkZ, effectiveNoTickViewDistance + 2); // clients need chunk 1 neighbour, and we need another 1 for sending those extra neighbours (as we require neighbours to send) +- } +- +- player.needsChunkCenterUpdate = true; +- this.playerViewDistanceBroadcastMap.update(player, chunkX, chunkZ, effectiveNoTickViewDistance + 1); // clients need an extra neighbour to render the full view distance configured +- player.needsChunkCenterUpdate = false; +- // Paper end - no-tick view distance ++ this.playerChunkManager.updatePlayer(player); // Paper - replace chunk loader + } + // Paper end + // Paper start +@@ -395,43 +352,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + this.regionManagers.add(this.dataRegionManager); + // Paper end + // Paper start - no-tick view distance +- this.setNoTickViewDistance(this.level.paperConfig.noTickViewDistance); +- this.playerViewDistanceTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets, +- (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, +- com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { +- if (newState.size() != 1) { +- return; +- } +- LevelChunk chunk = ChunkMap.this.level.getChunkSource().getChunkAtIfLoadedMainThreadNoCache(rangeX, rangeZ); +- if (chunk == null || !chunk.areNeighboursLoaded(2)) { +- return; +- } +- +- ChunkPos chunkPos = new ChunkPos(rangeX, rangeZ); +- ChunkMap.this.level.getChunkSource().addTicketAtLevel(TicketType.PLAYER, chunkPos, 31, chunkPos); // entity ticking level, TODO check on update +- }, +- (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, +- com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { +- if (newState != null) { +- return; +- } +- ChunkPos chunkPos = new ChunkPos(rangeX, rangeZ); +- ChunkMap.this.level.getChunkSource().removeTicketAtLevel(TicketType.PLAYER, chunkPos, 31, chunkPos); // entity ticking level, TODO check on update +- }); +- this.playerViewDistanceNoTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets); +- this.playerViewDistanceBroadcastMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets, +- (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, +- com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { +- if (player.needsChunkCenterUpdate) { +- player.needsChunkCenterUpdate = false; +- player.connection.send(new ClientboundSetChunkCacheCenterPacket(currPosX, currPosZ)); +- } +- ChunkMap.this.updateChunkTracking(player, new ChunkPos(rangeX, rangeZ), new Packet[2], false, true); // unloaded, loaded +- }, +- (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, +- com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { +- ChunkMap.this.updateChunkTracking(player, new ChunkPos(rangeX, rangeZ), null, true, false); // unloaded, loaded +- }); ++ this.setNoTickViewDistance(this.level.paperConfig.noTickViewDistance); // Paper - replace chunk loading system + // Paper end - no-tick view distance + this.playerMobDistanceMap = this.level.paperConfig.perPlayerMobSpawns ? new com.destroystokyo.paper.util.PlayerMobDistanceMap() : null; // Paper + // Paper start - use distance map to optimise entity tracker +@@ -538,6 +459,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + } + + public void checkHighPriorityChunks(ServerPlayer player) { ++ if (true) return; // Paper - replace player chunk loader + int currentTick = MinecraftServer.currentTick; + if (currentTick - player.lastHighPriorityChecked < 20 || !player.isRealPlayer) { // weed out fake players + return; +@@ -545,7 +467,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + player.lastHighPriorityChecked = currentTick; + it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap priorities = new it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap(); + +- int viewDistance = getEffectiveNoTickViewDistance(); ++ int viewDistance = 10;//int viewDistance = getEffectiveNoTickViewDistance(); // Paper - replace player chunk loader + net.minecraft.core.BlockPos.MutableBlockPos pos = new net.minecraft.core.BlockPos.MutableBlockPos(); + + // Prioritize circular near +@@ -611,7 +533,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + } + + private boolean shouldSkipPrioritization(ChunkPos coord) { +- if (playerViewDistanceNoTickMap.getObjectsInRange(coord.toLong()) == null) return true; ++ if (true) return true; // Paper - replace player chunk loader - unused outside paper player loader logic + ChunkHolder chunk = getUpdatingChunkIfPresent(coord.toLong()); + return chunk != null && (chunk.isFullChunkReady()); + } +@@ -1549,7 +1471,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + int k = this.viewDistance; + + this.viewDistance = j; +- this.setNoTickViewDistance(this.getRawNoTickViewDistance()); //Paper - no-tick view distance - propagate changes to no-tick, which does the actual chunk loading/sending ++ this.playerChunkManager.setTickDistance(Mth.clamp(viewDistance, 2, 32)); // Paper - replace player loader system + } + + } +@@ -1557,26 +1479,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + // Paper start - no-tick view distance + public final void setNoTickViewDistance(int viewDistance) { + viewDistance = viewDistance == -1 ? -1 : Mth.clamp(viewDistance, 2, 32); +- +- this.noTickViewDistance = viewDistance; +- int loadViewDistance = this.getLoadViewDistance(); +- this.distanceManager.setNoTickViewDistance(loadViewDistance + 2 + 2); // add 2 to account for the change to 31 -> 33 tickets // see notes in the distance map updating for the other + 2 +- +- if (this.level != null && this.level.players != null) { // this can be called from constructor, where these aren't set +- for (ServerPlayer player : this.level.players) { +- net.minecraft.server.network.ServerGamePacketListenerImpl connection = player.connection; +- if (connection != null) { +- // moved in from PlayerList +- connection.send(new net.minecraft.network.protocol.game.ClientboundSetChunkCacheRadiusPacket(loadViewDistance)); +- } +- this.updateMaps(player); +- // Paper end - no-tick view distance +- } +- } ++ this.playerChunkManager.setLoadDistance(viewDistance == -1 ? -1 : viewDistance + 1); // Paper - replace player loader system - add 1 here, we need an extra one to send to clients for chunks in this viewDistance to render + + } + +- protected void updateChunkTracking(ServerPlayer player, ChunkPos pos, Packet[] packets, boolean withinMaxWatchDistance, boolean withinViewDistance) { ++ public void updateChunkTracking(ServerPlayer player, ChunkPos pos, Packet[] packets, boolean withinMaxWatchDistance, boolean withinViewDistance) { // Paper - public + if (player.level == this.level) { + if (withinViewDistance && !withinMaxWatchDistance) { + ChunkHolder playerchunk = this.getVisibleChunkIfPresent(pos.toLong()); +@@ -1905,6 +1812,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + */ // Paper end - replaced by distance map + + this.updateMaps(player); // Paper - distance maps ++ this.playerChunkManager.updatePlayer(player); // Paper - respond to movement immediately + + } + +@@ -1913,7 +1821,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + // Paper start - per player view distance + // there can be potential desync with player's last mapped section and the view distance map, so use the + // view distance map here. +- com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet inRange = this.playerViewDistanceBroadcastMap.getObjectsInRange(chunkPos); ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet inRange = this.playerChunkManager.broadcastMap.getObjectsInRange(chunkPos); // Paper - replace player chunk loader system + + if (inRange == null) { + return Stream.empty(); +@@ -1929,8 +1837,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + continue; + } + ServerPlayer player = (ServerPlayer)temp; +- int viewDistance = this.playerViewDistanceBroadcastMap.getLastViewDistance(player); +- long lastPosition = this.playerViewDistanceBroadcastMap.getLastCoordinate(player); ++ if (!this.playerChunkManager.isChunkSent(player, chunkPos.x, chunkPos.z)) continue; // Paper - replace player chunk management ++ int viewDistance = this.playerChunkManager.broadcastMap.getLastViewDistance(player); // Paper - replace player chunk loader system ++ long lastPosition = this.playerChunkManager.broadcastMap.getLastCoordinate(player); // Paper - replace player chunk loader system + + int distX = Math.abs(MCUtil.getCoordinateX(lastPosition) - chunkPos.x); + int distZ = Math.abs(MCUtil.getCoordinateZ(lastPosition) - chunkPos.z); +@@ -1945,6 +1854,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + continue; + } + ServerPlayer player = (ServerPlayer)temp; ++ if (!this.playerChunkManager.isChunkSent(player, chunkPos.x, chunkPos.z)) continue; // Paper - replace player chunk management + players.add(player); + } + } +@@ -2358,7 +2268,7 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially + double vec3d_dy = player.getY() - this.entity.getY(); + double vec3d_dz = player.getZ() - this.entity.getZ(); + // Paper end - remove allocation of Vec3D here +- int i = Math.min(this.getEffectiveRange(), (ChunkMap.this.viewDistance - 1) * 16); ++ int i = Math.min(this.getEffectiveRange(), player.getBukkitEntity().getViewDistance() * 16); // Paper - per player view distance + boolean flag = vec3d_dx >= (double) (-i) && vec3d_dx <= (double) i && vec3d_dz >= (double) (-i) && vec3d_dz <= (double) i && this.entity.broadcastToPlayer(player); // Paper - remove allocation of Vec3D here + + // CraftBukkit start - respect vanish API +diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java +index 1cc4e0a1f3d8235ef88b48e01ca8b78a263d2676..0b34536cdffab31a717b613042d7098109846586 100644 +--- a/src/main/java/net/minecraft/server/level/DistanceManager.java ++++ b/src/main/java/net/minecraft/server/level/DistanceManager.java +@@ -46,7 +46,7 @@ public abstract class DistanceManager { + public final Long2ObjectOpenHashMap>> tickets = new Long2ObjectOpenHashMap(); + private final DistanceManager.ChunkTicketTracker ticketTracker = new DistanceManager.ChunkTicketTracker(); + public static final int MOB_SPAWN_RANGE = 8; // private final ChunkMapDistance.b f = new ChunkMapDistance.b(8); // Paper - no longer used +- private final DistanceManager.PlayerTicketTracker playerTicketManager = new DistanceManager.PlayerTicketTracker(33); ++ //private final DistanceManager.PlayerTicketTracker playerTicketManager = new DistanceManager.PlayerTicketTracker(33); // Paper - no longer used + // Paper start use a queue, but still keep unique requirement + public final java.util.Queue pendingChunkUpdates = new java.util.ArrayDeque() { + @Override +@@ -113,7 +113,7 @@ public abstract class DistanceManager { + public boolean runAllUpdates(ChunkMap playerchunkmap) { + //this.f.a(); // Paper - no longer used + org.spigotmc.AsyncCatcher.catchOp("DistanceManagerTick"); // Paper +- this.playerTicketManager.runAllUpdates(); ++ //this.playerTicketManager.runAllUpdates(); // Paper - no longer used + int i = Integer.MAX_VALUE - this.ticketTracker.runDistanceUpdates(Integer.MAX_VALUE); + boolean flag = i != 0; + +@@ -313,7 +313,7 @@ public abstract class DistanceManager { + org.spigotmc.AsyncCatcher.catchOp("ChunkMapDistance::addPriorityTicket"); + long pair = coords.toLong(); + ChunkHolder chunk = chunkMap.getUpdatingChunkIfPresent(pair); +- boolean needsTicket = chunkMap.playerViewDistanceNoTickMap.getObjectsInRange(pair) != null && !hasPlayerTicket(coords, 33); ++ boolean needsTicket = false; // Paper - replace old loader system + + if (needsTicket) { + Ticket ticket = new Ticket<>(TicketType.PLAYER, 33, coords); +@@ -411,7 +411,7 @@ public abstract class DistanceManager { + return new ObjectOpenHashSet(); + })).add(player); + //this.f.update(i, 0, true); // Paper - no longer used +- this.playerTicketManager.update(i, 0, true); ++ //this.playerTicketManager.update(i, 0, true); // Paper - no longer used + } + + public void removePlayer(SectionPos pos, ServerPlayer player) { +@@ -423,7 +423,7 @@ public abstract class DistanceManager { + if (objectset == null || objectset.isEmpty()) { // Paper + this.playersPerChunk.remove(i); + //this.f.update(i, Integer.MAX_VALUE, false); // Paper - no longer used +- this.playerTicketManager.update(i, Integer.MAX_VALUE, false); ++ //this.playerTicketManager.update(i, Integer.MAX_VALUE, false); // Paper - no longer used + } + + } +@@ -442,7 +442,7 @@ public abstract class DistanceManager { + } + + protected void setNoTickViewDistance(int i) { // Paper - force abi breakage on usage change +- this.playerTicketManager.updateViewDistance(i); ++ throw new UnsupportedOperationException("use world api"); // Paper - no longer relevant + } + + public int getNaturalSpawnChunkCount() { +@@ -563,6 +563,7 @@ public abstract class DistanceManager { + } + } + ++ /* Paper - replace old loader system + private class FixedPlayerDistanceChunkTracker extends ChunkTracker { + + protected final Long2ByteMap chunks = new Long2ByteOpenHashMap(); +@@ -858,4 +859,5 @@ public abstract class DistanceManager { + } + // Paper end + } ++ */ // Paper - replace old loader system + } +diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +index 108f2212f8bd00247bf73ff4f3ba42830abad459..0d8a47770435c27519af4ebd78835ec787551733 100644 +--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java ++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +@@ -928,6 +928,7 @@ public class ServerChunkCache extends ChunkSource { + this.level.timings.doChunkMap.stopTiming(); // Spigot + this.level.getProfiler().popPush("chunks"); + this.level.timings.chunks.startTiming(); // Paper - timings ++ this.chunkMap.playerChunkManager.tick(); // Paper - this is mostly is to account for view distance changes + this.tickChunks(); + this.level.timings.chunks.stopTiming(); // Paper - timings + this.level.timings.doChunkUnload.startTiming(); // Spigot +@@ -1218,6 +1219,7 @@ public class ServerChunkCache extends ChunkSource { + public boolean pollTask() { + try { + boolean execChunkTask = com.destroystokyo.paper.io.chunk.ChunkTaskManager.pollChunkWaitQueue() || ServerChunkCache.this.level.asyncChunkTaskManager.pollNextChunkTask(); // Paper ++ ServerChunkCache.this.chunkMap.playerChunkManager.tickMidTick(); // Paper + if (ServerChunkCache.this.runDistanceManagerUpdates()) { + return true; + } else { +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index 4345c0e1cf00cb8cd0cacc93ebc0a69009be7eff..483df59a805d2724551042aa4470bc439fc2e7e9 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -261,7 +261,7 @@ public class ServerPlayer extends Player { + + public double lastEntitySpawnRadiusSquared; // Paper - optimise isOutsideRange, this field is in blocks + public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet cachedSingleHashSet; // Paper +- boolean needsChunkCenterUpdate; // Paper - no-tick view distance ++ public boolean needsChunkCenterUpdate; // Paper - no-tick view distance // Paper - public + public org.bukkit.event.player.PlayerQuitEvent.QuitReason quitReason = null; // Paper - there are a lot of changes to do if we change all methods leading to the event + + public ServerPlayer(MinecraftServer server, ServerLevel world, GameProfile profile) { +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index 4c8f6d2d72194d313e7383b5a499c8ca1a84e1da..ad9fb50791779a5fe7d22268b71bd10d9c9ff3f0 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -267,7 +267,7 @@ public abstract class PlayerList { + boolean flag1 = gamerules.getBoolean(GameRules.RULE_REDUCEDDEBUGINFO); + + // Spigot - view distance +- playerconnection.send(new ClientboundLoginPacket(player.getId(), player.gameMode.getGameModeForPlayer(), player.gameMode.getPreviousGameModeForPlayer(), BiomeManager.obfuscateSeed(worldserver1.getSeed()), worlddata.isHardcore(), this.server.levelKeys(), this.registryHolder, worldserver1.dimensionType(), worldserver1.dimension(), this.getMaxPlayers(), worldserver1.getChunkSource().chunkMap.getLoadViewDistance(), flag1, !flag, worldserver1.isDebug(), worldserver1.isFlat())); // Paper - no-tick view distance ++ playerconnection.send(new ClientboundLoginPacket(player.getId(), player.gameMode.getGameModeForPlayer(), player.gameMode.getPreviousGameModeForPlayer(), BiomeManager.obfuscateSeed(worldserver1.getSeed()), worlddata.isHardcore(), this.server.levelKeys(), this.registryHolder, worldserver1.dimensionType(), worldserver1.dimension(), this.getMaxPlayers(), worldserver1.getChunkSource().chunkMap.playerChunkManager.getLoadDistance(), flag1, !flag, worldserver1.isDebug(), worldserver1.isFlat())); // Paper - no-tick view distance // Paper - replace old player chunk management + player.getBukkitEntity().sendSupportedChannels(); // CraftBukkit + playerconnection.send(new ClientboundCustomPayloadPacket(ClientboundCustomPayloadPacket.BRAND, (new FriendlyByteBuf(Unpooled.buffer())).writeUtf(this.getServer().getServerModName()))); + playerconnection.send(new ClientboundChangeDifficultyPacket(worlddata.getDifficulty(), worlddata.isDifficultyLocked())); +@@ -940,7 +940,7 @@ public abstract class PlayerList { + // CraftBukkit start + LevelData worlddata = worldserver1.getLevelData(); + entityplayer1.connection.send(new ClientboundRespawnPacket(worldserver1.dimensionType(), worldserver1.dimension(), BiomeManager.obfuscateSeed(worldserver1.getSeed()), entityplayer1.gameMode.getGameModeForPlayer(), entityplayer1.gameMode.getPreviousGameModeForPlayer(), worldserver1.isDebug(), worldserver1.isFlat(), flag)); +- entityplayer1.connection.send(new ClientboundSetChunkCacheRadiusPacket(worldserver1.getChunkSource().chunkMap.getLoadViewDistance())); // Spigot // Paper - no-tick view distance ++ entityplayer1.connection.send(new ClientboundSetChunkCacheRadiusPacket(worldserver1.getChunkSource().chunkMap.playerChunkManager.getLoadDistance())); // Spigot // Paper - no-tick view distance// Paper - replace old player chunk management + entityplayer1.setLevel(worldserver1); + entityplayer1.unsetRemoved(); + entityplayer1.connection.teleport(new Location(worldserver1.getWorld(), entityplayer1.getX(), entityplayer1.getY(), entityplayer1.getZ(), entityplayer1.getYRot(), entityplayer1.getXRot())); +@@ -1215,7 +1215,7 @@ public abstract class PlayerList { + // Really shouldn't happen... + backingSet = world != null ? world.players.toArray() : players.toArray(); + } else { +- com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet nearbyPlayers = chunkMap.playerViewDistanceBroadcastMap.getObjectsInRange(MCUtil.fastFloor(x) >> 4, MCUtil.fastFloor(z) >> 4); ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet nearbyPlayers = chunkMap.playerChunkManager.broadcastMap.getObjectsInRange(MCUtil.fastFloor(x) >> 4, MCUtil.fastFloor(z) >> 4); // Paper - replace old player chunk management + if (nearbyPlayers == null) { + return; + } +diff --git a/src/main/java/net/minecraft/world/item/EnderEyeItem.java b/src/main/java/net/minecraft/world/item/EnderEyeItem.java +index 7ccfe737fdf7f07b731ea0ff82e897564350705c..fe709bee0e657ff9bfc6deb6d4bd8ce4eccfcf06 100644 +--- a/src/main/java/net/minecraft/world/item/EnderEyeItem.java ++++ b/src/main/java/net/minecraft/world/item/EnderEyeItem.java +@@ -60,9 +60,10 @@ public class EnderEyeItem extends Item { + + // CraftBukkit start - Use relative location for far away sounds + // world.b(1038, blockposition1.c(1, 0, 1), 0); +- int viewDistance = world.getCraftServer().getViewDistance() * 16; ++ //int viewDistance = world.getCraftServer().getViewDistance() * 16; // Paper - apply view distance patch + BlockPos soundPos = blockposition1.offset(1, 0, 1); + for (ServerPlayer player : world.getServer().getPlayerList().players) { ++ final int viewDistance = player.getBukkitEntity().getViewDistance(); // Paper - apply view distance patch + double deltaX = soundPos.getX() - player.getX(); + double deltaZ = soundPos.getZ() - player.getZ(); + double distanceSquared = deltaX * deltaX + deltaZ * deltaZ; +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index 6ca0134c76adc335bb5125831e332fd709f09de7..83062654f7a3b05ba0ddb97645d615988bbe511b 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -597,7 +597,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + this.sendBlockUpdated(blockposition, iblockdata1, iblockdata, i); + // Paper start - per player view distance - allow block updates for non-ticking chunks in player view distance + // if copied from above +- } else if ((i & 2) != 0 && (!this.isClientSide || (i & 4) == 0) && (this.isClientSide || chunk == null || ((ServerLevel)this).getChunkSource().chunkMap.playerViewDistanceBroadcastMap.getObjectsInRange(MCUtil.getCoordinateKey(blockposition)) != null)) { ++ } else if ((i & 2) != 0 && (!this.isClientSide || (i & 4) == 0) && (this.isClientSide || chunk == null || ((ServerLevel)this).getChunkSource().chunkMap.playerChunkManager.broadcastMap.getObjectsInRange(MCUtil.getCoordinateKey(blockposition)) != null)) { // Paper - replace old player chunk management + ((ServerLevel)this).getChunkSource().blockChanged(blockposition); + // Paper end - per player view distance + } +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 f0c43f9f636a5dd1f0dfbae604dfa1f4ff9ebd4e..9e75ee7f43722f05dd5df4ef00f7d3e271f4c6e5 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +@@ -293,11 +293,12 @@ public class LevelChunk implements ChunkAccess { + ChunkMap chunkMap = chunkProviderServer.chunkMap; + // this code handles the addition of ticking tickets - the distance map handles the removal + if (!areNeighboursLoaded(bitsetBefore, 2) && areNeighboursLoaded(bitsetAfter, 2)) { +- if (chunkMap.playerViewDistanceTickMap.getObjectsInRange(this.coordinateKey) != null) { ++ if (chunkMap.playerChunkManager.tickMap.getObjectsInRange(this.coordinateKey) != null) { // Paper - replace old player chunk loading system + // now we're ready for entity ticking + chunkProviderServer.mainThreadProcessor.execute(() -> { + // double check that this condition still holds. +- if (LevelChunk.this.areNeighboursLoaded(2) && chunkMap.playerViewDistanceTickMap.getObjectsInRange(LevelChunk.this.coordinateKey) != null) { ++ if (LevelChunk.this.areNeighboursLoaded(2) && chunkMap.playerChunkManager.tickMap.getObjectsInRange(LevelChunk.this.coordinateKey) != null) { // Paper - replace old player chunk loading system ++ chunkMap.playerChunkManager.onChunkPlayerTickReady(this.chunkPos.x, this.chunkPos.z); // Paper - replace old player chunk + chunkProviderServer.addTicketAtLevel(net.minecraft.server.level.TicketType.PLAYER, LevelChunk.this.chunkPos, 31, LevelChunk.this.chunkPos); // 31 -> entity ticking, TODO check on update + } + }); +@@ -306,31 +307,18 @@ public class LevelChunk implements ChunkAccess { + + // this code handles the chunk sending + if (!areNeighboursLoaded(bitsetBefore, 1) && areNeighboursLoaded(bitsetAfter, 1)) { +- if (chunkMap.playerViewDistanceBroadcastMap.getObjectsInRange(this.coordinateKey) != null) { +- // now we're ready to send +- chunkMap.mainThreadMailbox.tell(ChunkTaskPriorityQueueSorter.message(chunkMap.getUpdatingChunkIfPresent(this.coordinateKey), (() -> { // Copied frm PlayerChunkMap +- // double check that this condition still holds. +- if (!LevelChunk.this.areNeighboursLoaded(1)) { +- return; +- } +- com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet inRange = chunkMap.playerViewDistanceBroadcastMap.getObjectsInRange(LevelChunk.this.coordinateKey); +- if (inRange == null) { +- return; +- } +- +- // broadcast +- Object[] backingSet = inRange.getBackingSet(); +- Packet[] chunkPackets = new Packet[2]; +- for (int index = 0, len = backingSet.length; index < len; ++index) { +- Object temp = backingSet[index]; +- if (!(temp instanceof net.minecraft.server.level.ServerPlayer)) { +- continue; +- } +- net.minecraft.server.level.ServerPlayer player = (net.minecraft.server.level.ServerPlayer)temp; +- chunkMap.playerLoadedChunk(player, chunkPackets, LevelChunk.this); +- } +- }))); +- } ++ // Paper start - replace old player chunk loading system ++ chunkProviderServer.mainThreadProcessor.execute(() -> { ++ if (!LevelChunk.this.areNeighboursLoaded(1)) { ++ return; ++ } ++ LevelChunk.this.postProcessGeneration(); ++ if (!LevelChunk.this.areNeighboursLoaded(1)) { ++ return; ++ } ++ chunkMap.playerChunkManager.onChunkSendReady(this.chunkPos.x, this.chunkPos.z); ++ }); ++ // Paper end - replace old player chunk loading system + } + // Paper end - no-tick view distance + } +@@ -871,6 +859,7 @@ public class LevelChunk implements ChunkAccess { + // Paper end - neighbour cache + org.bukkit.Server server = this.level.getCraftServer(); + this.level.getChunkSource().addLoadedChunk(this); // Paper ++ ((ServerLevel)this.level).getChunkSource().chunkMap.playerChunkManager.onChunkLoad(this.chunkPos.x, this.chunkPos.z); // Paper - rewrite player chunk management + if (server != null) { + /* + * If it's a new world, the first few chunks are generated inside +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index 40463344f364618dd2e7330cb0168ff69a5fa58b..7e56d6e60f1e1bd8dbf768afcbe06addcd5c5a60 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -2039,14 +2039,14 @@ public class CraftWorld extends CraftRegionAccessor implements World { + throw new IllegalArgumentException("View distance " + viewDistance + " is out of range of [2, 32]"); + } + net.minecraft.server.level.ChunkMap chunkMap = getHandle().getChunkSource().chunkMap; +- if (viewDistance != chunkMap.getEffectiveViewDistance()) { ++ if (true) { // Paper - replace old player chunk management + chunkMap.setViewDistance(viewDistance); + } + } + + @Override + public int getNoTickViewDistance() { +- return getHandle().getChunkSource().chunkMap.getEffectiveNoTickViewDistance(); ++ return getHandle().getChunkSource().chunkMap.playerChunkManager.getTargetNoTickViewDistance(); // Paper - replace old player chunk management + } + + @Override +@@ -2055,11 +2055,22 @@ public class CraftWorld extends CraftRegionAccessor implements World { + throw new IllegalArgumentException("View distance " + viewDistance + " is out of range of [2, 32]"); + } + net.minecraft.server.level.ChunkMap chunkMap = getHandle().getChunkSource().chunkMap; +- if (viewDistance != chunkMap.getRawNoTickViewDistance()) { ++ if (true) { // Paper - replace old player chunk management + chunkMap.setNoTickViewDistance(viewDistance); + } + } + // Paper end - per player view distance ++ // Paper start - add view distances ++ @Override ++ public int getSendViewDistance() { ++ return getHandle().getChunkSource().chunkMap.playerChunkManager.getTargetSendDistance(); ++ } ++ ++ @Override ++ public void setSendViewDistance(int viewDistance) { ++ getHandle().getChunkSource().chunkMap.playerChunkManager.setTargetSendDistance(viewDistance); ++ } ++ // Paper end - add view distances + + // Spigot start + private final org.bukkit.World.Spigot spigot = new org.bukkit.World.Spigot() +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index abf1bd1d3f29e998f08ff86395c3f16d27a5b7d2..8c8698f6e17562651fe790dc19825e428d5d0352 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -517,15 +517,70 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + } + } + ++ // Paper start - implement view distances ++ @Override ++ public int getSendViewDistance() { ++ net.minecraft.server.level.ChunkMap chunkMap = this.getHandle().getLevel().getChunkSource().chunkMap; ++ io.papermc.paper.chunk.PlayerChunkLoader.PlayerLoaderData data = chunkMap.playerChunkManager.getData(this.getHandle()); ++ if (data == null) { ++ return chunkMap.playerChunkManager.getTargetSendDistance(); ++ } ++ return data.getTargetSendViewDistance(); ++ } ++ ++ @Override ++ public void setSendViewDistance(int viewDistance) { ++ net.minecraft.server.level.ChunkMap chunkMap = this.getHandle().getLevel().getChunkSource().chunkMap; ++ io.papermc.paper.chunk.PlayerChunkLoader.PlayerLoaderData data = chunkMap.playerChunkManager.getData(this.getHandle()); ++ if (data == null) { ++ throw new IllegalStateException("Player is not attached to world"); ++ } ++ ++ data.setTargetSendViewDistance(viewDistance); ++ } ++ ++ @Override ++ public int getNoTickViewDistance() { ++ net.minecraft.server.level.ChunkMap chunkMap = this.getHandle().getLevel().getChunkSource().chunkMap; ++ io.papermc.paper.chunk.PlayerChunkLoader.PlayerLoaderData data = chunkMap.playerChunkManager.getData(this.getHandle()); ++ if (data == null) { ++ return chunkMap.playerChunkManager.getTargetNoTickViewDistance(); ++ } ++ return data.getTargetNoTickViewDistance(); ++ } ++ ++ @Override ++ public void setNoTickViewDistance(int viewDistance) { ++ net.minecraft.server.level.ChunkMap chunkMap = this.getHandle().getLevel().getChunkSource().chunkMap; ++ io.papermc.paper.chunk.PlayerChunkLoader.PlayerLoaderData data = chunkMap.playerChunkManager.getData(this.getHandle()); ++ if (data == null) { ++ throw new IllegalStateException("Player is not attached to world"); ++ } ++ ++ data.setTargetNoTickViewDistance(viewDistance); ++ } ++ + @Override + public int getViewDistance() { +- throw new NotImplementedException("Per-Player View Distance APIs need further understanding to properly implement (There are per world view distances though!)"); // TODO ++ net.minecraft.server.level.ChunkMap chunkMap = this.getHandle().getLevel().getChunkSource().chunkMap; ++ io.papermc.paper.chunk.PlayerChunkLoader.PlayerLoaderData data = chunkMap.playerChunkManager.getData(this.getHandle()); ++ if (data == null) { ++ return chunkMap.playerChunkManager.getTargetViewDistance(); ++ } ++ return data.getTargetTickViewDistance(); + } + + @Override + public void setViewDistance(int viewDistance) { +- throw new NotImplementedException("Per-Player View Distance APIs need further understanding to properly implement (There are per world view distances though!)"); // TODO ++ net.minecraft.server.level.ChunkMap chunkMap = this.getHandle().getLevel().getChunkSource().chunkMap; ++ io.papermc.paper.chunk.PlayerChunkLoader.PlayerLoaderData data = chunkMap.playerChunkManager.getData(this.getHandle()); ++ if (data == null) { ++ throw new IllegalStateException("Player is not attached to world"); ++ } ++ ++ data.setTargetTickViewDistance(viewDistance); + } ++ // Paper end - implement view distances + + @Override + public T getClientOption(com.destroystokyo.paper.ClientOption type) { diff --git a/patches/server/0772-Replace-ticket-level-propagator.patch b/patches/server/0772-Replace-ticket-level-propagator.patch new file mode 100644 index 0000000000..5ed73f61e6 --- /dev/null +++ b/patches/server/0772-Replace-ticket-level-propagator.patch @@ -0,0 +1,248 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sun, 21 Mar 2021 16:25:42 -0700 +Subject: [PATCH] Replace ticket level propagator + +Mojang's propagator is slow, and this isn't surprising +given it's built on the same utilities the vanilla light engine +is built on. The simple propagator I wrote is approximately 4x +faster when simulating player movement. For a long time timing +reports have shown this function take up significant tick, ( +approx 10% or more), and async sampling data shows the level +propagation alone takes up a significant amount. So this +should help with that. A big side effect is that mid-tick +will be more effective, since more time will be allocated +to actually processing chunk tasks vs the ticket level updates. + +diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java +index 0b34536cdffab31a717b613042d7098109846586..24fa3d847f7c0aec52133ffc658cd90a602a44d5 100644 +--- a/src/main/java/net/minecraft/server/level/DistanceManager.java ++++ b/src/main/java/net/minecraft/server/level/DistanceManager.java +@@ -36,6 +36,7 @@ import net.minecraft.world.level.chunk.LevelChunk; + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++import it.unimi.dsi.fastutil.longs.Long2IntLinkedOpenHashMap; // Paper + public abstract class DistanceManager { + + static final Logger LOGGER = LogManager.getLogger(); +@@ -44,7 +45,7 @@ public abstract class DistanceManager { + private static final int INITIAL_TICKET_LIST_CAPACITY = 4; + final Long2ObjectMap> playersPerChunk = new Long2ObjectOpenHashMap(); + public final Long2ObjectOpenHashMap>> tickets = new Long2ObjectOpenHashMap(); +- private final DistanceManager.ChunkTicketTracker ticketTracker = new DistanceManager.ChunkTicketTracker(); ++ //private final DistanceManager.ChunkTicketTracker ticketTracker = new DistanceManager.ChunkTicketTracker(); // Paper - replace ticket level propagator + public static final int MOB_SPAWN_RANGE = 8; // private final ChunkMapDistance.b f = new ChunkMapDistance.b(8); // Paper - no longer used + //private final DistanceManager.PlayerTicketTracker playerTicketManager = new DistanceManager.PlayerTicketTracker(33); // Paper - no longer used + // Paper start use a queue, but still keep unique requirement +@@ -77,6 +78,46 @@ public abstract class DistanceManager { + this.mainThreadExecutor = mainThreadExecutor; + } + ++ // Paper start - replace ticket level propagator ++ protected final Long2IntLinkedOpenHashMap ticketLevelUpdates = new Long2IntLinkedOpenHashMap() { ++ @Override ++ protected void rehash(int newN) { ++ // no downsizing allowed ++ if (newN < this.n) { ++ return; ++ } ++ super.rehash(newN); ++ } ++ }; ++ protected final io.papermc.paper.util.misc.Delayed8WayDistancePropagator2D ticketLevelPropagator = new io.papermc.paper.util.misc.Delayed8WayDistancePropagator2D( ++ (long coordinate, byte oldLevel, byte newLevel) -> { ++ DistanceManager.this.ticketLevelUpdates.putAndMoveToLast(coordinate, convertBetweenTicketLevels(newLevel)); ++ } ++ ); ++ // function for converting between ticket levels and propagator levels and vice versa ++ // the problem is the ticket level propagator will propagate from a set source down to zero, whereas mojang expects ++ // levels to propagate from a set value up to a maximum value. so we need to convert the levels we put into the propagator ++ // and the levels we get out of the propagator ++ ++ // this maps so that GOLDEN_TICKET + 1 will be 0 in the propagator, GOLDEN_TICKET will be 1, and so on ++ // we need GOLDEN_TICKET+1 as 0 because anything >= GOLDEN_TICKET+1 should be unloaded ++ public static int convertBetweenTicketLevels(final int level) { ++ return ChunkMap.MAX_CHUNK_DISTANCE - level + 1; ++ } ++ ++ protected final int getPropagatedTicketLevel(final long coordinate) { ++ return convertBetweenTicketLevels(this.ticketLevelPropagator.getLevel(coordinate)); ++ } ++ ++ protected final void updateTicketLevel(final long coordinate, final int ticketLevel) { ++ if (ticketLevel > ChunkMap.MAX_CHUNK_DISTANCE) { ++ this.ticketLevelPropagator.removeSource(coordinate); ++ } else { ++ this.ticketLevelPropagator.setSource(coordinate, convertBetweenTicketLevels(ticketLevel)); ++ } ++ } ++ // Paper end - replace ticket level propagator ++ + protected void purgeStaleTickets() { + ++this.ticketTickCounter; + ObjectIterator objectiterator = this.tickets.long2ObjectEntrySet().fastIterator(); +@@ -87,7 +128,7 @@ public abstract class DistanceManager { + if ((entry.getValue()).removeIf((ticket) -> { // CraftBukkit - decompile error + return ticket.timedOut(this.ticketTickCounter); + })) { +- this.ticketTracker.update(entry.getLongKey(), DistanceManager.getTicketLevelAt((SortedArraySet) entry.getValue()), false); ++ this.updateTicketLevel(entry.getLongKey(), getTicketLevelAt(entry.getValue())); // Paper - replace ticket level propagator + } + + if (((SortedArraySet) entry.getValue()).isEmpty()) { +@@ -110,60 +151,93 @@ public abstract class DistanceManager { + @Nullable + protected abstract ChunkHolder updateChunkScheduling(long pos, int level, @Nullable ChunkHolder holder, int k); + ++ protected long ticketLevelUpdateCount; // Paper - replace ticket level propagator + public boolean runAllUpdates(ChunkMap playerchunkmap) { + //this.f.a(); // Paper - no longer used + org.spigotmc.AsyncCatcher.catchOp("DistanceManagerTick"); // Paper + //this.playerTicketManager.runAllUpdates(); // Paper - no longer used +- int i = Integer.MAX_VALUE - this.ticketTracker.runDistanceUpdates(Integer.MAX_VALUE); +- boolean flag = i != 0; ++ boolean flag = this.ticketLevelPropagator.propagateUpdates(); // Paper - replace ticket level propagator + + if (flag) { + ; + } + +- // Paper start +- if (!this.pendingChunkUpdates.isEmpty()) { +- this.pollingPendingChunkUpdates = true; try { // Paper - Chunk priority +- while(!this.pendingChunkUpdates.isEmpty()) { +- ChunkHolder remove = this.pendingChunkUpdates.remove(); +- remove.isUpdateQueued = false; +- remove.updateFutures(playerchunkmap, this.mainThreadExecutor); +- } +- } finally { this.pollingPendingChunkUpdates = false; } // Paper - Chunk priority +- // Paper end +- return true; +- } else { +- if (!this.ticketsToRelease.isEmpty()) { +- LongIterator longiterator = this.ticketsToRelease.iterator(); ++ // Paper start - replace level propagator ++ ticket_update_loop: ++ while (!this.ticketLevelUpdates.isEmpty()) { ++ flag = true; + +- while (longiterator.hasNext()) { +- long j = longiterator.nextLong(); ++ boolean oldPolling = this.pollingPendingChunkUpdates; ++ this.pollingPendingChunkUpdates = true; ++ try { ++ for (java.util.Iterator iterator = this.ticketLevelUpdates.long2IntEntrySet().fastIterator(); iterator.hasNext();) { ++ Long2IntMap.Entry entry = iterator.next(); ++ long key = entry.getLongKey(); ++ int newLevel = entry.getIntValue(); ++ ChunkHolder chunk = this.getChunk(key); ++ ++ if (chunk == null && newLevel > ChunkMap.MAX_CHUNK_DISTANCE) { ++ // not loaded and it shouldn't be loaded! ++ continue; ++ } ++ ++ int currentLevel = chunk == null ? ChunkMap.MAX_CHUNK_DISTANCE + 1 : chunk.getTicketLevel(); ++ ++ if (currentLevel == newLevel) { ++ // nothing to do ++ continue; ++ } + +- if (this.getTickets(j).stream().anyMatch((ticket) -> { +- return ticket.getType() == TicketType.PLAYER; +- })) { +- ChunkHolder playerchunk = playerchunkmap.getUpdatingChunkIfPresent(j); ++ this.updateChunkScheduling(key, newLevel, chunk, currentLevel); ++ } + +- if (playerchunk == null) { +- throw new IllegalStateException(); ++ long recursiveCheck = ++this.ticketLevelUpdateCount; ++ while (!this.ticketLevelUpdates.isEmpty()) { ++ long key = this.ticketLevelUpdates.firstLongKey(); ++ int newLevel = this.ticketLevelUpdates.removeFirstInt(); ++ ChunkHolder chunk = this.getChunk(key); ++ ++ if (chunk == null) { ++ if (newLevel <= ChunkMap.MAX_CHUNK_DISTANCE) { ++ throw new IllegalStateException("Expected chunk holder to be created"); + } ++ // not loaded and it shouldn't be loaded! ++ continue; ++ } + +- CompletableFuture> completablefuture = playerchunk.getEntityTickingChunkFuture(); ++ int currentLevel = chunk.oldTicketLevel; + +- completablefuture.thenAccept((either) -> { +- this.mainThreadExecutor.execute(() -> { +- this.ticketThrottlerReleaser.tell(ChunkTaskPriorityQueueSorter.release(() -> { +- }, j, false)); +- }); +- }); ++ if (currentLevel == newLevel) { ++ // nothing to do ++ continue; ++ } ++ ++ chunk.updateFutures(playerchunkmap, this.mainThreadExecutor); ++ if (recursiveCheck != this.ticketLevelUpdateCount) { ++ // back to the start, we must create player chunks and update the ticket level fields before ++ // processing the actual level updates ++ continue ticket_update_loop; + } + } + +- this.ticketsToRelease.clear(); +- } ++ for (;;) { ++ if (recursiveCheck != this.ticketLevelUpdateCount) { ++ continue ticket_update_loop; ++ } ++ ChunkHolder pendingUpdate = this.pendingChunkUpdates.poll(); ++ if (pendingUpdate == null) { ++ break; ++ } + +- return flag; ++ pendingUpdate.updateFutures(playerchunkmap, this.mainThreadExecutor); ++ } ++ } finally { ++ this.pollingPendingChunkUpdates = oldPolling; ++ } + } ++ ++ return flag; ++ // Paper end - replace level propagator + } + boolean pollingPendingChunkUpdates = false; // Paper - Chunk priority + +@@ -175,7 +249,7 @@ public abstract class DistanceManager { + + ticket1.setCreatedTick(this.ticketTickCounter); + if (ticket.getTicketLevel() < j) { +- this.ticketTracker.update(i, ticket.getTicketLevel(), true); ++ this.updateTicketLevel(i, ticket.getTicketLevel()); // Paper - replace ticket level propagator + } + + return ticket == ticket1; // CraftBukkit +@@ -219,7 +293,7 @@ public abstract class DistanceManager { + // Paper start - Chunk priority + int newLevel = getTicketLevelAt(arraysetsorted); + if (newLevel > oldLevel) { +- this.ticketTracker.update(i, newLevel, false); ++ this.updateTicketLevel(i, newLevel); // Paper // Paper - replace ticket level propagator + } + // Paper end + return removed; // CraftBukkit +@@ -507,7 +581,7 @@ public abstract class DistanceManager { + SortedArraySet> tickets = entry.getValue(); + if (tickets.remove(target)) { + // copied from removeTicket +- this.ticketTracker.update(entry.getLongKey(), DistanceManager.getTicketLevelAt(tickets), false); ++ this.updateTicketLevel(entry.getLongKey(), getTicketLevelAt(tickets)); // Paper - replace ticket level propagator + + // can't use entry after it's removed + if (tickets.isEmpty()) { diff --git a/patches/server/0773-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch b/patches/server/0773-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch new file mode 100644 index 0000000000..d6691bf0a0 --- /dev/null +++ b/patches/server/0773-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch @@ -0,0 +1,759 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sun, 2 Feb 2020 02:25:10 -0800 +Subject: [PATCH] Attempt to recalculate regionfile header if it is corrupt + +Instead of trying to relocate the chunk, which is seems to never +be the correct choice, so we end up duplicating or swapping chunks, +we instead drop the current regionfile header and recalculate - +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 c81392f5b4a6dcef9c1864c1b2c268914b904561..ad4081efec9c7eaf315ddb660f813f6ef3cfbb5b 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 +@@ -70,6 +70,13 @@ public class ChunkSerializer { + private static final String SKYLIGHT_STATE_TAG = "starlight.skylight_state"; + private static final String STARLIGHT_VERSION_TAG = "starlight.light_version"; + // Paper end - replace light engine impl ++ // Paper start ++ // TODO: Check on update ++ public static long getLastWorldSaveTime(CompoundTag chunkData) { ++ CompoundTag levelData = chunkData.getCompound("Level"); ++ return levelData.getLong("LastUpdate"); ++ } ++ // Paper end + + private static final Logger LOGGER = LogManager.getLogger(); + public static final String TAG_UPGRADE_DATA = "UpgradeData"; +@@ -124,7 +131,7 @@ public class ChunkSerializer { + } + // Paper end + BiomeSource worldchunkmanager = chunkgenerator.getBiomeSource(); +- CompoundTag nbttagcompound1 = nbt.getCompound("Level"); // Paper - diff on change, see ChunkSerializer#getChunkCoordinate ++ CompoundTag nbttagcompound1 = nbt.getCompound("Level"); // Paper - diff on change, see ChunkSerializer#getChunkCoordinate // Paper - diff on change + ChunkPos chunkcoordintpair1 = new ChunkPos(nbttagcompound1.getInt("xPos"), nbttagcompound1.getInt("zPos")); // Paper - diff on change, see ChunkSerializer#getChunkCoordinate + + if (!Objects.equals(pos, chunkcoordintpair1)) { +@@ -495,7 +502,7 @@ public class ChunkSerializer { + nbttagcompound.put("Level", nbttagcompound1); + nbttagcompound1.putInt("xPos", chunkcoordintpair.x); + nbttagcompound1.putInt("zPos", chunkcoordintpair.z); +- nbttagcompound1.putLong("LastUpdate", asyncsavedata != null ? asyncsavedata.worldTime : world.getGameTime()); // Paper - async chunk unloading ++ nbttagcompound1.putLong("LastUpdate", asyncsavedata != null ? asyncsavedata.worldTime : world.getGameTime()); // Paper - async chunk unloading // Paper - diff on change + nbttagcompound1.putLong("InhabitedTime", chunk.getInhabitedTime()); + nbttagcompound1.putString("Status", chunk.getStatus().getName()); + UpgradeData chunkconverter = chunk.getUpgradeData(); +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 037bbd562e2f35e17c324cd200c55c5e6cb5d768..b889dbad607b6508fb4987d21d3be691a5b37072 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 +@@ -34,7 +34,7 @@ public class ChunkStorage implements AutoCloseable { + this.fixerUpper = dataFixer; + // Paper start - async chunk io + // remove IO worker +- this.regionFileCache = new RegionFileStorage(directory, dsync); // Paper - nuke IOWorker ++ this.regionFileCache = new RegionFileStorage(directory, dsync, true); // Paper - nuke IOWorker // Paper + // Paper end - async chunk io + } + +diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java +index c8298a597818227de33a4afce4698ec0666cf758..6baceb6ce9021c489be6e79d338a9704285afa26 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java ++++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java +@@ -9,6 +9,27 @@ import java.util.BitSet; + public class RegionBitmap { + private final BitSet used = new BitSet(); + ++ // Paper start ++ public final void copyFrom(RegionBitmap other) { ++ BitSet thisBitset = this.used; ++ BitSet otherBitset = other.used; ++ ++ for (int i = 0; i < Math.max(thisBitset.size(), otherBitset.size()); ++i) { ++ thisBitset.set(i, otherBitset.get(i)); ++ } ++ } ++ ++ public final boolean tryAllocate(int from, int length) { ++ BitSet bitset = this.used; ++ int firstSet = bitset.nextSetBit(from); ++ if (firstSet > 0 && firstSet < (from + length)) { ++ return false; ++ } ++ bitset.set(from, from + length); ++ return true; ++ } ++ // Paper end ++ + public void force(int start, int size) { + this.used.set(start, start + size); + } +diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java +index c22391a0d4b7db49bd3994b0887939a7d8019391..4881e6ef4393a3d4fc1bd88e2574dcb6d7028e40 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java ++++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java +@@ -55,6 +55,341 @@ public class RegionFile implements AutoCloseable { + public final java.util.concurrent.locks.ReentrantLock fileLock = new java.util.concurrent.locks.ReentrantLock(true); // Paper + public final File regionFile; // Paper + ++ // Paper start - try to recover from RegionFile header corruption ++ private static long roundToSectors(long bytes) { ++ long sectors = bytes >>> 12; // 4096 = 2^12 ++ long remainingBytes = bytes & 4095; ++ long sign = -remainingBytes; // sign is 1 if nonzero ++ return sectors + (sign >>> 63); ++ } ++ ++ private static final CompoundTag OVERSIZED_COMPOUND = new CompoundTag(); ++ ++ private CompoundTag attemptRead(long sector, int chunkDataLength, long fileLength) throws IOException { ++ try { ++ if (chunkDataLength < 0) { ++ return null; ++ } ++ ++ long offset = sector * 4096L + 4L; // offset for chunk data ++ ++ if ((offset + chunkDataLength) > fileLength) { ++ return null; ++ } ++ ++ ByteBuffer chunkData = ByteBuffer.allocate(chunkDataLength); ++ if (chunkDataLength != this.file.read(chunkData, offset)) { ++ return null; ++ } ++ ++ ((java.nio.Buffer)chunkData).flip(); ++ ++ byte compressionType = chunkData.get(); ++ if (compressionType < 0) { // compressionType & 128 != 0 ++ // oversized chunk ++ return OVERSIZED_COMPOUND; ++ } ++ ++ RegionFileVersion compression = RegionFileVersion.fromId(compressionType); ++ if (compression == null) { ++ return null; ++ } ++ ++ InputStream input = compression.wrap(new ByteArrayInputStream(chunkData.array(), chunkData.position(), chunkDataLength - chunkData.position())); ++ ++ return NbtIo.read((java.io.DataInput)new DataInputStream(new BufferedInputStream(input))); ++ } catch (Exception ex) { ++ return null; ++ } ++ } ++ ++ private int getLength(long sector) throws IOException { ++ ByteBuffer length = ByteBuffer.allocate(4); ++ if (4 != this.file.read(length, sector * 4096L)) { ++ return -1; ++ } ++ ++ return length.getInt(0); ++ } ++ ++ private void backupRegionFile() { ++ File backup = new File(this.regionFile.getParent(), this.regionFile.getName() + "." + new java.util.Random().nextLong() + ".backup"); ++ this.backupRegionFile(backup); ++ } ++ ++ private void backupRegionFile(File to) { ++ try { ++ this.file.force(true); ++ LOGGER.warn("Backing up regionfile \"" + this.regionFile.getAbsolutePath() + "\" to " + to.getAbsolutePath()); ++ java.nio.file.Files.copy(this.regionFile.toPath(), to.toPath()); ++ LOGGER.warn("Backed up the regionfile to " + to.getAbsolutePath()); ++ } catch (IOException ex) { ++ LOGGER.error("Failed to backup to " + to.getAbsolutePath(), ex); ++ } ++ } ++ ++ // note: only call for CHUNK regionfiles ++ void recalculateHeader() throws IOException { ++ if (!this.canRecalcHeader) { ++ return; ++ } ++ synchronized (this) { ++ LOGGER.warn("Corrupt regionfile header detected! Attempting to re-calculate header offsets for regionfile " + this.regionFile.getAbsolutePath(), new Throwable()); ++ ++ // try to backup file so maybe it could be sent to us for further investigation ++ ++ this.backupRegionFile(); ++ CompoundTag[] compounds = new CompoundTag[32 * 32]; // only in the regionfile (i.e exclude mojang/aikar oversized data) ++ int[] rawLengths = new int[32 * 32]; // length of chunk data including 4 byte length field, bytes ++ int[] sectorOffsets = new int[32 * 32]; // in sectors ++ boolean[] hasAikarOversized = new boolean[32 * 32]; ++ ++ long fileLength = this.file.size(); ++ long totalSectors = roundToSectors(fileLength); ++ ++ // search the regionfile from start to finish for the most up-to-date chunk data ++ ++ for (long i = 2, maxSector = Math.min((long)(Integer.MAX_VALUE >>> 8), totalSectors); i < maxSector; ++i) { // first two sectors are header, skip ++ int chunkDataLength = this.getLength(i); ++ CompoundTag compound = this.attemptRead(i, chunkDataLength, fileLength); ++ if (compound == null || compound == OVERSIZED_COMPOUND) { ++ continue; ++ } ++ ++ ChunkPos chunkPos = ChunkSerializer.getChunkCoordinate(compound); ++ int location = (chunkPos.x & 31) | ((chunkPos.z & 31) << 5); ++ ++ CompoundTag otherCompound = compounds[location]; ++ ++ if (otherCompound != null && ChunkSerializer.getLastWorldSaveTime(otherCompound) > ChunkSerializer.getLastWorldSaveTime(compound)) { ++ continue; // don't overwrite newer data. ++ } ++ ++ // aikar oversized? ++ File aikarOversizedFile = this.getOversizedFile(chunkPos.x, chunkPos.z); ++ boolean isAikarOversized = false; ++ if (aikarOversizedFile.exists()) { ++ try { ++ CompoundTag aikarOversizedCompound = this.getOversizedData(chunkPos.x, chunkPos.z); ++ if (ChunkSerializer.getLastWorldSaveTime(compound) == ChunkSerializer.getLastWorldSaveTime(aikarOversizedCompound)) { ++ // best we got for an id. hope it's good enough ++ isAikarOversized = true; ++ } ++ } catch (Exception ex) { ++ LOGGER.error("Failed to read aikar oversized data for absolute chunk (" + chunkPos.x + "," + chunkPos.z + ") in regionfile " + this.regionFile.getAbsolutePath() + ", oversized data for this chunk will be lost", ex); ++ // fall through, if we can't read aikar oversized we can't risk corrupting chunk data ++ } ++ } ++ ++ hasAikarOversized[location] = isAikarOversized; ++ compounds[location] = compound; ++ rawLengths[location] = chunkDataLength + 4; ++ sectorOffsets[location] = (int)i; ++ ++ int chunkSectorLength = (int)roundToSectors(rawLengths[location]); ++ i += chunkSectorLength; ++ --i; // gets incremented next iteration ++ } ++ ++ // forge style oversized data is already handled by the local search, and aikar data we just hope ++ // we get it right as aikar data has no identifiers we could use to try and find its corresponding ++ // local data compound ++ ++ java.nio.file.Path containingFolder = this.externalFileDir; ++ File[] regionFiles = containingFolder.toFile().listFiles(); ++ boolean[] oversized = new boolean[32 * 32]; ++ RegionFileVersion[] oversizedCompressionTypes = new RegionFileVersion[32 * 32]; ++ ++ if (regionFiles != null) { ++ ChunkPos ourLowerLeftPosition = RegionFileStorage.getRegionFileCoordinates(this.regionFile); ++ ++ if (ourLowerLeftPosition == null) { ++ LOGGER.fatal("Unable to get chunk location of regionfile " + this.regionFile.getAbsolutePath() + ", cannot recover oversized chunks"); ++ } else { ++ int lowerXBound = ourLowerLeftPosition.x; // inclusive ++ int lowerZBound = ourLowerLeftPosition.z; // inclusive ++ int upperXBound = lowerXBound + 32 - 1; // inclusive ++ int upperZBound = lowerZBound + 32 - 1; // inclusive ++ ++ // read mojang oversized data ++ for (File regionFile : regionFiles) { ++ ChunkPos oversizedCoords = getOversizedChunkPair(regionFile); ++ if (oversizedCoords == null) { ++ continue; ++ } ++ ++ if ((oversizedCoords.x < lowerXBound || oversizedCoords.x > upperXBound) || (oversizedCoords.z < lowerZBound || oversizedCoords.z > upperZBound)) { ++ continue; // not in our regionfile ++ } ++ ++ // ensure oversized data is valid & is newer than data in the regionfile ++ ++ int location = (oversizedCoords.x & 31) | ((oversizedCoords.z & 31) << 5); ++ ++ byte[] chunkData; ++ try { ++ chunkData = Files.readAllBytes(regionFile.toPath()); ++ } catch (Exception ex) { ++ LOGGER.error("Failed to read oversized chunk data in file " + regionFile.getAbsolutePath() + ", data will be lost", ex); ++ continue; ++ } ++ ++ CompoundTag compound = null; ++ ++ // We do not know the compression type, as it's stored in the regionfile. So we need to try all of them ++ RegionFileVersion compression = null; ++ for (RegionFileVersion compressionType : RegionFileVersion.VERSIONS.values()) { ++ try { ++ DataInputStream in = new DataInputStream(new BufferedInputStream(compressionType.wrap(new ByteArrayInputStream(chunkData)))); // typical java ++ compound = NbtIo.read((java.io.DataInput)in); ++ compression = compressionType; ++ break; // reaches here iff readNBT does not throw ++ } catch (Exception ex) { ++ continue; ++ } ++ } ++ ++ if (compound == null) { ++ LOGGER.error("Failed to read oversized chunk data in file " + regionFile.getAbsolutePath() + ", it's corrupt. Its data will be lost"); ++ continue; ++ } ++ ++ if (compounds[location] == null || ChunkSerializer.getLastWorldSaveTime(compound) > ChunkSerializer.getLastWorldSaveTime(compounds[location])) { ++ oversized[location] = true; ++ oversizedCompressionTypes[location] = compression; ++ } ++ } ++ } ++ } ++ ++ // now we need to calculate a new offset header ++ ++ int[] calculatedOffsets = new int[32 * 32]; ++ RegionBitmap newSectorAllocations = new RegionBitmap(); ++ newSectorAllocations.force(0, 2); // make space for header ++ ++ // allocate sectors for normal chunks ++ ++ for (int chunkX = 0; chunkX < 32; ++chunkX) { ++ for (int chunkZ = 0; chunkZ < 32; ++chunkZ) { ++ int location = chunkX | (chunkZ << 5); ++ ++ if (oversized[location]) { ++ continue; ++ } ++ ++ int rawLength = rawLengths[location]; // bytes ++ int sectorOffset = sectorOffsets[location]; // sectors ++ int sectorLength = (int)roundToSectors(rawLength); ++ ++ if (newSectorAllocations.tryAllocate(sectorOffset, sectorLength)) { ++ calculatedOffsets[location] = sectorOffset << 8 | (sectorLength > 255 ? 255 : sectorLength); // support forge style oversized ++ } else { ++ LOGGER.error("Failed to allocate space for local chunk (overlapping data??) at (" + chunkX + "," + chunkZ + ") in regionfile " + this.regionFile.getAbsolutePath() + ", chunk will be regenerated"); ++ } ++ } ++ } ++ ++ // allocate sectors for oversized chunks ++ ++ for (int chunkX = 0; chunkX < 32; ++chunkX) { ++ for (int chunkZ = 0; chunkZ < 32; ++chunkZ) { ++ int location = chunkX | (chunkZ << 5); ++ ++ if (!oversized[location]) { ++ continue; ++ } ++ ++ int sectorOffset = newSectorAllocations.allocate(1); ++ int sectorLength = 1; ++ ++ try { ++ this.file.write(this.createExternalStub(oversizedCompressionTypes[location]), sectorOffset * 4096); ++ // only allocate in the new offsets if the write succeeds ++ calculatedOffsets[location] = sectorOffset << 8 | (sectorLength > 255 ? 255 : sectorLength); // support forge style oversized ++ } catch (IOException ex) { ++ newSectorAllocations.free(sectorOffset, sectorLength); ++ LOGGER.error("Failed to write new oversized chunk data holder, local chunk at (" + chunkX + "," + chunkZ + ") in regionfile " + this.regionFile.getAbsolutePath() + " will be regenerated"); ++ } ++ } ++ } ++ ++ // rewrite aikar oversized data ++ ++ this.oversizedCount = 0; ++ for (int chunkX = 0; chunkX < 32; ++chunkX) { ++ for (int chunkZ = 0; chunkZ < 32; ++chunkZ) { ++ int location = chunkX | (chunkZ << 5); ++ int isAikarOversized = hasAikarOversized[location] ? 1 : 0; ++ ++ this.oversizedCount += isAikarOversized; ++ this.oversized[location] = (byte)isAikarOversized; ++ } ++ } ++ ++ if (this.oversizedCount > 0) { ++ try { ++ this.writeOversizedMeta(); ++ } catch (Exception ex) { ++ LOGGER.error("Failed to write aikar oversized chunk meta, all aikar style oversized chunk data will be lost for regionfile " + this.regionFile.getAbsolutePath(), ex); ++ this.getOversizedMetaFile().delete(); ++ } ++ } else { ++ this.getOversizedMetaFile().delete(); ++ } ++ ++ this.usedSectors.copyFrom(newSectorAllocations); ++ ++ // before we overwrite the old sectors, print a summary of the chunks that got changed. ++ ++ LOGGER.info("Starting summary of changes for regionfile " + this.regionFile.getAbsolutePath()); ++ ++ for (int chunkX = 0; chunkX < 32; ++chunkX) { ++ for (int chunkZ = 0; chunkZ < 32; ++chunkZ) { ++ int location = chunkX | (chunkZ << 5); ++ ++ int oldOffset = this.offsets.get(location); ++ int newOffset = calculatedOffsets[location]; ++ ++ if (oldOffset == newOffset) { ++ continue; ++ } ++ ++ this.offsets.put(location, newOffset); // overwrite incorrect offset ++ ++ if (oldOffset == 0) { ++ // found lost data ++ LOGGER.info("Found missing data for local chunk (" + chunkX + "," + chunkZ + ") in regionfile " + this.regionFile.getAbsolutePath()); ++ } else if (newOffset == 0) { ++ LOGGER.warn("Data for local chunk (" + chunkX + "," + chunkZ + ") could not be recovered in regionfile " + this.regionFile.getAbsolutePath() + ", it will be regenerated"); ++ } else { ++ LOGGER.info("Local chunk (" + chunkX + "," + chunkZ + ") changed to point to newer data or correct chunk in regionfile " + this.regionFile.getAbsolutePath()); ++ } ++ } ++ } ++ ++ LOGGER.info("End of change summary for regionfile " + this.regionFile.getAbsolutePath()); ++ ++ // simply destroy the timestamp header, it's not used ++ ++ for (int i = 0; i < 32 * 32; ++i) { ++ this.timestamps.put(i, calculatedOffsets[i] != 0 ? (int)System.currentTimeMillis() : 0); // write a valid timestamp for valid chunks, I do not want to find out whatever dumb program actually checks this ++ } ++ ++ // write new header ++ try { ++ this.flush(); ++ this.file.force(true); // try to ensure it goes through... ++ LOGGER.info("Successfully wrote new header to disk for regionfile " + this.regionFile.getAbsolutePath()); ++ } catch (IOException ex) { ++ LOGGER.fatal("Failed to write new header to disk for regionfile " + this.regionFile.getAbsolutePath(), ex); ++ } ++ } ++ } ++ ++ final boolean canRecalcHeader; // final forces compile fail on new constructor ++ // Paper end ++ + // Paper start - Cache chunk status + private final ChunkStatus[] statuses = new ChunkStatus[32 * 32]; + +@@ -82,8 +417,19 @@ public class RegionFile implements AutoCloseable { + public RegionFile(File file, File directory, boolean dsync) throws IOException { + this(file.toPath(), directory.toPath(), RegionFileVersion.VERSION_DEFLATE, dsync); + } ++ // Paper start - add can recalc flag ++ public RegionFile(File file, File directory, boolean dsync, boolean canRecalcHeader) throws IOException { ++ this(file.toPath(), directory.toPath(), RegionFileVersion.VERSION_DEFLATE, dsync, canRecalcHeader); ++ } ++ // Paper end - add can recalc flag + + public RegionFile(Path file, Path directory, RegionFileVersion outputChunkStreamVersion, boolean dsync) throws IOException { ++ // Paper start - add can recalc flag ++ this(file, directory, outputChunkStreamVersion, dsync, false); ++ } ++ public RegionFile(Path file, Path directory, RegionFileVersion outputChunkStreamVersion, boolean dsync, boolean canRecalcHeader) throws IOException { ++ this.canRecalcHeader = canRecalcHeader; ++ // Paper end - add can recalc flag + this.header = ByteBuffer.allocateDirect(8192); + this.regionFile = file.toFile(); // Paper + initOversizedState(); // Paper +@@ -112,14 +458,16 @@ public class RegionFile implements AutoCloseable { + RegionFile.LOGGER.warn("Region file {} has truncated header: {}", file, i); + } + +- long j = Files.size(file); ++ final long j = Files.size(file); final long regionFileSize = j; // Paper - recalculate header on header corruption + ++ boolean needsHeaderRecalc = false; // Paper - recalculate header on header corruption ++ boolean hasBackedUp = false; // Paper - recalculate header on header corruption + for (int k = 0; k < 1024; ++k) { +- int l = this.offsets.get(k); ++ final int l = this.offsets.get(k); final int headerLocation = l; // Paper - we expect this to be the header location + + if (l != 0) { +- int i1 = RegionFile.getSectorNumber(l); +- int j1 = RegionFile.getNumSectors(l); ++ final int i1 = RegionFile.getSectorNumber(l); final int offset = i1; // Paper - we expect this to be offset in file in sectors ++ int j1 = RegionFile.getNumSectors(l); final int sectorLength; // Paper - diff on change, we expect this to be sector length of region - watch out for reassignments + // Spigot start + if (j1 == 255) { + // We're maxed out, so we need to read the proper length from the section +@@ -128,32 +476,102 @@ public class RegionFile implements AutoCloseable { + j1 = (realLen.getInt(0) + 4) / 4096 + 1; + } + // Spigot end ++ sectorLength = j1; // Paper - diff on change, we expect this to be sector length of region + + if (i1 < 2) { + RegionFile.LOGGER.warn("Region file {} has invalid sector at index: {}; sector {} overlaps with header", file, k, i1); +- this.offsets.put(k, 0); ++ //this.offsets.put(k, 0); // Paper - we catch this, but need it in the header for the summary change + } else if (j1 == 0) { + RegionFile.LOGGER.warn("Region file {} has an invalid sector at index: {}; size has to be > 0", file, k); +- this.offsets.put(k, 0); ++ //this.offsets.put(k, 0); // Paper - we catch this, but need it in the header for the summary change + } else if ((long) i1 * 4096L > j) { + RegionFile.LOGGER.warn("Region file {} has an invalid sector at index: {}; sector {} is out of bounds", file, k, i1); +- this.offsets.put(k, 0); ++ //this.offsets.put(k, 0); // Paper - we catch this, but need it in the header for the summary change + } else { +- this.usedSectors.force(i1, j1); ++ //this.usedSectors.force(i1, j1); // Paper - move this down so we can check if it fails to allocate ++ } ++ // Paper start - recalculate header on header corruption ++ if (offset < 2 || sectorLength <= 0 || ((long)offset * 4096L) > regionFileSize) { ++ if (canRecalcHeader) { ++ LOGGER.error("Detected invalid header for regionfile " + this.regionFile.getAbsolutePath() + "! Recalculating header..."); ++ needsHeaderRecalc = true; ++ break; ++ } else { ++ // location = chunkX | (chunkZ << 5); ++ LOGGER.fatal("Detected invalid header for regionfile " + this.regionFile.getAbsolutePath() + ++ "! Cannot recalculate, removing local chunk (" + (headerLocation & 31) + "," + (headerLocation >>> 5) + ") from header"); ++ if (!hasBackedUp) { ++ hasBackedUp = true; ++ this.backupRegionFile(); ++ } ++ this.timestamps.put(headerLocation, 0); // be consistent, delete the timestamp too ++ this.offsets.put(headerLocation, 0); // delete the entry from header ++ continue; ++ } ++ } ++ boolean failedToAllocate = !this.usedSectors.tryAllocate(offset, sectorLength); ++ if (failedToAllocate) { ++ LOGGER.error("Overlapping allocation by local chunk (" + (headerLocation & 31) + "," + (headerLocation >>> 5) + ") in regionfile " + this.regionFile.getAbsolutePath()); + } ++ if (failedToAllocate & !canRecalcHeader) { ++ // location = chunkX | (chunkZ << 5); ++ LOGGER.fatal("Detected invalid header for regionfile " + this.regionFile.getAbsolutePath() + ++ "! Cannot recalculate, removing local chunk (" + (headerLocation & 31) + "," + (headerLocation >>> 5) + ") from header"); ++ if (!hasBackedUp) { ++ hasBackedUp = true; ++ this.backupRegionFile(); ++ } ++ this.timestamps.put(headerLocation, 0); // be consistent, delete the timestamp too ++ this.offsets.put(headerLocation, 0); // delete the entry from header ++ continue; ++ } ++ needsHeaderRecalc |= failedToAllocate; ++ // Paper end - recalculate header on header corruption + } + } ++ // Paper start - recalculate header on header corruption ++ // we move the recalc here so comparison to old header is correct when logging to console ++ if (needsHeaderRecalc) { // true if header gave us overlapping allocations or had other issues ++ LOGGER.error("Recalculating regionfile " + this.regionFile.getAbsolutePath() + ", header gave erroneous offsets & locations"); ++ this.recalculateHeader(); ++ } ++ // Paper end + } + + } + } + + private Path getExternalChunkPath(ChunkPos chunkPos) { +- String s = "c." + chunkPos.x + "." + chunkPos.z + ".mcc"; ++ String s = "c." + chunkPos.x + "." + chunkPos.z + ".mcc"; // Paper - diff on change + + return this.externalFileDir.resolve(s); + } + ++ // Paper start ++ private static ChunkPos getOversizedChunkPair(File file) { ++ String fileName = file.getName(); ++ ++ if (!fileName.startsWith("c.") || !fileName.endsWith(".mcc")) { ++ return null; ++ } ++ ++ String[] split = fileName.split("\\."); ++ ++ if (split.length != 4) { ++ return null; ++ } ++ ++ try { ++ int x = Integer.parseInt(split[1]); ++ int z = Integer.parseInt(split[2]); ++ ++ return new ChunkPos(x, z); ++ } catch (NumberFormatException ex) { ++ return null; ++ } ++ } ++ // Paper end ++ + @Nullable + public synchronized DataInputStream getChunkDataInputStream(ChunkPos pos) throws IOException { + int i = this.getOffset(pos); +@@ -177,6 +595,12 @@ public class RegionFile implements AutoCloseable { + ((java.nio.Buffer) bytebuffer).flip(); // CraftBukkit - decompile error + if (bytebuffer.remaining() < 5) { + RegionFile.LOGGER.error("Chunk {} header is truncated: expected {} but read {}", pos, l, bytebuffer.remaining()); ++ // Paper start - recalculate header on regionfile corruption ++ if (this.canRecalcHeader) { ++ this.recalculateHeader(); ++ return this.getChunkDataInputStream(pos); ++ } ++ // Paper end - recalculate header on regionfile corruption + return null; + } else { + int i1 = bytebuffer.getInt(); +@@ -184,6 +608,12 @@ public class RegionFile implements AutoCloseable { + + if (i1 == 0) { + RegionFile.LOGGER.warn("Chunk {} is allocated, but stream is missing", pos); ++ // Paper start - recalculate header on regionfile corruption ++ if (this.canRecalcHeader) { ++ this.recalculateHeader(); ++ return this.getChunkDataInputStream(pos); ++ } ++ // Paper end - recalculate header on regionfile corruption + return null; + } else { + int j1 = i1 - 1; +@@ -191,17 +621,49 @@ public class RegionFile implements AutoCloseable { + if (RegionFile.isExternalStreamChunk(b0)) { + if (j1 != 0) { + RegionFile.LOGGER.warn("Chunk has both internal and external streams"); ++ // Paper start - recalculate header on regionfile corruption ++ if (this.canRecalcHeader) { ++ this.recalculateHeader(); ++ return this.getChunkDataInputStream(pos); ++ } ++ // Paper end - recalculate header on regionfile corruption + } + +- return this.createExternalChunkInputStream(pos, RegionFile.getExternalChunkVersion(b0)); ++ // Paper start - recalculate header on regionfile corruption ++ final DataInputStream ret = this.createExternalChunkInputStream(pos, RegionFile.getExternalChunkVersion(b0)); ++ if (ret == null && this.canRecalcHeader) { ++ this.recalculateHeader(); ++ return this.getChunkDataInputStream(pos); ++ } ++ return ret; ++ // Paper end - recalculate header on regionfile corruption + } else if (j1 > bytebuffer.remaining()) { + RegionFile.LOGGER.error("Chunk {} stream is truncated: expected {} but read {}", pos, j1, bytebuffer.remaining()); ++ // Paper start - recalculate header on regionfile corruption ++ if (this.canRecalcHeader) { ++ this.recalculateHeader(); ++ return this.getChunkDataInputStream(pos); ++ } ++ // Paper end - recalculate header on regionfile corruption + return null; + } else if (j1 < 0) { + RegionFile.LOGGER.error("Declared size {} of chunk {} is negative", i1, pos); ++ // Paper start - recalculate header on regionfile corruption ++ if (this.canRecalcHeader) { ++ this.recalculateHeader(); ++ return this.getChunkDataInputStream(pos); ++ } ++ // Paper end - recalculate header on regionfile corruption + return null; + } else { +- return this.createChunkInputStream(pos, b0, RegionFile.createStream(bytebuffer, j1)); ++ // Paper start - recalculate header on regionfile corruption ++ final DataInputStream ret = this.createChunkInputStream(pos, b0, RegionFile.createStream(bytebuffer, j1)); ++ if (ret == null && this.canRecalcHeader) { ++ this.recalculateHeader(); ++ return this.getChunkDataInputStream(pos); ++ } ++ return ret; ++ // Paper end - recalculate header on regionfile corruption + } + } + } +@@ -376,10 +838,15 @@ public class RegionFile implements AutoCloseable { + } + + private ByteBuffer createExternalStub() { ++ // Paper start - add version param ++ return this.createExternalStub(this.version); ++ } ++ private ByteBuffer createExternalStub(RegionFileVersion version) { ++ // Paper end - add version param + ByteBuffer bytebuffer = ByteBuffer.allocate(5); + + bytebuffer.putInt(1); +- bytebuffer.put((byte) (this.version.getId() | 128)); ++ bytebuffer.put((byte) (version.getId() | 128)); // Paper - replace with version param + ((java.nio.Buffer) bytebuffer).flip(); // CraftBukkit - decompile error + return bytebuffer; + } +diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +index 6496108953effae82391b5c1ea6fdec8482731cd..4e0006b73fbd2634aa42334ae9dde79b4eccaf38 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java ++++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +@@ -25,7 +25,15 @@ public class RegionFileStorage implements AutoCloseable { + private final File folder; + private final boolean sync; + ++ private final boolean isChunkData; // Paper ++ + RegionFileStorage(File directory, boolean dsync) { ++ // Paper start - add isChunkData param ++ this(directory, dsync, false); ++ } ++ RegionFileStorage(File directory, boolean dsync, boolean isChunkData) { ++ this.isChunkData = isChunkData; ++ // Paper end - add isChunkData param + this.folder = directory; + this.sync = dsync; + } +@@ -90,9 +98,9 @@ public class RegionFileStorage implements AutoCloseable { + + File file = this.folder; + int j = chunkcoordintpair.getRegionX(); +- File file1 = new File(file, "r." + j + "." + chunkcoordintpair.getRegionZ() + ".mca"); ++ File file1 = new File(file, "r." + j + "." + chunkcoordintpair.getRegionZ() + ".mca"); // Paper - diff on change + if (existingOnly && !file1.exists()) return null; // CraftBukkit +- RegionFile regionfile1 = new RegionFile(file1, this.folder, this.sync); ++ RegionFile regionfile1 = new RegionFile(file1, this.folder, this.sync, this.isChunkData); // Paper - allow for chunk regionfiles to regen header + + this.regionCache.putAndMoveToFirst(i, regionfile1); + // Paper start +@@ -180,6 +188,13 @@ public class RegionFileStorage implements AutoCloseable { + if (regionfile == null) { + return null; + } ++ // Paper start - Add regionfile parameter ++ return this.read(pos, regionfile); ++ } ++ public CompoundTag read(ChunkPos pos, RegionFile regionfile) throws IOException { ++ // We add the regionfile parameter to avoid the potential deadlock (on fileLock) if we went back to obtain a regionfile ++ // if we decide to re-read ++ // Paper end + // CraftBukkit end + try { // Paper + DataInputStream datainputstream = regionfile.getChunkDataInputStream(pos); +@@ -196,6 +211,17 @@ public class RegionFileStorage implements AutoCloseable { + try { + if (datainputstream != null) { + nbttagcompound = NbtIo.read((DataInput) datainputstream); ++ // Paper start - recover from corrupt regionfile header ++ if (this.isChunkData) { ++ ChunkPos chunkPos = ChunkSerializer.getChunkCoordinate(nbttagcompound); ++ if (!chunkPos.equals(pos)) { ++ MinecraftServer.LOGGER.error("Attempting to read chunk data at " + pos.toString() + " but got chunk data for " + chunkPos.toString() + " instead! Attempting regionfile recalculation for regionfile " + regionfile.regionFile.getAbsolutePath()); ++ regionfile.recalculateHeader(); ++ regionfile.fileLock.lock(); // otherwise we will unlock twice and only lock once. ++ return this.read(pos, regionfile); ++ } ++ } ++ // Paper end - recover from corrupt regionfile header + break label43; + } + +diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileVersion.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileVersion.java +index b7835b9b904e7d4bff64f7189049e334f5ab4d6f..492bba91e0e61c678e5067a6f855674d42d7f4ea 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileVersion.java ++++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileVersion.java +@@ -12,7 +12,7 @@ import java.util.zip.InflaterInputStream; + import javax.annotation.Nullable; + + public class RegionFileVersion { +- private static final Int2ObjectMap VERSIONS = new Int2ObjectOpenHashMap<>(); ++ public static final Int2ObjectMap VERSIONS = new Int2ObjectOpenHashMap<>(); // Paper - public + public static final RegionFileVersion VERSION_GZIP = register(new RegionFileVersion(1, GZIPInputStream::new, GZIPOutputStream::new)); + public static final RegionFileVersion VERSION_DEFLATE = register(new RegionFileVersion(2, InflaterInputStream::new, DeflaterOutputStream::new)); + public static final RegionFileVersion VERSION_NONE = register(new RegionFileVersion(3, (inputStream) -> { diff --git a/patches/server/0774-Custom-table-implementation-for-blockstate-state-loo.patch b/patches/server/0774-Custom-table-implementation-for-blockstate-state-loo.patch new file mode 100644 index 0000000000..acc5535802 --- /dev/null +++ b/patches/server/0774-Custom-table-implementation-for-blockstate-state-loo.patch @@ -0,0 +1,343 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Thu, 11 Mar 2021 20:05:44 -0800 +Subject: [PATCH] Custom table implementation for blockstate state lookups + +Testing some redstone intensive machines showed to bring about a 10% +improvement. + +diff --git a/src/main/java/io/papermc/paper/util/table/ZeroCollidingReferenceStateTable.java b/src/main/java/io/papermc/paper/util/table/ZeroCollidingReferenceStateTable.java +new file mode 100644 +index 0000000000000000000000000000000000000000..57d0cd3ad6f972e986c72a57f1a6e36003f190c2 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/table/ZeroCollidingReferenceStateTable.java +@@ -0,0 +1,160 @@ ++package io.papermc.paper.util.table; ++ ++import com.google.common.collect.Table; ++import net.minecraft.world.level.block.state.StateHolder; ++import net.minecraft.world.level.block.state.properties.Property; ++import java.util.Collection; ++import java.util.HashSet; ++import java.util.Map; ++import java.util.Set; ++ ++public final class ZeroCollidingReferenceStateTable { ++ ++ // upper 32 bits: starting index ++ // lower 32 bits: bitset for contained ids ++ protected final long[] this_index_table; ++ protected final Comparable[] this_table; ++ protected final StateHolder this_state; ++ ++ protected long[] index_table; ++ protected StateHolder[][] value_table; ++ ++ public ZeroCollidingReferenceStateTable(final StateHolder state, final Map, Comparable> this_map) { ++ this.this_state = state; ++ this.this_index_table = this.create_table(this_map.keySet()); ++ ++ int max_id = -1; ++ for (final Property property : this_map.keySet()) { ++ final int id = lookup_vindex(property, this.this_index_table); ++ if (id > max_id) { ++ max_id = id; ++ } ++ } ++ ++ this.this_table = new Comparable[max_id + 1]; ++ for (final Map.Entry, Comparable> entry : this_map.entrySet()) { ++ this.this_table[lookup_vindex(entry.getKey(), this.this_index_table)] = entry.getValue(); ++ } ++ } ++ ++ public void loadInTable(final Table, Comparable, StateHolder> table, ++ final Map, Comparable> this_map) { ++ final Set> combined = new HashSet<>(table.rowKeySet()); ++ combined.addAll(this_map.keySet()); ++ ++ this.index_table = this.create_table(combined); ++ ++ int max_id = -1; ++ for (final Property property : combined) { ++ final int id = lookup_vindex(property, this.index_table); ++ if (id > max_id) { ++ max_id = id; ++ } ++ } ++ ++ this.value_table = new StateHolder[max_id + 1][]; ++ ++ final Map, Map, StateHolder>> map = table.rowMap(); ++ for (final Property property : map.keySet()) { ++ final Map, StateHolder> propertyMap = map.get(property); ++ ++ final int id = lookup_vindex(property, this.index_table); ++ final StateHolder[] states = this.value_table[id] = new StateHolder[property.getPossibleValues().size()]; ++ ++ for (final Map.Entry, StateHolder> entry : propertyMap.entrySet()) { ++ if (entry.getValue() == null) { ++ // TODO what ++ continue; ++ } ++ ++ states[((Property)property).getIdFor(entry.getKey())] = entry.getValue(); ++ } ++ } ++ ++ ++ for (final Map.Entry, Comparable> entry : this_map.entrySet()) { ++ final Property property = entry.getKey(); ++ final int index = lookup_vindex(property, this.index_table); ++ ++ if (this.value_table[index] == null) { ++ this.value_table[index] = new StateHolder[property.getPossibleValues().size()]; ++ } ++ ++ this.value_table[index][((Property)property).getIdFor(entry.getValue())] = this.this_state; ++ } ++ } ++ ++ ++ protected long[] create_table(final Collection> collection) { ++ int max_id = -1; ++ for (final Property property : collection) { ++ final int id = property.getId(); ++ if (id > max_id) { ++ max_id = id; ++ } ++ } ++ ++ final long[] ret = new long[((max_id + 1) + 31) >>> 5]; // ceil((max_id + 1) / 32) ++ ++ for (final Property property : collection) { ++ final int id = property.getId(); ++ ++ ret[id >>> 5] |= (1L << (id & 31)); ++ } ++ ++ int total = 0; ++ for (int i = 1, len = ret.length; i < len; ++i) { ++ ret[i] |= (long)(total += Long.bitCount(ret[i - 1] & 0xFFFFFFFFL)) << 32; ++ } ++ ++ return ret; ++ } ++ ++ public Comparable get(final Property state) { ++ final Comparable[] table = this.this_table; ++ final int index = lookup_vindex(state, this.this_index_table); ++ ++ if (index < 0 || index >= table.length) { ++ return null; ++ } ++ return table[index]; ++ } ++ ++ public StateHolder get(final Property property, final Comparable with) { ++ final int withId = ((Property)property).getIdFor(with); ++ if (withId < 0) { ++ return null; ++ } ++ ++ final int index = lookup_vindex(property, this.index_table); ++ final StateHolder[][] table = this.value_table; ++ if (index < 0 || index >= table.length) { ++ return null; ++ } ++ ++ final StateHolder[] values = table[index]; ++ ++ if (withId >= values.length) { ++ return null; ++ } ++ ++ return values[withId]; ++ } ++ ++ protected static int lookup_vindex(final Property property, final long[] index_table) { ++ final int id = property.getId(); ++ final long bitset_mask = (1L << (id & 31)); ++ final long lower_mask = bitset_mask - 1; ++ final int index = id >>> 5; ++ if (index >= index_table.length) { ++ return -1; ++ } ++ final long index_value = index_table[index]; ++ final long contains_check = ((index_value & bitset_mask) - 1) >> (Long.SIZE - 1); // -1L if doesn't contain ++ ++ // index = total bits set in lower table values (upper 32 bits of index_value) plus total bits set in lower indices below id ++ // contains_check is 0 if the bitset had id set, else it's -1: so index is unaffected if contains_check == 0, ++ // otherwise it comes out as -1. ++ return (int)(((index_value >>> 32) + Long.bitCount(index_value & lower_mask)) | contains_check); ++ } ++} +diff --git a/src/main/java/net/minecraft/world/level/block/state/StateHolder.java b/src/main/java/net/minecraft/world/level/block/state/StateHolder.java +index baf1cb77eb170a44d821eae572d059f18ea46d7e..11c05776c051ba6a7e8416fd8591babafda35a80 100644 +--- a/src/main/java/net/minecraft/world/level/block/state/StateHolder.java ++++ b/src/main/java/net/minecraft/world/level/block/state/StateHolder.java +@@ -40,11 +40,13 @@ public abstract class StateHolder { + private final ImmutableMap, Comparable> values; + private Table, Comparable, S> neighbours; + protected final MapCodec propertiesCodec; ++ protected final io.papermc.paper.util.table.ZeroCollidingReferenceStateTable optimisedTable; // Paper - optimise state lookup + + protected StateHolder(O owner, ImmutableMap, Comparable> entries, MapCodec codec) { + this.owner = owner; + this.values = entries; + this.propertiesCodec = codec; ++ this.optimisedTable = new io.papermc.paper.util.table.ZeroCollidingReferenceStateTable(this, entries); // Paper - optimise state lookup + } + + public > S cycle(Property property) { +@@ -85,11 +87,11 @@ public abstract class StateHolder { + } + + public > boolean hasProperty(Property property) { +- return this.values.containsKey(property); ++ return this.optimisedTable.get(property) != null; // Paper - optimise state lookup + } + + public > T getValue(Property property) { +- Comparable comparable = this.values.get(property); ++ Comparable comparable = this.optimisedTable.get(property); // Paper - optimise state lookup + if (comparable == null) { + throw new IllegalArgumentException("Cannot get property " + property + " as it does not exist in " + this.owner); + } else { +@@ -98,24 +100,18 @@ public abstract class StateHolder { + } + + public > Optional getOptionalValue(Property property) { +- Comparable comparable = this.values.get(property); ++ Comparable comparable = this.optimisedTable.get(property); // Paper - optimise state lookup + return comparable == null ? Optional.empty() : Optional.of(property.getValueClass().cast(comparable)); + } + + public , V extends T> S setValue(Property property, V value) { +- Comparable comparable = this.values.get(property); +- if (comparable == null) { +- throw new IllegalArgumentException("Cannot set property " + property + " as it does not exist in " + this.owner); +- } else if (comparable == value) { +- return (S)this; +- } else { +- S object = this.neighbours.get(property, value); +- if (object == null) { +- throw new IllegalArgumentException("Cannot set property " + property + " to " + value + " on " + this.owner + ", it is not an allowed value"); +- } else { +- return object; +- } ++ // Paper start - optimise state lookup ++ final S ret = (S)this.optimisedTable.get(property, value); ++ if (ret == null) { ++ throw new IllegalArgumentException("Cannot set property " + property + " to " + value + " on " + this.owner + ", it is not an allowed value"); + } ++ return ret; ++ // Paper end - optimise state lookup + } + + public void populateNeighbours(Map, Comparable>, S> states) { +@@ -134,7 +130,7 @@ public abstract class StateHolder { + } + } + +- this.neighbours = (Table, Comparable, S>)(table.isEmpty() ? table : ArrayTable.create(table)); ++ this.neighbours = (Table, Comparable, S>)(table.isEmpty() ? table : ArrayTable.create(table)); this.optimisedTable.loadInTable((Table)this.neighbours, this.values); // Paper - optimise state lookup + } + } + +diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java b/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java +index ff1a0d125edd2ea10c870cbb62ae9aa23644b6dc..233215280f8494dbc33a2fd0b14e37e59f1cb643 100644 +--- a/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java ++++ b/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java +@@ -7,6 +7,13 @@ import java.util.Optional; + public class BooleanProperty extends Property { + private final ImmutableSet values = ImmutableSet.of(true, false); + ++ // Paper start - optimise iblockdata state lookup ++ @Override ++ public final int getIdFor(final Boolean value) { ++ return value.booleanValue() ? 1 : 0; ++ } ++ // Paper end - optimise iblockdata state lookup ++ + protected BooleanProperty(String name) { + super(name, Boolean.class); + } +diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java b/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java +index bcf8b24e9f9e9870c1a1d27c721a6a433305d55a..6b05e766162b2cf8b499e5b0effb2cd15e57bea3 100644 +--- a/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java ++++ b/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java +@@ -17,6 +17,15 @@ public class EnumProperty & StringRepresentable> extends Prope + private final ImmutableSet values; + private final Map names = Maps.newHashMap(); + ++ // Paper start - optimise iblockdata state lookup ++ private int[] idLookupTable; ++ ++ @Override ++ public final int getIdFor(final T value) { ++ return this.idLookupTable[value.ordinal()]; ++ } ++ // Paper end - optimise iblockdata state lookup ++ + protected EnumProperty(String name, Class type, Collection values) { + super(name, type); + this.values = ImmutableSet.copyOf(values); +@@ -31,6 +40,14 @@ public class EnumProperty & StringRepresentable> extends Prope + this.names.put(string, enum_); + } + ++ // Paper start - optimise iblockdata state lookup ++ int id = 0; ++ this.idLookupTable = new int[type.getEnumConstants().length]; ++ java.util.Arrays.fill(this.idLookupTable, -1); ++ for (final T value : this.getPossibleValues()) { ++ this.idLookupTable[value.ordinal()] = id++; ++ } ++ // Paper end - optimise iblockdata state lookup + } + + @Override +diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java b/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java +index 72f508321ebffcca31240fbdd068b4d185454cbc..d16156f8a4a2507e114dc651fd0af9cdffb3c8e0 100644 +--- a/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java ++++ b/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java +@@ -13,6 +13,16 @@ public class IntegerProperty extends Property { + public final int min; + public final int max; + ++ // Paper start - optimise iblockdata state lookup ++ @Override ++ public final int getIdFor(final Integer value) { ++ final int val = value.intValue(); ++ final int ret = val - this.min; ++ ++ return ret | ((this.max - ret) >> 31); ++ } ++ // Paper end - optimise iblockdata state lookup ++ + protected IntegerProperty(String name, int min, int max) { + super(name, Integer.class); + this.min = min; +diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/Property.java b/src/main/java/net/minecraft/world/level/block/state/properties/Property.java +index 81b43e0b0146729a8a1c6ade82634c86cde67857..44118dbd1ed212a0911f1244e7fea78cee275aad 100644 +--- a/src/main/java/net/minecraft/world/level/block/state/properties/Property.java ++++ b/src/main/java/net/minecraft/world/level/block/state/properties/Property.java +@@ -20,6 +20,17 @@ public abstract class Property> { + }, this::getName); + private final Codec> valueCodec = this.codec.xmap(this::value, Property.Value::value); + ++ // Paper start - optimise iblockdata state lookup ++ private static final java.util.concurrent.atomic.AtomicInteger ID_GENERATOR = new java.util.concurrent.atomic.AtomicInteger(); ++ private final int id = ID_GENERATOR.getAndIncrement(); ++ ++ public final int getId() { ++ return this.id; ++ } ++ ++ public abstract int getIdFor(final T value); ++ // Paper end - optimise state lookup ++ + protected Property(String name, Class type) { + this.clazz = type; + this.name = name; diff --git a/patches/server/0775-Detail-more-information-in-watchdog-dumps.patch b/patches/server/0775-Detail-more-information-in-watchdog-dumps.patch new file mode 100644 index 0000000000..178848d5ce --- /dev/null +++ b/patches/server/0775-Detail-more-information-in-watchdog-dumps.patch @@ -0,0 +1,296 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Thu, 26 Mar 2020 21:59:32 -0700 +Subject: [PATCH] Detail more information in watchdog dumps + +- Dump position, world, velocity, and uuid for currently ticking entities +- Dump player name, player uuid, position, and world for packet handling + +diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java +index 23757f466da571aec35a373112dcbba0d1f46dcb..3321bb7582a3e0227fbc1d41982529c23548269b 100644 +--- a/src/main/java/net/minecraft/network/Connection.java ++++ b/src/main/java/net/minecraft/network/Connection.java +@@ -481,7 +481,14 @@ public class Connection extends SimpleChannelInboundHandler> { + } + + if (this.packetListener instanceof ServerGamePacketListenerImpl) { ++ // Paper start - detailed watchdog information ++ net.minecraft.network.protocol.PacketUtils.packetProcessing.push(this.packetListener); ++ try { ++ // Paper end - detailed watchdog information + ((ServerGamePacketListenerImpl) this.packetListener).tick(); ++ } finally { // Paper start - detailed watchdog information ++ net.minecraft.network.protocol.PacketUtils.packetProcessing.pop(); ++ } // Paper start - detailed watchdog information + } + + if (!this.isConnected() && !this.disconnectionHandled) { +diff --git a/src/main/java/net/minecraft/network/protocol/PacketUtils.java b/src/main/java/net/minecraft/network/protocol/PacketUtils.java +index bcf53ec07b8eeec7a88fb67e6fb908362e6f51b0..acc12307f61e1e055896b68fe16654c9c4a606a0 100644 +--- a/src/main/java/net/minecraft/network/protocol/PacketUtils.java ++++ b/src/main/java/net/minecraft/network/protocol/PacketUtils.java +@@ -20,6 +20,24 @@ public class PacketUtils { + + private static final Logger LOGGER = LogManager.getLogger(); + ++ // Paper start - detailed watchdog information ++ public static final java.util.concurrent.ConcurrentLinkedDeque packetProcessing = new java.util.concurrent.ConcurrentLinkedDeque<>(); ++ static final java.util.concurrent.atomic.AtomicLong totalMainThreadPacketsProcessed = new java.util.concurrent.atomic.AtomicLong(); ++ ++ public static long getTotalProcessedPackets() { ++ return totalMainThreadPacketsProcessed.get(); ++ } ++ ++ public static java.util.List getCurrentPacketProcessors() { ++ java.util.List ret = new java.util.ArrayList<>(4); ++ for (PacketListener listener : packetProcessing) { ++ ret.add(listener); ++ } ++ ++ return ret; ++ } ++ // Paper end - detailed watchdog information ++ + public PacketUtils() {} + + public static void ensureRunningOnSameThread(Packet packet, T listener, ServerLevel world) throws RunningOnDifferentThreadException { +@@ -30,6 +48,8 @@ public class PacketUtils { + if (!engine.isSameThread()) { + Timing timing = MinecraftTimings.getPacketTiming(packet); // Paper - timings + engine.execute(() -> { ++ packetProcessing.push(listener); // Paper - detailed watchdog information ++ try { // Paper - detailed watchdog information + if (MinecraftServer.getServer().hasStopped() || (listener instanceof ServerGamePacketListenerImpl && ((ServerGamePacketListenerImpl) listener).processedDisconnect)) return; // CraftBukkit, MC-142590 + if (listener.getConnection().isConnected()) { + try (Timing ignored = timing.startTiming()) { // Paper - timings +@@ -53,6 +73,12 @@ public class PacketUtils { + } else { + PacketUtils.LOGGER.debug("Ignoring packet due to disconnection: {}", packet); + } ++ // Paper start - detailed watchdog information ++ } finally { ++ totalMainThreadPacketsProcessed.getAndIncrement(); ++ packetProcessing.pop(); ++ } ++ // Paper end - detailed watchdog information + + }); + throw RunningOnDifferentThreadException.RUNNING_ON_DIFFERENT_THREAD; +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 12b13945702a03132d1b4c42864c8036d1357020..b5a2445bbd691cbfed0d0bf7c688b1f10c267039 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -962,7 +962,26 @@ public class ServerLevel extends Level implements WorldGenLevel { + + } + ++ // Paper start - log detailed entity tick information ++ // TODO replace with varhandle ++ static final java.util.concurrent.atomic.AtomicReference currentlyTickingEntity = new java.util.concurrent.atomic.AtomicReference<>(); ++ ++ public static List getCurrentlyTickingEntities() { ++ Entity ticking = currentlyTickingEntity.get(); ++ List ret = java.util.Arrays.asList(ticking == null ? new Entity[0] : new Entity[] { ticking }); ++ ++ return ret; ++ } ++ // Paper end - log detailed entity tick information ++ + public void tickNonPassenger(Entity entity) { ++ // Paper start - log detailed entity tick information ++ io.papermc.paper.util.TickThread.ensureTickThread("Cannot tick an entity off-main"); ++ try { ++ if (currentlyTickingEntity.get() == null) { ++ currentlyTickingEntity.lazySet(entity); ++ } ++ // Paper end - log detailed entity tick information + ++TimingHistory.entityTicks; // Paper - timings + // Spigot start + co.aikar.timings.Timing timer; // Paper +@@ -1002,7 +1021,13 @@ public class ServerLevel extends Level implements WorldGenLevel { + this.tickPassenger(entity, entity1); + } + // } finally { timer.stopTiming(); } // Paper - timings - move up +- ++ // Paper start - log detailed entity tick information ++ } finally { ++ if (currentlyTickingEntity.get() == entity) { ++ currentlyTickingEntity.lazySet(null); ++ } ++ } ++ // Paper end - log detailed entity tick information + } + + 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 481e84fda6dccfaf684c922a12fa19ed35c87b3c..94857a736d2a16e8ade286c6f2ddf8bd798008eb 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -888,7 +888,42 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n + return this.onGround; + } + ++ // Paper start - detailed watchdog information ++ public final Object posLock = new Object(); // Paper - log detailed entity tick information ++ ++ private Vec3 moveVector; ++ private double moveStartX; ++ private double moveStartY; ++ private double moveStartZ; ++ ++ public final Vec3 getMoveVector() { ++ return this.moveVector; ++ } ++ ++ public final double getMoveStartX() { ++ return this.moveStartX; ++ } ++ ++ public final double getMoveStartY() { ++ return this.moveStartY; ++ } ++ ++ public final double getMoveStartZ() { ++ return this.moveStartZ; ++ } ++ // Paper end - detailed watchdog information ++ + public void move(MoverType movementType, Vec3 movement) { ++ // Paper start - detailed watchdog information ++ io.papermc.paper.util.TickThread.ensureTickThread("Cannot move an entity off-main"); ++ synchronized (this.posLock) { ++ this.moveStartX = this.getX(); ++ this.moveStartY = this.getY(); ++ this.moveStartZ = this.getZ(); ++ this.moveVector = movement; ++ } ++ try { ++ // Paper end - detailed watchdog information + if (this.noPhysics) { + this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z); + } else { +@@ -1079,6 +1114,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n + this.level.getProfiler().pop(); + } + } ++ // Paper start - detailed watchdog information ++ } finally { ++ synchronized (this.posLock) { // Paper ++ this.moveVector = null; ++ } // Paper ++ } ++ // Paper end - detailed watchdog information + } + + protected void tryCheckInsideBlocks() { +@@ -3896,7 +3938,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n + } + + public void setDeltaMovement(Vec3 velocity) { ++ synchronized (this.posLock) { // Paper + this.deltaMovement = velocity; ++ } // Paper + } + + public void setDeltaMovement(double x, double y, double z) { +@@ -3972,7 +4016,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n + } + // Paper end - fix MC-4 + if (this.position.x != x || this.position.y != y || this.position.z != z) { ++ synchronized (this.posLock) { // Paper + this.position = new Vec3(x, y, z); ++ } // Paper + int i = Mth.floor(x); + int j = Mth.floor(y); + int k = Mth.floor(z); +diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java +index dcfbe77bdb25d9c58ffb7b75c48bdb580bc0de47..bee38307494188800886a1622fed229b88dbd8f1 100644 +--- a/src/main/java/org/spigotmc/WatchdogThread.java ++++ b/src/main/java/org/spigotmc/WatchdogThread.java +@@ -23,6 +23,78 @@ public class WatchdogThread extends Thread + private volatile long lastTick; + private volatile boolean stopping; + ++ // Paper start - log detailed tick information ++ private void dumpEntity(net.minecraft.world.entity.Entity entity) { ++ Logger log = Bukkit.getServer().getLogger(); ++ double posX, posY, posZ; ++ net.minecraft.world.phys.Vec3 mot; ++ double moveStartX, moveStartY, moveStartZ; ++ net.minecraft.world.phys.Vec3 moveVec; ++ synchronized (entity.posLock) { ++ posX = entity.getX(); ++ posY = entity.getY(); ++ posZ = entity.getZ(); ++ mot = entity.getDeltaMovement(); ++ moveStartX = entity.getMoveStartX(); ++ moveStartY = entity.getMoveStartY(); ++ moveStartZ = entity.getMoveStartZ(); ++ moveVec = entity.getMoveVector(); ++ } ++ ++ String entityType = entity.getMinecraftKey().toString(); ++ java.util.UUID entityUUID = entity.getUUID(); ++ net.minecraft.world.level.Level world = entity.level; ++ ++ log.log(Level.SEVERE, "Ticking entity: " + entityType + ", entity class: " + entity.getClass().getName()); ++ log.log(Level.SEVERE, "Entity status: removed: " + entity.isRemoved() + ", valid: " + entity.valid + ", alive: " + entity.isAlive() + ", is passenger: " + entity.isPassenger()); ++ log.log(Level.SEVERE, "Entity UUID: " + entityUUID); ++ log.log(Level.SEVERE, "Position: world: '" + (world == null ? "unknown world?" : world.getWorld().getName()) + "' at location (" + posX + ", " + posY + ", " + posZ + ")"); ++ log.log(Level.SEVERE, "Velocity: " + (mot == null ? "unknown velocity" : mot.toString()) + " (in blocks per tick)"); ++ log.log(Level.SEVERE, "Entity AABB: " + entity.getBoundingBox()); ++ if (moveVec != null) { ++ log.log(Level.SEVERE, "Move call information: "); ++ log.log(Level.SEVERE, "Start position: (" + moveStartX + ", " + moveStartY + ", " + moveStartZ + ")"); ++ log.log(Level.SEVERE, "Move vector: " + moveVec.toString()); ++ } ++ } ++ ++ private void dumpTickingInfo() { ++ Logger log = Bukkit.getServer().getLogger(); ++ ++ // ticking entities ++ for (net.minecraft.world.entity.Entity entity : net.minecraft.server.level.ServerLevel.getCurrentlyTickingEntities()) { ++ this.dumpEntity(entity); ++ net.minecraft.world.entity.Entity vehicle = entity.getVehicle(); ++ if (vehicle != null) { ++ log.log(Level.SEVERE, "Detailing vehicle for above entity:"); ++ this.dumpEntity(vehicle); ++ } ++ } ++ ++ // packet processors ++ for (net.minecraft.network.PacketListener packetListener : net.minecraft.network.protocol.PacketUtils.getCurrentPacketProcessors()) { ++ if (packetListener instanceof net.minecraft.server.network.ServerGamePacketListenerImpl) { ++ net.minecraft.server.level.ServerPlayer player = ((net.minecraft.server.network.ServerGamePacketListenerImpl)packetListener).player; ++ long totalPackets = net.minecraft.network.protocol.PacketUtils.getTotalProcessedPackets(); ++ if (player == null) { ++ log.log(Level.SEVERE, "Handling packet for player connection or ticking player connection (null player): " + packetListener); ++ log.log(Level.SEVERE, "Total packets processed on the main thread for all players: " + totalPackets); ++ } else { ++ this.dumpEntity(player); ++ net.minecraft.world.entity.Entity vehicle = player.getVehicle(); ++ if (vehicle != null) { ++ log.log(Level.SEVERE, "Detailing vehicle for above entity:"); ++ this.dumpEntity(vehicle); ++ } ++ log.log(Level.SEVERE, "Total packets processed on the main thread for all players: " + totalPackets); ++ } ++ } else { ++ log.log(Level.SEVERE, "Handling packet for connection: " + packetListener); ++ } ++ } ++ } ++ // Paper end - log detailed tick information ++ + private WatchdogThread(long timeoutTime, boolean restart) + { + super( "Paper Watchdog Thread" ); +@@ -121,6 +193,7 @@ public class WatchdogThread extends Thread + log.log( Level.SEVERE, "------------------------------" ); + log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper + com.destroystokyo.paper.io.chunk.ChunkTaskManager.dumpAllChunkLoadInfo(); // Paper ++ this.dumpTickingInfo(); // Paper - log detailed tick information + WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( server.serverThread.getId(), Integer.MAX_VALUE ), log ); + log.log( Level.SEVERE, "------------------------------" ); + // diff --git a/patches/server/0776-Optimise-collision-checking-in-player-move-packet-ha.patch b/patches/server/0776-Optimise-collision-checking-in-player-move-packet-ha.patch new file mode 100644 index 0000000000..3c84567bbf --- /dev/null +++ b/patches/server/0776-Optimise-collision-checking-in-player-move-packet-ha.patch @@ -0,0 +1,168 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Thu, 2 Jul 2020 12:02:43 -0700 +Subject: [PATCH] Optimise collision checking in player move packet handling + +Move collision logic to just the hasNewCollision call instead of getCubes + hasNewCollision + +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index 73613c05784446c97420af46697cbc09bc4fd481..559572d3ff37e016ed07d428cf5d73a00ef48d70 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -582,12 +582,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser + return; + } + +- boolean flag = worldserver.noCollision(entity, entity.getBoundingBox().deflate(0.0625D)); ++ AABB oldBox = entity.getBoundingBox(); // Paper - copy from player movement packet + + d6 = d3 - this.vehicleLastGoodX; // Paper - diff on change, used for checking large move vectors above + d7 = d4 - this.vehicleLastGoodY - 1.0E-6D; // Paper - diff on change, used for checking large move vectors above + d8 = d5 - this.vehicleLastGoodZ; // Paper - diff on change, used for checking large move vectors above + entity.move(MoverType.PLAYER, new Vec3(d6, d7, d8)); ++ boolean didCollide = toX != entity.getX() || toY != entity.getY() || toZ != entity.getZ(); // Paper - needed here as the difference in Y can be reset - also note: this is only a guess at whether collisions took place, floating point errors can make this true when it shouldn't be... + double d11 = d7; + + d6 = d3 - entity.getX(); +@@ -601,16 +602,23 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser + boolean flag1 = false; + + if (d10 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot +- flag1 = true; ++ flag1 = true; // Paper - diff on change, this should be moved wrongly + ServerGamePacketListenerImpl.LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", entity.getName().getString(), this.player.getName().getString(), Math.sqrt(d10)); + } + Location curPos = this.getCraftPlayer().getLocation(); // Spigot + + entity.absMoveTo(d3, d4, d5, f, f1); + this.player.absMoveTo(d3, d4, d5, this.player.getYRot(), this.player.getXRot()); // CraftBukkit +- boolean flag2 = worldserver.noCollision(entity, entity.getBoundingBox().deflate(0.0625D)); +- +- if (flag && (flag1 || !flag2)) { ++ // Paper start - optimise out extra getCubes ++ boolean teleportBack = flag1; // violating this is always a fail ++ if (!teleportBack) { ++ // note: only call after setLocation, or else getBoundingBox is wrong ++ AABB newBox = entity.getBoundingBox(); ++ if (didCollide || !oldBox.equals(newBox)) { ++ teleportBack = this.hasNewCollision(worldserver, entity, oldBox, newBox); ++ } // else: no collision at all detected, why do we care? ++ } ++ if (teleportBack) { // Paper end - optimise out extra getCubes + entity.absMoveTo(d0, d1, d2, f, f1); + this.player.absMoveTo(d0, d1, d2, this.player.getYRot(), this.player.getXRot()); // CraftBukkit + this.connection.send(new ClientboundMoveVehiclePacket(entity)); +@@ -696,7 +704,32 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser + } + + private boolean noBlocksAround(Entity entity) { +- return entity.level.getBlockStates(entity.getBoundingBox().inflate(0.0625D).expandTowards(0.0D, -0.55D, 0.0D)).allMatch(BlockBehaviour.BlockStateBase::isAir); ++ // Paper start - stop using streams, this is already a known fixed problem in Entity#move ++ AABB box = entity.getBoundingBox().inflate(0.0625D).expandTowards(0.0D, -0.55D, 0.0D); ++ int minX = Mth.floor(box.minX); ++ int minY = Mth.floor(box.minY); ++ int minZ = Mth.floor(box.minZ); ++ int maxX = Mth.floor(box.maxX); ++ int maxY = Mth.floor(box.maxY); ++ int maxZ = Mth.floor(box.maxZ); ++ ++ Level world = entity.level; ++ BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); ++ ++ for (int y = minY; y <= maxY; ++y) { ++ for (int z = minZ; z <= maxZ; ++z) { ++ for (int x = minX; x <= maxX; ++x) { ++ pos.set(x, y, z); ++ BlockState type = world.getTypeIfLoaded(pos); ++ if (type != null && !type.isAir()) { ++ return false; ++ } ++ } ++ } ++ } ++ ++ return true; ++ // Paper end - stop using streams, this is already a known fixed problem in Entity#move + } + + @Override +@@ -1233,7 +1266,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser + } + + if (this.awaitingPositionFromClient != null) { +- if (this.tickCount - this.awaitingTeleportTime > 20) { ++ if (false && this.tickCount - this.awaitingTeleportTime > 20) { // Paper - this will greatly screw with clients with > 1000ms RTT + this.awaitingTeleportTime = this.tickCount; + this.teleport(this.awaitingPositionFromClient.x, this.awaitingPositionFromClient.y, this.awaitingPositionFromClient.z, this.player.getYRot(), this.player.getXRot()); + } +@@ -1327,7 +1360,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser + } + } + +- AABB axisalignedbb = this.player.getBoundingBox(); ++ AABB axisalignedbb = this.player.getBoundingBox(); // Paper - diff on change, should be old AABB + + d7 = d0 - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above + d8 = d1 - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above +@@ -1366,6 +1399,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser + } + + this.player.move(MoverType.PLAYER, new Vec3(d7, d8, d9)); ++ boolean didCollide = toX != this.player.getX() || toY != this.player.getY() || toZ != this.player.getZ(); // Paper - needed here as the difference in Y can be reset - also note: this is only a guess at whether collisions took place, floating point errors can make this true when it shouldn't be... + this.player.setOnGround(packet.isOnGround()); // CraftBukkit - SPIGOT-5810, SPIGOT-5835: reset by this.player.move + // Paper start - prevent position desync + if (this.awaitingPositionFromClient != null) { +@@ -1385,12 +1419,23 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser + boolean flag1 = false; + + if (!this.player.isChangingDimension() && d11 > org.spigotmc.SpigotConfig.movedWronglyThreshold && !this.player.isSleeping() && !this.player.gameMode.isCreative() && this.player.gameMode.getGameModeForPlayer() != GameType.SPECTATOR) { // Spigot +- flag1 = true; ++ flag1 = true; // Paper - diff on change, this should be moved wrongly + ServerGamePacketListenerImpl.LOGGER.warn("{} moved wrongly!", this.player.getName().getString()); + } + + this.player.absMoveTo(d0, d1, d2, f, f1); +- if (!this.player.noPhysics && !this.player.isSleeping() && (flag1 && worldserver.noCollision(this.player, axisalignedbb) || this.isPlayerCollidingWithAnythingNew((LevelReader) worldserver, axisalignedbb))) { ++ // Paper start - optimise out extra getCubes ++ // Original for reference: ++ // boolean teleportBack = flag1 && worldserver.getCubes(this.player, axisalignedbb) || (didCollide && this.a((IWorldReader) worldserver, axisalignedbb)); ++ boolean teleportBack = flag1; // violating this is always a fail ++ if (!this.player.noPhysics && !this.player.isSleeping() && !teleportBack) { ++ AABB newBox = this.player.getBoundingBox(); ++ if (didCollide || !axisalignedbb.equals(newBox)) { ++ // note: only call after setLocation, or else getBoundingBox is wrong ++ teleportBack = this.hasNewCollision(worldserver, this.player, axisalignedbb, newBox); ++ } // else: no collision at all detected, why do we care? ++ } ++ if (!this.player.noPhysics && !this.player.isSleeping() && teleportBack) { // Paper end - optimise out extra getCubes + this.teleport(d3, d4, d5, f, f1); + } else { + // CraftBukkit start - fire PlayerMoveEvent +@@ -1477,6 +1522,27 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser + } + } + ++ // Paper start - optimise out extra getCubes ++ private boolean hasNewCollision(final ServerLevel world, final Entity entity, final AABB oldBox, final AABB newBox) { ++ final List collisions = io.papermc.paper.util.CachedLists.getTempCollisionList(); ++ try { ++ io.papermc.paper.util.CollisionUtil.getCollisions(world, entity, newBox, collisions, false, true, ++ true, false, null, null); ++ ++ for (int i = 0, len = collisions.size(); i < len; ++i) { ++ final AABB box = collisions.get(i); ++ if (!io.papermc.paper.util.CollisionUtil.voxelShapeIntersect(box, oldBox)) { ++ return true; ++ } ++ } ++ ++ return false; ++ } finally { ++ io.papermc.paper.util.CachedLists.returnTempCollisionList(collisions); ++ } ++ } ++ // Paper end - optimise out extra getCubes ++ + private boolean isPlayerCollidingWithAnythingNew(LevelReader world, AABB box) { + Stream stream = world.getCollisions(this.player, this.player.getBoundingBox().deflate(9.999999747378752E-6D), (entity) -> { + return true; diff --git a/patches/server/0777-Manually-inline-methods-in-BlockPosition.patch b/patches/server/0777-Manually-inline-methods-in-BlockPosition.patch new file mode 100644 index 0000000000..547ad97708 --- /dev/null +++ b/patches/server/0777-Manually-inline-methods-in-BlockPosition.patch @@ -0,0 +1,63 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Mon, 6 Jul 2020 22:48:48 -0700 +Subject: [PATCH] Manually inline methods in BlockPosition + + +diff --git a/src/main/java/net/minecraft/core/BlockPos.java b/src/main/java/net/minecraft/core/BlockPos.java +index b70aa66732fb5e957aed0901f4c76358b2c56f8e..0cc0242d981586413bcc349df6e6fd3bc09710f1 100644 +--- a/src/main/java/net/minecraft/core/BlockPos.java ++++ b/src/main/java/net/minecraft/core/BlockPos.java +@@ -478,9 +478,9 @@ public class BlockPos extends Vec3i { + } + + public BlockPos.MutableBlockPos set(int x, int y, int z) { +- this.setX(x); +- this.setY(y); +- this.setZ(z); ++ this.x = x; // Paper - force inline ++ this.y = y; // Paper - force inline ++ this.z = z; // Paper - force inline + return this; + } + +@@ -544,19 +544,19 @@ public class BlockPos extends Vec3i { + // Paper start - comment out useless overrides @Override - TODO figure out why this is suddenly important to keep + @Override + public BlockPos.MutableBlockPos setX(int i) { +- super.setX(i); ++ this.x = i; // Paper + return this; + } + + @Override + public BlockPos.MutableBlockPos setY(int i) { +- super.setY(i); ++ this.y = i; // Paper + return this; + } + + @Override + public BlockPos.MutableBlockPos setZ(int i) { +- super.setZ(i); ++ this.z = i; // Paper + return this; + } + // Paper end +diff --git a/src/main/java/net/minecraft/core/Vec3i.java b/src/main/java/net/minecraft/core/Vec3i.java +index dc7598a011c2b290a42df35593de0b6689c99c57..a90085cfb2e3944c40bf15812320ff5e2690d3b1 100644 +--- a/src/main/java/net/minecraft/core/Vec3i.java ++++ b/src/main/java/net/minecraft/core/Vec3i.java +@@ -17,9 +17,9 @@ public class Vec3i implements Comparable { + return IntStream.of(vec3i.getX(), vec3i.getY(), vec3i.getZ()); + }); + public static final Vec3i ZERO = new Vec3i(0, 0, 0); +- private int x; +- private int y; +- private int z; ++ protected int x; // Paper - protected ++ protected int y; // Paper - protected ++ protected int z; // Paper - protected + + // Paper start + public boolean isValidLocation(net.minecraft.world.level.LevelHeightAccessor levelHeightAccessor) { diff --git a/patches/server/0778-Distance-manager-tick-timings.patch b/patches/server/0778-Distance-manager-tick-timings.patch new file mode 100644 index 0000000000..c7cc5bf242 --- /dev/null +++ b/patches/server/0778-Distance-manager-tick-timings.patch @@ -0,0 +1,40 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sat, 18 Jul 2020 16:03:57 -0700 +Subject: [PATCH] Distance manager tick timings + +Recently this has been taking up more time, so add a timings to +really figure out how much. + +diff --git a/src/main/java/co/aikar/timings/MinecraftTimings.java b/src/main/java/co/aikar/timings/MinecraftTimings.java +index eada966d7f108a6081be7a848f5c1dfcb1eed676..a977f7483f37df473096b2234dc1308bbaa6a8b6 100644 +--- a/src/main/java/co/aikar/timings/MinecraftTimings.java ++++ b/src/main/java/co/aikar/timings/MinecraftTimings.java +@@ -44,6 +44,7 @@ public final class MinecraftTimings { + + public static final Timing antiXrayUpdateTimer = Timings.ofSafe("anti-xray - update"); + public static final Timing antiXrayObfuscateTimer = Timings.ofSafe("anti-xray - obfuscate"); ++ public static final Timing distanceManagerTick = Timings.ofSafe("Distance Manager Tick"); // Paper - add timings for distance manager + + public static final Timing midTickChunkTasks = Timings.ofSafe("Mid Tick Chunk Tasks"); + +diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +index 0d8a47770435c27519af4ebd78835ec787551733..d7ec783caebd71364c1c3a414bcf1aac271b0574 100644 +--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java ++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +@@ -842,6 +842,7 @@ public class ServerChunkCache extends ChunkSource { + public boolean runDistanceManagerUpdates() { + if (distanceManager.delayDistanceManagerTick) return false; // Paper - Chunk priority + if (this.chunkMap.unloadingPlayerChunk) { net.minecraft.server.MinecraftServer.LOGGER.fatal("Cannot tick distance manager while unloading playerchunks", new Throwable()); throw new IllegalStateException("Cannot tick distance manager while unloading playerchunks"); } // Paper ++ co.aikar.timings.MinecraftTimings.distanceManagerTick.startTiming(); try { // Paper - add timings for distance manager + boolean flag = this.distanceManager.runAllUpdates(this.chunkMap); + boolean flag1 = this.chunkMap.promoteChunkMap(); + +@@ -851,6 +852,7 @@ public class ServerChunkCache extends ChunkSource { + this.clearCache(); + return true; + } ++ } finally { co.aikar.timings.MinecraftTimings.distanceManagerTick.stopTiming(); } // Paper - add timings for distance manager + } + + // Paper start - helper diff --git a/patches/server/0779-Name-craft-scheduler-threads-according-to-the-plugin.patch b/patches/server/0779-Name-craft-scheduler-threads-according-to-the-plugin.patch new file mode 100644 index 0000000000..7d081ee195 --- /dev/null +++ b/patches/server/0779-Name-craft-scheduler-threads-according-to-the-plugin.patch @@ -0,0 +1,33 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sun, 19 Jul 2020 15:17:01 -0700 +Subject: [PATCH] Name craft scheduler threads according to the plugin using + them + +Provides quick access to culprits running far more threads than +they should be + +diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java +index 2f3e2a404f55f09ae4db8261e495275e31228034..6d66f83afbeb650b10669fd7eeb24a315951fa86 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java ++++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java +@@ -25,7 +25,10 @@ class CraftAsyncTask extends CraftTask { + @Override + public void run() { + final Thread thread = Thread.currentThread(); +- synchronized (this.workers) { ++ // Paper start - name threads according to running plugin ++ final String nameBefore = thread.getName(); ++ thread.setName(nameBefore + " - " + this.getOwner().getName()); ++ try { synchronized (this.workers) { // Paper end - name threads according to running plugin + if (getPeriod() == CraftTask.CANCEL) { + // Never continue running after cancelled. + // Checking this with the lock is important! +@@ -92,6 +95,7 @@ class CraftAsyncTask extends CraftTask { + } + } + } ++ } finally { thread.setName(nameBefore); } // Paper - name worker thread according + } + + LinkedList getWorkers() { diff --git a/patches/server/0780-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch b/patches/server/0780-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch new file mode 100644 index 0000000000..eab8c3f0ed --- /dev/null +++ b/patches/server/0780-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch @@ -0,0 +1,34 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sun, 20 Sep 2020 16:10:49 -0700 +Subject: [PATCH] Make sure inlined getChunkAt has inlined logic for loaded + chunks + +Tux did some profiling some time ago and showed that the +previous getChunkAt method which had inlined logic for loaded +chunks did get inlined, but the standard CPS.getChunkAt +method was not inlined. + +Paper recently reverted this optimisation, so it's been reintroduced +here. + +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index 83062654f7a3b05ba0ddb97645d615988bbe511b..921aba3463b9f86a9e3c7d59aaa48b9760693eab 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -409,6 +409,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + + @Override + public final LevelChunk getChunk(int chunkX, int chunkZ) { // Paper - final to help inline ++ // Paper start - make sure loaded chunks get the inlined variant of this function ++ net.minecraft.server.level.ServerChunkCache cps = ((ServerLevel)this).getChunkSource(); ++ if (cps.mainThread == Thread.currentThread()) { ++ LevelChunk ifLoaded = cps.getChunkAtIfLoadedMainThread(chunkX, chunkZ); ++ if (ifLoaded != null) { ++ return ifLoaded; ++ } ++ } ++ // Paper end - make sure loaded chunks get the inlined variant of this function + return (LevelChunk) this.getChunk(chunkX, chunkZ, ChunkStatus.FULL, true); // Paper - avoid a method jump + } + diff --git a/patches/server/0781-Add-packet-limiter-config.patch b/patches/server/0781-Add-packet-limiter-config.patch new file mode 100644 index 0000000000..cc815c78b0 --- /dev/null +++ b/patches/server/0781-Add-packet-limiter-config.patch @@ -0,0 +1,205 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Fri, 30 Oct 2020 22:37:16 -0700 +Subject: [PATCH] Add packet limiter config + +Example config: +packet-limiter: + kick-message: '&cSent too many packets' + limits: + all: + interval: 7.0 + max-packet-rate: 500.0 + PacketPlayInAutoRecipe: + interval: 4.0 + max-packet-rate: 5.0 + action: DROP + +all section refers to all incoming packets, the action for all is +hard coded to KICK. + +For specific limits, the section name is the class's name, +and an action can be defined: DROP or KICK + +If interval or rate are less-than 0, the limit is ignored + +diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java +index fc07a8454f80c22bc3776a42b2c31f8793543f5c..7f5cd91b60a4527a915772cd4c7c7024e1e8aa2c 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java +@@ -545,4 +545,102 @@ public class PaperConfig { + playerMaxConcurrentChunkLoads = getDouble("settings.chunk-loading.player-max-concurrent-loads", 4.0); + globalMaxConcurrentChunkLoads = getDouble("settings.chunk-loading.global-max-concurrent-loads", 500.0); + } ++ ++ public static final class PacketLimit { ++ public final double packetLimitInterval; ++ public final double maxPacketRate; ++ public final ViolateAction violateAction; ++ ++ public PacketLimit(final double packetLimitInterval, final double maxPacketRate, final ViolateAction violateAction) { ++ this.packetLimitInterval = packetLimitInterval; ++ this.maxPacketRate = maxPacketRate; ++ this.violateAction = violateAction; ++ } ++ ++ public static enum ViolateAction { ++ KICK, DROP; ++ } ++ } ++ ++ public static String kickMessage; ++ public static PacketLimit allPacketsLimit; ++ public static java.util.Map>, PacketLimit> packetSpecificLimits = new java.util.HashMap<>(); ++ ++ private static void packetLimiter() { ++ packetSpecificLimits.clear(); ++ kickMessage = org.bukkit.ChatColor.translateAlternateColorCodes('&', getString("settings.packet-limiter.kick-message", "&cSent too many packets")); ++ allPacketsLimit = new PacketLimit( ++ getDouble("settings.packet-limiter.limits.all.interval", 7.0), ++ getDouble("settings.packet-limiter.limits.all.max-packet-rate", 500.0), ++ PacketLimit.ViolateAction.KICK ++ ); ++ if (allPacketsLimit.maxPacketRate <= 0.0 || allPacketsLimit.packetLimitInterval <= 0.0) { ++ allPacketsLimit = null; ++ } ++ final ConfigurationSection section = config.getConfigurationSection("settings.packet-limiter.limits"); ++ ++ // add default packets ++ ++ // auto recipe limiting ++ getDouble("settings.packet-limiter.limits." + ++ "PacketPlayInAutoRecipe" + ".interval", 4.0); ++ getDouble("settings.packet-limiter.limits." + ++ "PacketPlayInAutoRecipe" + ".max-packet-rate", 5.0); ++ getString("settings.packet-limiter.limits." + ++ "PacketPlayInAutoRecipe" + ".action", PacketLimit.ViolateAction.DROP.name()); ++ ++ final Map mojangToSpigot = new HashMap<>(); ++ final Map maps = io.papermc.paper.util.ObfHelper.INSTANCE.mappingsByObfName(); ++ if (maps != null) { ++ maps.forEach((spigotName, classMapping) -> ++ mojangToSpigot.put(classMapping.mojangName(), classMapping.obfName())); ++ } ++ ++ for (final String packetClassName : section.getKeys(false)) { ++ if (packetClassName.equals("all")) { ++ continue; ++ } ++ Class packetClazz = null; ++ ++ for (final String subpackage : List.of("game", "handshake", "login", "status")) { ++ final String fullName = "net.minecraft.network.protocol." + subpackage + "." + packetClassName; ++ try { ++ packetClazz = Class.forName(fullName); ++ break; ++ } catch (final ClassNotFoundException ex) { ++ try { ++ final String spigot = mojangToSpigot.get(fullName); ++ if (spigot != null) { ++ packetClazz = Class.forName(spigot); ++ } ++ } catch (final ClassNotFoundException ignore) {} ++ } ++ } ++ ++ if (packetClazz == null || !net.minecraft.network.protocol.Packet.class.isAssignableFrom(packetClazz)) { ++ MinecraftServer.LOGGER.warn("Packet '" + packetClassName + "' does not exist, cannot limit it! Please update paper.yml"); ++ continue; ++ } ++ ++ if (!(section.get(packetClassName.concat(".interval")) instanceof Number) || !(section.get(packetClassName.concat(".max-packet-rate")) instanceof Number)) { ++ throw new RuntimeException("Packet limit setting " + packetClassName + " is missing interval or max-packet-rate!"); ++ } ++ ++ final String actionString = section.getString(packetClassName.concat(".action"), "KICK"); ++ PacketLimit.ViolateAction action = PacketLimit.ViolateAction.KICK; ++ for (PacketLimit.ViolateAction test : PacketLimit.ViolateAction.values()) { ++ if (actionString.equalsIgnoreCase(test.name())) { ++ action = test; ++ break; ++ } ++ } ++ ++ final double interval = section.getDouble(packetClassName.concat(".interval")); ++ final double rate = section.getDouble(packetClassName.concat(".max-packet-rate")); ++ ++ if (interval > 0.0 && rate > 0.0) { ++ packetSpecificLimits.put((Class)packetClazz, new PacketLimit(interval, rate, action)); ++ } ++ } ++ } + } +diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java +index 3321bb7582a3e0227fbc1d41982529c23548269b..73457ae24ba6f605dd831a870499f1a990570e53 100644 +--- a/src/main/java/net/minecraft/network/Connection.java ++++ b/src/main/java/net/minecraft/network/Connection.java +@@ -148,6 +148,22 @@ public class Connection extends SimpleChannelInboundHandler> { + } + } + // Paper end - allow controlled flushing ++ // Paper start - packet limiter ++ protected final Object PACKET_LIMIT_LOCK = new Object(); ++ protected final io.papermc.paper.util.IntervalledCounter allPacketCounts = com.destroystokyo.paper.PaperConfig.allPacketsLimit != null ? new io.papermc.paper.util.IntervalledCounter( ++ (long)(com.destroystokyo.paper.PaperConfig.allPacketsLimit.packetLimitInterval * 1.0e9) ++ ) : null; ++ protected final java.util.Map>, io.papermc.paper.util.IntervalledCounter> packetSpecificLimits = new java.util.HashMap<>(); ++ ++ private boolean stopReadingPackets; ++ private void killForPacketSpam() { ++ this.sendPacket(new ClientboundDisconnectPacket(org.bukkit.craftbukkit.util.CraftChatMessage.fromString(com.destroystokyo.paper.PaperConfig.kickMessage, true)[0]), (future) -> { ++ this.disconnect(org.bukkit.craftbukkit.util.CraftChatMessage.fromString(com.destroystokyo.paper.PaperConfig.kickMessage, true)[0]); ++ }); ++ this.setReadOnly(); ++ this.stopReadingPackets = true; ++ } ++ // Paper end - packet limiter + + public Connection(PacketFlow side) { + this.receiving = side; +@@ -228,6 +244,45 @@ public class Connection extends SimpleChannelInboundHandler> { + + protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet packet) { + if (this.channel.isOpen()) { ++ // Paper start - packet limiter ++ if (this.stopReadingPackets) { ++ return; ++ } ++ if (this.allPacketCounts != null || ++ com.destroystokyo.paper.PaperConfig.packetSpecificLimits.containsKey(packet.getClass())) { ++ long time = System.nanoTime(); ++ synchronized (PACKET_LIMIT_LOCK) { ++ if (this.allPacketCounts != null) { ++ this.allPacketCounts.updateAndAdd(1, time); ++ if (this.allPacketCounts.getRate() >= com.destroystokyo.paper.PaperConfig.allPacketsLimit.maxPacketRate) { ++ this.killForPacketSpam(); ++ return; ++ } ++ } ++ ++ for (Class check = packet.getClass(); check != Object.class; check = check.getSuperclass()) { ++ com.destroystokyo.paper.PaperConfig.PacketLimit packetSpecificLimit = ++ com.destroystokyo.paper.PaperConfig.packetSpecificLimits.get(check); ++ if (packetSpecificLimit == null) { ++ continue; ++ } ++ io.papermc.paper.util.IntervalledCounter counter = this.packetSpecificLimits.computeIfAbsent((Class)check, (clazz) -> { ++ return new io.papermc.paper.util.IntervalledCounter((long)(packetSpecificLimit.packetLimitInterval * 1.0e9)); ++ }); ++ counter.updateAndAdd(1, time); ++ if (counter.getRate() >= packetSpecificLimit.maxPacketRate) { ++ switch (packetSpecificLimit.violateAction) { ++ case DROP: ++ return; ++ case KICK: ++ this.killForPacketSpam(); ++ return; ++ } ++ } ++ } ++ } ++ } ++ // Paper end - packet limiter + try { + Connection.genericsFtw(packet, this.packetListener); + } catch (RunningOnDifferentThreadException cancelledpackethandleexception) { diff --git a/patches/server/0782-Lag-compensate-block-breaking.patch b/patches/server/0782-Lag-compensate-block-breaking.patch new file mode 100644 index 0000000000..47ce12828a --- /dev/null +++ b/patches/server/0782-Lag-compensate-block-breaking.patch @@ -0,0 +1,155 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Fri, 14 Feb 2020 22:16:34 -0800 +Subject: [PATCH] Lag compensate block breaking + +Use time instead of ticks if ticks fall behind + +diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java +index 7f5cd91b60a4527a915772cd4c7c7024e1e8aa2c..1560ffc5d70b7185b4dd1ada7351a75cec893918 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java +@@ -643,4 +643,10 @@ public class PaperConfig { + } + } + } ++ ++ public static boolean lagCompensateBlockBreaking; ++ ++ private static void lagCompensateBlockBreaking() { ++ lagCompensateBlockBreaking = getBoolean("settings.lag-compensate-block-breaking", true); ++ } + } +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java +index 1c83fbc96a074c85a3e349e936ff1f3198596709..8975a806979ac028b916b826e0d051af5e760550 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java +@@ -56,14 +56,28 @@ public class ServerPlayerGameMode { + @Nullable + private GameType previousGameModeForPlayer; + private boolean isDestroyingBlock; +- private int destroyProgressStart; ++ private int destroyProgressStart; private long lastDigTime; // Paper - lag compensate block breaking + private BlockPos destroyPos; + private int gameTicks; + private boolean hasDelayedDestroy; + private BlockPos delayedDestroyPos; +- private int delayedTickStart; ++ private int delayedTickStart; private long hasDestroyedTooFastStartTime; // Paper - lag compensate block breaking + private int lastSentState; + ++ // Paper start - lag compensate block breaking ++ private int getTimeDiggingLagCompensate() { ++ int lagCompensated = (int)((System.nanoTime() - this.lastDigTime) / (50L * 1000L * 1000L)); ++ int tickDiff = this.gameTicks - this.destroyProgressStart; ++ return (com.destroystokyo.paper.PaperConfig.lagCompensateBlockBreaking && lagCompensated > (tickDiff + 1)) ? lagCompensated : tickDiff; // add one to ensure we don't lag compensate unless we need to ++ } ++ ++ private int getTimeDiggingTooFastLagCompensate() { ++ int lagCompensated = (int)((System.nanoTime() - this.hasDestroyedTooFastStartTime) / (50L * 1000L * 1000L)); ++ int tickDiff = this.gameTicks - this.delayedTickStart; ++ return (com.destroystokyo.paper.PaperConfig.lagCompensateBlockBreaking && lagCompensated > (tickDiff + 1)) ? lagCompensated : tickDiff; // add one to ensure we don't lag compensate unless we need to ++ } ++ // Paper end ++ + public ServerPlayerGameMode(ServerPlayer player) { + this.gameModeForPlayer = GameType.DEFAULT_MODE; + this.destroyPos = BlockPos.ZERO; +@@ -130,7 +144,7 @@ public class ServerPlayerGameMode { + if (iblockdata == null || iblockdata.isAir()) { // Paper + this.hasDelayedDestroy = false; + } else { +- float f = this.incrementDestroyProgress(iblockdata, this.delayedDestroyPos, this.delayedTickStart); ++ float f = this.updateBlockBreakAnimation(iblockdata, this.delayedDestroyPos, this.getTimeDiggingTooFastLagCompensate()); // Paper - lag compensate destroying blocks + + if (f >= 1.0F) { + this.hasDelayedDestroy = false; +@@ -150,7 +164,7 @@ public class ServerPlayerGameMode { + this.lastSentState = -1; + this.isDestroyingBlock = false; + } else { +- this.incrementDestroyProgress(iblockdata, this.destroyPos, this.destroyProgressStart); ++ this.updateBlockBreakAnimation(iblockdata, this.destroyPos, this.getTimeDiggingLagCompensate()); // Paper - lag compensate destroying + } + } + +@@ -158,6 +172,12 @@ public class ServerPlayerGameMode { + + private float incrementDestroyProgress(BlockState state, BlockPos pos, int i) { + int j = this.gameTicks - i; ++ // Paper start - change i (startTime) to totalTime ++ return this.updateBlockBreakAnimation(state, pos, j); ++ } ++ private float updateBlockBreakAnimation(BlockState state, BlockPos pos, int totalTime) { ++ int j = totalTime; ++ // Paper end + float f = state.getDestroyProgress(this.player, this.player.level, pos) * (float) (j + 1); + int k = (int) (f * 10.0F); + +@@ -226,7 +246,7 @@ public class ServerPlayerGameMode { + return; + } + +- this.destroyProgressStart = this.gameTicks; ++ this.destroyProgressStart = this.gameTicks; this.lastDigTime = System.nanoTime(); // Paper - lag compensate block breaking + float f = 1.0F; + + iblockdata = this.level.getBlockState(pos); +@@ -279,12 +299,12 @@ public class ServerPlayerGameMode { + int j = (int) (f * 10.0F); + + this.level.destroyBlockProgress(this.player.getId(), pos, j); +- this.player.connection.send(new ClientboundBlockBreakAckPacket(pos, this.level.getBlockState(pos), action, true, "actual start of destroying")); ++ if (!com.destroystokyo.paper.PaperConfig.lagCompensateBlockBreaking) this.player.connection.send(new ClientboundBlockBreakAckPacket(pos, this.level.getBlockState(pos), action, true, "actual start of destroying")); + this.lastSentState = j; + } + } else if (action == ServerboundPlayerActionPacket.Action.STOP_DESTROY_BLOCK) { + if (pos.equals(this.destroyPos)) { +- int k = this.gameTicks - this.destroyProgressStart; ++ int k = this.getTimeDiggingLagCompensate(); // Paper - lag compensate block breaking + + iblockdata = this.level.getBlockState(pos); + if (!iblockdata.isAir()) { +@@ -301,12 +321,18 @@ public class ServerPlayerGameMode { + this.isDestroyingBlock = false; + this.hasDelayedDestroy = true; + this.delayedDestroyPos = pos; +- this.delayedTickStart = this.destroyProgressStart; ++ this.delayedTickStart = this.destroyProgressStart; this.hasDestroyedTooFastStartTime = this.lastDigTime; // Paper - lag compensate block breaking + } + } + } + ++ // Paper start - this can cause clients on a lagging server to think they're not currently destroying a block ++ if (com.destroystokyo.paper.PaperConfig.lagCompensateBlockBreaking) { ++ this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); ++ } else { + this.player.connection.send(new ClientboundBlockBreakAckPacket(pos, this.level.getBlockState(pos), action, true, "stopped destroying")); ++ } ++ // Paper end - this can cause clients on a lagging server to think they're not currently destroying a block + } else if (action == ServerboundPlayerActionPacket.Action.ABORT_DESTROY_BLOCK) { + this.isDestroyingBlock = false; + if (!Objects.equals(this.destroyPos, pos) && !BlockPos.ZERO.equals(this.destroyPos)) { +@@ -318,7 +344,7 @@ public class ServerPlayerGameMode { + } + + this.level.destroyBlockProgress(this.player.getId(), pos, -1); +- this.player.connection.send(new ClientboundBlockBreakAckPacket(pos, this.level.getBlockState(pos), action, true, "aborted destroying")); ++ if (!com.destroystokyo.paper.PaperConfig.lagCompensateBlockBreaking) this.player.connection.send(new ClientboundBlockBreakAckPacket(pos, this.level.getBlockState(pos), action, true, "aborted destroying")); // Paper - this can cause clients on a lagging server to think they stopped destroying a block they're currently destroying + } + + } +@@ -328,7 +354,13 @@ public class ServerPlayerGameMode { + + public void destroyAndAck(BlockPos pos, ServerboundPlayerActionPacket.Action action, String reason) { + if (this.destroyBlock(pos)) { ++ // Paper start - this can cause clients on a lagging server to think they're not currently destroying a block ++ if (com.destroystokyo.paper.PaperConfig.lagCompensateBlockBreaking) { ++ this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); ++ } else { + this.player.connection.send(new ClientboundBlockBreakAckPacket(pos, this.level.getBlockState(pos), action, true, reason)); ++ } ++ // Paper end - this can cause clients on a lagging server to think they're not currently destroying a block + } else { + this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); // CraftBukkit - SPIGOT-5196 + } diff --git a/patches/server/0783-Use-hash-table-for-maintaing-changed-block-set.patch b/patches/server/0783-Use-hash-table-for-maintaing-changed-block-set.patch new file mode 100644 index 0000000000..bf82c170ea --- /dev/null +++ b/patches/server/0783-Use-hash-table-for-maintaing-changed-block-set.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Thu, 11 Mar 2021 21:17:02 -0800 +Subject: [PATCH] Use hash table for maintaing changed block set + +When a lot of block changes occur the iteration for checking can +add up a bit and cause a small performance impact. + +diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java +index 234a1eff2d664bf611c5337ee84d091b64cd7004..54cb9f909df7d1041cec553e1f54ff8ecff48bbe 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java ++++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java +@@ -41,6 +41,8 @@ import net.minecraft.world.level.lighting.LevelLightEngine; + import net.minecraft.server.MinecraftServer; + // CraftBukkit end + ++import it.unimi.dsi.fastutil.shorts.ShortOpenHashSet; // Paper ++ + public class ChunkHolder { + + public static final Either UNLOADED_CHUNK = Either.right(ChunkHolder.ChunkLoadingFailure.UNLOADED); +@@ -390,7 +392,7 @@ public class ChunkHolder { + if (i < 0 || i >= this.changedBlocksPerSection.length) return; // CraftBukkit - SPIGOT-6086, SPIGOT-6296 + if (this.changedBlocksPerSection[i] == null) { + this.hasChangedSections = true; +- this.changedBlocksPerSection[i] = new ShortArraySet(); ++ this.changedBlocksPerSection[i] = new ShortOpenHashSet(); // Paper - use a set to make setting constant-time + } + + this.changedBlocksPerSection[i].add(SectionPos.sectionRelativePos(pos)); diff --git a/patches/server/0784-Consolidate-flush-calls-for-entity-tracker-packets.patch b/patches/server/0784-Consolidate-flush-calls-for-entity-tracker-packets.patch new file mode 100644 index 0000000000..e6e5fe03e4 --- /dev/null +++ b/patches/server/0784-Consolidate-flush-calls-for-entity-tracker-packets.patch @@ -0,0 +1,52 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sat, 4 Apr 2020 17:00:20 -0700 +Subject: [PATCH] Consolidate flush calls for entity tracker packets + +Most server packets seem to be sent from here, so try to avoid +expensive flush calls from them. + +This change was motivated due to local testing: + +- My server spawn has 130 cows in it (for testing a prev. patch) +- Try to let 200 players join spawn + +Without this change, I could only get 20 players on before they +all started timing out due to the load put on the Netty I/O threads. + +With this change I could get all 200 on at 0ms ping. + +(one of the primary issues is that my CPU is kinda trash, and having +4 extra threads at 100% is just too much for it). + +So in general this patch should reduce Netty I/O thread load. + +diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +index d7ec783caebd71364c1c3a414bcf1aac271b0574..7470f3ba66c2e894b5a5b0ba392ecabf8b04aff9 100644 +--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java ++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +@@ -1069,7 +1069,24 @@ public class ServerChunkCache extends ChunkSource { + this.level.getProfiler().pop(); + } + ++ // Paper start - controlled flush for entity tracker packets ++ List disabledFlushes = new java.util.ArrayList<>(this.level.players.size()); ++ for (ServerPlayer player : this.level.players) { ++ net.minecraft.server.network.ServerGamePacketListenerImpl connection = player.connection; ++ if (connection != null) { ++ connection.connection.disableAutomaticFlush(); ++ disabledFlushes.add(connection.connection); ++ } ++ } ++ try { // Paper end - controlled flush for entity tracker packets + this.chunkMap.tick(); ++ // Paper start - controlled flush for entity tracker packets ++ } finally { ++ for (net.minecraft.network.Connection networkManager : disabledFlushes) { ++ networkManager.enableAutomaticFlush(); ++ } ++ } ++ // Paper end - controlled flush for entity tracker packets + } + + private void getFullChunk(long pos, Consumer chunkConsumer) { diff --git a/patches/server/0785-Don-t-lookup-fluid-state-when-raytracing.patch b/patches/server/0785-Don-t-lookup-fluid-state-when-raytracing.patch new file mode 100644 index 0000000000..bf4e1f243f --- /dev/null +++ b/patches/server/0785-Don-t-lookup-fluid-state-when-raytracing.patch @@ -0,0 +1,20 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Fri, 28 Aug 2020 12:33:47 -0700 +Subject: [PATCH] Don't lookup fluid state when raytracing + +Just use the iblockdata already retrieved, removes a getType call. + +diff --git a/src/main/java/net/minecraft/world/level/BlockGetter.java b/src/main/java/net/minecraft/world/level/BlockGetter.java +index fe4dba491b586757a16aa36e62682f364daa2602..b087dd208f62856c03db3aa2ae28ffc98d76e649 100644 +--- a/src/main/java/net/minecraft/world/level/BlockGetter.java ++++ b/src/main/java/net/minecraft/world/level/BlockGetter.java +@@ -84,7 +84,7 @@ public interface BlockGetter extends LevelHeightAccessor { + return BlockHitResult.miss(raytrace1.getTo(), Direction.getNearest(vec3d.x, vec3d.y, vec3d.z), new BlockPos(raytrace1.getTo())); + } + // Paper end +- FluidState fluid = this.getFluidState(blockposition); ++ FluidState fluid = iblockdata.getFluidState(); // Paper - don't need to go to world state again + Vec3 vec3d = raytrace1.getFrom(); + Vec3 vec3d1 = raytrace1.getTo(); + VoxelShape voxelshape = raytrace1.getBlockShape(iblockdata, this, blockposition); diff --git a/patches/server/0786-Time-scoreboard-search.patch b/patches/server/0786-Time-scoreboard-search.patch new file mode 100644 index 0000000000..48469b83af --- /dev/null +++ b/patches/server/0786-Time-scoreboard-search.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Tue, 21 Apr 2020 01:53:22 -0700 +Subject: [PATCH] Time scoreboard search + +Plugins leaking scoreboards will make this very expensive, +let server owners debug it easily + +diff --git a/src/main/java/co/aikar/timings/MinecraftTimings.java b/src/main/java/co/aikar/timings/MinecraftTimings.java +index a977f7483f37df473096b2234dc1308bbaa6a8b6..bb5f079a10b15d7b9f2ab5a8af67c559ffa5b371 100644 +--- a/src/main/java/co/aikar/timings/MinecraftTimings.java ++++ b/src/main/java/co/aikar/timings/MinecraftTimings.java +@@ -45,6 +45,7 @@ public final class MinecraftTimings { + public static final Timing antiXrayUpdateTimer = Timings.ofSafe("anti-xray - update"); + public static final Timing antiXrayObfuscateTimer = Timings.ofSafe("anti-xray - obfuscate"); + public static final Timing distanceManagerTick = Timings.ofSafe("Distance Manager Tick"); // Paper - add timings for distance manager ++ public static final Timing scoreboardScoreSearch = Timings.ofSafe("Scoreboard score search"); // Paper - add timings for scoreboard search + + public static final Timing midTickChunkTasks = Timings.ofSafe("Mid Tick Chunk Tasks"); + +diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java +index 8ccfe9488db44d7d2cf4040a5b4cead33da1d5f4..1a3b1eb7b70b9a668aa33ea943c13890eaa23a05 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java ++++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java +@@ -113,9 +113,18 @@ public final class CraftScoreboardManager implements ScoreboardManager { + + // CraftBukkit method + public void getScoreboardScores(ObjectiveCriteria criteria, String name, Consumer consumer) { ++ // Paper start - add timings for scoreboard search ++ // plugins leaking scoreboards will make this very expensive, let server owners debug it easily ++ co.aikar.timings.MinecraftTimings.scoreboardScoreSearch.startTimingIfSync(); ++ try { ++ // Paper end - add timings for scoreboard search + for (CraftScoreboard scoreboard : this.scoreboards) { + Scoreboard board = scoreboard.board; + board.forAllObjectives(criteria, name, (score) -> consumer.accept(score)); + } ++ } finally { // Paper start - add timings for scoreboard search ++ co.aikar.timings.MinecraftTimings.scoreboardScoreSearch.stopTimingIfSync(); ++ } ++ // Paper end - add timings for scoreboard search + } + } diff --git a/patches/server/0787-Send-full-pos-packets-for-hard-colliding-entities.patch b/patches/server/0787-Send-full-pos-packets-for-hard-colliding-entities.patch new file mode 100644 index 0000000000..8825572e72 --- /dev/null +++ b/patches/server/0787-Send-full-pos-packets-for-hard-colliding-entities.patch @@ -0,0 +1,38 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Tue, 16 Feb 2021 00:16:56 -0800 +Subject: [PATCH] Send full pos packets for hard colliding entities + +Prevent collision problems due to desync (i.e boats) + +Configurable under +`send-full-pos-for-hard-colliding-entities` + +diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java +index 1560ffc5d70b7185b4dd1ada7351a75cec893918..dcae40fde8087ce09ec8b9360f2abc860b88c975 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java +@@ -649,4 +649,10 @@ public class PaperConfig { + private static void lagCompensateBlockBreaking() { + lagCompensateBlockBreaking = getBoolean("settings.lag-compensate-block-breaking", true); + } ++ ++ public static boolean sendFullPosForHardCollidingEntities; ++ ++ private static void sendFullPosForHardCollidingEntities() { ++ sendFullPosForHardCollidingEntities = getBoolean("settings.send-full-pos-for-hard-colliding-entities", true); ++ } + } +diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java +index 9950541e0432240207458274b76b1c5d402ac704..b7c9294fdd3d799d410afba4a1118aa371c98533 100644 +--- a/src/main/java/net/minecraft/server/level/ServerEntity.java ++++ b/src/main/java/net/minecraft/server/level/ServerEntity.java +@@ -173,7 +173,7 @@ public class ServerEntity { + // Paper end - remove allocation of Vec3D here + boolean flag4 = k < -32768L || k > 32767L || l < -32768L || l > 32767L || i1 < -32768L || i1 > 32767L; + +- if (!flag4 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.isOnGround()) { ++ if (!flag4 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.isOnGround() && !(com.destroystokyo.paper.PaperConfig.sendFullPosForHardCollidingEntities && this.entity.hardCollides())) { // Paper - send full pos for hard colliding entities to prevent collision problems due to desync + if ((!flag2 || !flag3) && !(this.entity instanceof AbstractArrow)) { + if (flag2) { + packet1 = new ClientboundMoveEntityPacket.Pos(this.entity.getId(), (short) ((int) k), (short) ((int) l), (short) ((int) i1), this.entity.isOnGround()); diff --git a/patches/server/0788-Do-not-run-raytrace-logic-for-AIR.patch b/patches/server/0788-Do-not-run-raytrace-logic-for-AIR.patch new file mode 100644 index 0000000000..f8c4d1a7af --- /dev/null +++ b/patches/server/0788-Do-not-run-raytrace-logic-for-AIR.patch @@ -0,0 +1,22 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sun, 7 Mar 2021 13:15:04 -0800 +Subject: [PATCH] Do not run raytrace logic for AIR + +Saves approx. 5% for the raytrace call, as most (expensive) +raytracing tends to go through air and returning early is an +easy win. The remaining problems with this function +are mostly with the block getting itself. + +diff --git a/src/main/java/net/minecraft/world/level/BlockGetter.java b/src/main/java/net/minecraft/world/level/BlockGetter.java +index b087dd208f62856c03db3aa2ae28ffc98d76e649..6200a8ab4f7b2c40e7139cfb90a62f42c5828de2 100644 +--- a/src/main/java/net/minecraft/world/level/BlockGetter.java ++++ b/src/main/java/net/minecraft/world/level/BlockGetter.java +@@ -84,6 +84,7 @@ public interface BlockGetter extends LevelHeightAccessor { + return BlockHitResult.miss(raytrace1.getTo(), Direction.getNearest(vec3d.x, vec3d.y, vec3d.z), new BlockPos(raytrace1.getTo())); + } + // Paper end ++ if (iblockdata.isAir()) return null; // Paper - optimise air cases + FluidState fluid = iblockdata.getFluidState(); // Paper - don't need to go to world state again + Vec3 vec3d = raytrace1.getFrom(); + Vec3 vec3d1 = raytrace1.getTo(); diff --git a/patches/server/0789-Oprimise-map-impl-for-tracked-players.patch b/patches/server/0789-Oprimise-map-impl-for-tracked-players.patch new file mode 100644 index 0000000000..3353e126fe --- /dev/null +++ b/patches/server/0789-Oprimise-map-impl-for-tracked-players.patch @@ -0,0 +1,29 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Fri, 19 Feb 2021 22:51:52 -0800 +Subject: [PATCH] Oprimise map impl for tracked players + +Reference2BooleanOpenHashMap is going to have +better lookups than HashMap. + +diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java +index 45b668095459c16d2a723870d7533c69463bfde8..b5015f0a59aeb77636e6b1dd4df7381660d0788c 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -103,6 +103,7 @@ import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + + import org.bukkit.entity.Player; // CraftBukkit ++import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper + + public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider { + +@@ -2168,7 +2169,7 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially + final Entity entity; + private final int range; + SectionPos lastSectionPos; +- public final Set seenBy = Sets.newIdentityHashSet(); ++ public final Set seenBy = new ReferenceOpenHashSet<>(); // Paper - optimise map impl + + public TrackedEntity(Entity entity, int i, int j, boolean flag) { + this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, this.seenBy); // CraftBukkit diff --git a/patches/server/0790-Optimise-BlockSoil-nearby-water-lookup.patch b/patches/server/0790-Optimise-BlockSoil-nearby-water-lookup.patch new file mode 100644 index 0000000000..a3213ee164 --- /dev/null +++ b/patches/server/0790-Optimise-BlockSoil-nearby-water-lookup.patch @@ -0,0 +1,52 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Thu, 10 Jun 2021 14:36:00 -0700 +Subject: [PATCH] Optimise BlockSoil nearby water lookup + +Apparently the abstract block iteration was taking about +75% of the method call. + +diff --git a/src/main/java/net/minecraft/world/level/block/FarmBlock.java b/src/main/java/net/minecraft/world/level/block/FarmBlock.java +index aa1ba8b74ab70b6cede99e4853ac0203f388ab06..5955c95efbfa3e614ecf03de3e485a1ea88b7859 100644 +--- a/src/main/java/net/minecraft/world/level/block/FarmBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/FarmBlock.java +@@ -139,19 +139,28 @@ public class FarmBlock extends Block { + } + + private static boolean isNearWater(LevelReader world, BlockPos pos) { +- Iterator iterator = BlockPos.betweenClosed(pos.offset(-4, 0, -4), pos.offset(4, 1, 4)).iterator(); +- +- BlockPos blockposition1; +- +- do { +- if (!iterator.hasNext()) { +- return false; ++ // Paper start - remove abstract block iteration ++ int xOff = pos.getX(); ++ int yOff = pos.getY(); ++ int zOff = pos.getZ(); ++ ++ for (int dz = -4; dz <= 4; ++dz) { ++ int z = dz + zOff; ++ for (int dx = -4; dx <= 4; ++dx) { ++ int x = xOff + dx; ++ for (int dy = 0; dy <= 1; ++dy) { ++ int y = dy + yOff; ++ net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk)world.getChunk(x >> 4, z >> 4); ++ net.minecraft.world.level.material.FluidState fluid = chunk.getBlockData(x, y, z).getFluidState(); ++ if (fluid.is(FluidTags.WATER)) { ++ return true; ++ } ++ } + } ++ } + +- blockposition1 = (BlockPos) iterator.next(); +- } while (!world.getFluidState(blockposition1).is((Tag) FluidTags.WATER)); +- +- return true; ++ return false; ++ // Paper end - remove abstract block iteration + } + + @Override diff --git a/patches/server/0791-Allow-removal-addition-of-entities-to-entity-ticklis.patch b/patches/server/0791-Allow-removal-addition-of-entities-to-entity-ticklis.patch new file mode 100644 index 0000000000..d68c8524e7 --- /dev/null +++ b/patches/server/0791-Allow-removal-addition-of-entities-to-entity-ticklis.patch @@ -0,0 +1,89 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sat, 19 Jun 2021 22:47:17 -0700 +Subject: [PATCH] Allow removal/addition of entities to entity ticklist during + tick + +It really doesn't make any sense that we would iterate over removed +entities during tick. Sure - tick entity checks removed, but +does it check if the entity is in an entity ticking chunk? +No it doesn't. So, allowing removal while iteration +ENSURES only entities MARKED TO TICK are ticked. + +diff --git a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java +index b27c8db914cca3ff0ea8a24acddb9cb9870ce21d..4814e719e0b898464692075170889fdb2729a26a 100644 +--- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java ++++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java +@@ -9,57 +9,42 @@ import javax.annotation.Nullable; + import net.minecraft.world.entity.Entity; + + public class EntityTickList { +- private Int2ObjectMap active = new Int2ObjectLinkedOpenHashMap<>(); +- private Int2ObjectMap passive = new Int2ObjectLinkedOpenHashMap<>(); +- @Nullable +- private Int2ObjectMap iterated; ++ private final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet entities = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(true); // Paper - rewrite this, always keep this updated - why would we EVER tick an entity that's not ticking? + + private void ensureActiveIsNotIterated() { +- if (this.iterated == this.active) { +- this.passive.clear(); +- +- for(Entry entry : Int2ObjectMaps.fastIterable(this.active)) { +- this.passive.put(entry.getIntKey(), entry.getValue()); +- } +- +- Int2ObjectMap int2ObjectMap = this.active; +- this.active = this.passive; +- this.passive = int2ObjectMap; +- } ++ // Paper - replace with better logic, do not delay removals + + } + + public void add(Entity entity) { + io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist addition"); // Paper + this.ensureActiveIsNotIterated(); +- this.active.put(entity.getId(), entity); ++ this.entities.add(entity); // Paper - replace with better logic, do not delay removals/additions + } + + public void remove(Entity entity) { + io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist removal"); // Paper + this.ensureActiveIsNotIterated(); +- this.active.remove(entity.getId()); ++ this.entities.remove(entity); // Paper - replace with better logic, do not delay removals/additions + } + + public boolean contains(Entity entity) { +- return this.active.containsKey(entity.getId()); ++ return this.entities.contains(entity); // Paper - replace with better logic, do not delay removals/additions + } + + public void forEach(Consumer action) { + io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist iteration"); // Paper +- if (this.iterated != null) { +- throw new UnsupportedOperationException("Only one concurrent iteration supported"); +- } else { +- this.iterated = this.active; +- +- try { +- for(Entity entity : this.active.values()) { +- action.accept(entity); +- } +- } finally { +- this.iterated = null; ++ // Paper start - replace with better logic, do not delay removals/additions ++ // To ensure nothing weird happens with dimension travelling, do not iterate over new entries... ++ // (by dfl iterator() is configured to not iterate over new entries) ++ io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator iterator = this.entities.iterator(); ++ try { ++ while (iterator.hasNext()) { ++ action.accept(iterator.next()); + } +- ++ } finally { ++ iterator.finishedIterating(); + } ++ // Paper end - replace with better logic, do not delay removals/additions + } + } diff --git a/patches/server/0792-Optimise-random-block-ticking.patch b/patches/server/0792-Optimise-random-block-ticking.patch new file mode 100644 index 0000000000..efcf6e55bb --- /dev/null +++ b/patches/server/0792-Optimise-random-block-ticking.patch @@ -0,0 +1,369 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sun, 20 Jun 2021 16:19:26 -0700 +Subject: [PATCH] Optimise random block ticking + +Massive performance improvement for random block ticking. +The performance increase comes from the fact that the vast +majority of attempted block ticks (~95% in my testing) fail +because the randomly selected block is not tickable. + +Now only tickable blocks are targeted, however this means that +the maximum number of block ticks occurs per chunk. However, +not all chunks are going to be targeted. The percent chance +of a chunk being targeted is based on how many tickable blocks +are in the chunk. +This means that while block ticks are spread out less, the +total number of blocks ticked per world tick remains the same. +Therefore, the chance of a random tickable block being ticked +remains the same. + +diff --git a/src/main/java/io/papermc/paper/util/math/ThreadUnsafeRandom.java b/src/main/java/io/papermc/paper/util/math/ThreadUnsafeRandom.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e8b4053babe46999980b92643125405064af1c04 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/math/ThreadUnsafeRandom.java +@@ -0,0 +1,46 @@ ++package io.papermc.paper.util.math; ++ ++import java.util.Random; ++ ++public final class ThreadUnsafeRandom extends Random { ++ ++ // See javadoc and internal comments for java.util.Random where these values come from, how they are used, and the author for them. ++ private static final long multiplier = 0x5DEECE66DL; ++ private static final long addend = 0xBL; ++ private static final long mask = (1L << 48) - 1; ++ ++ private static long initialScramble(long seed) { ++ return (seed ^ multiplier) & mask; ++ } ++ ++ private long seed; ++ ++ @Override ++ public void setSeed(long seed) { ++ // note: called by Random constructor ++ this.seed = initialScramble(seed); ++ } ++ ++ @Override ++ protected int next(int bits) { ++ // avoid the expensive CAS logic used by superclass ++ return (int) (((this.seed = this.seed * multiplier + addend) & mask) >>> (48 - bits)); ++ } ++ ++ // Taken from ++ // https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ ++ // https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/master/2016/06/25/fastrange.c ++ // Original license is public domain ++ public static int fastRandomBounded(final long randomInteger, final long limit) { ++ // randomInteger must be [0, pow(2, 32)) ++ // limit must be [0, pow(2, 32)) ++ return (int)((randomInteger * limit) >>> 32); ++ } ++ ++ @Override ++ public int nextInt(int bound) { ++ // yes this breaks random's spec ++ // however there's nothing that uses this class that relies on it ++ return fastRandomBounded(this.next(32) & 0xFFFFFFFFL, bound); ++ } ++} +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index b5a2445bbd691cbfed0d0bf7c688b1f10c267039..3b26b6a24516bb2e2331edb7dc41a28b57835aa1 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -743,6 +743,10 @@ public class ServerLevel extends Level implements WorldGenLevel { + entityplayer.stopSleepInBed(false, false); + }); + } ++ // Paper start - optimise random block ticking ++ private final BlockPos.MutableBlockPos chunkTickMutablePosition = new BlockPos.MutableBlockPos(); ++ private final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(); ++ // Paper end + + public void tickChunk(LevelChunk chunk, int randomTickSpeed) { + ChunkPos chunkcoordintpair = chunk.getPos(); +@@ -752,10 +756,10 @@ public class ServerLevel extends Level implements WorldGenLevel { + ProfilerFiller gameprofilerfiller = this.getProfiler(); + + gameprofilerfiller.push("thunder"); +- BlockPos blockposition; ++ final BlockPos.MutableBlockPos blockposition = this.chunkTickMutablePosition; // Paper - use mutable to reduce allocation rate, final to force compile fail on change + + if (!this.paperConfig.disableThunder && flag && this.isThundering() && this.random.nextInt(100000) == 0) { // Paper - Disable thunder +- blockposition = this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15)); ++ blockposition.set(this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15))); // Paper + if (this.isRainingAt(blockposition)) { + DifficultyInstance difficultydamagescaler = this.getCurrentDifficultyAt(blockposition); + boolean flag1 = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && this.random.nextDouble() < (double) difficultydamagescaler.getEffectiveDifficulty() * paperConfig.skeleHorseSpawnChance && !this.getBlockState(blockposition.below()).is(Blocks.LIGHTNING_ROD); // Paper +@@ -778,65 +782,78 @@ public class ServerLevel extends Level implements WorldGenLevel { + } + + gameprofilerfiller.popPush("iceandsnow"); +- if (!this.paperConfig.disableIceAndSnow && this.random.nextInt(16) == 0) { // Paper - Disable ice and snow +- blockposition = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, this.getBlockRandomPos(j, 0, k, 15)); +- BlockPos blockposition1 = blockposition.below(); ++ if (!this.paperConfig.disableIceAndSnow && this.randomTickRandom.nextInt(16) == 0) { // Paper - Disable ice and snow // Paper - optimise random ticking ++ // Paper start - optimise chunk ticking ++ this.getRandomBlockPosition(j, 0, k, 15, blockposition); ++ int normalY = chunk.getHeight(Heightmap.Types.MOTION_BLOCKING, blockposition.getX() & 15, blockposition.getZ() & 15) + 1; ++ int downY = normalY - 1; ++ blockposition.setY(normalY); ++ // Paper end + Biome biomebase = this.getBiome(blockposition); + +- if (biomebase.shouldFreeze((LevelReader) this, blockposition1)) { +- org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition1, Blocks.ICE.defaultBlockState(), null); // CraftBukkit ++ // Paper start - optimise chunk ticking ++ blockposition.setY(downY); ++ if (biomebase.shouldFreeze(this, blockposition)) { ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition, Blocks.ICE.defaultBlockState(), null); // CraftBukkit ++ // Paper end + } + + if (flag) { ++ blockposition.setY(normalY); // Paper + if (biomebase.shouldSnow(this, blockposition)) { + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition, Blocks.SNOW.defaultBlockState(), null); // CraftBukkit + } + +- BlockState iblockdata = this.getBlockState(blockposition1); ++ blockposition.setY(downY); // Paper ++ BlockState iblockdata = this.getBlockState(blockposition); // Paper ++ blockposition.setY(normalY); // Paper + Biome.Precipitation biomebase_precipitation = this.getBiome(blockposition).getPrecipitation(); + +- if (biomebase_precipitation == Biome.Precipitation.RAIN && biomebase.isColdEnoughToSnow(blockposition1)) { ++ blockposition.setY(downY); // Paper ++ if (biomebase_precipitation == Biome.Precipitation.RAIN && biomebase.isColdEnoughToSnow(blockposition)) { // Paper + biomebase_precipitation = Biome.Precipitation.SNOW; + } + +- iblockdata.getBlock().handlePrecipitation(iblockdata, (Level) this, blockposition1, biomebase_precipitation); ++ iblockdata.getBlock().handlePrecipitation(iblockdata, (Level) this, blockposition, biomebase_precipitation); // Paper + } + } + +- gameprofilerfiller.popPush("tickBlocks"); ++ // Paper start - optimise random block ticking ++ gameprofilerfiller.popPush("randomTick"); + timings.chunkTicksBlocks.startTiming(); // Paper + if (randomTickSpeed > 0) { +- LevelChunkSection[] achunksection = chunk.getSections(); +- int l = achunksection.length; +- +- for (int i1 = 0; i1 < l; ++i1) { +- LevelChunkSection chunksection = achunksection[i1]; +- +- if (chunksection != LevelChunk.EMPTY_SECTION && chunksection.isRandomlyTicking()) { +- int j1 = chunksection.bottomBlockY(); +- +- for (int k1 = 0; k1 < randomTickSpeed; ++k1) { +- BlockPos blockposition2 = this.getBlockRandomPos(j, j1, k, 15); +- +- gameprofilerfiller.push("randomTick"); +- BlockState iblockdata1 = chunksection.getBlockState(blockposition2.getX() - j, blockposition2.getY() - j1, blockposition2.getZ() - k); ++ LevelChunkSection[] sections = chunk.getSections(); ++ int minSection = io.papermc.paper.util.WorldUtil.getMinSection(this); ++ for (int sectionIndex = 0; sectionIndex < sections.length; ++sectionIndex) { ++ LevelChunkSection section = sections[sectionIndex]; ++ if (section == null || section.tickingList.size() == 0) { ++ continue; ++ } + +- if (iblockdata1.isRandomlyTicking()) { +- iblockdata1.randomTick(this, blockposition2, this.random); +- } ++ int yPos = (sectionIndex + minSection) << 4; ++ for (int a = 0; a < randomTickSpeed; ++a) { ++ int tickingBlocks = section.tickingList.size(); ++ int index = this.randomTickRandom.nextInt(16 * 16 * 16); ++ if (index >= tickingBlocks) { ++ continue; ++ } + +- FluidState fluid = iblockdata1.getFluidState(); ++ long raw = section.tickingList.getRaw(index); ++ int location = com.destroystokyo.paper.util.maplist.IBlockDataList.getLocationFromRaw(raw); ++ int randomX = location & 15; ++ int randomY = ((location >>> (4 + 4)) & 255) | yPos; ++ int randomZ = (location >>> 4) & 15; + +- if (fluid.isRandomlyTicking()) { +- fluid.randomTick(this, blockposition2, this.random); +- } ++ BlockPos blockposition2 = blockposition.set(j + randomX, randomY, k + randomZ); ++ BlockState iblockdata = com.destroystokyo.paper.util.maplist.IBlockDataList.getBlockDataFromRaw(raw); + +- gameprofilerfiller.pop(); +- } ++ iblockdata.randomTick(this, blockposition2, this.randomTickRandom); ++ // We drop the fluid tick since LAVA is ALREADY TICKED by the above method (See LiquidBlock). ++ // TODO CHECK ON UPDATE + } + } + } +- ++ // Paper end - optimise random block ticking + timings.chunkTicksBlocks.stopTiming(); // Paper + gameprofilerfiller.pop(); + } +diff --git a/src/main/java/net/minecraft/util/BitStorage.java b/src/main/java/net/minecraft/util/BitStorage.java +index 07e1374ac3430662edd9f585e59b785e329f0820..9f9c0b56f0891e9c423d79f8ae4c3643a2b91048 100644 +--- a/src/main/java/net/minecraft/util/BitStorage.java ++++ b/src/main/java/net/minecraft/util/BitStorage.java +@@ -104,4 +104,32 @@ public class BitStorage { + } + + } ++ ++ // Paper start ++ public final void forEach(DataBitConsumer consumer) { ++ int i = 0; ++ long[] along = this.data; ++ int j = along.length; ++ ++ for (int k = 0; k < j; ++k) { ++ long l = along[k]; ++ ++ for (int i1 = 0; i1 < this.valuesPerLong; ++i1) { ++ consumer.accept(i, (int) (l & this.mask)); ++ l >>= this.bits; ++ ++i; ++ if (i >= this.size) { ++ return; ++ } ++ } ++ } ++ } ++ ++ @FunctionalInterface ++ public static interface DataBitConsumer { ++ ++ void accept(int location, int data); ++ ++ } ++ // Paper end + } +diff --git a/src/main/java/net/minecraft/world/entity/animal/Turtle.java b/src/main/java/net/minecraft/world/entity/animal/Turtle.java +index 925f16d5eb092518ef774f69a8d99689feb0f5d7..01d8af06f19427354cac95d691e65d31253fef94 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java ++++ b/src/main/java/net/minecraft/world/entity/animal/Turtle.java +@@ -91,7 +91,7 @@ public class Turtle extends Animal { + } + + public void setHomePos(BlockPos pos) { +- this.entityData.set(Turtle.HOME_POS, pos); ++ this.entityData.set(Turtle.HOME_POS, pos.immutable()); // Paper - called with mutablepos... + } + + public BlockPos getHomePos() { // Paper - public +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index 921aba3463b9f86a9e3c7d59aaa48b9760693eab..5efafcadda2bfc5cab8ce5d3ede83a0467fe696f 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -1363,10 +1363,18 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + public abstract TagContainer getTagManager(); + + public BlockPos getBlockRandomPos(int x, int y, int z, int l) { ++ // Paper start - allow use of mutable pos ++ BlockPos.MutableBlockPos ret = new BlockPos.MutableBlockPos(); ++ this.getRandomBlockPosition(x, y, z, l, ret); ++ return ret.immutable(); ++ } ++ public final BlockPos.MutableBlockPos getRandomBlockPosition(int x, int y, int z, int l, BlockPos.MutableBlockPos out) { ++ // Paper end + this.randValue = this.randValue * 3 + 1013904223; + int i1 = this.randValue >> 2; + +- return new BlockPos(x + (i1 & 15), y + (i1 >> 16 & l), z + (i1 >> 8 & 15)); ++ out.set(x + (i1 & 15), y + (i1 >> 16 & l), z + (i1 >> 8 & 15)); // Paper - change to setValues call ++ return out; // Paper + } + + public boolean noSave() { +diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java +index c1c95ac9deb134a0cf5c7763090ac5f3cddf24cc..72e3264dc74822f746fb84fec0be400047d2d9f5 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java ++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java +@@ -19,6 +19,7 @@ public class LevelChunkSection { + private short tickingBlockCount; + private short tickingFluidCount; + public final PalettedContainer states; // Paper - package-private // Paper - public ++ public final com.destroystokyo.paper.util.maplist.IBlockDataList tickingList = new com.destroystokyo.paper.util.maplist.IBlockDataList(); // Paper + + // Paper start - Anti-Xray - Add parameters + @Deprecated public LevelChunkSection(int yOffset) { this(yOffset, null, null, true); } // Notice for updates: Please make sure this constructor isn't used anywhere +@@ -79,6 +80,9 @@ public class LevelChunkSection { + --this.nonEmptyBlockCount; + if (blockState.isRandomlyTicking()) { + --this.tickingBlockCount; ++ // Paper start ++ this.tickingList.remove(x, y, z); ++ // Paper end + } + } + +@@ -90,6 +94,9 @@ public class LevelChunkSection { + ++this.nonEmptyBlockCount; + if (state.isRandomlyTicking()) { + ++this.tickingBlockCount; ++ // Paper start ++ this.tickingList.add(x, y, z, state); ++ // Paper end + } + } + +@@ -125,22 +132,28 @@ public class LevelChunkSection { + } + + public void recalcBlockCounts() { ++ // Paper start ++ this.tickingList.clear(); ++ // Paper end + this.nonEmptyBlockCount = 0; + this.tickingBlockCount = 0; + this.tickingFluidCount = 0; +- this.states.count((state, count) -> { ++ this.states.forEachLocation((state, location) -> { // Paper + FluidState fluidState = state.getFluidState(); + if (!state.isAir()) { +- this.nonEmptyBlockCount = (short)(this.nonEmptyBlockCount + count); ++ this.nonEmptyBlockCount = (short)(this.nonEmptyBlockCount + 1); // Paper + if (state.isRandomlyTicking()) { +- this.tickingBlockCount = (short)(this.tickingBlockCount + count); ++ // Paper start ++ this.tickingBlockCount = (short)(this.tickingBlockCount + 1); ++ this.tickingList.add(location, state); ++ // Paper end + } + } + + if (!fluidState.isEmpty()) { +- this.nonEmptyBlockCount = (short)(this.nonEmptyBlockCount + count); ++ this.nonEmptyBlockCount = (short)(this.nonEmptyBlockCount + 1); // Paper + if (fluidState.isRandomlyTicking()) { +- this.tickingFluidCount = (short)(this.tickingFluidCount + count); ++ this.tickingFluidCount = (short)(this.tickingFluidCount + 1); // Paper + } + } + +diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java +index 79fd7a6e8a6eb1f699d03801910d97066677311c..c9e942669458668a184aaec3bc0a5509dd6ab5f0 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java ++++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java +@@ -320,4 +320,12 @@ public class PalettedContainer implements PaletteResize { + public interface CountConsumer { + void accept(T object, int count); + } ++ ++ // Paper start ++ public void forEachLocation(PalettedContainer.CountConsumer datapaletteblock_a) { ++ this.storage.forEach((int location, int data) -> { ++ datapaletteblock_a.accept(this.palette.valueFor(data), location); ++ }); ++ } ++ // Paper end + } diff --git a/patches/server/0793-Optimise-non-flush-packet-sending.patch b/patches/server/0793-Optimise-non-flush-packet-sending.patch new file mode 100644 index 0000000000..096cdea845 --- /dev/null +++ b/patches/server/0793-Optimise-non-flush-packet-sending.patch @@ -0,0 +1,55 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Tue, 22 Sep 2020 01:49:19 -0700 +Subject: [PATCH] Optimise non-flush packet sending + +Places like entity tracking make heavy use of packet sending, +and internally netty will use some very expensive thread wakeup +calls when scheduling. + +Thanks to various hacks in ProtocolLib as well as other +plugins, we cannot simply use a queue of packets to group +send on execute. We have to call execute for each packet. + +Tux's suggestion here is exactly what was needed - tag +the Runnable indicating it should not make a wakeup call. + +Big thanks to Tux for making this possible as I had given +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 73457ae24ba6f605dd831a870499f1a990570e53..0dae504f49a4a3c66a01eb03896833a010cd5821 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; + import org.apache.logging.log4j.Marker; + import org.apache.logging.log4j.MarkerManager; + ++ ++import io.netty.util.concurrent.AbstractEventExecutor; // Paper + public class Connection extends SimpleChannelInboundHandler> { + + private static final float AVERAGE_PACKETS_SMOOTHING = 0.75F; +@@ -409,9 +411,19 @@ public class Connection extends SimpleChannelInboundHandler> { + if (this.channel.eventLoop().inEventLoop()) { + this.a(packet, callback, enumprotocol, enumprotocol1, flush); // Paper - add flush parameter + } else { ++ // Paper start - optimise packets that are not flushed ++ // note: since the type is not dynamic here, we need to actually copy the old executor code ++ // into two branches. On conflict, just re-copy - no changes were made inside the executor code. ++ if (!flush) { ++ AbstractEventExecutor.LazyRunnable run = () -> { ++ this.a(packet, callback, enumprotocol, enumprotocol1, flush); // Paper - add flush parameter ++ }; ++ this.channel.eventLoop().execute(run); ++ } else { // Paper end - optimise packets that are not flushed + this.channel.eventLoop().execute(() -> { +- this.a(packet, callback, enumprotocol, enumprotocol1, flush); // Paper - add flush parameter ++ this.a(packet, callback, enumprotocol, enumprotocol1, flush); // Paper - add flush parameter // Paper - diff on change + }); ++ } // Paper + } + + } diff --git a/patches/server/0794-Optimise-nearby-player-lookups.patch b/patches/server/0794-Optimise-nearby-player-lookups.patch new file mode 100644 index 0000000000..45b808ae11 --- /dev/null +++ b/patches/server/0794-Optimise-nearby-player-lookups.patch @@ -0,0 +1,430 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Thu, 27 Aug 2020 16:22:52 -0700 +Subject: [PATCH] Optimise nearby player lookups + +Use a distance map to map out close players. +Note that it's important that we cache the distance map value per chunk +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 54cb9f909df7d1041cec553e1f54ff8ecff48bbe..82b6b099e3014ab539f4155639efd33ebba88a3e 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); ++ // Paper start - optimise checkDespawn ++ LevelChunk chunk = this.getFullChunkUnchecked(); ++ if (chunk != null) { ++ chunk.updateGeneralAreaCache(); ++ } ++ // Paper end - optimise checkDespawn + } + // Paper end - optimise isOutsideOfRange + long lastAutoSaveTime; // Paper - incremental autosave +diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java +index b5015f0a59aeb77636e6b1dd4df7381660d0788c..bc315cc74cfded9f96e55f1d5de8a41334af8017 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -218,6 +218,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 ++ // 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); ++ public static final double GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE_SQUARED = GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE * GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE; ++ public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerGeneralAreaMap; ++ // Paper end - optimise checkDespawn + + void addPlayerToDistanceMaps(ServerPlayer player) { + int chunkX = MCUtil.getChunkCoordinate(player.getX()); +@@ -238,6 +244,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 ++ // Paper start - optimise checkDespawn ++ this.playerGeneralAreaMap.add(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS); ++ // Paper end - optimise checkDespawn + } + + void removePlayerFromDistanceMaps(ServerPlayer player) { +@@ -251,6 +260,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) { +@@ -269,6 +281,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 ++ // Paper start - optimise checkDespawn ++ this.playerGeneralAreaMap.update(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS); ++ // Paper end - optimise checkDespawn + } + // Paper end + // Paper start +@@ -427,6 +442,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + } + }); + // Paper end - optimise PlayerChunkMap#isOutsideRange ++ // 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, ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { ++ LevelChunk chunk = ChunkMap.this.level.getChunkSource().getChunkAtIfCachedImmediately(rangeX, rangeZ); ++ if (chunk != null) { ++ chunk.updateGeneralAreaCache(newState); ++ } ++ }, ++ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { ++ LevelChunk chunk = ChunkMap.this.level.getChunkSource().getChunkAtIfCachedImmediately(rangeX, rangeZ); ++ if (chunk != null) { ++ chunk.updateGeneralAreaCache(newState); ++ } ++ }); ++ // Paper end - optimise checkDespawn + } + + // Paper start - Chunk Prioritization +@@ -740,7 +772,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) { +@@ -755,6 +787,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 +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 3b26b6a24516bb2e2331edb7dc41a28b57835aa1..6e8115c3ab1f6986fdf3b3716cb09add91424306 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 { + } + } + // Paper end - rewrite ticklistserver ++ // Paper start - optimise checkDespawn ++ public final List playersAffectingSpawning = new java.util.ArrayList<>(); ++ // Paper end - optimise checkDespawn ++ // Paper start - optimise get nearest players for entity AI ++ @Override ++ public final ServerPlayer getNearestPlayer(net.minecraft.world.entity.ai.targeting.TargetingConditions condition, @Nullable LivingEntity source, ++ double centerX, double centerY, double centerZ) { ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet nearby; ++ nearby = this.getChunkSource().chunkMap.playerGeneralAreaMap.getObjectsInRange(Mth.floor(centerX) >> 4, Mth.floor(centerZ) >> 4); ++ ++ if (nearby == null) { ++ return null; ++ } ++ ++ Object[] backingSet = nearby.getBackingSet(); ++ ++ double closestDistanceSquared = Double.MAX_VALUE; ++ ServerPlayer closest = null; ++ ++ for (int i = 0, len = backingSet.length; i < len; ++i) { ++ Object _player = backingSet[i]; ++ if (!(_player instanceof ServerPlayer)) { ++ continue; ++ } ++ ServerPlayer player = (ServerPlayer)_player; ++ ++ double distanceSquared = player.distanceToSqr(centerX, centerY, centerZ); ++ if (distanceSquared < closestDistanceSquared && condition.test(source, player)) { ++ closest = player; ++ closestDistanceSquared = distanceSquared; ++ } ++ } ++ ++ return closest; ++ } ++ ++ @Override ++ public Player getNearestPlayer(net.minecraft.world.entity.ai.targeting.TargetingConditions pathfindertargetcondition, LivingEntity entityliving) { ++ return this.getNearestPlayer(pathfindertargetcondition, entityliving, entityliving.getX(), entityliving.getY(), entityliving.getZ()); ++ } ++ ++ @Override ++ public Player getNearestPlayer(net.minecraft.world.entity.ai.targeting.TargetingConditions pathfindertargetcondition, ++ double d0, double d1, double d2) { ++ return this.getNearestPlayer(pathfindertargetcondition, null, d0, d1, d2); ++ } ++ ++ @Override ++ public List getNearbyPlayers(net.minecraft.world.entity.ai.targeting.TargetingConditions condition, LivingEntity source, AABB axisalignedbb) { ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet nearby; ++ double centerX = (axisalignedbb.maxX + axisalignedbb.minX) * 0.5; ++ double centerZ = (axisalignedbb.maxZ + axisalignedbb.minZ) * 0.5; ++ nearby = this.getChunkSource().chunkMap.playerGeneralAreaMap.getObjectsInRange(Mth.floor(centerX) >> 4, Mth.floor(centerZ) >> 4); ++ ++ List ret = new java.util.ArrayList<>(); ++ ++ if (nearby == null) { ++ return ret; ++ } ++ ++ Object[] backingSet = nearby.getBackingSet(); ++ ++ for (int i = 0, len = backingSet.length; i < len; ++i) { ++ Object _player = backingSet[i]; ++ if (!(_player instanceof ServerPlayer)) { ++ continue; ++ } ++ ServerPlayer player = (ServerPlayer)_player; ++ ++ if (axisalignedbb.contains(player.getX(), player.getY(), player.getZ()) && condition.test(source, player)) { ++ ret.add(player); ++ } ++ } ++ ++ return ret; ++ } ++ // Paper end - optimise get nearest players for entity AI + + // 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 { + } + + public void tick(BooleanSupplier shouldKeepTicking) { ++ // Paper start - optimise checkDespawn ++ this.playersAffectingSpawning.clear(); ++ for (ServerPlayer player : this.players) { ++ if (net.minecraft.world.entity.EntitySelector.affectsSpawning.test(player)) { ++ this.playersAffectingSpawning.add(player); ++ } ++ } ++ // Paper end - optimise checkDespawn + ProfilerFiller gameprofilerfiller = this.getProfiler(); + + 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 bada11542390b7575466f0e7062470665b8266c4..8a864238e154e2131834d013652746b7e7a78c97 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 { + if (this.level.getDifficulty() == Difficulty.PEACEFUL && this.shouldDespawnInPeaceful()) { + this.discard(); + } else if (!this.isPersistenceRequired() && !this.requiresCustomPersistence()) { +- Player entityhuman = this.level.findNearbyPlayer(this, -1.0D, EntitySelector.affectsSpawning); // Paper ++ // Paper start - optimise checkDespawn ++ Player entityhuman = this.level.findNearbyPlayer(this, level.paperConfig.hardDespawnDistance + 1, EntitySelector.affectsSpawning); // Paper ++ if (entityhuman == null) { ++ entityhuman = ((ServerLevel)this.level).playersAffectingSpawning.isEmpty() ? null : ((ServerLevel)this.level).playersAffectingSpawning.get(0); ++ } ++ // Paper end - optimise checkDespawn + + if (entityhuman != null) { + double d0 = entityhuman.distanceToSqr((Entity) this); // CraftBukkit - decompile error +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index 5efafcadda2bfc5cab8ce5d3ede83a0467fe696f..b93056b91e7ebd49e6ddb53ccb6c05c056088df9 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -246,6 +246,69 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + return ret; + } + // Paper end ++ // Paper start - optimise checkDespawn ++ public final List getNearbyPlayers(@Nullable Entity source, double sourceX, double sourceY, ++ double sourceZ, double maxRange, @Nullable Predicate predicate) { ++ LevelChunk chunk; ++ if (maxRange < 0.0 || maxRange >= net.minecraft.server.level.ChunkMap.GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE || ++ (chunk = (LevelChunk)this.getChunkIfLoadedImmediately(Mth.floor(sourceX) >> 4, Mth.floor(sourceZ) >> 4)) == null) { ++ return this.getNearbyPlayersSlow(source, sourceX, sourceY, sourceZ, maxRange, predicate); ++ } ++ ++ List ret = new java.util.ArrayList<>(); ++ chunk.getNearestPlayers(sourceX, sourceY, sourceZ, predicate, maxRange, ret); ++ return ret; ++ } ++ ++ private List getNearbyPlayersSlow(@Nullable Entity source, double sourceX, double sourceY, ++ double sourceZ, double maxRange, @Nullable Predicate predicate) { ++ List ret = new java.util.ArrayList<>(); ++ double maxRangeSquared = maxRange * maxRange; ++ ++ for (net.minecraft.server.level.ServerPlayer player : (List)this.players()) { ++ if ((maxRange < 0.0 || player.distanceToSqr(sourceX, sourceY, sourceZ) < maxRangeSquared)) { ++ if (predicate == null || predicate.test(player)) { ++ ret.add(player); ++ } ++ } ++ } ++ ++ return ret; ++ } ++ ++ private net.minecraft.server.level.ServerPlayer getNearestPlayerSlow(@Nullable Entity source, double sourceX, double sourceY, ++ double sourceZ, double maxRange, @Nullable Predicate predicate) { ++ net.minecraft.server.level.ServerPlayer closest = null; ++ double closestRangeSquared = maxRange < 0.0 ? Double.MAX_VALUE : maxRange * maxRange; ++ ++ for (net.minecraft.server.level.ServerPlayer player : (List)this.players()) { ++ double distanceSquared = player.distanceToSqr(sourceX, sourceY, sourceZ); ++ if (distanceSquared < closestRangeSquared && (predicate == null || predicate.test(player))) { ++ closest = player; ++ closestRangeSquared = distanceSquared; ++ } ++ } ++ ++ return closest; ++ } ++ ++ ++ public final net.minecraft.server.level.ServerPlayer getNearestPlayer(@Nullable Entity source, double sourceX, double sourceY, ++ double sourceZ, double maxRange, @Nullable Predicate predicate) { ++ LevelChunk chunk; ++ if (maxRange < 0.0 || maxRange >= net.minecraft.server.level.ChunkMap.GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE || ++ (chunk = (LevelChunk)this.getChunkIfLoadedImmediately(Mth.floor(sourceX) >> 4, Mth.floor(sourceZ) >> 4)) == null) { ++ return this.getNearestPlayerSlow(source, sourceX, sourceY, sourceZ, maxRange, predicate); ++ } ++ ++ return chunk.findNearestPlayer(sourceX, sourceY, sourceZ, maxRange, predicate); ++ } ++ ++ @Override ++ public @Nullable Player getNearestPlayer(double d0, double d1, double d2, double d3, @Nullable Predicate predicate) { ++ return this.getNearestPlayer(null, d0, d1, d2, d3, predicate); ++ } ++ // 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 + 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 88145f04989c71a686aae1b486087ecdf55e268c..403f50ef908f65c62d4aaa7e5328faa0a7654480 100644 +--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java ++++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java +@@ -262,7 +262,7 @@ public final class NaturalSpawner { + blockposition_mutableblockposition.set(l, i, i1); + double d0 = (double) l + 0.5D; + double d1 = (double) i1 + 0.5D; +- Player entityhuman = world.getNearestPlayer(d0, (double) i, d1, -1.0D, false); ++ Player entityhuman = (chunk instanceof LevelChunk) ? ((LevelChunk)chunk).findNearestPlayer(d0, i, d1, 576.0D, net.minecraft.world.entity.EntitySelector.NO_SPECTATORS) : world.getNearestPlayer(d0, (double) i, d1, -1.0D, false); // Paper - use chunk's player cache to optimize search in range + + if (entityhuman != null) { + double d2 = entityhuman.distanceToSqr(d0, (double) i, d1); +@@ -335,7 +335,7 @@ public final class NaturalSpawner { + } + + private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel world, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double squaredDistance) { +- return squaredDistance <= 576.0D ? false : (world.getSharedSpawnPos().closerThan((Position) (new Vec3((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D)), 24.0D) ? false : Objects.equals(new ChunkPos(pos), chunk.getPos()) || world.isPositionEntityTicking((BlockPos) pos)); ++ return squaredDistance <= 576.0D ? false : (world.getSharedSpawnPos().closerThan((Position) (new Vec3((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D)), 24.0D) ? false : Objects.equals(new ChunkPos(pos), chunk.getPos()) || world.isPositionEntityTicking((BlockPos) pos)); // Paper - diff on change, copy into caller + } + + 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 +--- 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 { + } + } + // Paper end ++ // Paper start - optimise checkDespawn ++ private boolean playerGeneralAreaCacheSet; ++ private com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet playerGeneralAreaCache; ++ ++ public com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet getPlayerGeneralAreaCache() { ++ if (!this.playerGeneralAreaCacheSet) { ++ this.updateGeneralAreaCache(); ++ } ++ return this.playerGeneralAreaCache; ++ } ++ ++ public void updateGeneralAreaCache() { ++ this.updateGeneralAreaCache(((ServerLevel)this.level).getChunkSource().chunkMap.playerGeneralAreaMap.getObjectsInRange(this.coordinateKey)); ++ } ++ ++ public void updateGeneralAreaCache(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet value) { ++ this.playerGeneralAreaCacheSet = true; ++ this.playerGeneralAreaCache = value; ++ } ++ ++ public net.minecraft.server.level.ServerPlayer findNearestPlayer(double sourceX, double sourceY, double sourceZ, ++ double maxRange, java.util.function.Predicate predicate) { ++ if (!this.playerGeneralAreaCacheSet) { ++ this.updateGeneralAreaCache(); ++ } ++ ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet nearby = this.playerGeneralAreaCache; ++ ++ if (nearby == null) { ++ return null; ++ } ++ ++ Object[] backingSet = nearby.getBackingSet(); ++ double closestDistance = maxRange < 0.0 ? Double.MAX_VALUE : maxRange * maxRange; ++ net.minecraft.server.level.ServerPlayer closest = null; ++ for (int i = 0, len = backingSet.length; i < len; ++i) { ++ Object _player = backingSet[i]; ++ if (!(_player instanceof net.minecraft.server.level.ServerPlayer)) { ++ continue; ++ } ++ net.minecraft.server.level.ServerPlayer player = (net.minecraft.server.level.ServerPlayer)_player; ++ ++ double distance = player.distanceToSqr(sourceX, sourceY, sourceZ); ++ if (distance < closestDistance && predicate.test(player)) { ++ closest = player; ++ closestDistance = distance; ++ } ++ } ++ ++ return closest; ++ } ++ ++ public void getNearestPlayers(double sourceX, double sourceY, double sourceZ, java.util.function.Predicate predicate, ++ double range, java.util.List ret) { ++ if (!this.playerGeneralAreaCacheSet) { ++ this.updateGeneralAreaCache(); ++ } ++ ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet nearby = this.playerGeneralAreaCache; ++ ++ if (nearby == null) { ++ return; ++ } ++ ++ double rangeSquared = range * range; ++ ++ Object[] backingSet = nearby.getBackingSet(); ++ for (int i = 0, len = backingSet.length; i < len; ++i) { ++ Object _player = backingSet[i]; ++ if (!(_player instanceof net.minecraft.server.level.ServerPlayer)) { ++ continue; ++ } ++ net.minecraft.server.level.ServerPlayer player = (net.minecraft.server.level.ServerPlayer)_player; ++ ++ if (range >= 0.0) { ++ double distanceSquared = player.distanceToSqr(sourceX, sourceY, sourceZ); ++ if (distanceSquared > rangeSquared) { ++ continue; ++ } ++ } ++ ++ if (predicate == null || predicate.test(player)) { ++ ret.add(player); ++ } ++ } ++ } ++ // 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); diff --git a/patches/server/0795-Fix-Codec-log-spam.patch b/patches/server/0795-Fix-Codec-log-spam.patch new file mode 100644 index 0000000000..1c4d17c3b7 --- /dev/null +++ b/patches/server/0795-Fix-Codec-log-spam.patch @@ -0,0 +1,197 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sat, 19 Jun 2021 10:54:52 -0700 +Subject: [PATCH] Fix Codec log spam + +Mojang did NOT add dataconverters for world gen configurations +that they CHANGED. So, the codec fails to parse old data. + +This fixes two instances: +- IntProvider is new and Mojang did not account for old data. + Thankfully, only ColumnPlace needed to be special cased. +- TreeConfiguration had changes. Thankfully, they were + only renames for one value and thankfully defaults could + be provided for two new values (WITHOUT changing behavior). + +diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java +index 86d1bb22665bf46c7744ef653eda0caefc2a4337..a97295b420b55834687b76cfee1dc98c6d6acfd6 100644 +--- a/src/main/java/net/minecraft/server/MCUtil.java ++++ b/src/main/java/net/minecraft/server/MCUtil.java +@@ -718,4 +718,70 @@ public final class MCUtil { + public static int getTicketLevelFor(net.minecraft.world.level.chunk.ChunkStatus status) { + return net.minecraft.server.level.ChunkMap.MAX_VIEW_DISTANCE + net.minecraft.world.level.chunk.ChunkStatus.getDistance(status); + } ++ ++ public static com.mojang.serialization.MapCodec fieldWithFallbacks(com.mojang.serialization.Codec codec, String name, String ...fallback) { ++ return com.mojang.serialization.MapCodec.of( ++ new com.mojang.serialization.codecs.FieldEncoder<>(name, codec), ++ new FieldFallbackDecoder<>(name, java.util.Arrays.asList(fallback), codec), ++ () -> "FieldFallback[" + name + ": " + codec.toString() + "]" ++ ); ++ } ++ ++ // This is likely a common occurrence, sadly ++ public static final class FieldFallbackDecoder extends com.mojang.serialization.MapDecoder.Implementation { ++ protected final String name; ++ protected final List fallback; ++ private final com.mojang.serialization.Decoder elementCodec; ++ ++ public FieldFallbackDecoder(final String name, final List fallback, final com.mojang.serialization.Decoder elementCodec) { ++ this.name = name; ++ this.fallback = fallback; ++ this.elementCodec = elementCodec; ++ } ++ ++ @Override ++ public com.mojang.serialization.DataResult decode(final com.mojang.serialization.DynamicOps ops, final com.mojang.serialization.MapLike input) { ++ T value = input.get(name); ++ if (value == null) { ++ for (String fall : fallback) { ++ value = input.get(fall); ++ if (value != null) { ++ break; ++ } ++ } ++ if (value == null) { ++ return com.mojang.serialization.DataResult.error("No key " + name + " in " + input); ++ } ++ } ++ return elementCodec.parse(ops, value); ++ } ++ ++ @Override ++ public java.util.stream.Stream keys(final com.mojang.serialization.DynamicOps ops) { ++ return java.util.stream.Stream.of(ops.createString(name)); ++ } ++ ++ @Override ++ public boolean equals(final Object o) { ++ if (this == o) { ++ return true; ++ } ++ if (o == null || getClass() != o.getClass()) { ++ return false; ++ } ++ final FieldFallbackDecoder that = (FieldFallbackDecoder)o; ++ return java.util.Objects.equals(name, that.name) && java.util.Objects.equals(elementCodec, that.elementCodec) ++ && java.util.Objects.equals(fallback, that.fallback); ++ } ++ ++ @Override ++ public int hashCode() { ++ return java.util.Objects.hash(name, fallback, elementCodec); ++ } ++ ++ @Override ++ public String toString() { ++ return "FieldDecoder[" + name + ": " + elementCodec + ']'; ++ } ++ } + } +diff --git a/src/main/java/net/minecraft/util/valueproviders/IntProvider.java b/src/main/java/net/minecraft/util/valueproviders/IntProvider.java +index 020a19cd683dd3779c5116d12b3cdcd3b3ca69b4..c81a0eec12436c10869bfdcc21af09173f438331 100644 +--- a/src/main/java/net/minecraft/util/valueproviders/IntProvider.java ++++ b/src/main/java/net/minecraft/util/valueproviders/IntProvider.java +@@ -9,13 +9,44 @@ import net.minecraft.core.Registry; + + public abstract class IntProvider { + private static final Codec> CONSTANT_OR_DISPATCH_CODEC = Codec.either(Codec.INT, Registry.INT_PROVIDER_TYPES.dispatch(IntProvider::getType, IntProviderType::codec)); +- public static final Codec CODEC = CONSTANT_OR_DISPATCH_CODEC.xmap((either) -> { ++ public static final Codec CODEC_REAL = CONSTANT_OR_DISPATCH_CODEC.xmap((either) -> { // Paper - used by CODEC below + return either.map(ConstantInt::of, (intProvider) -> { + return intProvider; + }); + }, (intProvider) -> { + return intProvider.getType() == IntProviderType.CONSTANT ? Either.left(((ConstantInt)intProvider).getValue()) : Either.right(intProvider); + }); ++ // Paper start ++ public static final Codec CODEC = new Codec<>() { ++ @Override ++ public DataResult> decode(com.mojang.serialization.DynamicOps ops, T input) { ++ /* ++ UniformInt: ++ count -> { (old format) ++ base, spread ++ } -> {UniformInt} { (new format & type) ++ base, base + spread ++ } */ ++ ++ ++ if (ops.get(input, "base").result().isPresent() && ops.get(input, "spread").result().isPresent()) { ++ // detected old format ++ int base = ops.getNumberValue(ops.get(input, "base").result().get()).result().get().intValue(); ++ int spread = ops.getNumberValue(ops.get(input, "spread").result().get()).result().get().intValue(); ++ return DataResult.success(new com.mojang.datafixers.util.Pair<>(UniformInt.of(base, base + spread), input)); ++ } ++ ++ // not old format, forward to real codec ++ return CODEC_REAL.decode(ops, input); ++ } ++ ++ @Override ++ public DataResult encode(IntProvider input, com.mojang.serialization.DynamicOps ops, T prefix) { ++ // forward to real codec ++ return CODEC_REAL.encode(input, ops, prefix); ++ } ++ }; ++ // Paper end + public static final Codec NON_NEGATIVE_CODEC = codec(0, Integer.MAX_VALUE); + public static final Codec POSITIVE_CODEC = codec(1, Integer.MAX_VALUE); + +diff --git a/src/main/java/net/minecraft/world/level/levelgen/feature/blockplacers/ColumnPlacer.java b/src/main/java/net/minecraft/world/level/levelgen/feature/blockplacers/ColumnPlacer.java +index 05bba5410fbd9f8e333584ccbd65a909f3040322..82859e1b048ba8a96cc67e085e9ed01b6bd3a6cd 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/feature/blockplacers/ColumnPlacer.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/feature/blockplacers/ColumnPlacer.java +@@ -10,11 +10,28 @@ import net.minecraft.world.level.LevelAccessor; + import net.minecraft.world.level.block.state.BlockState; + + public class ColumnPlacer extends BlockPlacer { ++ // Paper start + public static final Codec CODEC = RecordCodecBuilder.create((instance) -> { +- return instance.group(IntProvider.NON_NEGATIVE_CODEC.fieldOf("size").forGetter((columnPlacer) -> { +- return columnPlacer.size; +- })).apply(instance, ColumnPlacer::new); ++ return instance.group( ++ IntProvider.NON_NEGATIVE_CODEC.optionalFieldOf("size").forGetter((columnPlacer) -> { ++ return java.util.Optional.of(columnPlacer.size); ++ }), ++ Codec.INT.optionalFieldOf("min_size").forGetter((columnPlacer) -> { ++ return java.util.Optional.empty(); ++ }), ++ Codec.INT.optionalFieldOf("extra_size").forGetter((columnPlacer) -> { ++ return java.util.Optional.empty(); ++ }) ++ ).apply(instance, ColumnPlacer::new); + }); ++ public ColumnPlacer(java.util.Optional size, java.util.Optional minSize, java.util.Optional extraSize) { ++ if (size.isPresent()) { ++ this.size = size.get(); ++ } else { ++ this.size = net.minecraft.util.valueproviders.BiasedToBottomInt.of(minSize.get().intValue(), minSize.get().intValue() + extraSize.get().intValue()); ++ } ++ } ++ // Paper end + private final IntProvider size; + + public ColumnPlacer(IntProvider size) { +diff --git a/src/main/java/net/minecraft/world/level/levelgen/feature/configurations/TreeConfiguration.java b/src/main/java/net/minecraft/world/level/levelgen/feature/configurations/TreeConfiguration.java +index 5da68897148192905c2747676c1ee2ee649f923f..b990099cf274f8cb0d96c139345cf0bf328affd6 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/feature/configurations/TreeConfiguration.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/feature/configurations/TreeConfiguration.java +@@ -18,13 +18,13 @@ public class TreeConfiguration implements FeatureConfiguration { + return treeConfiguration.trunkProvider; + }), TrunkPlacer.CODEC.fieldOf("trunk_placer").forGetter((treeConfiguration) -> { + return treeConfiguration.trunkPlacer; +- }), BlockStateProvider.CODEC.fieldOf("foliage_provider").forGetter((treeConfiguration) -> { ++ }), net.minecraft.server.MCUtil.fieldWithFallbacks(BlockStateProvider.CODEC, "foliage_provider", "leaves_provider").forGetter((treeConfiguration) -> { // Paper - provide fallback for rename + return treeConfiguration.foliageProvider; +- }), BlockStateProvider.CODEC.fieldOf("sapling_provider").forGetter((treeConfiguration) -> { ++ }), BlockStateProvider.CODEC.optionalFieldOf("sapling_provider", new SimpleStateProvider(Blocks.OAK_SAPLING.defaultBlockState())).forGetter((treeConfiguration) -> { // Paper - provide default - it looks like for now this is OK because it's just used to check canSurvive. Same check happens in 1.16.5 for the default we provide - so it should retain behavior... + return treeConfiguration.saplingProvider; + }), FoliagePlacer.CODEC.fieldOf("foliage_placer").forGetter((treeConfiguration) -> { + return treeConfiguration.foliagePlacer; +- }), BlockStateProvider.CODEC.fieldOf("dirt_provider").forGetter((treeConfiguration) -> { ++ }), BlockStateProvider.CODEC.optionalFieldOf("dirt_provider", new SimpleStateProvider(Blocks.DIRT.defaultBlockState())).forGetter((treeConfiguration) -> { // Paper - provide defaults, old data DOES NOT have this key (thankfully ALL OLD DATA used DIRT) + return treeConfiguration.dirtProvider; + }), FeatureSize.CODEC.fieldOf("minimum_size").forGetter((treeConfiguration) -> { + return treeConfiguration.minimumSize; diff --git a/patches/server/0796-Optimise-WorldServer-notify.patch b/patches/server/0796-Optimise-WorldServer-notify.patch new file mode 100644 index 0000000000..1d0b79025e --- /dev/null +++ b/patches/server/0796-Optimise-WorldServer-notify.patch @@ -0,0 +1,325 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Thu, 9 Jul 2020 13:34:59 -0700 +Subject: [PATCH] Optimise WorldServer#notify + +Iterating over all of the navigators in the world is pretty expensive. +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 bc315cc74cfded9f96e55f1d5de8a41334af8017..b556c1ef5d9f4667fa755ae843f4f50719c5013c 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -291,15 +291,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 { ++ // Paper start - optimise notify() ++ private io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet navigators; ++ ++ public io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet getNavigators() { ++ return this.navigators; ++ } ++ ++ public boolean addToNavigators(final Mob navigator) { ++ if (this.navigators == null) { ++ this.navigators = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(); ++ } ++ return this.navigators.add(navigator); ++ } ++ ++ public boolean removeFromNavigators(final Mob navigator) { ++ if (this.navigators == null) { ++ return false; ++ } ++ return this.navigators.remove(navigator); ++ } ++ // Paper end - optimise notify() + } + + public static final class DataRegionSectionData implements io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSectionData { + ++ // Paper start - optimise notify() ++ private io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet navigators; ++ ++ public io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet getNavigators() { ++ return this.navigators; ++ } ++ ++ public boolean addToNavigators(final io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section, final Mob navigator) { ++ if (this.navigators == null) { ++ this.navigators = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(); ++ } ++ final boolean ret = this.navigators.add(navigator); ++ if (ret) { ++ final DataRegionData data = (DataRegionData)section.getRegion().regionData; ++ if (!data.addToNavigators(navigator)) { ++ throw new IllegalStateException(); ++ } ++ } ++ return ret; ++ } ++ ++ public boolean removeFromNavigators(final io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section, final Mob navigator) { ++ if (this.navigators == null) { ++ return false; ++ } ++ final boolean ret = this.navigators.remove(navigator); ++ if (ret) { ++ final DataRegionData data = (DataRegionData)section.getRegion().regionData; ++ if (!data.removeFromNavigators(navigator)) { ++ throw new IllegalStateException(); ++ } ++ } ++ return ret; ++ } ++ // Paper end - optimise notify() ++ + @Override + public void removeFromRegion(final io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section, + final io.papermc.paper.chunk.SingleThreadChunkRegionManager.Region from) { + final DataRegionSectionData sectionData = (DataRegionSectionData)section.sectionData; + final DataRegionData fromData = (DataRegionData)from.regionData; ++ // Paper start - optimise notify() ++ if (sectionData.navigators != null) { ++ for (final Iterator iterator = sectionData.navigators.unsafeIterator(io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); iterator.hasNext();) { ++ if (!fromData.removeFromNavigators(iterator.next())) { ++ throw new IllegalStateException(); ++ } ++ } ++ } ++ // Paper end - optimise notify() + } + + @Override +@@ -309,6 +375,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; ++ // Paper start - optimise notify() ++ if (sectionData.navigators != null) { ++ for (final Iterator iterator = sectionData.navigators.unsafeIterator(io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); iterator.hasNext();) { ++ if (!newRegionData.addToNavigators(iterator.next())) { ++ throw new IllegalStateException(); ++ } ++ } ++ } ++ // Paper end - optimise notify() + } + } + +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 6e8115c3ab1f6986fdf3b3716cb09add91424306..cebaf0fb8187335ca303621a2cb412bb22584e23 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 { + public void tickNonPassenger(Entity entity) { + // Paper start - log detailed entity tick information + io.papermc.paper.util.TickThread.ensureTickThread("Cannot tick an entity off-main"); ++ this.entityManager.updateNavigatorsInRegion(entity); // Paper - optimise notify + try { + if (currentlyTickingEntity.get() == null) { + currentlyTickingEntity.lazySet(entity); +@@ -1524,9 +1525,19 @@ public class ServerLevel extends Level implements WorldGenLevel { + VoxelShape voxelshape1 = newState.getCollisionShape(this, pos); + + if (Shapes.joinIsNotEmpty(voxelshape, voxelshape1, BooleanOp.NOT_SAME)) { +- Iterator iterator = this.navigatingMobs.iterator(); ++ // Paper start - optimise notify() ++ io.papermc.paper.chunk.SingleThreadChunkRegionManager.Region region = this.getChunkSource().chunkMap.dataRegionManager.getRegion(pos.getX() >> 4, pos.getZ() >> 4); ++ if (region == null) { ++ return; ++ } ++ io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet navigatorsFromRegion = ((ChunkMap.DataRegionData)region.regionData).getNavigators(); ++ if (navigatorsFromRegion == null) { ++ return; ++ } ++ io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator iterator = navigatorsFromRegion.iterator(); + +- while (iterator.hasNext()) { ++ ++ try { while (iterator.hasNext()) { // Paper end - optimise notify() + // CraftBukkit start - fix SPIGOT-6362 + Mob entityinsentient; + try { +@@ -1545,6 +1556,11 @@ public class ServerLevel extends Level implements WorldGenLevel { + navigationabstract.recomputePath(pos); + } + } ++ // Paper start - optimise notify() ++ } finally { ++ iterator.finishedIterating(); ++ } ++ // Paper end - optimise notify() + + } + } // Paper +@@ -2326,10 +2342,12 @@ public class ServerLevel extends Level implements WorldGenLevel { + + public void onTickingStart(Entity entity) { + ServerLevel.this.entityTickList.add(entity); ++ ServerLevel.this.entityManager.addNavigatorsIfPathingToRegion(entity); // Paper - optimise notify + } + + public void onTickingEnd(Entity entity) { + ServerLevel.this.entityTickList.remove(entity); ++ ServerLevel.this.entityManager.removeNavigatorsFromData(entity); // Paper - optimise notify + } + + 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 e605daac0c90f5d0b9315d1499938feb0e478d0e..b5c385aeb86ac267cae9726354c896ee38a50634 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; + + public abstract class PathNavigation { + private static final int MAX_TIME_RECOMPUTE = 20; +- protected final Mob mob; ++ protected final Mob mob; public final Mob getEntity() { return this.mob; } // Paper - public accessor + protected final Level level; + @Nullable + protected Path path; +@@ -40,7 +40,7 @@ public abstract class PathNavigation { + protected long lastTimeoutCheck; + protected double timeoutLimit; + protected float maxDistanceToWaypoint = 0.5F; +- protected boolean hasDelayedRecomputation; ++ 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 { + public final PathFinder pathFinder; + private boolean isStuck; + ++ // Paper start ++ public boolean isViableForPathRecalculationChecking() { ++ return !this.needsPathRecalculation() && ++ (this.path != null && !this.path.isDone() && this.path.getNodeCount() != 0); ++ } ++ // Paper end ++ + public PathNavigation(Mob mob, Level world) { + this.mob = mob; + this.level = world; +@@ -404,7 +411,7 @@ public abstract class PathNavigation { + } + + public void recomputePath(BlockPos pos) { +- if (this.path != null && !this.path.isDone() && this.path.getNodeCount() != 0) { ++ if (this.path != null && !this.path.isDone() && this.path.getNodeCount() != 0) { // Paper - diff on change - needed for isViableForPathRecalculationChecking() + Node node = this.path.getEndNode(); + 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 a0dd49d17e1a83edbb0bf5cfbaba3f8fb665f75a..90427b7ee0ad69afa498ce6accd369d28a55427d 100644 +--- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java ++++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java +@@ -57,6 +57,65 @@ public class PersistentEntitySectionManager implements A + this.entityGetter = new LevelEntityGetterAdapter<>(this.visibleEntityStorage, this.sectionStorage); + } + ++ // Paper start - optimise notify() ++ public final void removeNavigatorsFromData(Entity entity, final int chunkX, final int chunkZ) { ++ if (!(entity instanceof net.minecraft.world.entity.Mob)) { ++ return; ++ } ++ io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section = ++ this.entitySliceManager.world.getChunkSource().chunkMap.dataRegionManager.getRegionSection(chunkX, chunkZ); ++ if (section != null) { ++ net.minecraft.server.level.ChunkMap.DataRegionSectionData sectionData = (net.minecraft.server.level.ChunkMap.DataRegionSectionData)section.sectionData; ++ sectionData.removeFromNavigators(section, ((net.minecraft.world.entity.Mob)entity)); ++ } ++ } ++ ++ public final void removeNavigatorsFromData(Entity entity) { ++ if (!(entity instanceof net.minecraft.world.entity.Mob)) { ++ return; ++ } ++ BlockPos entityPos = entity.blockPosition(); ++ io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section = ++ this.entitySliceManager.world.getChunkSource().chunkMap.dataRegionManager.getRegionSection(entityPos.getX() >> 4, entityPos.getZ() >> 4); ++ if (section != null) { ++ net.minecraft.server.level.ChunkMap.DataRegionSectionData sectionData = (net.minecraft.server.level.ChunkMap.DataRegionSectionData)section.sectionData; ++ sectionData.removeFromNavigators(section, ((net.minecraft.world.entity.Mob)entity)); ++ } ++ } ++ ++ public final void addNavigatorsIfPathingToRegion(Entity entity) { ++ if (!(entity instanceof net.minecraft.world.entity.Mob)) { ++ return; ++ } ++ BlockPos entityPos = entity.blockPosition(); ++ io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section = ++ this.entitySliceManager.world.getChunkSource().chunkMap.dataRegionManager.getRegionSection(entityPos.getX() >> 4, entityPos.getZ() >> 4); ++ if (section != null) { ++ net.minecraft.server.level.ChunkMap.DataRegionSectionData sectionData = (net.minecraft.server.level.ChunkMap.DataRegionSectionData)section.sectionData; ++ if (((net.minecraft.world.entity.Mob)entity).getNavigation().isViableForPathRecalculationChecking()) { ++ sectionData.addToNavigators(section, ((net.minecraft.world.entity.Mob)entity)); ++ } ++ } ++ } ++ ++ public final void updateNavigatorsInRegion(Entity entity) { ++ if (!(entity instanceof net.minecraft.world.entity.Mob)) { ++ return; ++ } ++ BlockPos entityPos = entity.blockPosition(); ++ io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section = ++ this.entitySliceManager.world.getChunkSource().chunkMap.dataRegionManager.getRegionSection(entityPos.getX() >> 4, entityPos.getZ() >> 4); ++ if (section != null) { ++ net.minecraft.server.level.ChunkMap.DataRegionSectionData sectionData = (net.minecraft.server.level.ChunkMap.DataRegionSectionData)section.sectionData; ++ if (((net.minecraft.world.entity.Mob)entity).getNavigation().isViableForPathRecalculationChecking()) { ++ sectionData.addToNavigators(section, ((net.minecraft.world.entity.Mob)entity)); ++ } else { ++ sectionData.removeFromNavigators(section, ((net.minecraft.world.entity.Mob)entity)); ++ } ++ } ++ } ++ // Paper end - optimise notify() ++ + void removeSectionIfEmpty(long sectionPos, EntitySection section) { + if (section.isEmpty()) { + this.sectionStorage.remove(sectionPos); +@@ -435,11 +494,25 @@ public class PersistentEntitySectionManager implements A + @Override + public void onMove() { + BlockPos blockposition = this.entity.blockPosition(); +- long i = SectionPos.asLong(blockposition); ++ long i = SectionPos.asLong(blockposition); final long newSectionPos = i; // Paper - diff on change, new position section + + if (i != this.currentSectionKey) { + PersistentEntitySectionManager.this.entitySliceManager.moveEntity((Entity)this.entity); // Paper +- Visibility visibility = this.currentSection.getStatus(); ++ Visibility visibility = this.currentSection.getStatus(); final Visibility oldVisibility = visibility; // Paper - diff on change - this should be OLD section visibility ++ // Paper start ++ int shift = PersistentEntitySectionManager.this.entitySliceManager.world.getChunkSource().chunkMap.dataRegionManager.regionChunkShift; ++ int oldChunkX = io.papermc.paper.util.CoordinateUtils.getChunkSectionX(this.currentSectionKey); ++ int oldChunkZ = io.papermc.paper.util.CoordinateUtils.getChunkSectionZ(this.currentSectionKey); ++ int oldRegionX = oldChunkX >> shift; ++ int oldRegionZ = oldChunkZ >> shift; ++ ++ int newRegionX = io.papermc.paper.util.CoordinateUtils.getChunkSectionX(newSectionPos) >> shift; ++ int newRegionZ = io.papermc.paper.util.CoordinateUtils.getChunkSectionZ(newSectionPos) >> shift; ++ ++ if (oldRegionX != newRegionX || oldRegionZ != newRegionZ) { ++ PersistentEntitySectionManager.this.removeNavigatorsFromData((Entity)this.entity, oldChunkX, oldChunkZ); ++ } ++ // Paper end + + if (!this.currentSection.remove(this.entity)) { + PersistentEntitySectionManager.LOGGER.warn("Entity {} wasn't found in section {} (moving to {})", this.entity, SectionPos.of(this.currentSectionKey), i); +@@ -451,6 +524,11 @@ public class PersistentEntitySectionManager implements A + entitysection.add(this.entity); // CraftBukkit - decompile error + this.currentSection = entitysection; + this.currentSectionKey = i; ++ // Paper start ++ if ((oldRegionX != newRegionX || oldRegionZ != newRegionZ) && oldVisibility.isTicking() && entitysection.getStatus().isTicking()) { ++ PersistentEntitySectionManager.this.addNavigatorsIfPathingToRegion((Entity)this.entity); ++ } ++ // Paper end + this.updateStatus(visibility, entitysection.getStatus()); + } + diff --git a/patches/server/0797-Remove-streams-for-villager-AI.patch b/patches/server/0797-Remove-streams-for-villager-AI.patch new file mode 100644 index 0000000000..227d34f188 --- /dev/null +++ b/patches/server/0797-Remove-streams-for-villager-AI.patch @@ -0,0 +1,327 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Thu, 27 Aug 2020 20:51:40 -0700 +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 +--- 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); +- }); ++ // Paper start - remove streams ++ List>> entries = this.behaviors.entries; ++ for (int i = 0; i < entries.size(); i++) { ++ ShufflingList.WeightedEntry> entry = entries.get(i); ++ Behavior behavior = entry.getData(); ++ if (behavior.getStatus() == Status.RUNNING) { ++ if (behavior.canStillUse(world, entity, time)) { ++ return true; ++ } ++ } ++ } ++ return false; ++ // Paper end - remove streams + } + + @Override +@@ -45,25 +53,35 @@ public class GateBehavior extends Behavior { + @Override + protected void start(ServerLevel world, E entity, long time) { + this.orderPolicy.apply(this.behaviors); +- this.runningPolicy.apply(this.behaviors.stream(), world, entity, time); ++ this.runningPolicy.apply(this.behaviors.entries, world, entity, time); // Paper - remove streams + } + + @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); +- }); ++ // Paper start - remove streams ++ List>> entries = this.behaviors.entries; ++ for (int i = 0; i < entries.size(); i++) { ++ ShufflingList.WeightedEntry> entry = entries.get(i); ++ Behavior behavior = entry.getData(); ++ if (behavior.getStatus() == Status.RUNNING) { ++ behavior.tickOrStop(world, entity, time); ++ } ++ } ++ // Paper end - remove streams + } + + @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); +- }); ++ // Paper start - remove streams ++ List>> entries = this.behaviors.entries; ++ for (int i = 0; i < entries.size(); i++) { ++ ShufflingList.WeightedEntry> entry = entries.get(i); ++ Behavior behavior = entry.getData(); ++ if (behavior.getStatus() == Status.RUNNING) { ++ behavior.doStop(world, entity, time); ++ } ++ } ++ // Paper end - remove streams + this.exitErasedMemories.forEach(entity.getBrain()::eraseMemory); + } + +@@ -94,25 +112,33 @@ public class GateBehavior extends Behavior { + public static enum RunningPolicy { + 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); +- }).findFirst(); ++ // Paper start - remove streams ++ public void apply(List>> tasks, ServerLevel world, E entity, long time) { ++ for (int i = 0; i < tasks.size(); i++) { ++ ShufflingList.WeightedEntry> task = tasks.get(i); ++ Behavior behavior = task.getData(); ++ if (behavior.getStatus() == Status.STOPPED && behavior.tryStart(world, entity, time)) { ++ break; ++ } ++ } ++ // Paper end - remove streams + } + }, + 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); +- }); ++ // Paper start - remove streams ++ public void apply(List>> tasks, ServerLevel world, E entity, long time) { ++ for (int i = 0; i < tasks.size(); i++) { ++ ShufflingList.WeightedEntry> task = tasks.get(i); ++ Behavior behavior = task.getData(); ++ if (behavior.getStatus() == Status.STOPPED) { ++ behavior.tryStart(world, entity, time); ++ } ++ } ++ // Paper end - remove streams + } + }; + +- public abstract void apply(Stream> tasks, ServerLevel world, E entity, long time); ++ 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 +--- 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; + import java.util.stream.Stream; + + public class ShufflingList { +- protected final List> entries; ++ public final List> entries; // Paper - public + private final Random random = new Random(); + private final boolean isUnsafe; // Paper + +diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java +index 49f3b25d28072b61f5cc97260df61df892a58714..71f2692c83feafbb31f45427e6c738cb3881c82c 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java ++++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java +@@ -25,17 +25,20 @@ public class NearestItemSensor extends Sensor { + protected void doTick(ServerLevel world, Mob entity) { + Brain brain = entity.getBrain(); + List list = world.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(8.0D, 4.0D, 8.0D), (itemEntity) -> { +- return true; ++ return itemEntity.closerThan(entity, 9.0D) && entity.wantsToPickUp(itemEntity.getItem()); // Paper - move predicate into getEntities + }); +- list.sort(Comparator.comparingDouble(entity::distanceToSqr)); ++ list.sort((e1, e2) -> Double.compare(entity.distanceToSqr(e1), entity.distanceToSqr(e2))); // better to take the sort perf hit than using line of sight more than we need to. ++ // Paper start - remove streams + // Paper start - remove streams in favour of lists + ItemEntity nearest = null; +- for (ItemEntity entityItem : list) { +- if (entity.wantsToPickUp(entityItem.getItem()) && entityItem.closerThan(entity, 9.0D) && entity.hasLineOfSight(entityItem)) { ++ for (int i = 0; i < list.size(); i++) { ++ ItemEntity entityItem = list.get(i); ++ if (entity.hasLineOfSight(entityItem)) { + nearest = entityItem; + break; + } + } ++ // Paper end - remove streams + 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 +--- 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 { + + @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 ++ // 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))); + Brain brain = entity.getBrain(); + + brain.setMemory(MemoryModuleType.NEAREST_PLAYERS, players); + +- Player nearest = null, nearestTargetable = null; +- for (Player player : players) { +- if (Sensor.isEntityTargetable(entity, player)) { +- if (nearest == null) nearest = player; +- if (Sensor.isEntityAttackable(entity, player)) { +- nearestTargetable = player; +- break; // Both variables are assigned, no reason to loop further +- } ++ Player firstTargetable = null; ++ Player firstAttackable = null; ++ for (int index = 0, len = players.size(); index < len; ++index) { ++ Player player = players.get(index); ++ if (firstTargetable == null && isEntityTargetable(entity, player)) { ++ firstTargetable = player; ++ } ++ if (firstAttackable == null && isEntityAttackable(entity, player)) { ++ firstAttackable = player; ++ } ++ ++ if (firstAttackable != null && firstTargetable != null) { ++ break; + } + } +- 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/server/0798-Rewrite-dataconverter-system.patch b/patches/server/0798-Rewrite-dataconverter-system.patch new file mode 100644 index 0000000000..3c4a01b0d4 --- /dev/null +++ b/patches/server/0798-Rewrite-dataconverter-system.patch @@ -0,0 +1,19646 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sat, 19 Jun 2021 10:43:01 -0700 +Subject: [PATCH] Rewrite dataconverter system + +Please see https://github.com/PaperMC/DataConverter +for details. + +diff --git a/src/main/java/ca/spottedleaf/dataconverter/converters/DataConverter.java b/src/main/java/ca/spottedleaf/dataconverter/converters/DataConverter.java +new file mode 100644 +index 0000000000000000000000000000000000000000..1863c606be715683d53863a0c9293525d199c9cf +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/converters/DataConverter.java +@@ -0,0 +1,54 @@ ++package ca.spottedleaf.dataconverter.converters; ++ ++import java.util.Comparator; ++ ++public abstract class DataConverter { ++ ++ public static final Comparator> LOWEST_VERSION_COMPARATOR = (x, y) -> { ++ return Long.compare(x.getEncodedVersion(), y.getEncodedVersion()); ++ }; ++ ++ protected final int toVersion; ++ protected final int versionStep; ++ ++ public DataConverter(final int toVersion) { ++ this.toVersion = toVersion; ++ this.versionStep = 0; ++ } ++ ++ public DataConverter(final int toVersion, final int versionStep) { ++ this.toVersion = toVersion; ++ this.versionStep = versionStep; ++ } ++ ++ public final int getToVersion() { ++ return this.toVersion; ++ } ++ ++ public final int getVersionStep() { ++ return this.versionStep; ++ } ++ ++ public final long getEncodedVersion() { ++ return encodeVersions(this.toVersion, this.versionStep); ++ } ++ ++ public abstract R convert(final T data, final long sourceVersion, final long toVersion); ++ ++ // step must be in the lower bits, so that encodeVersions(version, step) < encodeVersions(version, step + 1) ++ public static long encodeVersions(final int version, final int step) { ++ return ((long)version << 32) | (step & 0xFFFFFFFFL); ++ } ++ ++ public static int getVersion(final long encoded) { ++ return (int)(encoded >>> 32); ++ } ++ ++ public static int getStep(final long encoded) { ++ return (int)encoded; ++ } ++ ++ public static String encodedToString(final long encoded) { ++ return getVersion(encoded) + "." + getStep(encoded); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/converters/datatypes/DataHook.java b/src/main/java/ca/spottedleaf/dataconverter/converters/datatypes/DataHook.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0b92c5c66ad3a5198873f98287a5ced71c231d09 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/converters/datatypes/DataHook.java +@@ -0,0 +1,9 @@ ++package ca.spottedleaf.dataconverter.converters.datatypes; ++ ++public interface DataHook { ++ ++ public R preHook(final T data, final long fromVersion, final long toVersion); ++ ++ public R postHook(final T data, final long fromVersion, final long toVersion); ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/converters/datatypes/DataType.java b/src/main/java/ca/spottedleaf/dataconverter/converters/datatypes/DataType.java +new file mode 100644 +index 0000000000000000000000000000000000000000..b56a7f9ace3b947fed49101b6e9936721fb99ea5 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/converters/datatypes/DataType.java +@@ -0,0 +1,7 @@ ++package ca.spottedleaf.dataconverter.converters.datatypes; ++ ++public abstract class DataType { ++ ++ public abstract R convert(final T data, final long fromVersion, final long toVersion); ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/converters/datatypes/DataWalker.java b/src/main/java/ca/spottedleaf/dataconverter/converters/datatypes/DataWalker.java +new file mode 100644 +index 0000000000000000000000000000000000000000..cf9fae4451ead4860343b915fb70e3a7cdf0de31 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/converters/datatypes/DataWalker.java +@@ -0,0 +1,9 @@ ++package ca.spottedleaf.dataconverter.converters.datatypes; ++ ++import ca.spottedleaf.dataconverter.types.MapType; ++ ++public interface DataWalker { ++ ++ public MapType walk(final MapType data, final long fromVersion, final long toVersion); ++ ++} +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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/MCDataConverter.java +@@ -0,0 +1,69 @@ ++package ca.spottedleaf.dataconverter.minecraft; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.converters.datatypes.DataType; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCDataType; ++import ca.spottedleaf.dataconverter.types.json.JsonMapType; ++import ca.spottedleaf.dataconverter.types.nbt.NBTMapType; ++import com.google.gson.JsonObject; ++import it.unimi.dsi.fastutil.longs.LongArrayList; ++import net.minecraft.nbt.CompoundTag; ++ ++public final class MCDataConverter { ++ ++ private static final LongArrayList BREAKPOINTS = MCVersionRegistry.getBreakpoints(); ++ ++ public static CompoundTag convertTag(final MCDataType type, final CompoundTag data, final int fromVersion, final int toVersion) { ++ final NBTMapType wrapped = new NBTMapType(data); ++ ++ final NBTMapType replaced = (NBTMapType)convert(type, wrapped, fromVersion, toVersion); ++ ++ return replaced == null ? wrapped.getTag() : replaced.getTag(); ++ } ++ ++ public static JsonObject convertJson(final MCDataType type, final JsonObject data, final boolean compressed, final int fromVersion, final int toVersion) { ++ final JsonMapType wrapped = new JsonMapType(data, compressed); ++ ++ final JsonMapType replaced = (JsonMapType)convert(type, wrapped, fromVersion, toVersion); ++ ++ return replaced == null ? wrapped.getJson() : replaced.getJson(); ++ } ++ ++ public static R convert(final DataType type, final T data, int fromVersion, final int toVersion) { ++ Object ret = data; ++ ++ long currentVersion = DataConverter.encodeVersions(fromVersion < 99 ? 99 : fromVersion, Integer.MAX_VALUE); ++ final long nextVersion = DataConverter.encodeVersions(toVersion, Integer.MAX_VALUE); ++ ++ for (int i = 0, len = BREAKPOINTS.size(); i < len; ++i) { ++ final long breakpoint = BREAKPOINTS.getLong(i); ++ ++ if (currentVersion >= breakpoint) { ++ continue; ++ } ++ ++ final Object converted = type.convert((T)ret, currentVersion, Math.min(nextVersion, breakpoint - 1)); ++ if (converted != null) { ++ ret = converted; ++ } ++ ++ currentVersion = Math.min(nextVersion, breakpoint - 1); ++ ++ if (currentVersion == nextVersion) { ++ break; ++ } ++ } ++ ++ if (currentVersion != nextVersion) { ++ final Object converted = type.convert((T)ret, currentVersion, nextVersion); ++ if (converted != null) { ++ ret = converted; ++ } ++ } ++ ++ return (R)ret; ++ } ++ ++ private MCDataConverter() {} ++ ++} +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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/MCVersionRegistry.java +@@ -0,0 +1,332 @@ ++package ca.spottedleaf.dataconverter.minecraft; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; ++import it.unimi.dsi.fastutil.ints.IntArrayList; ++import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet; ++import it.unimi.dsi.fastutil.ints.IntRBTreeSet; ++import it.unimi.dsi.fastutil.longs.LongArrayList; ++import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet; ++import org.apache.logging.log4j.LogManager; ++import org.apache.logging.log4j.Logger; ++import java.lang.reflect.Field; ++import java.util.Arrays; ++import java.util.Comparator; ++import java.util.Locale; ++ ++public final class MCVersionRegistry { ++ ++ private static final Logger LOGGER = LogManager.getLogger(); ++ ++ protected static final Int2ObjectLinkedOpenHashMap VERSION_NAMES = new Int2ObjectLinkedOpenHashMap<>(); ++ protected static final IntArrayList VERSION_LIST; ++ protected static final LongArrayList DATA_VERSION_LIST; ++ ++ protected static final IntArrayList DATACONVERTER_VERSIONS_LIST; ++ protected static final IntLinkedOpenHashSet DATACONVERTER_VERSIONS_MAJOR = new IntLinkedOpenHashSet(); ++ protected static final LongLinkedOpenHashSet DATACONVERTER_VERSIONS = new LongLinkedOpenHashSet(); ++ protected static final Int2ObjectLinkedOpenHashMap SUBVERSIONS = new Int2ObjectLinkedOpenHashMap<>(); ++ protected static final LongArrayList BREAKPOINTS = new LongArrayList(); ++ static { ++ // Note: Some of these are nameless. ++ // Unless a data version is specified here, it will NOT have converters ran for it. Please add them on update! ++ final int[] converterVersions = new int[] { ++ 99, ++ 100, ++ 101, ++ 102, ++ 105, ++ 106, ++ 107, ++ 108, ++ 109, ++ 110, ++ 111, ++ 113, ++ 135, ++ 143, ++ 147, ++ 165, ++ 501, ++ 502, ++ 505, ++ 700, ++ 701, ++ 702, ++ 703, ++ 704, ++ 705, ++ 804, ++ 806, ++ 808, ++ 808, ++ 813, ++ 816, ++ 820, ++ 1022, ++ 1125, ++ 1344, ++ 1446, ++ 1450, ++ 1451, ++ 1451, ++ 1451, ++ 1451, ++ 1451, ++ 1451, ++ 1451, ++ 1451, ++ 1451, ++ 1456, ++ 1458, ++ 1460, ++ 1466, ++ 1470, ++ 1474, ++ 1475, ++ 1480, ++ 1481, ++ 1483, ++ 1484, ++ 1486, ++ 1487, ++ 1488, ++ 1490, ++ 1492, ++ 1494, ++ 1496, ++ 1500, ++ 1501, ++ 1502, ++ 1506, ++ 1510, ++ 1514, ++ 1515, ++ 1624, ++ 1800, ++ 1801, ++ 1802, ++ 1803, ++ 1904, ++ 1905, ++ 1906, ++ 1909, ++ 1911, ++ 1917, ++ 1918, ++ 1920, ++ 1925, ++ 1928, ++ 1929, ++ 1931, ++ 1936, ++ 1946, ++ 1948, ++ 1953, ++ 1955, ++ 1961, ++ 1963, ++ 2100, ++ 2202, ++ 2209, ++ 2211, ++ 2218, ++ 2501, ++ 2502, ++ 2503, ++ 2505, ++ 2508, ++ 2509, ++ 2511, ++ 2514, ++ 2516, ++ 2518, ++ 2519, ++ 2522, ++ 2523, ++ 2527, ++ 2528, ++ 2529, ++ 2531, ++ 2533, ++ 2535, ++ 2550, ++ 2551, ++ 2552, ++ 2553, ++ 2558, ++ 2568, ++ 2671, ++ 2679, ++ 2680, ++ 2684, ++ 2686, ++ 2688, ++ 2690, ++ 2691, ++ 2696, ++ 2700, ++ 2701, ++ 2702, ++ 2704, ++ 2707, ++ 2710, ++ 2717 ++ // All up to 1.17.1 ++ }; ++ Arrays.sort(converterVersions); ++ ++ DATACONVERTER_VERSIONS_MAJOR.addAll(DATACONVERTER_VERSIONS_LIST = new IntArrayList(converterVersions)); ++ ++ // add sub versions ++ registerSubVersion(MCVersions.V16W38A + 1, 1); ++ ++ registerSubVersion(MCVersions.V17W47A, 1); ++ registerSubVersion(MCVersions.V17W47A, 2); ++ registerSubVersion(MCVersions.V17W47A, 3); ++ registerSubVersion(MCVersions.V17W47A, 4); ++ registerSubVersion(MCVersions.V17W47A, 5); ++ registerSubVersion(MCVersions.V17W47A, 6); ++ 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 ++ // is needed for new converters ++ ++ // Too much changed in this version. ++ registerBreakpoint(MCVersions.V17W47A); ++ registerBreakpoint(MCVersions.V17W47A, Integer.MAX_VALUE); ++ ++ // final release of major version ++ registerBreakpoint(MCVersions.V1_17_1, Integer.MAX_VALUE); ++ ++ ++ } ++ ++ static { ++ final Field[] fields = MCVersions.class.getDeclaredFields(); ++ for (final Field field : fields) { ++ final String name = field.getName(); ++ final int value; ++ try { ++ value = field.getInt(null); ++ } catch (final Exception ex) { ++ throw new RuntimeException(ex); ++ } ++ ++ if (VERSION_NAMES.containsKey(value) && value != MCVersions.V15W33B) { // Mojang registered 15w33a and 15w33b under the same id. ++ LOGGER.warn("Error registering version \"" + name + "\", version number '" + value + "' is already associated with \"" + VERSION_NAMES.get(value) + "\""); ++ } ++ ++ VERSION_NAMES.put(value, name.substring(1).replace("_PRE", "-PRE").replace("_RC", "-RC").replace('_', '.').toLowerCase(Locale.ROOT)); ++ } ++ ++ for (final int version : DATACONVERTER_VERSIONS_MAJOR) { ++ if (VERSION_NAMES.containsKey(version)) { ++ continue; ++ } ++ ++ // find closest greatest version above this one ++ int closest = Integer.MAX_VALUE; ++ String closestName = null; ++ for (final int v : VERSION_NAMES.keySet()) { ++ if (v > version && v < closest) { ++ closest = v; ++ closestName = VERSION_NAMES.get(v); ++ } ++ } ++ ++ if (closestName == null) { ++ VERSION_NAMES.put(version, "unregistered_v" + version); ++ } else { ++ VERSION_NAMES.put(version, closestName + "-dev" + (closest - version)); ++ } ++ } ++ ++ // Explicit override for V99, as 99 is very special. ++ VERSION_NAMES.put(99, "pre_converter"); ++ ++ VERSION_LIST = new IntArrayList(new IntRBTreeSet(VERSION_NAMES.keySet())); ++ ++ DATA_VERSION_LIST = new LongArrayList(); ++ for (final int version : VERSION_LIST) { ++ DATA_VERSION_LIST.add(DataConverter.encodeVersions(version, 0)); ++ ++ final IntArrayList subVersions = SUBVERSIONS.get(version); ++ if (subVersions == null) { ++ continue; ++ } ++ ++ for (final int step : subVersions) { ++ DATA_VERSION_LIST.add(DataConverter.encodeVersions(version, step)); ++ } ++ } ++ ++ DATA_VERSION_LIST.sort(Comparator.naturalOrder()); ++ ++ for (final int version : DATACONVERTER_VERSIONS_MAJOR) { ++ DATACONVERTER_VERSIONS.add(DataConverter.encodeVersions(version, 0)); ++ ++ final IntArrayList subVersions = SUBVERSIONS.get(version); ++ if (subVersions == null) { ++ continue; ++ } ++ ++ for (final int step : subVersions) { ++ DATACONVERTER_VERSIONS.add(DataConverter.encodeVersions(version, step)); ++ } ++ } ++ } ++ ++ private static void registerSubVersion(final int version, final int step) { ++ if (DATA_VERSION_LIST != null) { ++ throw new IllegalStateException("Added too late!"); ++ } ++ SUBVERSIONS.computeIfAbsent(version, (final int keyInMap) -> { ++ return new IntArrayList(); ++ }).add(step); ++ } ++ ++ private static void registerBreakpoint(final int version) { ++ registerBreakpoint(version, 0); ++ } ++ ++ private static void registerBreakpoint(final int version, final int step) { ++ BREAKPOINTS.add(DataConverter.encodeVersions(version, step)); ++ } ++ ++ // returns only versions that have dataconverters ++ public static boolean hasDataConverters(final int version) { ++ return DATACONVERTER_VERSIONS_MAJOR.contains(version); ++ } ++ ++ public String getVersionName(final int version) { ++ return VERSION_NAMES.get(version); ++ } ++ ++ public boolean isRegisteredVersion(final int version) { ++ return VERSION_NAMES.containsKey(version); ++ } ++ ++ public static IntArrayList getVersionList() { ++ return VERSION_LIST; ++ } ++ ++ public static LongArrayList getDataVersionList() { ++ return DATA_VERSION_LIST; ++ } ++ ++ public static int getMaxVersion() { ++ return VERSION_LIST.getInt(VERSION_LIST.size() - 1); ++ } ++ ++ public static LongArrayList getBreakpoints() { ++ return BREAKPOINTS; ++ } ++ ++ public static void checkVersion(final long version) { ++ if (!DATACONVERTER_VERSIONS.contains(version)) { ++ throw new IllegalStateException("Version " + DataConverter.encodedToString(version) + " is not registered to have dataconverters, yet has a dataconverter"); ++ } ++ } ++} +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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/MCVersions.java +@@ -0,0 +1,366 @@ ++package ca.spottedleaf.dataconverter.minecraft; ++ ++@SuppressWarnings("unused") ++public final class MCVersions { ++ ++ /* https://minecraft.fandom.com/wiki/Data_version */ ++ ++ public static final int V15W32A = 100; ++ public static final int V15W32B = 103; ++ public static final int V15W32C = 104; ++ public static final int V15W33A = 111; ++ public static final int V15W33B = 111; ++ public static final int V15W33C = 112; ++ public static final int V15W34A = 114; ++ public static final int V15W34B = 115; ++ public static final int V15W34C = 116; ++ public static final int V15W34D = 117; ++ public static final int V15W35A = 118; ++ public static final int V15W35B = 119; ++ public static final int V15W35C = 120; ++ public static final int V15W35D = 121; ++ public static final int V15W35E = 122; ++ public static final int V15W36A = 123; ++ public static final int V15W36B = 124; ++ public static final int V15W36C = 125; ++ public static final int V15W36D = 126; ++ public static final int V15W37A = 127; ++ public static final int V15W38A = 128; ++ public static final int V15W38B = 129; ++ public static final int V15W39A = 130; ++ public static final int V15W39B = 131; ++ public static final int V15W39C = 132; ++ public static final int V15W40A = 133; ++ public static final int V15W40B = 134; ++ public static final int V15W41A = 136; ++ public static final int V15W41B = 137; ++ public static final int V15W42A = 138; ++ public static final int V15W43A = 139; ++ public static final int V15W43B = 140; ++ public static final int V15W43C = 141; ++ public static final int V15W44A = 142; ++ public static final int V15W44B = 143; ++ public static final int V15W45A = 145; ++ public static final int V15W46A = 146; ++ public static final int V15W47A = 148; ++ public static final int V15W47B = 149; ++ public static final int V15W47C = 150; ++ public static final int V15W49A = 151; ++ public static final int V15W49B = 152; ++ public static final int V15W50A = 153; ++ public static final int V15W51A = 154; ++ public static final int V15W51B = 155; ++ public static final int V16W02A = 156; ++ public static final int V16W03A = 157; ++ public static final int V16W04A = 158; ++ public static final int V16W05A = 159; ++ public static final int V16W05B = 160; ++ public static final int V16W06A = 161; ++ public static final int V16W07A = 162; ++ public static final int V16W07B = 163; ++ public static final int V1_9_PRE1 = 164; ++ public static final int V1_9_PRE2 = 165; ++ public static final int V1_9_PRE3 = 167; ++ public static final int V1_9_PRE4 = 168; ++ public static final int V1_9 = 169; ++ public static final int V1_9_1_PRE1 = 170; ++ public static final int V1_9_1_PRE2 = 171; ++ public static final int V1_9_1_PRE3 = 172; ++ public static final int V1_9_1 = 175; ++ public static final int V1_9_2 = 176; ++ public static final int V16W14A = 177; ++ public static final int V16W15A = 178; ++ public static final int V16W15B = 179; ++ public static final int V1_9_3_PRE1 = 180; ++ public static final int V1_9_3_PRE2 = 181; ++ public static final int V1_9_3_PRE3 = 182; ++ public static final int V1_9_3 = 183; ++ public static final int V1_9_4 = 184; ++ public static final int V16W20A = 501; ++ public static final int V16W21A = 503; ++ public static final int V16W21B = 504; ++ public static final int V1_10_PRE1 = 506; ++ public static final int V1_10_PRE2 = 507; ++ public static final int V1_10 = 510; ++ public static final int V1_10_1 = 511; ++ public static final int V1_10_2 = 512; ++ public static final int V16W32A = 800; ++ public static final int V16W32B = 801; ++ public static final int V16W33A = 802; ++ public static final int V16W35A = 803; ++ public static final int V16W36A = 805; ++ public static final int V16W38A = 807; ++ public static final int V16W39A = 809; ++ public static final int V16W39B = 811; ++ public static final int V16W39C = 812; ++ public static final int V16W40A = 813; ++ public static final int V16W41A = 814; ++ public static final int V16W42A = 815; ++ public static final int V16W43A = 816; ++ public static final int V16W44A = 817; ++ public static final int V1_11_PRE1 = 818; ++ public static final int V1_11 = 819; ++ public static final int V16W50A = 920; ++ public static final int V1_11_1 = 921; ++ public static final int V1_11_2 = 922; ++ public static final int V17W06A = 1022; ++ public static final int V17W13A = 1122; ++ public static final int V17W13B = 1123; ++ public static final int V17W14A = 1124; ++ public static final int V17W15A = 1125; ++ public static final int V17W16A = 1126; ++ public static final int V17W16B = 1127; ++ public static final int V17W17A = 1128; ++ public static final int V17W17B = 1129; ++ public static final int V17W18A = 1130; ++ public static final int V17W18B = 1131; ++ public static final int V1_12_PRE1 = 1132; ++ public static final int V1_12_PRE2 = 1133; ++ public static final int V1_12_PRE3 = 1134; ++ public static final int V1_12_PRE4 = 1135; ++ public static final int V1_12_PRE5 = 1136; ++ public static final int V1_12_PRE6 = 1137; ++ public static final int V1_12_PRE7 = 1138; ++ public static final int V1_12 = 1139; ++ public static final int V17W31A = 1239; ++ public static final int V1_12_1_PRE1 = 1240; ++ public static final int V1_12_1 = 1241; ++ public static final int V1_12_2_PRE1 = 1341; ++ public static final int V1_12_2_PRE2 = 1342; ++ public static final int V1_12_2 = 1343; ++ public static final int V17W43A = 1444; ++ public static final int V17W43B = 1445; ++ public static final int V17W45A = 1447; ++ public static final int V17W45B = 1448; ++ public static final int V17W46A = 1449; ++ public static final int V17W47A = 1451; ++ public static final int V17W47B = 1452; ++ public static final int V17W48A = 1453; ++ public static final int V17W49A = 1454; ++ public static final int V17W49B = 1455; ++ public static final int V17W50A = 1457; ++ public static final int V18W01A = 1459; ++ public static final int V18W02A = 1461; ++ public static final int V18W03A = 1462; ++ public static final int V18W03B = 1463; ++ public static final int V18W05A = 1464; ++ public static final int V18W06A = 1466; ++ public static final int V18W07A = 1467; ++ public static final int V18W07B = 1468; ++ public static final int V18W07C = 1469; ++ public static final int V18W08A = 1470; ++ public static final int V18W08B = 1471; ++ public static final int V18W09A = 1472; ++ public static final int V18W10A = 1473; ++ public static final int V18W10B = 1474; ++ public static final int V18W10C = 1476; ++ public static final int V18W10D = 1477; ++ public static final int V18W11A = 1478; ++ public static final int V18W14A = 1479; ++ public static final int V18W14B = 1481; ++ public static final int V18W15A = 1482; ++ public static final int V18W16A = 1483; ++ public static final int V18W19A = 1484; ++ public static final int V18W19B = 1485; ++ public static final int V18W20A = 1489; ++ public static final int V18W20B = 1491; ++ public static final int V18W20C = 1493; ++ public static final int V18W21A = 1495; ++ public static final int V18W21B = 1496; ++ public static final int V18W22A = 1497; ++ public static final int V18W22B = 1498; ++ public static final int V18W22C = 1499; ++ public static final int V1_13_PRE1 = 1501; ++ public static final int V1_13_PRE2 = 1502; ++ public static final int V1_13_PRE3 = 1503; ++ public static final int V1_13_PRE4 = 1504; ++ public static final int V1_13_PRE5 = 1511; ++ public static final int V1_13_PRE6 = 1512; ++ public static final int V1_13_PRE7 = 1513; ++ public static final int V1_13_PRE8 = 1516; ++ public static final int V1_13_PRE9 = 1517; ++ public static final int V1_13_PRE10 = 1518; ++ public static final int V1_13 = 1519; ++ public static final int V18W30A = 1620; ++ public static final int V18W30B = 1621; ++ public static final int V18W31A = 1622; ++ public static final int V18W32A = 1623; ++ public static final int V18W33A = 1625; ++ public static final int V1_13_1_PRE1 = 1626; ++ public static final int V1_13_1_PRE2 = 1627; ++ public static final int V1_13_1 = 1628; ++ public static final int V1_13_2_PRE1 = 1629; ++ public static final int V1_13_2_PRE2 = 1630; ++ public static final int V1_13_2 = 1631; ++ public static final int V18W43A = 1901; ++ public static final int V18W43B = 1902; ++ public static final int V18W43C = 1903; ++ public static final int V18W44A = 1907; ++ public static final int V18W45A = 1908; ++ public static final int V18W46A = 1910; ++ public static final int V18W47A = 1912; ++ public static final int V18W47B = 1913; ++ public static final int V18W48A = 1914; ++ public static final int V18W48B = 1915; ++ public static final int V18W49A = 1916; ++ public static final int V18W50A = 1919; ++ public static final int V19W02A = 1921; ++ public static final int V19W03A = 1922; ++ public static final int V19W03B = 1923; ++ public static final int V19W03C = 1924; ++ public static final int V19W04A = 1926; ++ public static final int V19W04B = 1927; ++ public static final int V19W05A = 1930; ++ public static final int V19W06A = 1931; ++ public static final int V19W07A = 1932; ++ public static final int V19W08A = 1933; ++ public static final int V19W08B = 1934; ++ public static final int V19W09A = 1935; ++ public static final int V19W11A = 1937; ++ public static final int V19W11B = 1938; ++ public static final int V19W12A = 1940; ++ public static final int V19W12B = 1941; ++ public static final int V19W13A = 1942; ++ public static final int V19W13B = 1943; ++ public static final int V19W14A = 1944; ++ public static final int V19W14B = 1945; ++ public static final int V1_14_PRE1 = 1947; ++ public static final int V1_14_PRE2 = 1948; ++ public static final int V1_14_PRE3 = 1949; ++ public static final int V1_14_PRE4 = 1950; ++ public static final int V1_14_PRE5 = 1951; ++ public static final int V1_14 = 1952; ++ public static final int V1_14_1_PRE1 = 1955; ++ public static final int V1_14_1_PRE2 = 1956; ++ public static final int V1_14_1 = 1957; ++ public static final int V1_14_2_PRE1 = 1958; ++ public static final int V1_14_2_PRE2 = 1959; ++ public static final int V1_14_2_PRE3 = 1960; ++ public static final int V1_14_2_PRE4 = 1962; ++ public static final int V1_14_2 = 1963; ++ public static final int V1_14_3_PRE1 = 1964; ++ public static final int V1_14_3_PRE2 = 1965; ++ public static final int V1_14_3_PRE3 = 1966; ++ public static final int V1_14_3_PRE4 = 1967; ++ public static final int V1_14_3 = 1968; ++ public static final int V1_14_4_PRE1 = 1969; ++ public static final int V1_14_4_PRE2 = 1970; ++ public static final int V1_14_4_PRE3 = 1971; ++ public static final int V1_14_4_PRE4 = 1972; ++ public static final int V1_14_4_PRE5 = 1973; ++ public static final int V1_14_4_PRE6 = 1974; ++ public static final int V1_14_4_PRE7 = 1975; ++ public static final int V1_14_4 = 1976; ++ public static final int V19W34A = 2200; ++ public static final int V19W35A = 2201; ++ public static final int V19W36A = 2203; ++ public static final int V19W37A = 2204; ++ public static final int V19W38A = 2205; ++ public static final int V19W38B = 2206; ++ public static final int V19W39A = 2207; ++ public static final int V19W40A = 2208; ++ public static final int V19W41A = 2210; ++ public static final int V19W42A = 2212; ++ public static final int V19W44A = 2213; ++ public static final int V19W45A = 2214; ++ public static final int V19W45B = 2215; ++ public static final int V19W46A = 2216; ++ public static final int V19W46B = 2217; ++ public static final int V1_15_PRE1 = 2218; ++ public static final int V1_15_PRE2 = 2219; ++ public static final int V1_15_PRE3 = 2220; ++ public static final int V1_15_PRE4 = 2221; ++ public static final int V1_15_PRE5 = 2222; ++ public static final int V1_15_PRE6 = 2223; ++ public static final int V1_15_PRE7 = 2224; ++ public static final int V1_15 = 2225; ++ public static final int V1_15_1_PRE1 = 2226; ++ public static final int V1_15_1 = 2227; ++ public static final int V1_15_2_PRE1 = 2228; ++ public static final int V1_15_2_PRE2 = 2229; ++ public static final int V1_15_2 = 2230; ++ public static final int V20W06A = 2504; ++ public static final int V20W07A = 2506; ++ public static final int V20W08A = 2507; ++ public static final int V20W09A = 2510; ++ public static final int V20W10A = 2512; ++ public static final int V20W11A = 2513; ++ public static final int V20W12A = 2515; ++ public static final int V20W13A = 2520; ++ public static final int V20W13B = 2521; ++ public static final int V20W14A = 2524; ++ public static final int V20W15A = 2525; ++ public static final int V20W16A = 2526; ++ public static final int V20W17A = 2529; ++ public static final int V20W18A = 2532; ++ public static final int V20W19A = 2534; ++ public static final int V20W20A = 2536; ++ public static final int V20W20B = 2537; ++ public static final int V20W21A = 2554; ++ public static final int V20W22A = 2555; ++ public static final int V1_16_PRE1 = 2556; ++ public static final int V1_16_PRE2 = 2557; ++ public static final int V1_16_PRE3 = 2559; ++ public static final int V1_16_PRE4 = 2560; ++ public static final int V1_16_PRE5 = 2561; ++ public static final int V1_16_PRE6 = 2562; ++ public static final int V1_16_PRE7 = 2563; ++ public static final int V1_16_PRE8 = 2564; ++ public static final int V1_16_RC1 = 2565; ++ public static final int V1_16 = 2566; ++ public static final int V1_16_1 = 2567; ++ public static final int V20W27A = 2569; ++ public static final int V20W28A = 2570; ++ public static final int V20W29A = 2571; ++ public static final int V20W30A = 2572; ++ public static final int V1_16_2_PRE1 = 2573; ++ public static final int V1_16_2_PRE2 = 2574; ++ public static final int V1_16_2_PRE3 = 2575; ++ public static final int V1_16_2_RC1 = 2576; ++ public static final int V1_16_2_RC2 = 2577; ++ public static final int V1_16_2 = 2578; ++ public static final int V1_16_3_RC1 = 2579; ++ public static final int V1_16_3 = 2580; ++ public static final int V1_16_4_PRE1 = 2581; ++ public static final int V1_16_4_PRE2 = 2582; ++ public static final int V1_16_4_RC1 = 2583; ++ public static final int V1_16_4 = 2584; ++ public static final int V1_16_5_RC1 = 2585; ++ public static final int V1_16_5 = 2586; ++ public static final int V20W45A = 2681; ++ public static final int V20W46A = 2682; ++ public static final int V20W48A = 2683; ++ public static final int V20W49A = 2685; ++ public static final int V20W51A = 2687; ++ public static final int V21W03A = 2689; ++ public static final int V21W05A = 2690; ++ public static final int V21W05B = 2692; ++ public static final int V21W06A = 2694; ++ public static final int V21W07A = 2695; ++ public static final int V21W08A = 2697; ++ public static final int V21W08B = 2698; ++ public static final int V21W10A = 2699; ++ public static final int V21W11A = 2703; ++ public static final int V21W13A = 2705; ++ public static final int V21W14A = 2706; ++ public static final int V21W15A = 2709; ++ public static final int V21W16A = 2711; ++ public static final int V21W17A = 2712; ++ public static final int V21W18A = 2713; ++ public static final int V21W19A = 2714; ++ public static final int V21W20A = 2715; ++ public static final int V1_17_PRE1 = 2716; ++ public static final int V1_17_PRE2 = 2718; ++ public static final int V1_17_PRE3 = 2719; ++ public static final int V1_17_PRE4 = 2720; ++ public static final int V1_17_PRE5 = 2721; ++ public static final int V1_17_RC1 = 2722; ++ public static final int V1_17_RC2 = 2723; ++ public static final int V1_17 = 2724; ++ public static final int V1_17_1_PRE1 = 2725; ++ public static final int V1_17_1_PRE2 = 2726; ++ public static final int V1_17_1_PRE3 = 2727; ++ 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; ++} +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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/advancements/ConverterAbstractAdvancementsRename.java +@@ -0,0 +1,34 @@ ++package ca.spottedleaf.dataconverter.minecraft.converters.advancements; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.types.MapType; ++import java.util.ArrayList; ++import java.util.function.Function; ++ ++public final class ConverterAbstractAdvancementsRename { ++ ++ private ConverterAbstractAdvancementsRename() {} ++ ++ public static void register(final int version, final Function renamer) { ++ register(version, 0, renamer); ++ } ++ ++ public static void register(final int version, final int subVersion, final Function renamer) { ++ 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); ++ } ++ } ++ return null; ++ } ++ }); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/blockname/ConverterAbstractBlockRename.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/blockname/ConverterAbstractBlockRename.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ba9daaab1abd53a3fbdebd78e05ba363251188c6 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/blockname/ConverterAbstractBlockRename.java +@@ -0,0 +1,73 @@ ++package ca.spottedleaf.dataconverter.minecraft.converters.blockname; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.ConverterAbstractStringValueTypeRename; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.types.MapType; ++import java.util.function.Function; ++ ++public final class ConverterAbstractBlockRename { ++ ++ private ConverterAbstractBlockRename() {} ++ ++ public static void register(final int version, final Function renamer) { ++ register(version, 0, renamer); ++ } ++ ++ public static void register(final int version, final int subVersion, final Function renamer) { ++ ConverterAbstractStringValueTypeRename.register(version, subVersion, MCTypeRegistry.BLOCK_NAME, renamer); ++ MCTypeRegistry.BLOCK_STATE.addStructureConverter(new DataConverter<>(version, subVersion) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final String name = data.getString("Name"); ++ if (name != null) { ++ final String converted = renamer.apply(name); ++ if (converted != null) { ++ data.setString("Name", converted); ++ } ++ } ++ return null; ++ } ++ }); ++ } ++ ++ public static void registerAndFixJigsaw(final int version, final Function renamer) { ++ registerAndFixJigsaw(version, 0, renamer); ++ } ++ ++ public static void registerAndFixJigsaw(final int version, final int subVersion, final Function renamer) { ++ register(version, subVersion, renamer); ++ // TODO check on update, minecraft:jigsaw can change ++ MCTypeRegistry.TILE_ENTITY.addConverterForId("minecraft:jigsaw", new DataConverter<>(version, subVersion) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final String finalState = data.getString("final_state"); ++ if (finalState == null || finalState.isEmpty()) { ++ return null; ++ } ++ ++ final int nbtStart1 = finalState.indexOf('['); ++ final int nbtStart2 = finalState.indexOf('{'); ++ int stateNameEnd = finalState.length(); ++ if (nbtStart1 > 0) { ++ stateNameEnd = Math.min(stateNameEnd, nbtStart1); ++ } ++ ++ if (nbtStart2 > 0) { ++ stateNameEnd = Math.min(stateNameEnd, nbtStart2); ++ } ++ ++ final String blockStateName = finalState.substring(0, stateNameEnd); ++ final String converted = renamer.apply(blockStateName); ++ if (converted == null) { ++ return null; ++ } ++ ++ final String convertedState = converted.concat(finalState.substring(stateNameEnd)); ++ data.setString("final_state", convertedState); ++ ++ return null; ++ } ++ }); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/chunk/ConverterFlattenChunk.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/chunk/ConverterFlattenChunk.java +new file mode 100644 +index 0000000000000000000000000000000000000000..64226434a38dc5e4a9103c61aa8f9c20bce9550c +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/chunk/ConverterFlattenChunk.java +@@ -0,0 +1,1016 @@ ++package ca.spottedleaf.dataconverter.minecraft.converters.chunk; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.HelperBlockFlatteningV1450; ++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.HelperItemNameV102; ++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 com.mojang.datafixers.DataFixUtils; ++import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; ++import it.unimi.dsi.fastutil.ints.Int2ObjectMap; ++import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; ++import it.unimi.dsi.fastutil.ints.IntArrayList; ++import it.unimi.dsi.fastutil.ints.IntIterator; ++import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; ++import net.minecraft.util.datafix.PackedBitStorage; ++import org.apache.logging.log4j.LogManager; ++import org.apache.logging.log4j.Logger; ++import java.util.Arrays; ++import java.util.BitSet; ++import java.util.HashMap; ++import java.util.Iterator; ++import java.util.Map; ++import java.util.Objects; ++ ++import static it.unimi.dsi.fastutil.HashCommon.arraySize; ++ ++public final class ConverterFlattenChunk extends DataConverter, MapType> { ++ ++ private static final Logger LOGGER = LogManager.getLogger(); ++ ++ static final BitSet VIRTUAL_SET = new BitSet(256); ++ static final BitSet IDS_NEEDING_FIX_SET = new BitSet(256); ++ ++ static { ++ IDS_NEEDING_FIX_SET.set(2); ++ IDS_NEEDING_FIX_SET.set(3); ++ IDS_NEEDING_FIX_SET.set(110); ++ IDS_NEEDING_FIX_SET.set(140); ++ IDS_NEEDING_FIX_SET.set(144); ++ IDS_NEEDING_FIX_SET.set(25); ++ IDS_NEEDING_FIX_SET.set(86); ++ IDS_NEEDING_FIX_SET.set(26); ++ IDS_NEEDING_FIX_SET.set(176); ++ IDS_NEEDING_FIX_SET.set(177); ++ IDS_NEEDING_FIX_SET.set(175); ++ IDS_NEEDING_FIX_SET.set(64); ++ IDS_NEEDING_FIX_SET.set(71); ++ IDS_NEEDING_FIX_SET.set(193); ++ IDS_NEEDING_FIX_SET.set(194); ++ IDS_NEEDING_FIX_SET.set(195); ++ IDS_NEEDING_FIX_SET.set(196); ++ IDS_NEEDING_FIX_SET.set(197); ++ ++ VIRTUAL_SET.set(54); ++ VIRTUAL_SET.set(146); ++ VIRTUAL_SET.set(25); ++ VIRTUAL_SET.set(26); ++ VIRTUAL_SET.set(51); ++ VIRTUAL_SET.set(53); ++ VIRTUAL_SET.set(67); ++ VIRTUAL_SET.set(108); ++ VIRTUAL_SET.set(109); ++ VIRTUAL_SET.set(114); ++ VIRTUAL_SET.set(128); ++ VIRTUAL_SET.set(134); ++ VIRTUAL_SET.set(135); ++ VIRTUAL_SET.set(136); ++ VIRTUAL_SET.set(156); ++ VIRTUAL_SET.set(163); ++ VIRTUAL_SET.set(164); ++ VIRTUAL_SET.set(180); ++ VIRTUAL_SET.set(203); ++ VIRTUAL_SET.set(55); ++ VIRTUAL_SET.set(85); ++ VIRTUAL_SET.set(113); ++ VIRTUAL_SET.set(188); ++ VIRTUAL_SET.set(189); ++ VIRTUAL_SET.set(190); ++ VIRTUAL_SET.set(191); ++ VIRTUAL_SET.set(192); ++ VIRTUAL_SET.set(93); ++ VIRTUAL_SET.set(94); ++ VIRTUAL_SET.set(101); ++ VIRTUAL_SET.set(102); ++ VIRTUAL_SET.set(160); ++ VIRTUAL_SET.set(106); ++ VIRTUAL_SET.set(107); ++ VIRTUAL_SET.set(183); ++ VIRTUAL_SET.set(184); ++ VIRTUAL_SET.set(185); ++ VIRTUAL_SET.set(186); ++ VIRTUAL_SET.set(187); ++ VIRTUAL_SET.set(132); ++ VIRTUAL_SET.set(139); ++ VIRTUAL_SET.set(199); ++ } ++ ++ static final boolean[] VIRTUAL = toBooleanArray(VIRTUAL_SET); ++ static final boolean[] IDS_NEEDING_FIX = toBooleanArray(IDS_NEEDING_FIX_SET); ++ ++ private static boolean[] toBooleanArray(final BitSet set) { ++ final boolean[] ret = new boolean[4096]; ++ for (int i = 0; i < 4096; ++i) { ++ ret[i] = set.get(i); ++ } ++ ++ return ret; ++ } ++ ++ static final MapType PUMPKIN = HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:pumpkin'}"); ++ static final MapType SNOWY_PODZOL = HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:podzol',Properties:{snowy:'true'}}"); ++ static final MapType SNOWY_GRASS = HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:grass_block',Properties:{snowy:'true'}}"); ++ static final MapType SNOWY_MYCELIUM = HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:mycelium',Properties:{snowy:'true'}}"); ++ static final MapType UPPER_SUNFLOWER = HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:sunflower',Properties:{half:'upper'}}"); ++ static final MapType UPPER_LILAC = HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:lilac',Properties:{half:'upper'}}"); ++ static final MapType UPPER_TALL_GRASS = HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:tall_grass',Properties:{half:'upper'}}"); ++ static final MapType UPPER_LARGE_FERN = HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:large_fern',Properties:{half:'upper'}}"); ++ static final MapType UPPER_ROSE_BUSH = HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:rose_bush',Properties:{half:'upper'}}"); ++ static final MapType UPPER_PEONY = HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:peony',Properties:{half:'upper'}}"); ++ ++ static final Map> FLOWER_POT_MAP = new HashMap<>(); ++ static { ++ FLOWER_POT_MAP.put("minecraft:air0", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:flower_pot'}")); ++ FLOWER_POT_MAP.put("minecraft:red_flower0", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_poppy'}")); ++ FLOWER_POT_MAP.put("minecraft:red_flower1", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_blue_orchid'}")); ++ FLOWER_POT_MAP.put("minecraft:red_flower2", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_allium'}")); ++ FLOWER_POT_MAP.put("minecraft:red_flower3", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_azure_bluet'}")); ++ FLOWER_POT_MAP.put("minecraft:red_flower4", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_red_tulip'}")); ++ FLOWER_POT_MAP.put("minecraft:red_flower5", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_orange_tulip'}")); ++ FLOWER_POT_MAP.put("minecraft:red_flower6", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_white_tulip'}")); ++ FLOWER_POT_MAP.put("minecraft:red_flower7", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_pink_tulip'}")); ++ FLOWER_POT_MAP.put("minecraft:red_flower8", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_oxeye_daisy'}")); ++ FLOWER_POT_MAP.put("minecraft:yellow_flower0", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_dandelion'}")); ++ FLOWER_POT_MAP.put("minecraft:sapling0", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_oak_sapling'}")); ++ FLOWER_POT_MAP.put("minecraft:sapling1", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_spruce_sapling'}")); ++ FLOWER_POT_MAP.put("minecraft:sapling2", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_birch_sapling'}")); ++ FLOWER_POT_MAP.put("minecraft:sapling3", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_jungle_sapling'}")); ++ FLOWER_POT_MAP.put("minecraft:sapling4", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_acacia_sapling'}")); ++ FLOWER_POT_MAP.put("minecraft:sapling5", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_dark_oak_sapling'}")); ++ FLOWER_POT_MAP.put("minecraft:red_mushroom0", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_red_mushroom'}")); ++ FLOWER_POT_MAP.put("minecraft:brown_mushroom0", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_brown_mushroom'}")); ++ FLOWER_POT_MAP.put("minecraft:deadbush0", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_dead_bush'}")); ++ FLOWER_POT_MAP.put("minecraft:tallgrass2", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_fern'}")); ++ FLOWER_POT_MAP.put("minecraft:cactus0", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:potted_cactus'}")); // we change default to empty ++ } ++ ++ static final Map> SKULL_MAP = new HashMap<>(); ++ static { ++ mapSkull(SKULL_MAP, 0, "skeleton", "skull"); ++ mapSkull(SKULL_MAP, 1, "wither_skeleton", "skull"); ++ mapSkull(SKULL_MAP, 2, "zombie", "head"); ++ mapSkull(SKULL_MAP, 3, "player", "head"); ++ mapSkull(SKULL_MAP, 4, "creeper", "head"); ++ mapSkull(SKULL_MAP, 5, "dragon", "head"); ++ }; ++ ++ private static void mapSkull(final Map> into, final int oldId, final String newId, final String skullType) { ++ into.put(oldId + "north", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + newId + "_wall_" + skullType + "',Properties:{facing:'north'}}")); ++ into.put(oldId + "east", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + newId + "_wall_" + skullType + "',Properties:{facing:'east'}}")); ++ into.put(oldId + "south", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + newId + "_wall_" + skullType + "',Properties:{facing:'south'}}")); ++ into.put(oldId + "west", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + newId + "_wall_" + skullType + "',Properties:{facing:'west'}}")); ++ ++ for (int rotation = 0; rotation < 16; ++rotation) { ++ into.put(oldId + "" + rotation, ++ HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + newId + "_" + skullType + "',Properties:{rotation:'" + rotation + "'}}")); ++ } ++ } ++ ++ static final Map> DOOR_MAP = new HashMap<>(); ++ static { ++ mapDoor(DOOR_MAP, "oak_door", 1024); ++ mapDoor(DOOR_MAP, "iron_door", 1136); ++ mapDoor(DOOR_MAP, "spruce_door", 3088); ++ mapDoor(DOOR_MAP, "birch_door", 3104); ++ mapDoor(DOOR_MAP, "jungle_door", 3120); ++ mapDoor(DOOR_MAP, "acacia_door", 3136); ++ mapDoor(DOOR_MAP, "dark_oak_door", 3152); ++ }; ++ ++ private static void mapDoor(final Map> into, final String type, final int oldId) { ++ into.put("minecraft:" + type + "eastlowerleftfalsefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'east',half:'lower',hinge:'left',open:'false',powered:'false'}}")); ++ into.put("minecraft:" + type + "eastlowerleftfalsetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'east',half:'lower',hinge:'left',open:'false',powered:'true'}}")); ++ into.put("minecraft:" + type + "eastlowerlefttruefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'east',half:'lower',hinge:'left',open:'true',powered:'false'}}")); ++ into.put("minecraft:" + type + "eastlowerlefttruetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'east',half:'lower',hinge:'left',open:'true',powered:'true'}}")); ++ into.put("minecraft:" + type + "eastlowerrightfalsefalse", Objects.requireNonNull(HelperBlockFlatteningV1450.getNBTForId(oldId))); ++ into.put("minecraft:" + type + "eastlowerrightfalsetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'true'}}")); ++ into.put("minecraft:" + type + "eastlowerrighttruefalse", Objects.requireNonNull(HelperBlockFlatteningV1450.getNBTForId(oldId + 4))); ++ into.put("minecraft:" + type + "eastlowerrighttruetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'true'}}")); ++ into.put("minecraft:" + type + "eastupperleftfalsefalse", Objects.requireNonNull(HelperBlockFlatteningV1450.getNBTForId(oldId + 8))); ++ into.put("minecraft:" + type + "eastupperleftfalsetrue", Objects.requireNonNull(HelperBlockFlatteningV1450.getNBTForId(oldId + 10))); ++ into.put("minecraft:" + type + "eastupperlefttruefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'east',half:'upper',hinge:'left',open:'true',powered:'false'}}")); ++ into.put("minecraft:" + type + "eastupperlefttruetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'east',half:'upper',hinge:'left',open:'true',powered:'true'}}")); ++ into.put("minecraft:" + type + "eastupperrightfalsefalse", Objects.requireNonNull(HelperBlockFlatteningV1450.getNBTForId(oldId + 9))); ++ into.put("minecraft:" + type + "eastupperrightfalsetrue", Objects.requireNonNull(HelperBlockFlatteningV1450.getNBTForId(oldId + 11))); ++ into.put("minecraft:" + type + "eastupperrighttruefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'east',half:'upper',hinge:'right',open:'true',powered:'false'}}")); ++ into.put("minecraft:" + type + "eastupperrighttruetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'east',half:'upper',hinge:'right',open:'true',powered:'true'}}")); ++ into.put("minecraft:" + type + "northlowerleftfalsefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'north',half:'lower',hinge:'left',open:'false',powered:'false'}}")); ++ into.put("minecraft:" + type + "northlowerleftfalsetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'north',half:'lower',hinge:'left',open:'false',powered:'true'}}")); ++ into.put("minecraft:" + type + "northlowerlefttruefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'north',half:'lower',hinge:'left',open:'true',powered:'false'}}")); ++ into.put("minecraft:" + type + "northlowerlefttruetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'north',half:'lower',hinge:'left',open:'true',powered:'true'}}")); ++ into.put("minecraft:" + type + "northlowerrightfalsefalse", Objects.requireNonNull(HelperBlockFlatteningV1450.getNBTForId(oldId + 3))); ++ into.put("minecraft:" + type + "northlowerrightfalsetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'true'}}")); ++ into.put("minecraft:" + type + "northlowerrighttruefalse", Objects.requireNonNull(HelperBlockFlatteningV1450.getNBTForId(oldId + 7))); ++ into.put("minecraft:" + type + "northlowerrighttruetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'true'}}")); ++ into.put("minecraft:" + type + "northupperleftfalsefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'north',half:'upper',hinge:'left',open:'false',powered:'false'}}")); ++ into.put("minecraft:" + type + "northupperleftfalsetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'north',half:'upper',hinge:'left',open:'false',powered:'true'}}")); ++ into.put("minecraft:" + type + "northupperlefttruefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'north',half:'upper',hinge:'left',open:'true',powered:'false'}}")); ++ into.put("minecraft:" + type + "northupperlefttruetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'north',half:'upper',hinge:'left',open:'true',powered:'true'}}")); ++ into.put("minecraft:" + type + "northupperrightfalsefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'north',half:'upper',hinge:'right',open:'false',powered:'false'}}")); ++ into.put("minecraft:" + type + "northupperrightfalsetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'north',half:'upper',hinge:'right',open:'false',powered:'true'}}")); ++ into.put("minecraft:" + type + "northupperrighttruefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'north',half:'upper',hinge:'right',open:'true',powered:'false'}}")); ++ into.put("minecraft:" + type + "northupperrighttruetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'north',half:'upper',hinge:'right',open:'true',powered:'true'}}")); ++ into.put("minecraft:" + type + "southlowerleftfalsefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'south',half:'lower',hinge:'left',open:'false',powered:'false'}}")); ++ into.put("minecraft:" + type + "southlowerleftfalsetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'south',half:'lower',hinge:'left',open:'false',powered:'true'}}")); ++ into.put("minecraft:" + type + "southlowerlefttruefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'south',half:'lower',hinge:'left',open:'true',powered:'false'}}")); ++ into.put("minecraft:" + type + "southlowerlefttruetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'south',half:'lower',hinge:'left',open:'true',powered:'true'}}")); ++ into.put("minecraft:" + type + "southlowerrightfalsefalse", Objects.requireNonNull(HelperBlockFlatteningV1450.getNBTForId(oldId + 1))); ++ into.put("minecraft:" + type + "southlowerrightfalsetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'true'}}")); ++ into.put("minecraft:" + type + "southlowerrighttruefalse", Objects.requireNonNull(HelperBlockFlatteningV1450.getNBTForId(oldId + 5))); ++ into.put("minecraft:" + type + "southlowerrighttruetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'true'}}")); ++ into.put("minecraft:" + type + "southupperleftfalsefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'south',half:'upper',hinge:'left',open:'false',powered:'false'}}")); ++ into.put("minecraft:" + type + "southupperleftfalsetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'south',half:'upper',hinge:'left',open:'false',powered:'true'}}")); ++ into.put("minecraft:" + type + "southupperlefttruefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'south',half:'upper',hinge:'left',open:'true',powered:'false'}}")); ++ into.put("minecraft:" + type + "southupperlefttruetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'south',half:'upper',hinge:'left',open:'true',powered:'true'}}")); ++ into.put("minecraft:" + type + "southupperrightfalsefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'south',half:'upper',hinge:'right',open:'false',powered:'false'}}")); ++ into.put("minecraft:" + type + "southupperrightfalsetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'south',half:'upper',hinge:'right',open:'false',powered:'true'}}")); ++ into.put("minecraft:" + type + "southupperrighttruefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'south',half:'upper',hinge:'right',open:'true',powered:'false'}}")); ++ into.put("minecraft:" + type + "southupperrighttruetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'south',half:'upper',hinge:'right',open:'true',powered:'true'}}")); ++ into.put("minecraft:" + type + "westlowerleftfalsefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'west',half:'lower',hinge:'left',open:'false',powered:'false'}}")); ++ into.put("minecraft:" + type + "westlowerleftfalsetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'west',half:'lower',hinge:'left',open:'false',powered:'true'}}")); ++ into.put("minecraft:" + type + "westlowerlefttruefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'west',half:'lower',hinge:'left',open:'true',powered:'false'}}")); ++ into.put("minecraft:" + type + "westlowerlefttruetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'west',half:'lower',hinge:'left',open:'true',powered:'true'}}")); ++ into.put("minecraft:" + type + "westlowerrightfalsefalse", Objects.requireNonNull(HelperBlockFlatteningV1450.getNBTForId(oldId + 2))); ++ into.put("minecraft:" + type + "westlowerrightfalsetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'true'}}")); ++ into.put("minecraft:" + type + "westlowerrighttruefalse", Objects.requireNonNull(HelperBlockFlatteningV1450.getNBTForId(oldId + 6))); ++ into.put("minecraft:" + type + "westlowerrighttruetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'true'}}")); ++ into.put("minecraft:" + type + "westupperleftfalsefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'west',half:'upper',hinge:'left',open:'false',powered:'false'}}")); ++ into.put("minecraft:" + type + "westupperleftfalsetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'west',half:'upper',hinge:'left',open:'false',powered:'true'}}")); ++ into.put("minecraft:" + type + "westupperlefttruefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'west',half:'upper',hinge:'left',open:'true',powered:'false'}}")); ++ into.put("minecraft:" + type + "westupperlefttruetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'west',half:'upper',hinge:'left',open:'true',powered:'true'}}")); ++ into.put("minecraft:" + type + "westupperrightfalsefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'west',half:'upper',hinge:'right',open:'false',powered:'false'}}")); ++ into.put("minecraft:" + type + "westupperrightfalsetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'west',half:'upper',hinge:'right',open:'false',powered:'true'}}")); ++ into.put("minecraft:" + type + "westupperrighttruefalse", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'west',half:'upper',hinge:'right',open:'true',powered:'false'}}")); ++ into.put("minecraft:" + type + "westupperrighttruetrue", HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + type + "',Properties:{facing:'west',half:'upper',hinge:'right',open:'true',powered:'true'}}")); ++ } ++ ++ static final Map> NOTE_BLOCK_MAP = new HashMap<>(); ++ static { ++ for(int note = 0; note < 26; ++note) { ++ NOTE_BLOCK_MAP.put("true" + note, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:note_block',Properties:{powered:'true',note:'" + note + "'}}")); ++ NOTE_BLOCK_MAP.put("false" + note, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:note_block',Properties:{powered:'false',note:'" + note + "'}}")); ++ } ++ } ++ ++ static final Int2ObjectOpenHashMap DYE_COLOR_MAP = new Int2ObjectOpenHashMap<>(); ++ static { ++ DYE_COLOR_MAP.put(0, "white"); ++ DYE_COLOR_MAP.put(1, "orange"); ++ DYE_COLOR_MAP.put(2, "magenta"); ++ DYE_COLOR_MAP.put(3, "light_blue"); ++ DYE_COLOR_MAP.put(4, "yellow"); ++ DYE_COLOR_MAP.put(5, "lime"); ++ DYE_COLOR_MAP.put(6, "pink"); ++ DYE_COLOR_MAP.put(7, "gray"); ++ DYE_COLOR_MAP.put(8, "light_gray"); ++ DYE_COLOR_MAP.put(9, "cyan"); ++ DYE_COLOR_MAP.put(10, "purple"); ++ DYE_COLOR_MAP.put(11, "blue"); ++ DYE_COLOR_MAP.put(12, "brown"); ++ DYE_COLOR_MAP.put(13, "green"); ++ DYE_COLOR_MAP.put(14, "red"); ++ DYE_COLOR_MAP.put(15, "black"); ++ } ++ ++ static final Map> BED_BLOCK_MAP = new HashMap<>(); ++ ++ static { ++ for (final Int2ObjectMap.Entry entry : DYE_COLOR_MAP.int2ObjectEntrySet()) { ++ if (!Objects.equals(entry.getValue(), "red")) { ++ addBeds(BED_BLOCK_MAP, entry.getIntKey(), entry.getValue()); ++ } ++ } ++ } ++ ++ private static void addBeds(final Map> into, final int colourId, final String colourName) { ++ into.put("southfalsefoot" + colourId, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + colourName + "_bed',Properties:{facing:'south',occupied:'false',part:'foot'}}")); ++ into.put("westfalsefoot" + colourId, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + colourName + "_bed',Properties:{facing:'west',occupied:'false',part:'foot'}}")); ++ into.put("northfalsefoot" + colourId, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + colourName + "_bed',Properties:{facing:'north',occupied:'false',part:'foot'}}")); ++ into.put("eastfalsefoot" + colourId, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + colourName + "_bed',Properties:{facing:'east',occupied:'false',part:'foot'}}")); ++ into.put("southfalsehead" + colourId, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + colourName + "_bed',Properties:{facing:'south',occupied:'false',part:'head'}}")); ++ into.put("westfalsehead" + colourId, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + colourName + "_bed',Properties:{facing:'west',occupied:'false',part:'head'}}")); ++ into.put("northfalsehead" + colourId, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + colourName + "_bed',Properties:{facing:'north',occupied:'false',part:'head'}}")); ++ into.put("eastfalsehead" + colourId, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + colourName + "_bed',Properties:{facing:'east',occupied:'false',part:'head'}}")); ++ into.put("southtruehead" + colourId, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + colourName + "_bed',Properties:{facing:'south',occupied:'true',part:'head'}}")); ++ into.put("westtruehead" + colourId, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + colourName + "_bed',Properties:{facing:'west',occupied:'true',part:'head'}}")); ++ into.put("northtruehead" + colourId, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + colourName + "_bed',Properties:{facing:'north',occupied:'true',part:'head'}}")); ++ into.put("easttruehead" + colourId, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + colourName + "_bed',Properties:{facing:'east',occupied:'true',part:'head'}}")); ++ } ++ ++ static final Map> BANNER_BLOCK_MAP = new HashMap<>(); ++ ++ static { ++ for (final Int2ObjectMap.Entry entry : DYE_COLOR_MAP.int2ObjectEntrySet()) { ++ if (!Objects.equals(entry.getValue(), "white")) { ++ addBanners(BANNER_BLOCK_MAP, 15 - entry.getIntKey(), entry.getValue()); ++ } ++ } ++ } ++ ++ private static void addBanners(final Map> into, final int colourId, final String colourName) { ++ for(int rotation = 0; rotation < 16; ++rotation) { ++ into.put("" + rotation + "_" + colourId, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + colourName + "_banner',Properties:{rotation:'" + rotation + "'}}")); ++ } ++ ++ into.put("north_" + colourId, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + colourName + "_wall_banner',Properties:{facing:'north'}}")); ++ into.put("south_" + colourId, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + colourName + "_wall_banner',Properties:{facing:'south'}}")); ++ into.put("west_" + colourId, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + colourName + "_wall_banner',Properties:{facing:'west'}}")); ++ into.put("east_" + colourId, HelperBlockFlatteningV1450.parseTag("{Name:'minecraft:" + colourName + "_wall_banner',Properties:{facing:'east'}}")); ++ } ++ ++ static final MapType AIR = Objects.requireNonNull(HelperBlockFlatteningV1450.getNBTForId(0)); ++ ++ public ConverterFlattenChunk() { ++ super(MCVersions.V17W47A, 1); ++ } ++ ++ static String getName(final MapType blockState) { ++ return blockState.getString("Name"); ++ } ++ ++ static String getProperty(final MapType blockState, final String propertyName) { ++ final MapType properties = blockState.getMap("Properties"); ++ if (properties == null) { ++ return ""; ++ } ++ ++ return properties.getString(propertyName, ""); ++ } ++ ++ static int getSideMask(final boolean noLeft, final boolean noRight, final boolean noBack, final boolean noForward) { ++ if (noBack) { ++ if (noRight) { ++ return 2; ++ } else if (noLeft) { ++ return 128; ++ } else { ++ return 1; ++ } ++ } else if (noForward) { ++ if (noLeft) { ++ return 32; ++ } else if (noRight) { ++ return 8; ++ } else { ++ return 16; ++ } ++ } else if (noRight) { ++ return 4; ++ } else if (noLeft) { ++ return 64; ++ } else { ++ return 0; ++ } ++ } ++ ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType level = data.getMap("Level"); ++ if (level == null) { ++ return null; ++ } ++ ++ if (!level.hasKey("Sections", ObjectType.LIST)) { ++ return null; ++ } ++ ++ data.setMap("Level", new UpgradeChunk(level).writeBackToLevel()); ++ ++ return null; ++ } ++ ++ static enum Direction { ++ DOWN(AxisDirection.NEGATIVE, Axis.Y), ++ UP(AxisDirection.POSITIVE, Axis.Y), ++ NORTH(AxisDirection.NEGATIVE, Axis.Z), ++ SOUTH(AxisDirection.POSITIVE, Axis.Z), ++ WEST(AxisDirection.NEGATIVE, Axis.X), ++ EAST(AxisDirection.POSITIVE, Axis.X); ++ ++ private final Axis axis; ++ private final AxisDirection axisDirection; ++ ++ private Direction(final AxisDirection axisDirection, final Axis axis) { ++ this.axis = axis; ++ this.axisDirection = axisDirection; ++ } ++ ++ public AxisDirection getAxisDirection() { ++ return this.axisDirection; ++ } ++ ++ public Axis getAxis() { ++ return this.axis; ++ } ++ ++ public static enum AxisDirection { ++ POSITIVE(1), ++ NEGATIVE(-1); ++ ++ private final int step; ++ ++ private AxisDirection(final int step) { ++ this.step = step; ++ } ++ ++ public int getStep() { ++ return this.step; ++ } ++ } ++ ++ public static enum Axis { ++ X, Y, Z; ++ } ++ } ++ ++ static class DataLayer { ++ private final byte[] data; ++ ++ public DataLayer() { ++ this.data = new byte[2048]; ++ } ++ ++ public DataLayer(final byte[] data) { ++ this.data = data; ++ if (data.length != 2048) { ++ throw new IllegalArgumentException("ChunkNibbleArrays should be 2048 bytes not: " + data.length); ++ } ++ } ++ ++ public static DataLayer getOrNull(final byte[] data) { ++ return data == null ? null : new DataLayer(data); ++ } ++ ++ public static DataLayer getOrCreate(final byte[] data) { ++ return data == null ? new DataLayer() : new DataLayer(data); ++ } ++ ++ public int get(final int index) { ++ final byte value = this.data[index >>> 1]; ++ ++ // if we are an even index, we want lower 4 bits ++ // if we are an odd index, we want upper 4 bits ++ return ((value >>> ((index & 1) << 2)) & 0xF); ++ } ++ ++ public int get(final int x, final int y, final int z) { ++ final int index = y << 8 | z << 4 | x; ++ final byte value = this.data[index >>> 1]; ++ ++ // if we are an even index, we want lower 4 bits ++ // if we are an odd index, we want upper 4 bits ++ return ((value >>> ((index & 1) << 2)) & 0xF); ++ } ++ } ++ ++ static final class UpgradeChunk { ++ int sides; ++ ++ final Section[] sections = new Section[16]; ++ final MapType level; ++ final int blockX; ++ final int blockZ; ++ final Int2ObjectLinkedOpenHashMap> tileEntities = new Int2ObjectLinkedOpenHashMap<>(16); ++ ++ public UpgradeChunk(final MapType level) { ++ this.level = level; ++ this.blockX = level.getInt("xPos") << 4; ++ this.blockZ = level.getInt("zPos") << 4; ++ ++ final ListType tileEntities = level.getList("TileEntities", ObjectType.MAP); ++ if (tileEntities != null) { ++ for (int i = 0, len = tileEntities.size(); i < len; ++i) { ++ final MapType tileEntity = tileEntities.getMap(i); ++ ++ final int x = (tileEntity.getInt("x") - this.blockX) & 15; ++ final int y = tileEntity.getInt("y"); ++ final int z = (tileEntity.getInt("z") - this.blockZ) & 15; ++ final int index = (y << 8) | (z << 4) | x; ++ if (this.tileEntities.put(index, tileEntity) != null) { ++ LOGGER.warn("In chunk: {}x{} found a duplicate block entity at position (ConverterFlattenChunk): [{}, {}, {}]", this.blockX, this.blockZ, x, y, z); ++ } ++ } ++ } ++ ++ final boolean convertedFromAlphaFormat = level.getBoolean("convertedFromAlphaFormat"); ++ final ListType sections = level.getList("Sections", ObjectType.MAP); ++ if (sections != null) { ++ for (int i = 0, len = sections.size(); i < len; ++i) { ++ final MapType sectionData = sections.getMap(i); ++ final Section section = new Section(sectionData); ++ ++ if (section.y < 0 || section.y > 15) { ++ LOGGER.warn("In chunk: {}x{} found an invalid chunk section y (ConverterFlattenChunk): {}", this.blockX, this.blockZ, section.y); ++ continue; ++ } ++ ++ if (this.sections[section.y] != null) { ++ LOGGER.warn("In chunk: {}x{} found a duplicate chunk section (ConverterFlattenChunk): {}", this.blockX, this.blockZ, section.y); ++ } ++ ++ this.sides = section.upgrade(this.sides); ++ this.sections[section.y] = section; ++ } ++ } ++ ++ for (final Section section : this.sections) { ++ if (section == null) { ++ continue; ++ } ++ ++ final int yIndex = section.y << (8 + 4); ++ ++ for (final Iterator> iterator = section.toFix.int2ObjectEntrySet().fastIterator(); iterator.hasNext();) { ++ final Int2ObjectMap.Entry fixEntry = iterator.next(); ++ final IntIterator positionIterator = fixEntry.getValue().iterator(); ++ switch (fixEntry.getIntKey()) { ++ case 2: { // grass block ++ while (positionIterator.hasNext()) { ++ final int position = positionIterator.nextInt() | yIndex; ++ final MapType blockState = this.getBlock(position); ++ if (!"minecraft:grass_block".equals(getName(blockState))) { ++ continue; ++ } ++ ++ final String blockAbove = getName(getBlock(relative(position, Direction.UP))); ++ if ("minecraft:snow".equals(blockAbove) || "minecraft:snow_layer".equals(blockAbove)) { ++ this.setBlock(position, SNOWY_GRASS); ++ } ++ } ++ break; ++ } ++ case 3: { // dirt ++ while (positionIterator.hasNext()) { ++ final int position = positionIterator.nextInt() | yIndex; ++ final MapType blockState = this.getBlock(position); ++ if (!"minecraft:podzol".equals(getName(blockState))) { ++ continue; ++ } ++ ++ final String blockAbove = getName(getBlock(relative(position, Direction.UP))); ++ if ("minecraft:snow".equals(blockAbove) || "minecraft:snow_layer".equals(blockAbove)) { ++ this.setBlock(position, SNOWY_PODZOL); ++ } ++ } ++ break; ++ } ++ case 25: { // note block ++ while (positionIterator.hasNext()) { ++ final int position = positionIterator.nextInt() | yIndex; ++ final MapType tile = this.removeBlockEntity(position); ++ if (tile != null) { ++ final String state = Boolean.toString(tile.getBoolean("powered")) + (byte) Math.min(Math.max(tile.getInt("note"), 0), 24); ++ this.setBlock(position, NOTE_BLOCK_MAP.getOrDefault(state, NOTE_BLOCK_MAP.get("false0"))); ++ } ++ } ++ break; ++ } ++ case 26: { // bed ++ while (positionIterator.hasNext()) { ++ final int position = positionIterator.nextInt() | yIndex; ++ final MapType tile = this.getBlockEntity(position); ++ ++ if (tile == null) { ++ continue; ++ } ++ ++ final MapType blockState = this.getBlock(position); ++ ++ final int colour = tile.getInt("color"); ++ if (colour != 14 && colour >= 0 && colour < 16) { ++ final String state = getProperty(blockState, "facing") + getProperty(blockState, "occupied") + getProperty(blockState, "part") + colour; ++ ++ final MapType update = BED_BLOCK_MAP.get(state); ++ if (update != null) { ++ this.setBlock(position, update); ++ } ++ } ++ } ++ break; ++ } ++ case 64: // oak door ++ case 71: // iron door ++ case 193: // spruce door ++ case 194: // birch door ++ case 195: // jungle door ++ case 196: // acacia door ++ case 197: { // dark oak door ++ // aka the door updater ++ while (positionIterator.hasNext()) { ++ final int position = positionIterator.nextInt() | yIndex; ++ final MapType blockState = this.getBlock(position); ++ if (!getName(blockState).endsWith("_door")) { ++ continue; ++ } ++ ++ if (!"lower".equals(getProperty(blockState, "half"))) { ++ continue; ++ } ++ ++ final int positionAbove = relative(position, Direction.UP); ++ final MapType blockStateAbove = this.getBlock(positionAbove); ++ ++ final String name = getName(blockState); ++ if (name.equals(getName(blockStateAbove))) { ++ final String facingBelow = getProperty(blockState, "facing"); ++ final String openBelow = getProperty(blockState, "open"); ++ final String hingeAbove = convertedFromAlphaFormat ? "left" : getProperty(blockStateAbove, "hinge"); ++ final String poweredAbove = convertedFromAlphaFormat ? "false" : getProperty(blockStateAbove, "powered"); ++ ++ this.setBlock(position, DOOR_MAP.get(name + facingBelow + "lower" + hingeAbove + openBelow + poweredAbove)); ++ this.setBlock(positionAbove, DOOR_MAP.get(name + facingBelow + "upper" + hingeAbove + openBelow + poweredAbove)); ++ } ++ } ++ break; ++ } ++ case 86: { // pumpkin ++ while (positionIterator.hasNext()) { ++ final int position = positionIterator.nextInt() | yIndex; ++ final MapType blockState = this.getBlock(position); ++ ++ // I guess this is some terrible hack to convert carved pumpkins from world gen into ++ // regular pumpkins? ++ ++ if ("minecraft:carved_pumpkin".equals(getName(blockState))) { ++ final String downName = getName(this.getBlock(relative(position, Direction.DOWN))); ++ if ("minecraft:grass_block".equals(downName) || "minecraft:dirt".equals(downName)) { ++ this.setBlock(position, PUMPKIN); ++ } ++ } ++ } ++ break; ++ } ++ case 110: { // mycelium ++ while (positionIterator.hasNext()) { ++ final int position = positionIterator.nextInt() | yIndex; ++ final MapType blockState = this.getBlock(position); ++ if ("minecraft:mycelium".equals(getName(blockState))) { ++ final String nameAbove = getName(this.getBlock(relative(position, Direction.UP))); ++ if ("minecraft:snow".equals(nameAbove) || "minecraft:snow_layer".equals(nameAbove)) { ++ this.setBlock(position, SNOWY_MYCELIUM); ++ } ++ } ++ } ++ break; ++ } ++ case 140: { // flower pot ++ while (positionIterator.hasNext()) { ++ final int position = positionIterator.nextInt() | yIndex; ++ final MapType tile = this.removeBlockEntity(position); ++ if (tile == null) { ++ continue; ++ } ++ ++ final String item; ++ if (tile.hasKey("Item", ObjectType.NUMBER)) { ++ // the item name converter should have migrated to number, however no legacy converter ++ // ever did this. so we can get data with versions above v102 (old worlds, converted prior to DFU) ++ // that didn't convert. so just do it here. ++ item = HelperItemNameV102.getNameFromId(tile.getInt("Item")); ++ } else { ++ item = tile.getString("Item", ""); ++ } ++ ++ final String state = item + tile.getInt("Data"); ++ this.setBlock(position, FLOWER_POT_MAP.getOrDefault(state, FLOWER_POT_MAP.get("minecraft:air0"))); ++ } ++ break; ++ } ++ case 144: { // mob head ++ while (positionIterator.hasNext()) { ++ final int position = positionIterator.nextInt() | yIndex; ++ final MapType tile = this.getBlockEntity(position); ++ if (tile == null) { ++ continue; ++ } ++ ++ final String typeString = Integer.toString(tile.getInt("SkullType")); ++ final String facing = getProperty(this.getBlock(position), "facing"); ++ final String state; ++ if (!"up".equals(facing) && !"down".equals(facing)) { ++ state = typeString + facing; ++ } else { ++ state = typeString + tile.getInt("Rot"); ++ } ++ ++ tile.remove("SkullType"); ++ tile.remove("facing"); ++ tile.remove("Rot"); ++ ++ this.setBlock(position, SKULL_MAP.getOrDefault(state, SKULL_MAP.get("0north"))); ++ } ++ break; ++ } ++ case 175: { // sunflower ++ while (positionIterator.hasNext()) { ++ final int position = positionIterator.nextInt() | yIndex; ++ final MapType blockState = this.getBlock(position); ++ if (!"upper".equals(getProperty(blockState, "half"))) { ++ continue; ++ } ++ ++ final MapType blockStateBelow = this.getBlock(relative(position, Direction.DOWN)); ++ final String nameBelow = getName(blockStateBelow); ++ switch (nameBelow) { ++ case "minecraft:sunflower": ++ this.setBlock(position, UPPER_SUNFLOWER); ++ break; ++ case "minecraft:lilac": ++ this.setBlock(position, UPPER_LILAC); ++ break; ++ case "minecraft:tall_grass": ++ this.setBlock(position, UPPER_TALL_GRASS); ++ break; ++ case "minecraft:large_fern": ++ this.setBlock(position, UPPER_LARGE_FERN); ++ break; ++ case "minecraft:rose_bush": ++ this.setBlock(position, UPPER_ROSE_BUSH); ++ break; ++ case "minecraft:peony": ++ this.setBlock(position, UPPER_PEONY); ++ break; ++ } ++ } ++ break; ++ } ++ case 176: // free standing banner ++ case 177: { // wall mounted banner ++ while (positionIterator.hasNext()) { ++ final int position = positionIterator.nextInt() | yIndex; ++ final MapType tile = this.getBlockEntity(position); ++ ++ if (tile == null) { ++ continue; ++ } ++ ++ final MapType blockState = this.getBlock(position); ++ ++ final int base = tile.getInt("Base"); ++ if (base != 15 && base >= 0 && base < 16) { ++ final String state = getProperty(blockState, fixEntry.getIntKey() == 176 ? "rotation" : "facing") + "_" + base; ++ final MapType update = BANNER_BLOCK_MAP.get(state); ++ if (update != null) { ++ this.setBlock(position, update); ++ } ++ } ++ } ++ break; ++ } ++ } ++ } ++ } ++ } ++ ++ private MapType getBlockEntity(final int index) { ++ return this.tileEntities.get(index); ++ } ++ ++ private MapType removeBlockEntity(final int index) { ++ return this.tileEntities.remove(index); ++ } ++ ++ public static int relative(final int index, final Direction direction) { ++ switch (direction.getAxis()) { ++ case X: ++ int j = (index & 15) + direction.getAxisDirection().getStep(); ++ return j >= 0 && j <= 15 ? index & -16 | j : -1; ++ case Y: ++ int k = (index >> 8) + direction.getAxisDirection().getStep(); ++ return k >= 0 && k <= 255 ? index & 255 | k << 8 : -1; ++ case Z: ++ int l = (index >> 4 & 15) + direction.getAxisDirection().getStep(); ++ return l >= 0 && l <= 15 ? index & -241 | l << 4 : -1; ++ default: ++ return -1; ++ } ++ } ++ ++ private void setBlock(final int index, final MapType blockState) { ++ if (index >= 0 && index <= 65535) { ++ final Section section = this.getSection(index); ++ if (section != null) { ++ section.setBlock(index & 4095, blockState); ++ } ++ } ++ } ++ ++ private Section getSection(final int index) { ++ final int y = index >> 12; ++ return y < this.sections.length ? this.sections[y] : null; ++ } ++ ++ public MapType getBlock(int i) { ++ if (i >= 0 && i <= 65535) { ++ final Section section = this.getSection(i); ++ return section == null ? AIR : section.getBlock(i & 4095); ++ } else { ++ return AIR; ++ } ++ } ++ ++ public MapType writeBackToLevel() { ++ if (this.tileEntities.isEmpty()) { ++ this.level.remove("TileEntities"); ++ } else { ++ final ListType tileEntities = Types.NBT.createEmptyList(); ++ this.tileEntities.values().forEach(tileEntities::addMap); ++ this.level.setList("TileEntities", tileEntities); ++ } ++ ++ final MapType indices = Types.NBT.createEmptyMap(); ++ final ListType sections = Types.NBT.createEmptyList(); ++ for (final Section section : this.sections) { ++ if (section == null) { ++ continue; ++ } ++ ++ sections.addMap(section.writeBackToSection()); ++ indices.setInts(Integer.toString(section.y), Arrays.copyOf(section.update.elements(), section.update.size())); ++ } ++ ++ this.level.setList("Sections", sections); ++ ++ final MapType upgradeData = Types.NBT.createEmptyMap(); ++ upgradeData.setByte("Sides", (byte)this.sides); ++ upgradeData.setMap("Indices", indices); ++ ++ this.level.setMap("UpgradeData", upgradeData); ++ ++ return this.level; ++ } ++ } ++ ++ static class Section { ++ final Palette palette = new Palette(); ++ ++ static final class Palette extends Reference2IntOpenHashMap> { ++ ++ final ListType paletteStates = Types.NBT.createEmptyList(); ++ ++ private int find(final MapType k) { ++ if (((k) == (null))) ++ return containsNullKey ? n : -(n + 1); ++ MapType curr; ++ final Object[] key = this.key; ++ int pos; ++ // The starting point. ++ if (((curr = (MapType)key[pos = (it.unimi.dsi.fastutil.HashCommon.mix(System.identityHashCode(k))) & mask]) == (null))) ++ return -(pos + 1); ++ if (((k) == (curr))) ++ return pos; ++ // There's always an unused entry. ++ while (true) { ++ if (((curr = (MapType)key[pos = (pos + 1) & mask]) == (null))) ++ return -(pos + 1); ++ if (((k) == (curr))) ++ return pos; ++ } ++ } ++ ++ private void insert(final int pos, final MapType k, final int v) { ++ if (pos == n) ++ containsNullKey = true; ++ ((Object[])key)[pos] = k; ++ value[pos] = v; ++ if (size++ >= maxFill) ++ rehash(arraySize(size + 1, f)); ++ } ++ ++ private MapType[] byId = new MapType[4]; ++ private MapType last = null; ++ ++ public int getOrCreateId(final MapType k) { ++ if (k == this.last) { ++ return this.size - 1; ++ } ++ final int pos = find(k); ++ if (pos >= 0) { ++ return this.value[pos]; ++ } ++ ++ final int insert = this.size; ++ MapType inPalette = k; ++ ++ if ("%%FILTER_ME%%".equals(getName(k))) { ++ inPalette = AIR; ++ } ++ ++ if (insert >= this.byId.length) { ++ this.byId = Arrays.copyOf(this.byId, this.byId.length * 2); ++ this.byId[insert] = k; ++ } else { ++ this.byId[insert] = k; ++ } ++ this.paletteStates.addMap(inPalette); ++ ++ this.last = k; ++ ++ this.insert(-pos - 1, k, insert); ++ ++ return insert; ++ } ++ ++ } ++ ++ final MapType section; ++ final boolean hasData; ++ final Int2ObjectLinkedOpenHashMap toFix = new Int2ObjectLinkedOpenHashMap<>(); ++ final IntArrayList update = new IntArrayList(); ++ final int y; ++ final int[] buffer = new int[4096]; ++ ++ public Section(final MapType section) { ++ this.section = section; ++ this.y = section.getInt("Y"); ++ this.hasData = section.hasKey("Blocks", ObjectType.BYTE_ARRAY); ++ } ++ ++ public MapType getBlock(final int index) { ++ if (index >= 0 && index <= 4095) { ++ final MapType state = this.palette.byId[this.buffer[index]]; ++ return state == null ? AIR : state; ++ } else { ++ return AIR; ++ } ++ } ++ ++ public void setBlock(final int index, final MapType blockState) { ++ this.buffer[index] = this.palette.getOrCreateId(blockState); ++ } ++ ++ public int upgrade(int sides) { ++ if (!this.hasData) { ++ return sides; ++ } ++ ++ final byte[] blocks = this.section.getBytes("Blocks"); ++ final DataLayer data = DataLayer.getOrNull(this.section.getBytes("Data")); ++ final DataLayer add = DataLayer.getOrNull(this.section.getBytes("Add")); ++ ++ this.palette.getOrCreateId(AIR); ++ ++ for (int index = 0; index < 4096; ++index) { ++ final int x = index & 15; ++ final int z = index >> 4 & 15; ++ ++ int blockStateId = (blocks[index] & 255) << 4; ++ if (data != null) { ++ blockStateId |= data.get(index); ++ } ++ if (add != null) { ++ blockStateId |= add.get(index) << 12; ++ } ++ if (IDS_NEEDING_FIX[blockStateId >>> 4]) { ++ this.addFix(blockStateId >>> 4, index); ++ } ++ ++ if (VIRTUAL[blockStateId >>> 4]) { ++ final int additionalSides = getSideMask(x == 0, x == 15, z == 0, z == 15); ++ if (additionalSides == 0) { ++ this.update.add(index); ++ } else { ++ sides |= additionalSides; ++ } ++ } ++ ++ this.setBlock(index, HelperBlockFlatteningV1450.getNBTForId(blockStateId)); ++ } ++ ++ return sides; ++ } ++ ++ private void addFix(final int block, final int index) { ++ this.toFix.computeIfAbsent(block, (final int keyInMap) -> { ++ return new IntArrayList(); ++ }).add(index); ++ } ++ ++ // Note: modifies the current section and returns it. ++ public MapType writeBackToSection() { ++ if (!this.hasData) { ++ return this.section; ++ } ++ ++ this.section.setList("Palette", this.palette.paletteStates.copy()); // deep copy to ensure palette compound tags are NOT shared ++ ++ final int bitSize = Math.max(4, DataFixUtils.ceillog2(this.palette.size())); ++ final PackedBitStorage packedIds = new PackedBitStorage(bitSize, 4096); ++ ++ for(int index = 0; index < this.buffer.length; ++index) { ++ packedIds.set(index, this.buffer[index]); ++ } ++ ++ this.section.setLongs("BlockStates", packedIds.getRaw()); ++ ++ this.section.remove("Blocks"); ++ this.section.remove("Data"); ++ this.section.remove("Add"); ++ ++ return this.section; ++ } ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/entity/ConverterAbstractEntityRename.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/entity/ConverterAbstractEntityRename.java +new file mode 100644 +index 0000000000000000000000000000000000000000..06c075e643415d98b73734b749d9043091ebf9e5 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/entity/ConverterAbstractEntityRename.java +@@ -0,0 +1,38 @@ ++package ca.spottedleaf.dataconverter.minecraft.converters.entity; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.ConverterAbstractStringValueTypeRename; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.types.MapType; ++import java.util.function.Function; ++ ++public final class ConverterAbstractEntityRename { ++ ++ private ConverterAbstractEntityRename() {} ++ ++ public static void register(final int version, final Function renamer) { ++ register(version, 0, renamer); ++ } ++ ++ public static void register(final int version, final int subVersion, final Function renamer) { ++ MCTypeRegistry.ENTITY.addStructureConverter(new DataConverter<>(version) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final String id = data.getString("id"); ++ if (id == null) { ++ return null; ++ } ++ ++ final String converted = renamer.apply(id); ++ ++ if (converted != null) { ++ data.setString("id", converted); ++ } ++ ++ return null; ++ } ++ }); ++ ConverterAbstractStringValueTypeRename.register(version, subVersion, MCTypeRegistry.ENTITY_NAME, renamer); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/entity/ConverterFlattenEntity.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/entity/ConverterFlattenEntity.java +new file mode 100644 +index 0000000000000000000000000000000000000000..afad2d92f78d4727ff4440ad2778f018d5a2a609 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/entity/ConverterFlattenEntity.java +@@ -0,0 +1,371 @@ ++package ca.spottedleaf.dataconverter.minecraft.converters.entity; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.HelperBlockFlatteningV1450; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.ObjectType; ++ ++import java.util.HashMap; ++import java.util.Map; ++ ++public final class ConverterFlattenEntity extends DataConverter, MapType> { ++ ++ private static final Map BLOCK_NAME_TO_ID = new HashMap<>(); ++ static { ++ BLOCK_NAME_TO_ID.put("minecraft:air", 0); ++ BLOCK_NAME_TO_ID.put("minecraft:stone", 1); ++ BLOCK_NAME_TO_ID.put("minecraft:grass", 2); ++ BLOCK_NAME_TO_ID.put("minecraft:dirt", 3); ++ BLOCK_NAME_TO_ID.put("minecraft:cobblestone", 4); ++ BLOCK_NAME_TO_ID.put("minecraft:planks", 5); ++ BLOCK_NAME_TO_ID.put("minecraft:sapling", 6); ++ BLOCK_NAME_TO_ID.put("minecraft:bedrock", 7); ++ BLOCK_NAME_TO_ID.put("minecraft:flowing_water", 8); ++ BLOCK_NAME_TO_ID.put("minecraft:water", 9); ++ BLOCK_NAME_TO_ID.put("minecraft:flowing_lava", 10); ++ BLOCK_NAME_TO_ID.put("minecraft:lava", 11); ++ BLOCK_NAME_TO_ID.put("minecraft:sand", 12); ++ BLOCK_NAME_TO_ID.put("minecraft:gravel", 13); ++ BLOCK_NAME_TO_ID.put("minecraft:gold_ore", 14); ++ BLOCK_NAME_TO_ID.put("minecraft:iron_ore", 15); ++ BLOCK_NAME_TO_ID.put("minecraft:coal_ore", 16); ++ BLOCK_NAME_TO_ID.put("minecraft:log", 17); ++ BLOCK_NAME_TO_ID.put("minecraft:leaves", 18); ++ BLOCK_NAME_TO_ID.put("minecraft:sponge", 19); ++ BLOCK_NAME_TO_ID.put("minecraft:glass", 20); ++ BLOCK_NAME_TO_ID.put("minecraft:lapis_ore", 21); ++ BLOCK_NAME_TO_ID.put("minecraft:lapis_block", 22); ++ BLOCK_NAME_TO_ID.put("minecraft:dispenser", 23); ++ BLOCK_NAME_TO_ID.put("minecraft:sandstone", 24); ++ BLOCK_NAME_TO_ID.put("minecraft:noteblock", 25); ++ BLOCK_NAME_TO_ID.put("minecraft:bed", 26); ++ BLOCK_NAME_TO_ID.put("minecraft:golden_rail", 27); ++ BLOCK_NAME_TO_ID.put("minecraft:detector_rail", 28); ++ BLOCK_NAME_TO_ID.put("minecraft:sticky_piston", 29); ++ BLOCK_NAME_TO_ID.put("minecraft:web", 30); ++ BLOCK_NAME_TO_ID.put("minecraft:tallgrass", 31); ++ BLOCK_NAME_TO_ID.put("minecraft:deadbush", 32); ++ BLOCK_NAME_TO_ID.put("minecraft:piston", 33); ++ BLOCK_NAME_TO_ID.put("minecraft:piston_head", 34); ++ BLOCK_NAME_TO_ID.put("minecraft:wool", 35); ++ BLOCK_NAME_TO_ID.put("minecraft:piston_extension", 36); ++ BLOCK_NAME_TO_ID.put("minecraft:yellow_flower", 37); ++ BLOCK_NAME_TO_ID.put("minecraft:red_flower", 38); ++ BLOCK_NAME_TO_ID.put("minecraft:brown_mushroom", 39); ++ BLOCK_NAME_TO_ID.put("minecraft:red_mushroom", 40); ++ BLOCK_NAME_TO_ID.put("minecraft:gold_block", 41); ++ BLOCK_NAME_TO_ID.put("minecraft:iron_block", 42); ++ BLOCK_NAME_TO_ID.put("minecraft:double_stone_slab", 43); ++ BLOCK_NAME_TO_ID.put("minecraft:stone_slab", 44); ++ BLOCK_NAME_TO_ID.put("minecraft:brick_block", 45); ++ BLOCK_NAME_TO_ID.put("minecraft:tnt", 46); ++ BLOCK_NAME_TO_ID.put("minecraft:bookshelf", 47); ++ BLOCK_NAME_TO_ID.put("minecraft:mossy_cobblestone", 48); ++ BLOCK_NAME_TO_ID.put("minecraft:obsidian", 49); ++ BLOCK_NAME_TO_ID.put("minecraft:torch", 50); ++ BLOCK_NAME_TO_ID.put("minecraft:fire", 51); ++ BLOCK_NAME_TO_ID.put("minecraft:mob_spawner", 52); ++ BLOCK_NAME_TO_ID.put("minecraft:oak_stairs", 53); ++ BLOCK_NAME_TO_ID.put("minecraft:chest", 54); ++ BLOCK_NAME_TO_ID.put("minecraft:redstone_wire", 55); ++ BLOCK_NAME_TO_ID.put("minecraft:diamond_ore", 56); ++ BLOCK_NAME_TO_ID.put("minecraft:diamond_block", 57); ++ BLOCK_NAME_TO_ID.put("minecraft:crafting_table", 58); ++ BLOCK_NAME_TO_ID.put("minecraft:wheat", 59); ++ BLOCK_NAME_TO_ID.put("minecraft:farmland", 60); ++ BLOCK_NAME_TO_ID.put("minecraft:furnace", 61); ++ BLOCK_NAME_TO_ID.put("minecraft:lit_furnace", 62); ++ BLOCK_NAME_TO_ID.put("minecraft:standing_sign", 63); ++ BLOCK_NAME_TO_ID.put("minecraft:wooden_door", 64); ++ BLOCK_NAME_TO_ID.put("minecraft:ladder", 65); ++ BLOCK_NAME_TO_ID.put("minecraft:rail", 66); ++ BLOCK_NAME_TO_ID.put("minecraft:stone_stairs", 67); ++ BLOCK_NAME_TO_ID.put("minecraft:wall_sign", 68); ++ BLOCK_NAME_TO_ID.put("minecraft:lever", 69); ++ BLOCK_NAME_TO_ID.put("minecraft:stone_pressure_plate", 70); ++ BLOCK_NAME_TO_ID.put("minecraft:iron_door", 71); ++ BLOCK_NAME_TO_ID.put("minecraft:wooden_pressure_plate", 72); ++ BLOCK_NAME_TO_ID.put("minecraft:redstone_ore", 73); ++ BLOCK_NAME_TO_ID.put("minecraft:lit_redstone_ore", 74); ++ BLOCK_NAME_TO_ID.put("minecraft:unlit_redstone_torch", 75); ++ BLOCK_NAME_TO_ID.put("minecraft:redstone_torch", 76); ++ BLOCK_NAME_TO_ID.put("minecraft:stone_button", 77); ++ BLOCK_NAME_TO_ID.put("minecraft:snow_layer", 78); ++ BLOCK_NAME_TO_ID.put("minecraft:ice", 79); ++ BLOCK_NAME_TO_ID.put("minecraft:snow", 80); ++ BLOCK_NAME_TO_ID.put("minecraft:cactus", 81); ++ BLOCK_NAME_TO_ID.put("minecraft:clay", 82); ++ BLOCK_NAME_TO_ID.put("minecraft:reeds", 83); ++ BLOCK_NAME_TO_ID.put("minecraft:jukebox", 84); ++ BLOCK_NAME_TO_ID.put("minecraft:fence", 85); ++ BLOCK_NAME_TO_ID.put("minecraft:pumpkin", 86); ++ BLOCK_NAME_TO_ID.put("minecraft:netherrack", 87); ++ BLOCK_NAME_TO_ID.put("minecraft:soul_sand", 88); ++ BLOCK_NAME_TO_ID.put("minecraft:glowstone", 89); ++ BLOCK_NAME_TO_ID.put("minecraft:portal", 90); ++ BLOCK_NAME_TO_ID.put("minecraft:lit_pumpkin", 91); ++ BLOCK_NAME_TO_ID.put("minecraft:cake", 92); ++ BLOCK_NAME_TO_ID.put("minecraft:unpowered_repeater", 93); ++ BLOCK_NAME_TO_ID.put("minecraft:powered_repeater", 94); ++ BLOCK_NAME_TO_ID.put("minecraft:stained_glass", 95); ++ BLOCK_NAME_TO_ID.put("minecraft:trapdoor", 96); ++ BLOCK_NAME_TO_ID.put("minecraft:monster_egg", 97); ++ BLOCK_NAME_TO_ID.put("minecraft:stonebrick", 98); ++ BLOCK_NAME_TO_ID.put("minecraft:brown_mushroom_block", 99); ++ BLOCK_NAME_TO_ID.put("minecraft:red_mushroom_block", 100); ++ BLOCK_NAME_TO_ID.put("minecraft:iron_bars", 101); ++ BLOCK_NAME_TO_ID.put("minecraft:glass_pane", 102); ++ BLOCK_NAME_TO_ID.put("minecraft:melon_block", 103); ++ BLOCK_NAME_TO_ID.put("minecraft:pumpkin_stem", 104); ++ BLOCK_NAME_TO_ID.put("minecraft:melon_stem", 105); ++ BLOCK_NAME_TO_ID.put("minecraft:vine", 106); ++ BLOCK_NAME_TO_ID.put("minecraft:fence_gate", 107); ++ BLOCK_NAME_TO_ID.put("minecraft:brick_stairs", 108); ++ BLOCK_NAME_TO_ID.put("minecraft:stone_brick_stairs", 109); ++ BLOCK_NAME_TO_ID.put("minecraft:mycelium", 110); ++ BLOCK_NAME_TO_ID.put("minecraft:waterlily", 111); ++ BLOCK_NAME_TO_ID.put("minecraft:nether_brick", 112); ++ BLOCK_NAME_TO_ID.put("minecraft:nether_brick_fence", 113); ++ BLOCK_NAME_TO_ID.put("minecraft:nether_brick_stairs", 114); ++ BLOCK_NAME_TO_ID.put("minecraft:nether_wart", 115); ++ BLOCK_NAME_TO_ID.put("minecraft:enchanting_table", 116); ++ BLOCK_NAME_TO_ID.put("minecraft:brewing_stand", 117); ++ BLOCK_NAME_TO_ID.put("minecraft:cauldron", 118); ++ BLOCK_NAME_TO_ID.put("minecraft:end_portal", 119); ++ BLOCK_NAME_TO_ID.put("minecraft:end_portal_frame", 120); ++ BLOCK_NAME_TO_ID.put("minecraft:end_stone", 121); ++ BLOCK_NAME_TO_ID.put("minecraft:dragon_egg", 122); ++ BLOCK_NAME_TO_ID.put("minecraft:redstone_lamp", 123); ++ BLOCK_NAME_TO_ID.put("minecraft:lit_redstone_lamp", 124); ++ BLOCK_NAME_TO_ID.put("minecraft:double_wooden_slab", 125); ++ BLOCK_NAME_TO_ID.put("minecraft:wooden_slab", 126); ++ BLOCK_NAME_TO_ID.put("minecraft:cocoa", 127); ++ BLOCK_NAME_TO_ID.put("minecraft:sandstone_stairs", 128); ++ BLOCK_NAME_TO_ID.put("minecraft:emerald_ore", 129); ++ BLOCK_NAME_TO_ID.put("minecraft:ender_chest", 130); ++ BLOCK_NAME_TO_ID.put("minecraft:tripwire_hook", 131); ++ BLOCK_NAME_TO_ID.put("minecraft:tripwire", 132); ++ BLOCK_NAME_TO_ID.put("minecraft:emerald_block", 133); ++ BLOCK_NAME_TO_ID.put("minecraft:spruce_stairs", 134); ++ BLOCK_NAME_TO_ID.put("minecraft:birch_stairs", 135); ++ BLOCK_NAME_TO_ID.put("minecraft:jungle_stairs", 136); ++ BLOCK_NAME_TO_ID.put("minecraft:command_block", 137); ++ BLOCK_NAME_TO_ID.put("minecraft:beacon", 138); ++ BLOCK_NAME_TO_ID.put("minecraft:cobblestone_wall", 139); ++ BLOCK_NAME_TO_ID.put("minecraft:flower_pot", 140); ++ BLOCK_NAME_TO_ID.put("minecraft:carrots", 141); ++ BLOCK_NAME_TO_ID.put("minecraft:potatoes", 142); ++ BLOCK_NAME_TO_ID.put("minecraft:wooden_button", 143); ++ BLOCK_NAME_TO_ID.put("minecraft:skull", 144); ++ BLOCK_NAME_TO_ID.put("minecraft:anvil", 145); ++ BLOCK_NAME_TO_ID.put("minecraft:trapped_chest", 146); ++ BLOCK_NAME_TO_ID.put("minecraft:light_weighted_pressure_plate", 147); ++ BLOCK_NAME_TO_ID.put("minecraft:heavy_weighted_pressure_plate", 148); ++ BLOCK_NAME_TO_ID.put("minecraft:unpowered_comparator", 149); ++ BLOCK_NAME_TO_ID.put("minecraft:powered_comparator", 150); ++ BLOCK_NAME_TO_ID.put("minecraft:daylight_detector", 151); ++ BLOCK_NAME_TO_ID.put("minecraft:redstone_block", 152); ++ BLOCK_NAME_TO_ID.put("minecraft:quartz_ore", 153); ++ BLOCK_NAME_TO_ID.put("minecraft:hopper", 154); ++ BLOCK_NAME_TO_ID.put("minecraft:quartz_block", 155); ++ BLOCK_NAME_TO_ID.put("minecraft:quartz_stairs", 156); ++ BLOCK_NAME_TO_ID.put("minecraft:activator_rail", 157); ++ BLOCK_NAME_TO_ID.put("minecraft:dropper", 158); ++ BLOCK_NAME_TO_ID.put("minecraft:stained_hardened_clay", 159); ++ BLOCK_NAME_TO_ID.put("minecraft:stained_glass_pane", 160); ++ BLOCK_NAME_TO_ID.put("minecraft:leaves2", 161); ++ BLOCK_NAME_TO_ID.put("minecraft:log2", 162); ++ BLOCK_NAME_TO_ID.put("minecraft:acacia_stairs", 163); ++ BLOCK_NAME_TO_ID.put("minecraft:dark_oak_stairs", 164); ++ BLOCK_NAME_TO_ID.put("minecraft:slime", 165); ++ BLOCK_NAME_TO_ID.put("minecraft:barrier", 166); ++ BLOCK_NAME_TO_ID.put("minecraft:iron_trapdoor", 167); ++ BLOCK_NAME_TO_ID.put("minecraft:prismarine", 168); ++ BLOCK_NAME_TO_ID.put("minecraft:sea_lantern", 169); ++ BLOCK_NAME_TO_ID.put("minecraft:hay_block", 170); ++ BLOCK_NAME_TO_ID.put("minecraft:carpet", 171); ++ BLOCK_NAME_TO_ID.put("minecraft:hardened_clay", 172); ++ BLOCK_NAME_TO_ID.put("minecraft:coal_block", 173); ++ BLOCK_NAME_TO_ID.put("minecraft:packed_ice", 174); ++ BLOCK_NAME_TO_ID.put("minecraft:double_plant", 175); ++ BLOCK_NAME_TO_ID.put("minecraft:standing_banner", 176); ++ BLOCK_NAME_TO_ID.put("minecraft:wall_banner", 177); ++ BLOCK_NAME_TO_ID.put("minecraft:daylight_detector_inverted", 178); ++ BLOCK_NAME_TO_ID.put("minecraft:red_sandstone", 179); ++ BLOCK_NAME_TO_ID.put("minecraft:red_sandstone_stairs", 180); ++ BLOCK_NAME_TO_ID.put("minecraft:double_stone_slab2", 181); ++ BLOCK_NAME_TO_ID.put("minecraft:stone_slab2", 182); ++ BLOCK_NAME_TO_ID.put("minecraft:spruce_fence_gate", 183); ++ BLOCK_NAME_TO_ID.put("minecraft:birch_fence_gate", 184); ++ BLOCK_NAME_TO_ID.put("minecraft:jungle_fence_gate", 185); ++ BLOCK_NAME_TO_ID.put("minecraft:dark_oak_fence_gate", 186); ++ BLOCK_NAME_TO_ID.put("minecraft:acacia_fence_gate", 187); ++ BLOCK_NAME_TO_ID.put("minecraft:spruce_fence", 188); ++ BLOCK_NAME_TO_ID.put("minecraft:birch_fence", 189); ++ BLOCK_NAME_TO_ID.put("minecraft:jungle_fence", 190); ++ BLOCK_NAME_TO_ID.put("minecraft:dark_oak_fence", 191); ++ BLOCK_NAME_TO_ID.put("minecraft:acacia_fence", 192); ++ BLOCK_NAME_TO_ID.put("minecraft:spruce_door", 193); ++ BLOCK_NAME_TO_ID.put("minecraft:birch_door", 194); ++ BLOCK_NAME_TO_ID.put("minecraft:jungle_door", 195); ++ BLOCK_NAME_TO_ID.put("minecraft:acacia_door", 196); ++ BLOCK_NAME_TO_ID.put("minecraft:dark_oak_door", 197); ++ BLOCK_NAME_TO_ID.put("minecraft:end_rod", 198); ++ BLOCK_NAME_TO_ID.put("minecraft:chorus_plant", 199); ++ BLOCK_NAME_TO_ID.put("minecraft:chorus_flower", 200); ++ BLOCK_NAME_TO_ID.put("minecraft:purpur_block", 201); ++ BLOCK_NAME_TO_ID.put("minecraft:purpur_pillar", 202); ++ BLOCK_NAME_TO_ID.put("minecraft:purpur_stairs", 203); ++ BLOCK_NAME_TO_ID.put("minecraft:purpur_double_slab", 204); ++ BLOCK_NAME_TO_ID.put("minecraft:purpur_slab", 205); ++ BLOCK_NAME_TO_ID.put("minecraft:end_bricks", 206); ++ BLOCK_NAME_TO_ID.put("minecraft:beetroots", 207); ++ BLOCK_NAME_TO_ID.put("minecraft:grass_path", 208); ++ BLOCK_NAME_TO_ID.put("minecraft:end_gateway", 209); ++ BLOCK_NAME_TO_ID.put("minecraft:repeating_command_block", 210); ++ BLOCK_NAME_TO_ID.put("minecraft:chain_command_block", 211); ++ BLOCK_NAME_TO_ID.put("minecraft:frosted_ice", 212); ++ BLOCK_NAME_TO_ID.put("minecraft:magma", 213); ++ BLOCK_NAME_TO_ID.put("minecraft:nether_wart_block", 214); ++ BLOCK_NAME_TO_ID.put("minecraft:red_nether_brick", 215); ++ BLOCK_NAME_TO_ID.put("minecraft:bone_block", 216); ++ BLOCK_NAME_TO_ID.put("minecraft:structure_void", 217); ++ BLOCK_NAME_TO_ID.put("minecraft:observer", 218); ++ BLOCK_NAME_TO_ID.put("minecraft:white_shulker_box", 219); ++ BLOCK_NAME_TO_ID.put("minecraft:orange_shulker_box", 220); ++ BLOCK_NAME_TO_ID.put("minecraft:magenta_shulker_box", 221); ++ BLOCK_NAME_TO_ID.put("minecraft:light_blue_shulker_box", 222); ++ BLOCK_NAME_TO_ID.put("minecraft:yellow_shulker_box", 223); ++ BLOCK_NAME_TO_ID.put("minecraft:lime_shulker_box", 224); ++ BLOCK_NAME_TO_ID.put("minecraft:pink_shulker_box", 225); ++ BLOCK_NAME_TO_ID.put("minecraft:gray_shulker_box", 226); ++ BLOCK_NAME_TO_ID.put("minecraft:silver_shulker_box", 227); ++ BLOCK_NAME_TO_ID.put("minecraft:cyan_shulker_box", 228); ++ BLOCK_NAME_TO_ID.put("minecraft:purple_shulker_box", 229); ++ BLOCK_NAME_TO_ID.put("minecraft:blue_shulker_box", 230); ++ BLOCK_NAME_TO_ID.put("minecraft:brown_shulker_box", 231); ++ BLOCK_NAME_TO_ID.put("minecraft:green_shulker_box", 232); ++ BLOCK_NAME_TO_ID.put("minecraft:red_shulker_box", 233); ++ BLOCK_NAME_TO_ID.put("minecraft:black_shulker_box", 234); ++ BLOCK_NAME_TO_ID.put("minecraft:white_glazed_terracotta", 235); ++ BLOCK_NAME_TO_ID.put("minecraft:orange_glazed_terracotta", 236); ++ BLOCK_NAME_TO_ID.put("minecraft:magenta_glazed_terracotta", 237); ++ BLOCK_NAME_TO_ID.put("minecraft:light_blue_glazed_terracotta", 238); ++ BLOCK_NAME_TO_ID.put("minecraft:yellow_glazed_terracotta", 239); ++ BLOCK_NAME_TO_ID.put("minecraft:lime_glazed_terracotta", 240); ++ BLOCK_NAME_TO_ID.put("minecraft:pink_glazed_terracotta", 241); ++ BLOCK_NAME_TO_ID.put("minecraft:gray_glazed_terracotta", 242); ++ BLOCK_NAME_TO_ID.put("minecraft:silver_glazed_terracotta", 243); ++ BLOCK_NAME_TO_ID.put("minecraft:cyan_glazed_terracotta", 244); ++ BLOCK_NAME_TO_ID.put("minecraft:purple_glazed_terracotta", 245); ++ BLOCK_NAME_TO_ID.put("minecraft:blue_glazed_terracotta", 246); ++ BLOCK_NAME_TO_ID.put("minecraft:brown_glazed_terracotta", 247); ++ BLOCK_NAME_TO_ID.put("minecraft:green_glazed_terracotta", 248); ++ BLOCK_NAME_TO_ID.put("minecraft:red_glazed_terracotta", 249); ++ BLOCK_NAME_TO_ID.put("minecraft:black_glazed_terracotta", 250); ++ BLOCK_NAME_TO_ID.put("minecraft:concrete", 251); ++ BLOCK_NAME_TO_ID.put("minecraft:concrete_powder", 252); ++ BLOCK_NAME_TO_ID.put("minecraft:structure_block", 255); ++ } ++ ++ protected static final int VERSION = MCVersions.V17W47A; ++ ++ protected final String[] paths; ++ ++ public ConverterFlattenEntity(final String... paths) { ++ super(VERSION, 3); ++ this.paths = paths; ++ } ++ ++ private static void register(final String id, final String... paths) { ++ MCTypeRegistry.ENTITY.addConverterForId(id, new ConverterFlattenEntity(paths)); ++ } ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:falling_block", new DataConverter<>(VERSION, 3) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final int blockId; ++ if (data.hasKey("Block")) { ++ final Number id = data.getNumber("Block"); ++ if (id != null) { ++ blockId = id.intValue(); ++ } else { ++ blockId = getBlockId(data.getString("Block")); ++ } ++ } else { ++ final Number tileId = data.getNumber("TileID"); ++ if (tileId != null) { ++ blockId = tileId.intValue(); ++ } else { ++ blockId = data.getByte("Tile") & 255; ++ } ++ } ++ ++ final int blockData = data.getInt("Data") & 15; ++ ++ data.remove("Block"); // from type update ++ data.remove("Data"); ++ data.remove("TileID"); ++ data.remove("Tile"); ++ ++ // key is from type update ++ data.setMap("BlockState", HelperBlockFlatteningV1450.getNBTForId((blockId << 4) | blockData).copy()); // copy to avoid problems with later state datafixers ++ ++ return null; ++ } ++ }); ++ register("minecraft:enderman", "carried", "carriedData", "carriedBlockState"); ++ register("minecraft:arrow", "inTile", "inData", "inBlockState"); ++ register("minecraft:spectral_arrow", "inTile", "inData", "inBlockState"); ++ register("minecraft:egg", "inTile"); ++ register("minecraft:ender_pearl", "inTile"); ++ register("minecraft:fireball", "inTile"); ++ register("minecraft:potion", "inTile"); ++ register("minecraft:small_fireball", "inTile"); ++ register("minecraft:snowball", "inTile"); ++ register("minecraft:wither_skull", "inTile"); ++ register("minecraft:xp_bottle", "inTile"); ++ register("minecraft:commandblock_minecart", "DisplayTile", "DisplayData", "DisplayState"); ++ register("minecraft:minecart", "DisplayTile", "DisplayData", "DisplayState"); ++ register("minecraft:chest_minecart", "DisplayTile", "DisplayData", "DisplayState"); ++ register("minecraft:furnace_minecart", "DisplayTile", "DisplayData", "DisplayState"); ++ register("minecraft:tnt_minecart", "DisplayTile", "DisplayData", "DisplayState"); ++ register("minecraft:hopper_minecart", "DisplayTile", "DisplayData", "DisplayState"); ++ register("minecraft:spawner_minecart", "DisplayTile", "DisplayData", "DisplayState"); ++ } ++ ++ public static int getBlockId(final String block) { ++ final Integer ret = BLOCK_NAME_TO_ID.get(block); ++ return ret == null ? 0 : ret.intValue(); ++ } ++ ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (this.paths.length == 1) { ++ data.remove(this.paths[0]); ++ return null; ++ } ++ final String idPath = this.paths[0]; ++ final String dataPath = this.paths[1]; ++ final String outputStatePath = this.paths[2]; ++ ++ final int blockId; ++ if (data.hasKey(idPath, ObjectType.NUMBER)) { ++ blockId = data.getInt(idPath); ++ } else { ++ blockId = getBlockId(data.getString(idPath)); ++ } ++ ++ final int blockData = data.getInt(dataPath) & 15; ++ ++ data.remove(idPath); // from type update ++ data.remove(dataPath); ++ ++ data.setMap(outputStatePath, HelperBlockFlatteningV1450.getNBTForId((blockId << 4) | blockData).copy()); // copy to avoid problems with later state datafixers ++ ++ 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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/helpers/ConverterAbstractStringValueTypeRename.java +@@ -0,0 +1,24 @@ ++package ca.spottedleaf.dataconverter.minecraft.converters.helpers; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCValueType; ++import java.util.function.Function; ++ ++public final class ConverterAbstractStringValueTypeRename { ++ ++ private ConverterAbstractStringValueTypeRename() {} ++ ++ public static void register(final int version, final MCValueType type, final Function renamer) { ++ register(version, 0, type, renamer); ++ } ++ public static void register(final int version, final int subVersion, final MCValueType type, final Function renamer) { ++ type.addConverter(new DataConverter<>(version, subVersion) { ++ @Override ++ public Object convert(final Object data, final long sourceVersion, final long toVersion) { ++ final String ret = (data instanceof String) ? renamer.apply((String)data) : null; ++ return ret == data ? null : ret; ++ } ++ }); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/helpers/HelperBlockFlatteningV1450.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/helpers/HelperBlockFlatteningV1450.java +new file mode 100644 +index 0000000000000000000000000000000000000000..02ee521dc0f61f3f01d443c46c1066d1ecbeea7f +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/helpers/HelperBlockFlatteningV1450.java +@@ -0,0 +1,1830 @@ ++package ca.spottedleaf.dataconverter.minecraft.converters.helpers; ++ ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.nbt.NBTMapType; ++import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; ++import net.minecraft.nbt.TagParser; ++import java.util.HashMap; ++import java.util.Map; ++ ++public final class HelperBlockFlatteningV1450 { ++ ++ protected static final MapType[] FLATTENED_BY_ID = new MapType[4096]; ++ protected static final MapType[] BLOCK_DEFAULTS = new MapType[4096]; ++ ++ private static final Object2IntOpenHashMap> ID_BY_OLD_NBT = new Object2IntOpenHashMap>(64, 0.7f) { ++ @Override ++ public int put(final MapType o, final int v) { ++ if (this.containsKey(o)) { ++ throw new RuntimeException("Already contains mapping for " + o); ++ } ++ ++ return super.put(o, v); ++ } ++ }; ++ static { ++ ID_BY_OLD_NBT.defaultReturnValue(-1); ++ } ++ ++ private static final Object2IntOpenHashMap ID_BY_OLD_NAME = new Object2IntOpenHashMap(64, 0.7f) { ++ @Override ++ public int put(final String o, final int v) { ++ if (this.containsKey(o)) { ++ throw new RuntimeException("Already contains mapping for " + o); ++ } ++ ++ return super.put(o, v); ++ } ++ }; ++ static { ++ ID_BY_OLD_NAME.defaultReturnValue(-1); ++ } ++ ++ // map used to ensure that each parsed block state contains no duplicates ++ protected static final Map, MapType> IDENTITY_ENSURE = new HashMap<>(); ++ ++ public static MapType parseTag(final String blockstate) { ++ try { ++ final MapType ret = new NBTMapType(TagParser.parseTag(blockstate.replace('\'', '"'))); ++ ++ synchronized (IDENTITY_ENSURE) { ++ final MapType identity = IDENTITY_ENSURE.putIfAbsent(ret, ret); ++ ++ return identity == null ? ret : identity; ++ } ++ ++ } catch (final Exception ex) { ++ throw new RuntimeException("Exception parsing " + blockstate, ex); ++ } ++ } ++ ++ private static void register(final int id, final String flattened, final String... preFlattenings) { ++ final MapType flattenedNBT = parseTag(flattened); ++ if (FLATTENED_BY_ID[id] != null) { ++ throw new RuntimeException("Mapping already exists for id " + id); ++ } ++ FLATTENED_BY_ID[id] = flattenedNBT; ++ ++ // it's important that we register ids from smallest to largest, so that ++ // the default is going to be correct ++ final int block = id >> 4; ++ if (BLOCK_DEFAULTS[block] == null) { ++ BLOCK_DEFAULTS[block] = flattenedNBT; ++ } ++ ++ for (final String preFlattening : preFlattenings) { ++ final MapType preFlatteningNBT = parseTag(preFlattening); ++ final String name = preFlatteningNBT.getString("Name"); ++ if (name == null) { ++ throw new RuntimeException("Name does not exist for pre flattenings for id " + id); ++ } ++ ++ // putIfAbsent so we default to the lowest id, which is going to be the block default ++ ID_BY_OLD_NAME.putIfAbsent(name, id); ++ ID_BY_OLD_NBT.put(preFlatteningNBT, id); ++ } ++ } ++ ++ private static void finalizeMaps() { ++ for(int i = 0; i < FLATTENED_BY_ID.length; ++i) { ++ if (FLATTENED_BY_ID[i] == null) { ++ FLATTENED_BY_ID[i] = BLOCK_DEFAULTS[i >> 4]; ++ } ++ } ++ } ++ ++ public static MapType flattenNBT(final MapType old) { ++ final int id = ID_BY_OLD_NBT.getInt(old); ++ final MapType ret = getNBTForIdRaw(id); ++ ++ return ret == null ? old : ret; ++ } ++ ++ public static String getNewBlockName(final String old) { ++ final int id = ID_BY_OLD_NAME.getInt(old); ++ final MapType ret = getNBTForIdRaw(id); ++ return ret == null ? old : ret.getString("Name"); ++ } ++ ++ public static String getNameForId(final int block) { ++ final MapType nbt = getNBTForIdRaw(block); ++ return nbt == null ? "minecraft:air" : nbt.getString("Name"); ++ } ++ ++ protected static MapType getNBTForIdRaw(final int block) { ++ return block >= 0 && block < FLATTENED_BY_ID.length ? FLATTENED_BY_ID[block] : null; ++ } ++ ++ public static MapType getNBTForId(final int block) { ++ MapType ret = getNBTForIdRaw(block); ++ return ret == null ? FLATTENED_BY_ID[0] : ret; ++ } ++ ++ private HelperBlockFlatteningV1450() {} ++ ++ static { ++ ID_BY_OLD_NBT.defaultReturnValue(-1); ++ register(0, "{Name:'minecraft:air'}", "{Name:'minecraft:air'}"); ++ register(16, "{Name:'minecraft:stone'}", "{Name:'minecraft:stone',Properties:{variant:'stone'}}"); ++ register(17, "{Name:'minecraft:granite'}", "{Name:'minecraft:stone',Properties:{variant:'granite'}}"); ++ register(18, "{Name:'minecraft:polished_granite'}", "{Name:'minecraft:stone',Properties:{variant:'smooth_granite'}}"); ++ register(19, "{Name:'minecraft:diorite'}", "{Name:'minecraft:stone',Properties:{variant:'diorite'}}"); ++ register(20, "{Name:'minecraft:polished_diorite'}", "{Name:'minecraft:stone',Properties:{variant:'smooth_diorite'}}"); ++ register(21, "{Name:'minecraft:andesite'}", "{Name:'minecraft:stone',Properties:{variant:'andesite'}}"); ++ register(22, "{Name:'minecraft:polished_andesite'}", "{Name:'minecraft:stone',Properties:{variant:'smooth_andesite'}}"); ++ register(32, "{Name:'minecraft:grass_block',Properties:{snowy:'false'}}", "{Name:'minecraft:grass',Properties:{snowy:'false'}}", "{Name:'minecraft:grass',Properties:{snowy:'true'}}"); ++ register(48, "{Name:'minecraft:dirt'}", "{Name:'minecraft:dirt',Properties:{snowy:'false',variant:'dirt'}}", "{Name:'minecraft:dirt',Properties:{snowy:'true',variant:'dirt'}}"); ++ register(49, "{Name:'minecraft:coarse_dirt'}", "{Name:'minecraft:dirt',Properties:{snowy:'false',variant:'coarse_dirt'}}", "{Name:'minecraft:dirt',Properties:{snowy:'true',variant:'coarse_dirt'}}"); ++ register(50, "{Name:'minecraft:podzol',Properties:{snowy:'false'}}", "{Name:'minecraft:dirt',Properties:{snowy:'false',variant:'podzol'}}", "{Name:'minecraft:dirt',Properties:{snowy:'true',variant:'podzol'}}"); ++ register(64, "{Name:'minecraft:cobblestone'}", "{Name:'minecraft:cobblestone'}"); ++ register(80, "{Name:'minecraft:oak_planks'}", "{Name:'minecraft:planks',Properties:{variant:'oak'}}"); ++ register(81, "{Name:'minecraft:spruce_planks'}", "{Name:'minecraft:planks',Properties:{variant:'spruce'}}"); ++ register(82, "{Name:'minecraft:birch_planks'}", "{Name:'minecraft:planks',Properties:{variant:'birch'}}"); ++ register(83, "{Name:'minecraft:jungle_planks'}", "{Name:'minecraft:planks',Properties:{variant:'jungle'}}"); ++ register(84, "{Name:'minecraft:acacia_planks'}", "{Name:'minecraft:planks',Properties:{variant:'acacia'}}"); ++ register(85, "{Name:'minecraft:dark_oak_planks'}", "{Name:'minecraft:planks',Properties:{variant:'dark_oak'}}"); ++ register(96, "{Name:'minecraft:oak_sapling',Properties:{stage:'0'}}", "{Name:'minecraft:sapling',Properties:{stage:'0',type:'oak'}}"); ++ register(97, "{Name:'minecraft:spruce_sapling',Properties:{stage:'0'}}", "{Name:'minecraft:sapling',Properties:{stage:'0',type:'spruce'}}"); ++ register(98, "{Name:'minecraft:birch_sapling',Properties:{stage:'0'}}", "{Name:'minecraft:sapling',Properties:{stage:'0',type:'birch'}}"); ++ register(99, "{Name:'minecraft:jungle_sapling',Properties:{stage:'0'}}", "{Name:'minecraft:sapling',Properties:{stage:'0',type:'jungle'}}"); ++ register(100, "{Name:'minecraft:acacia_sapling',Properties:{stage:'0'}}", "{Name:'minecraft:sapling',Properties:{stage:'0',type:'acacia'}}"); ++ register(101, "{Name:'minecraft:dark_oak_sapling',Properties:{stage:'0'}}", "{Name:'minecraft:sapling',Properties:{stage:'0',type:'dark_oak'}}"); ++ register(104, "{Name:'minecraft:oak_sapling',Properties:{stage:'1'}}", "{Name:'minecraft:sapling',Properties:{stage:'1',type:'oak'}}"); ++ register(105, "{Name:'minecraft:spruce_sapling',Properties:{stage:'1'}}", "{Name:'minecraft:sapling',Properties:{stage:'1',type:'spruce'}}"); ++ register(106, "{Name:'minecraft:birch_sapling',Properties:{stage:'1'}}", "{Name:'minecraft:sapling',Properties:{stage:'1',type:'birch'}}"); ++ register(107, "{Name:'minecraft:jungle_sapling',Properties:{stage:'1'}}", "{Name:'minecraft:sapling',Properties:{stage:'1',type:'jungle'}}"); ++ register(108, "{Name:'minecraft:acacia_sapling',Properties:{stage:'1'}}", "{Name:'minecraft:sapling',Properties:{stage:'1',type:'acacia'}}"); ++ register(109, "{Name:'minecraft:dark_oak_sapling',Properties:{stage:'1'}}", "{Name:'minecraft:sapling',Properties:{stage:'1',type:'dark_oak'}}"); ++ register(112, "{Name:'minecraft:bedrock'}", "{Name:'minecraft:bedrock'}"); ++ register(128, "{Name:'minecraft:water',Properties:{level:'0'}}", "{Name:'minecraft:flowing_water',Properties:{level:'0'}}"); ++ register(129, "{Name:'minecraft:water',Properties:{level:'1'}}", "{Name:'minecraft:flowing_water',Properties:{level:'1'}}"); ++ register(130, "{Name:'minecraft:water',Properties:{level:'2'}}", "{Name:'minecraft:flowing_water',Properties:{level:'2'}}"); ++ register(131, "{Name:'minecraft:water',Properties:{level:'3'}}", "{Name:'minecraft:flowing_water',Properties:{level:'3'}}"); ++ register(132, "{Name:'minecraft:water',Properties:{level:'4'}}", "{Name:'minecraft:flowing_water',Properties:{level:'4'}}"); ++ register(133, "{Name:'minecraft:water',Properties:{level:'5'}}", "{Name:'minecraft:flowing_water',Properties:{level:'5'}}"); ++ register(134, "{Name:'minecraft:water',Properties:{level:'6'}}", "{Name:'minecraft:flowing_water',Properties:{level:'6'}}"); ++ register(135, "{Name:'minecraft:water',Properties:{level:'7'}}", "{Name:'minecraft:flowing_water',Properties:{level:'7'}}"); ++ register(136, "{Name:'minecraft:water',Properties:{level:'8'}}", "{Name:'minecraft:flowing_water',Properties:{level:'8'}}"); ++ register(137, "{Name:'minecraft:water',Properties:{level:'9'}}", "{Name:'minecraft:flowing_water',Properties:{level:'9'}}"); ++ register(138, "{Name:'minecraft:water',Properties:{level:'10'}}", "{Name:'minecraft:flowing_water',Properties:{level:'10'}}"); ++ register(139, "{Name:'minecraft:water',Properties:{level:'11'}}", "{Name:'minecraft:flowing_water',Properties:{level:'11'}}"); ++ register(140, "{Name:'minecraft:water',Properties:{level:'12'}}", "{Name:'minecraft:flowing_water',Properties:{level:'12'}}"); ++ register(141, "{Name:'minecraft:water',Properties:{level:'13'}}", "{Name:'minecraft:flowing_water',Properties:{level:'13'}}"); ++ register(142, "{Name:'minecraft:water',Properties:{level:'14'}}", "{Name:'minecraft:flowing_water',Properties:{level:'14'}}"); ++ register(143, "{Name:'minecraft:water',Properties:{level:'15'}}", "{Name:'minecraft:flowing_water',Properties:{level:'15'}}"); ++ register(144, "{Name:'minecraft:water',Properties:{level:'0'}}", "{Name:'minecraft:water',Properties:{level:'0'}}"); ++ register(145, "{Name:'minecraft:water',Properties:{level:'1'}}", "{Name:'minecraft:water',Properties:{level:'1'}}"); ++ register(146, "{Name:'minecraft:water',Properties:{level:'2'}}", "{Name:'minecraft:water',Properties:{level:'2'}}"); ++ register(147, "{Name:'minecraft:water',Properties:{level:'3'}}", "{Name:'minecraft:water',Properties:{level:'3'}}"); ++ register(148, "{Name:'minecraft:water',Properties:{level:'4'}}", "{Name:'minecraft:water',Properties:{level:'4'}}"); ++ register(149, "{Name:'minecraft:water',Properties:{level:'5'}}", "{Name:'minecraft:water',Properties:{level:'5'}}"); ++ register(150, "{Name:'minecraft:water',Properties:{level:'6'}}", "{Name:'minecraft:water',Properties:{level:'6'}}"); ++ register(151, "{Name:'minecraft:water',Properties:{level:'7'}}", "{Name:'minecraft:water',Properties:{level:'7'}}"); ++ register(152, "{Name:'minecraft:water',Properties:{level:'8'}}", "{Name:'minecraft:water',Properties:{level:'8'}}"); ++ register(153, "{Name:'minecraft:water',Properties:{level:'9'}}", "{Name:'minecraft:water',Properties:{level:'9'}}"); ++ register(154, "{Name:'minecraft:water',Properties:{level:'10'}}", "{Name:'minecraft:water',Properties:{level:'10'}}"); ++ register(155, "{Name:'minecraft:water',Properties:{level:'11'}}", "{Name:'minecraft:water',Properties:{level:'11'}}"); ++ register(156, "{Name:'minecraft:water',Properties:{level:'12'}}", "{Name:'minecraft:water',Properties:{level:'12'}}"); ++ register(157, "{Name:'minecraft:water',Properties:{level:'13'}}", "{Name:'minecraft:water',Properties:{level:'13'}}"); ++ register(158, "{Name:'minecraft:water',Properties:{level:'14'}}", "{Name:'minecraft:water',Properties:{level:'14'}}"); ++ register(159, "{Name:'minecraft:water',Properties:{level:'15'}}", "{Name:'minecraft:water',Properties:{level:'15'}}"); ++ register(160, "{Name:'minecraft:lava',Properties:{level:'0'}}", "{Name:'minecraft:flowing_lava',Properties:{level:'0'}}"); ++ register(161, "{Name:'minecraft:lava',Properties:{level:'1'}}", "{Name:'minecraft:flowing_lava',Properties:{level:'1'}}"); ++ register(162, "{Name:'minecraft:lava',Properties:{level:'2'}}", "{Name:'minecraft:flowing_lava',Properties:{level:'2'}}"); ++ register(163, "{Name:'minecraft:lava',Properties:{level:'3'}}", "{Name:'minecraft:flowing_lava',Properties:{level:'3'}}"); ++ register(164, "{Name:'minecraft:lava',Properties:{level:'4'}}", "{Name:'minecraft:flowing_lava',Properties:{level:'4'}}"); ++ register(165, "{Name:'minecraft:lava',Properties:{level:'5'}}", "{Name:'minecraft:flowing_lava',Properties:{level:'5'}}"); ++ register(166, "{Name:'minecraft:lava',Properties:{level:'6'}}", "{Name:'minecraft:flowing_lava',Properties:{level:'6'}}"); ++ register(167, "{Name:'minecraft:lava',Properties:{level:'7'}}", "{Name:'minecraft:flowing_lava',Properties:{level:'7'}}"); ++ register(168, "{Name:'minecraft:lava',Properties:{level:'8'}}", "{Name:'minecraft:flowing_lava',Properties:{level:'8'}}"); ++ register(169, "{Name:'minecraft:lava',Properties:{level:'9'}}", "{Name:'minecraft:flowing_lava',Properties:{level:'9'}}"); ++ register(170, "{Name:'minecraft:lava',Properties:{level:'10'}}", "{Name:'minecraft:flowing_lava',Properties:{level:'10'}}"); ++ register(171, "{Name:'minecraft:lava',Properties:{level:'11'}}", "{Name:'minecraft:flowing_lava',Properties:{level:'11'}}"); ++ register(172, "{Name:'minecraft:lava',Properties:{level:'12'}}", "{Name:'minecraft:flowing_lava',Properties:{level:'12'}}"); ++ register(173, "{Name:'minecraft:lava',Properties:{level:'13'}}", "{Name:'minecraft:flowing_lava',Properties:{level:'13'}}"); ++ register(174, "{Name:'minecraft:lava',Properties:{level:'14'}}", "{Name:'minecraft:flowing_lava',Properties:{level:'14'}}"); ++ register(175, "{Name:'minecraft:lava',Properties:{level:'15'}}", "{Name:'minecraft:flowing_lava',Properties:{level:'15'}}"); ++ register(176, "{Name:'minecraft:lava',Properties:{level:'0'}}", "{Name:'minecraft:lava',Properties:{level:'0'}}"); ++ register(177, "{Name:'minecraft:lava',Properties:{level:'1'}}", "{Name:'minecraft:lava',Properties:{level:'1'}}"); ++ register(178, "{Name:'minecraft:lava',Properties:{level:'2'}}", "{Name:'minecraft:lava',Properties:{level:'2'}}"); ++ register(179, "{Name:'minecraft:lava',Properties:{level:'3'}}", "{Name:'minecraft:lava',Properties:{level:'3'}}"); ++ register(180, "{Name:'minecraft:lava',Properties:{level:'4'}}", "{Name:'minecraft:lava',Properties:{level:'4'}}"); ++ register(181, "{Name:'minecraft:lava',Properties:{level:'5'}}", "{Name:'minecraft:lava',Properties:{level:'5'}}"); ++ register(182, "{Name:'minecraft:lava',Properties:{level:'6'}}", "{Name:'minecraft:lava',Properties:{level:'6'}}"); ++ register(183, "{Name:'minecraft:lava',Properties:{level:'7'}}", "{Name:'minecraft:lava',Properties:{level:'7'}}"); ++ register(184, "{Name:'minecraft:lava',Properties:{level:'8'}}", "{Name:'minecraft:lava',Properties:{level:'8'}}"); ++ register(185, "{Name:'minecraft:lava',Properties:{level:'9'}}", "{Name:'minecraft:lava',Properties:{level:'9'}}"); ++ register(186, "{Name:'minecraft:lava',Properties:{level:'10'}}", "{Name:'minecraft:lava',Properties:{level:'10'}}"); ++ register(187, "{Name:'minecraft:lava',Properties:{level:'11'}}", "{Name:'minecraft:lava',Properties:{level:'11'}}"); ++ register(188, "{Name:'minecraft:lava',Properties:{level:'12'}}", "{Name:'minecraft:lava',Properties:{level:'12'}}"); ++ register(189, "{Name:'minecraft:lava',Properties:{level:'13'}}", "{Name:'minecraft:lava',Properties:{level:'13'}}"); ++ register(190, "{Name:'minecraft:lava',Properties:{level:'14'}}", "{Name:'minecraft:lava',Properties:{level:'14'}}"); ++ register(191, "{Name:'minecraft:lava',Properties:{level:'15'}}", "{Name:'minecraft:lava',Properties:{level:'15'}}"); ++ register(192, "{Name:'minecraft:sand'}", "{Name:'minecraft:sand',Properties:{variant:'sand'}}"); ++ register(193, "{Name:'minecraft:red_sand'}", "{Name:'minecraft:sand',Properties:{variant:'red_sand'}}"); ++ register(208, "{Name:'minecraft:gravel'}", "{Name:'minecraft:gravel'}"); ++ register(224, "{Name:'minecraft:gold_ore'}", "{Name:'minecraft:gold_ore'}"); ++ register(240, "{Name:'minecraft:iron_ore'}", "{Name:'minecraft:iron_ore'}"); ++ register(256, "{Name:'minecraft:coal_ore'}", "{Name:'minecraft:coal_ore'}"); ++ register(272, "{Name:'minecraft:oak_log',Properties:{axis:'y'}}", "{Name:'minecraft:log',Properties:{axis:'y',variant:'oak'}}"); ++ register(273, "{Name:'minecraft:spruce_log',Properties:{axis:'y'}}", "{Name:'minecraft:log',Properties:{axis:'y',variant:'spruce'}}"); ++ register(274, "{Name:'minecraft:birch_log',Properties:{axis:'y'}}", "{Name:'minecraft:log',Properties:{axis:'y',variant:'birch'}}"); ++ register(275, "{Name:'minecraft:jungle_log',Properties:{axis:'y'}}", "{Name:'minecraft:log',Properties:{axis:'y',variant:'jungle'}}"); ++ register(276, "{Name:'minecraft:oak_log',Properties:{axis:'x'}}", "{Name:'minecraft:log',Properties:{axis:'x',variant:'oak'}}"); ++ register(277, "{Name:'minecraft:spruce_log',Properties:{axis:'x'}}", "{Name:'minecraft:log',Properties:{axis:'x',variant:'spruce'}}"); ++ register(278, "{Name:'minecraft:birch_log',Properties:{axis:'x'}}", "{Name:'minecraft:log',Properties:{axis:'x',variant:'birch'}}"); ++ register(279, "{Name:'minecraft:jungle_log',Properties:{axis:'x'}}", "{Name:'minecraft:log',Properties:{axis:'x',variant:'jungle'}}"); ++ register(280, "{Name:'minecraft:oak_log',Properties:{axis:'z'}}", "{Name:'minecraft:log',Properties:{axis:'z',variant:'oak'}}"); ++ register(281, "{Name:'minecraft:spruce_log',Properties:{axis:'z'}}", "{Name:'minecraft:log',Properties:{axis:'z',variant:'spruce'}}"); ++ register(282, "{Name:'minecraft:birch_log',Properties:{axis:'z'}}", "{Name:'minecraft:log',Properties:{axis:'z',variant:'birch'}}"); ++ register(283, "{Name:'minecraft:jungle_log',Properties:{axis:'z'}}", "{Name:'minecraft:log',Properties:{axis:'z',variant:'jungle'}}"); ++ register(284, "{Name:'minecraft:oak_bark'}", "{Name:'minecraft:log',Properties:{axis:'none',variant:'oak'}}"); ++ register(285, "{Name:'minecraft:spruce_bark'}", "{Name:'minecraft:log',Properties:{axis:'none',variant:'spruce'}}"); ++ register(286, "{Name:'minecraft:birch_bark'}", "{Name:'minecraft:log',Properties:{axis:'none',variant:'birch'}}"); ++ register(287, "{Name:'minecraft:jungle_bark'}", "{Name:'minecraft:log',Properties:{axis:'none',variant:'jungle'}}"); ++ register(288, "{Name:'minecraft:oak_leaves',Properties:{check_decay:'false',decayable:'true'}}", "{Name:'minecraft:leaves',Properties:{check_decay:'false',decayable:'true',variant:'oak'}}"); ++ register(289, "{Name:'minecraft:spruce_leaves',Properties:{check_decay:'false',decayable:'true'}}", "{Name:'minecraft:leaves',Properties:{check_decay:'false',decayable:'true',variant:'spruce'}}"); ++ register(290, "{Name:'minecraft:birch_leaves',Properties:{check_decay:'false',decayable:'true'}}", "{Name:'minecraft:leaves',Properties:{check_decay:'false',decayable:'true',variant:'birch'}}"); ++ register(291, "{Name:'minecraft:jungle_leaves',Properties:{check_decay:'false',decayable:'true'}}", "{Name:'minecraft:leaves',Properties:{check_decay:'false',decayable:'true',variant:'jungle'}}"); ++ register(292, "{Name:'minecraft:oak_leaves',Properties:{check_decay:'false',decayable:'false'}}", "{Name:'minecraft:leaves',Properties:{check_decay:'false',decayable:'false',variant:'oak'}}"); ++ register(293, "{Name:'minecraft:spruce_leaves',Properties:{check_decay:'false',decayable:'false'}}", "{Name:'minecraft:leaves',Properties:{check_decay:'false',decayable:'false',variant:'spruce'}}"); ++ register(294, "{Name:'minecraft:birch_leaves',Properties:{check_decay:'false',decayable:'false'}}", "{Name:'minecraft:leaves',Properties:{check_decay:'false',decayable:'false',variant:'birch'}}"); ++ register(295, "{Name:'minecraft:jungle_leaves',Properties:{check_decay:'false',decayable:'false'}}", "{Name:'minecraft:leaves',Properties:{check_decay:'false',decayable:'false',variant:'jungle'}}"); ++ register(296, "{Name:'minecraft:oak_leaves',Properties:{check_decay:'true',decayable:'true'}}", "{Name:'minecraft:leaves',Properties:{check_decay:'true',decayable:'true',variant:'oak'}}"); ++ register(297, "{Name:'minecraft:spruce_leaves',Properties:{check_decay:'true',decayable:'true'}}", "{Name:'minecraft:leaves',Properties:{check_decay:'true',decayable:'true',variant:'spruce'}}"); ++ register(298, "{Name:'minecraft:birch_leaves',Properties:{check_decay:'true',decayable:'true'}}", "{Name:'minecraft:leaves',Properties:{check_decay:'true',decayable:'true',variant:'birch'}}"); ++ register(299, "{Name:'minecraft:jungle_leaves',Properties:{check_decay:'true',decayable:'true'}}", "{Name:'minecraft:leaves',Properties:{check_decay:'true',decayable:'true',variant:'jungle'}}"); ++ register(300, "{Name:'minecraft:oak_leaves',Properties:{check_decay:'true',decayable:'false'}}", "{Name:'minecraft:leaves',Properties:{check_decay:'true',decayable:'false',variant:'oak'}}"); ++ register(301, "{Name:'minecraft:spruce_leaves',Properties:{check_decay:'true',decayable:'false'}}", "{Name:'minecraft:leaves',Properties:{check_decay:'true',decayable:'false',variant:'spruce'}}"); ++ register(302, "{Name:'minecraft:birch_leaves',Properties:{check_decay:'true',decayable:'false'}}", "{Name:'minecraft:leaves',Properties:{check_decay:'true',decayable:'false',variant:'birch'}}"); ++ register(303, "{Name:'minecraft:jungle_leaves',Properties:{check_decay:'true',decayable:'false'}}", "{Name:'minecraft:leaves',Properties:{check_decay:'true',decayable:'false',variant:'jungle'}}"); ++ register(304, "{Name:'minecraft:sponge'}", "{Name:'minecraft:sponge',Properties:{wet:'false'}}"); ++ register(305, "{Name:'minecraft:wet_sponge'}", "{Name:'minecraft:sponge',Properties:{wet:'true'}}"); ++ register(320, "{Name:'minecraft:glass'}", "{Name:'minecraft:glass'}"); ++ register(336, "{Name:'minecraft:lapis_ore'}", "{Name:'minecraft:lapis_ore'}"); ++ register(352, "{Name:'minecraft:lapis_block'}", "{Name:'minecraft:lapis_block'}"); ++ register(368, "{Name:'minecraft:dispenser',Properties:{facing:'down',triggered:'false'}}", "{Name:'minecraft:dispenser',Properties:{facing:'down',triggered:'false'}}"); ++ register(369, "{Name:'minecraft:dispenser',Properties:{facing:'up',triggered:'false'}}", "{Name:'minecraft:dispenser',Properties:{facing:'up',triggered:'false'}}"); ++ register(370, "{Name:'minecraft:dispenser',Properties:{facing:'north',triggered:'false'}}", "{Name:'minecraft:dispenser',Properties:{facing:'north',triggered:'false'}}"); ++ register(371, "{Name:'minecraft:dispenser',Properties:{facing:'south',triggered:'false'}}", "{Name:'minecraft:dispenser',Properties:{facing:'south',triggered:'false'}}"); ++ register(372, "{Name:'minecraft:dispenser',Properties:{facing:'west',triggered:'false'}}", "{Name:'minecraft:dispenser',Properties:{facing:'west',triggered:'false'}}"); ++ register(373, "{Name:'minecraft:dispenser',Properties:{facing:'east',triggered:'false'}}", "{Name:'minecraft:dispenser',Properties:{facing:'east',triggered:'false'}}"); ++ register(376, "{Name:'minecraft:dispenser',Properties:{facing:'down',triggered:'true'}}", "{Name:'minecraft:dispenser',Properties:{facing:'down',triggered:'true'}}"); ++ register(377, "{Name:'minecraft:dispenser',Properties:{facing:'up',triggered:'true'}}", "{Name:'minecraft:dispenser',Properties:{facing:'up',triggered:'true'}}"); ++ register(378, "{Name:'minecraft:dispenser',Properties:{facing:'north',triggered:'true'}}", "{Name:'minecraft:dispenser',Properties:{facing:'north',triggered:'true'}}"); ++ register(379, "{Name:'minecraft:dispenser',Properties:{facing:'south',triggered:'true'}}", "{Name:'minecraft:dispenser',Properties:{facing:'south',triggered:'true'}}"); ++ register(380, "{Name:'minecraft:dispenser',Properties:{facing:'west',triggered:'true'}}", "{Name:'minecraft:dispenser',Properties:{facing:'west',triggered:'true'}}"); ++ register(381, "{Name:'minecraft:dispenser',Properties:{facing:'east',triggered:'true'}}", "{Name:'minecraft:dispenser',Properties:{facing:'east',triggered:'true'}}"); ++ register(384, "{Name:'minecraft:sandstone'}", "{Name:'minecraft:sandstone',Properties:{type:'sandstone'}}"); ++ register(385, "{Name:'minecraft:chiseled_sandstone'}", "{Name:'minecraft:sandstone',Properties:{type:'chiseled_sandstone'}}"); ++ register(386, "{Name:'minecraft:cut_sandstone'}", "{Name:'minecraft:sandstone',Properties:{type:'smooth_sandstone'}}"); ++ register(400, "{Name:'minecraft:note_block'}", "{Name:'minecraft:noteblock'}"); ++ register(416, "{Name:'minecraft:red_bed',Properties:{facing:'south',occupied:'false',part:'foot'}}", "{Name:'minecraft:bed',Properties:{facing:'south',occupied:'false',part:'foot'}}", "{Name:'minecraft:bed',Properties:{facing:'south',occupied:'true',part:'foot'}}"); ++ register(417, "{Name:'minecraft:red_bed',Properties:{facing:'west',occupied:'false',part:'foot'}}", "{Name:'minecraft:bed',Properties:{facing:'west',occupied:'false',part:'foot'}}", "{Name:'minecraft:bed',Properties:{facing:'west',occupied:'true',part:'foot'}}"); ++ register(418, "{Name:'minecraft:red_bed',Properties:{facing:'north',occupied:'false',part:'foot'}}", "{Name:'minecraft:bed',Properties:{facing:'north',occupied:'false',part:'foot'}}", "{Name:'minecraft:bed',Properties:{facing:'north',occupied:'true',part:'foot'}}"); ++ register(419, "{Name:'minecraft:red_bed',Properties:{facing:'east',occupied:'false',part:'foot'}}", "{Name:'minecraft:bed',Properties:{facing:'east',occupied:'false',part:'foot'}}", "{Name:'minecraft:bed',Properties:{facing:'east',occupied:'true',part:'foot'}}"); ++ register(424, "{Name:'minecraft:red_bed',Properties:{facing:'south',occupied:'false',part:'head'}}", "{Name:'minecraft:bed',Properties:{facing:'south',occupied:'false',part:'head'}}"); ++ register(425, "{Name:'minecraft:red_bed',Properties:{facing:'west',occupied:'false',part:'head'}}", "{Name:'minecraft:bed',Properties:{facing:'west',occupied:'false',part:'head'}}"); ++ register(426, "{Name:'minecraft:red_bed',Properties:{facing:'north',occupied:'false',part:'head'}}", "{Name:'minecraft:bed',Properties:{facing:'north',occupied:'false',part:'head'}}"); ++ register(427, "{Name:'minecraft:red_bed',Properties:{facing:'east',occupied:'false',part:'head'}}", "{Name:'minecraft:bed',Properties:{facing:'east',occupied:'false',part:'head'}}"); ++ register(428, "{Name:'minecraft:red_bed',Properties:{facing:'south',occupied:'true',part:'head'}}", "{Name:'minecraft:bed',Properties:{facing:'south',occupied:'true',part:'head'}}"); ++ register(429, "{Name:'minecraft:red_bed',Properties:{facing:'west',occupied:'true',part:'head'}}", "{Name:'minecraft:bed',Properties:{facing:'west',occupied:'true',part:'head'}}"); ++ register(430, "{Name:'minecraft:red_bed',Properties:{facing:'north',occupied:'true',part:'head'}}", "{Name:'minecraft:bed',Properties:{facing:'north',occupied:'true',part:'head'}}"); ++ register(431, "{Name:'minecraft:red_bed',Properties:{facing:'east',occupied:'true',part:'head'}}", "{Name:'minecraft:bed',Properties:{facing:'east',occupied:'true',part:'head'}}"); ++ register(432, "{Name:'minecraft:powered_rail',Properties:{powered:'false',shape:'north_south'}}", "{Name:'minecraft:golden_rail',Properties:{powered:'false',shape:'north_south'}}"); ++ register(433, "{Name:'minecraft:powered_rail',Properties:{powered:'false',shape:'east_west'}}", "{Name:'minecraft:golden_rail',Properties:{powered:'false',shape:'east_west'}}"); ++ register(434, "{Name:'minecraft:powered_rail',Properties:{powered:'false',shape:'ascending_east'}}", "{Name:'minecraft:golden_rail',Properties:{powered:'false',shape:'ascending_east'}}"); ++ register(435, "{Name:'minecraft:powered_rail',Properties:{powered:'false',shape:'ascending_west'}}", "{Name:'minecraft:golden_rail',Properties:{powered:'false',shape:'ascending_west'}}"); ++ register(436, "{Name:'minecraft:powered_rail',Properties:{powered:'false',shape:'ascending_north'}}", "{Name:'minecraft:golden_rail',Properties:{powered:'false',shape:'ascending_north'}}"); ++ register(437, "{Name:'minecraft:powered_rail',Properties:{powered:'false',shape:'ascending_south'}}", "{Name:'minecraft:golden_rail',Properties:{powered:'false',shape:'ascending_south'}}"); ++ register(440, "{Name:'minecraft:powered_rail',Properties:{powered:'true',shape:'north_south'}}", "{Name:'minecraft:golden_rail',Properties:{powered:'true',shape:'north_south'}}"); ++ register(441, "{Name:'minecraft:powered_rail',Properties:{powered:'true',shape:'east_west'}}", "{Name:'minecraft:golden_rail',Properties:{powered:'true',shape:'east_west'}}"); ++ register(442, "{Name:'minecraft:powered_rail',Properties:{powered:'true',shape:'ascending_east'}}", "{Name:'minecraft:golden_rail',Properties:{powered:'true',shape:'ascending_east'}}"); ++ register(443, "{Name:'minecraft:powered_rail',Properties:{powered:'true',shape:'ascending_west'}}", "{Name:'minecraft:golden_rail',Properties:{powered:'true',shape:'ascending_west'}}"); ++ register(444, "{Name:'minecraft:powered_rail',Properties:{powered:'true',shape:'ascending_north'}}", "{Name:'minecraft:golden_rail',Properties:{powered:'true',shape:'ascending_north'}}"); ++ register(445, "{Name:'minecraft:powered_rail',Properties:{powered:'true',shape:'ascending_south'}}", "{Name:'minecraft:golden_rail',Properties:{powered:'true',shape:'ascending_south'}}"); ++ register(448, "{Name:'minecraft:detector_rail',Properties:{powered:'false',shape:'north_south'}}", "{Name:'minecraft:detector_rail',Properties:{powered:'false',shape:'north_south'}}"); ++ register(449, "{Name:'minecraft:detector_rail',Properties:{powered:'false',shape:'east_west'}}", "{Name:'minecraft:detector_rail',Properties:{powered:'false',shape:'east_west'}}"); ++ register(450, "{Name:'minecraft:detector_rail',Properties:{powered:'false',shape:'ascending_east'}}", "{Name:'minecraft:detector_rail',Properties:{powered:'false',shape:'ascending_east'}}"); ++ register(451, "{Name:'minecraft:detector_rail',Properties:{powered:'false',shape:'ascending_west'}}", "{Name:'minecraft:detector_rail',Properties:{powered:'false',shape:'ascending_west'}}"); ++ register(452, "{Name:'minecraft:detector_rail',Properties:{powered:'false',shape:'ascending_north'}}", "{Name:'minecraft:detector_rail',Properties:{powered:'false',shape:'ascending_north'}}"); ++ register(453, "{Name:'minecraft:detector_rail',Properties:{powered:'false',shape:'ascending_south'}}", "{Name:'minecraft:detector_rail',Properties:{powered:'false',shape:'ascending_south'}}"); ++ register(456, "{Name:'minecraft:detector_rail',Properties:{powered:'true',shape:'north_south'}}", "{Name:'minecraft:detector_rail',Properties:{powered:'true',shape:'north_south'}}"); ++ register(457, "{Name:'minecraft:detector_rail',Properties:{powered:'true',shape:'east_west'}}", "{Name:'minecraft:detector_rail',Properties:{powered:'true',shape:'east_west'}}"); ++ register(458, "{Name:'minecraft:detector_rail',Properties:{powered:'true',shape:'ascending_east'}}", "{Name:'minecraft:detector_rail',Properties:{powered:'true',shape:'ascending_east'}}"); ++ register(459, "{Name:'minecraft:detector_rail',Properties:{powered:'true',shape:'ascending_west'}}", "{Name:'minecraft:detector_rail',Properties:{powered:'true',shape:'ascending_west'}}"); ++ register(460, "{Name:'minecraft:detector_rail',Properties:{powered:'true',shape:'ascending_north'}}", "{Name:'minecraft:detector_rail',Properties:{powered:'true',shape:'ascending_north'}}"); ++ register(461, "{Name:'minecraft:detector_rail',Properties:{powered:'true',shape:'ascending_south'}}", "{Name:'minecraft:detector_rail',Properties:{powered:'true',shape:'ascending_south'}}"); ++ register(464, "{Name:'minecraft:sticky_piston',Properties:{extended:'false',facing:'down'}}", "{Name:'minecraft:sticky_piston',Properties:{extended:'false',facing:'down'}}"); ++ register(465, "{Name:'minecraft:sticky_piston',Properties:{extended:'false',facing:'up'}}", "{Name:'minecraft:sticky_piston',Properties:{extended:'false',facing:'up'}}"); ++ register(466, "{Name:'minecraft:sticky_piston',Properties:{extended:'false',facing:'north'}}", "{Name:'minecraft:sticky_piston',Properties:{extended:'false',facing:'north'}}"); ++ register(467, "{Name:'minecraft:sticky_piston',Properties:{extended:'false',facing:'south'}}", "{Name:'minecraft:sticky_piston',Properties:{extended:'false',facing:'south'}}"); ++ register(468, "{Name:'minecraft:sticky_piston',Properties:{extended:'false',facing:'west'}}", "{Name:'minecraft:sticky_piston',Properties:{extended:'false',facing:'west'}}"); ++ register(469, "{Name:'minecraft:sticky_piston',Properties:{extended:'false',facing:'east'}}", "{Name:'minecraft:sticky_piston',Properties:{extended:'false',facing:'east'}}"); ++ register(472, "{Name:'minecraft:sticky_piston',Properties:{extended:'true',facing:'down'}}", "{Name:'minecraft:sticky_piston',Properties:{extended:'true',facing:'down'}}"); ++ register(473, "{Name:'minecraft:sticky_piston',Properties:{extended:'true',facing:'up'}}", "{Name:'minecraft:sticky_piston',Properties:{extended:'true',facing:'up'}}"); ++ register(474, "{Name:'minecraft:sticky_piston',Properties:{extended:'true',facing:'north'}}", "{Name:'minecraft:sticky_piston',Properties:{extended:'true',facing:'north'}}"); ++ register(475, "{Name:'minecraft:sticky_piston',Properties:{extended:'true',facing:'south'}}", "{Name:'minecraft:sticky_piston',Properties:{extended:'true',facing:'south'}}"); ++ register(476, "{Name:'minecraft:sticky_piston',Properties:{extended:'true',facing:'west'}}", "{Name:'minecraft:sticky_piston',Properties:{extended:'true',facing:'west'}}"); ++ register(477, "{Name:'minecraft:sticky_piston',Properties:{extended:'true',facing:'east'}}", "{Name:'minecraft:sticky_piston',Properties:{extended:'true',facing:'east'}}"); ++ register(480, "{Name:'minecraft:cobweb'}", "{Name:'minecraft:web'}"); ++ register(496, "{Name:'minecraft:dead_bush'}", "{Name:'minecraft:tallgrass',Properties:{type:'dead_bush'}}"); ++ register(497, "{Name:'minecraft:grass'}", "{Name:'minecraft:tallgrass',Properties:{type:'tall_grass'}}"); ++ register(498, "{Name:'minecraft:fern'}", "{Name:'minecraft:tallgrass',Properties:{type:'fern'}}"); ++ register(512, "{Name:'minecraft:dead_bush'}", "{Name:'minecraft:deadbush'}"); ++ register(528, "{Name:'minecraft:piston',Properties:{extended:'false',facing:'down'}}", "{Name:'minecraft:piston',Properties:{extended:'false',facing:'down'}}"); ++ register(529, "{Name:'minecraft:piston',Properties:{extended:'false',facing:'up'}}", "{Name:'minecraft:piston',Properties:{extended:'false',facing:'up'}}"); ++ register(530, "{Name:'minecraft:piston',Properties:{extended:'false',facing:'north'}}", "{Name:'minecraft:piston',Properties:{extended:'false',facing:'north'}}"); ++ register(531, "{Name:'minecraft:piston',Properties:{extended:'false',facing:'south'}}", "{Name:'minecraft:piston',Properties:{extended:'false',facing:'south'}}"); ++ register(532, "{Name:'minecraft:piston',Properties:{extended:'false',facing:'west'}}", "{Name:'minecraft:piston',Properties:{extended:'false',facing:'west'}}"); ++ register(533, "{Name:'minecraft:piston',Properties:{extended:'false',facing:'east'}}", "{Name:'minecraft:piston',Properties:{extended:'false',facing:'east'}}"); ++ register(536, "{Name:'minecraft:piston',Properties:{extended:'true',facing:'down'}}", "{Name:'minecraft:piston',Properties:{extended:'true',facing:'down'}}"); ++ register(537, "{Name:'minecraft:piston',Properties:{extended:'true',facing:'up'}}", "{Name:'minecraft:piston',Properties:{extended:'true',facing:'up'}}"); ++ register(538, "{Name:'minecraft:piston',Properties:{extended:'true',facing:'north'}}", "{Name:'minecraft:piston',Properties:{extended:'true',facing:'north'}}"); ++ register(539, "{Name:'minecraft:piston',Properties:{extended:'true',facing:'south'}}", "{Name:'minecraft:piston',Properties:{extended:'true',facing:'south'}}"); ++ register(540, "{Name:'minecraft:piston',Properties:{extended:'true',facing:'west'}}", "{Name:'minecraft:piston',Properties:{extended:'true',facing:'west'}}"); ++ register(541, "{Name:'minecraft:piston',Properties:{extended:'true',facing:'east'}}", "{Name:'minecraft:piston',Properties:{extended:'true',facing:'east'}}"); ++ register(544, "{Name:'minecraft:piston_head',Properties:{facing:'down',short:'false',type:'normal'}}", "{Name:'minecraft:piston_head',Properties:{facing:'down',short:'false',type:'normal'}}", "{Name:'minecraft:piston_head',Properties:{facing:'down',short:'true',type:'normal'}}"); ++ register(545, "{Name:'minecraft:piston_head',Properties:{facing:'up',short:'false',type:'normal'}}", "{Name:'minecraft:piston_head',Properties:{facing:'up',short:'false',type:'normal'}}", "{Name:'minecraft:piston_head',Properties:{facing:'up',short:'true',type:'normal'}}"); ++ register(546, "{Name:'minecraft:piston_head',Properties:{facing:'north',short:'false',type:'normal'}}", "{Name:'minecraft:piston_head',Properties:{facing:'north',short:'false',type:'normal'}}", "{Name:'minecraft:piston_head',Properties:{facing:'north',short:'true',type:'normal'}}"); ++ register(547, "{Name:'minecraft:piston_head',Properties:{facing:'south',short:'false',type:'normal'}}", "{Name:'minecraft:piston_head',Properties:{facing:'south',short:'false',type:'normal'}}", "{Name:'minecraft:piston_head',Properties:{facing:'south',short:'true',type:'normal'}}"); ++ register(548, "{Name:'minecraft:piston_head',Properties:{facing:'west',short:'false',type:'normal'}}", "{Name:'minecraft:piston_head',Properties:{facing:'west',short:'false',type:'normal'}}", "{Name:'minecraft:piston_head',Properties:{facing:'west',short:'true',type:'normal'}}"); ++ register(549, "{Name:'minecraft:piston_head',Properties:{facing:'east',short:'false',type:'normal'}}", "{Name:'minecraft:piston_head',Properties:{facing:'east',short:'false',type:'normal'}}", "{Name:'minecraft:piston_head',Properties:{facing:'east',short:'true',type:'normal'}}"); ++ register(552, "{Name:'minecraft:piston_head',Properties:{facing:'down',short:'false',type:'sticky'}}", "{Name:'minecraft:piston_head',Properties:{facing:'down',short:'false',type:'sticky'}}", "{Name:'minecraft:piston_head',Properties:{facing:'down',short:'true',type:'sticky'}}"); ++ register(553, "{Name:'minecraft:piston_head',Properties:{facing:'up',short:'false',type:'sticky'}}", "{Name:'minecraft:piston_head',Properties:{facing:'up',short:'false',type:'sticky'}}", "{Name:'minecraft:piston_head',Properties:{facing:'up',short:'true',type:'sticky'}}"); ++ register(554, "{Name:'minecraft:piston_head',Properties:{facing:'north',short:'false',type:'sticky'}}", "{Name:'minecraft:piston_head',Properties:{facing:'north',short:'false',type:'sticky'}}", "{Name:'minecraft:piston_head',Properties:{facing:'north',short:'true',type:'sticky'}}"); ++ register(555, "{Name:'minecraft:piston_head',Properties:{facing:'south',short:'false',type:'sticky'}}", "{Name:'minecraft:piston_head',Properties:{facing:'south',short:'false',type:'sticky'}}", "{Name:'minecraft:piston_head',Properties:{facing:'south',short:'true',type:'sticky'}}"); ++ register(556, "{Name:'minecraft:piston_head',Properties:{facing:'west',short:'false',type:'sticky'}}", "{Name:'minecraft:piston_head',Properties:{facing:'west',short:'false',type:'sticky'}}", "{Name:'minecraft:piston_head',Properties:{facing:'west',short:'true',type:'sticky'}}"); ++ register(557, "{Name:'minecraft:piston_head',Properties:{facing:'east',short:'false',type:'sticky'}}", "{Name:'minecraft:piston_head',Properties:{facing:'east',short:'false',type:'sticky'}}", "{Name:'minecraft:piston_head',Properties:{facing:'east',short:'true',type:'sticky'}}"); ++ register(560, "{Name:'minecraft:white_wool'}", "{Name:'minecraft:wool',Properties:{color:'white'}}"); ++ register(561, "{Name:'minecraft:orange_wool'}", "{Name:'minecraft:wool',Properties:{color:'orange'}}"); ++ register(562, "{Name:'minecraft:magenta_wool'}", "{Name:'minecraft:wool',Properties:{color:'magenta'}}"); ++ register(563, "{Name:'minecraft:light_blue_wool'}", "{Name:'minecraft:wool',Properties:{color:'light_blue'}}"); ++ register(564, "{Name:'minecraft:yellow_wool'}", "{Name:'minecraft:wool',Properties:{color:'yellow'}}"); ++ register(565, "{Name:'minecraft:lime_wool'}", "{Name:'minecraft:wool',Properties:{color:'lime'}}"); ++ register(566, "{Name:'minecraft:pink_wool'}", "{Name:'minecraft:wool',Properties:{color:'pink'}}"); ++ register(567, "{Name:'minecraft:gray_wool'}", "{Name:'minecraft:wool',Properties:{color:'gray'}}"); ++ register(568, "{Name:'minecraft:light_gray_wool'}", "{Name:'minecraft:wool',Properties:{color:'silver'}}"); ++ register(569, "{Name:'minecraft:cyan_wool'}", "{Name:'minecraft:wool',Properties:{color:'cyan'}}"); ++ register(570, "{Name:'minecraft:purple_wool'}", "{Name:'minecraft:wool',Properties:{color:'purple'}}"); ++ register(571, "{Name:'minecraft:blue_wool'}", "{Name:'minecraft:wool',Properties:{color:'blue'}}"); ++ register(572, "{Name:'minecraft:brown_wool'}", "{Name:'minecraft:wool',Properties:{color:'brown'}}"); ++ register(573, "{Name:'minecraft:green_wool'}", "{Name:'minecraft:wool',Properties:{color:'green'}}"); ++ register(574, "{Name:'minecraft:red_wool'}", "{Name:'minecraft:wool',Properties:{color:'red'}}"); ++ register(575, "{Name:'minecraft:black_wool'}", "{Name:'minecraft:wool',Properties:{color:'black'}}"); ++ register(576, "{Name:'minecraft:moving_piston',Properties:{facing:'down',type:'normal'}}", "{Name:'minecraft:piston_extension',Properties:{facing:'down',type:'normal'}}"); ++ register(577, "{Name:'minecraft:moving_piston',Properties:{facing:'up',type:'normal'}}", "{Name:'minecraft:piston_extension',Properties:{facing:'up',type:'normal'}}"); ++ register(578, "{Name:'minecraft:moving_piston',Properties:{facing:'north',type:'normal'}}", "{Name:'minecraft:piston_extension',Properties:{facing:'north',type:'normal'}}"); ++ register(579, "{Name:'minecraft:moving_piston',Properties:{facing:'south',type:'normal'}}", "{Name:'minecraft:piston_extension',Properties:{facing:'south',type:'normal'}}"); ++ register(580, "{Name:'minecraft:moving_piston',Properties:{facing:'west',type:'normal'}}", "{Name:'minecraft:piston_extension',Properties:{facing:'west',type:'normal'}}"); ++ register(581, "{Name:'minecraft:moving_piston',Properties:{facing:'east',type:'normal'}}", "{Name:'minecraft:piston_extension',Properties:{facing:'east',type:'normal'}}"); ++ register(584, "{Name:'minecraft:moving_piston',Properties:{facing:'down',type:'sticky'}}", "{Name:'minecraft:piston_extension',Properties:{facing:'down',type:'sticky'}}"); ++ register(585, "{Name:'minecraft:moving_piston',Properties:{facing:'up',type:'sticky'}}", "{Name:'minecraft:piston_extension',Properties:{facing:'up',type:'sticky'}}"); ++ register(586, "{Name:'minecraft:moving_piston',Properties:{facing:'north',type:'sticky'}}", "{Name:'minecraft:piston_extension',Properties:{facing:'north',type:'sticky'}}"); ++ register(587, "{Name:'minecraft:moving_piston',Properties:{facing:'south',type:'sticky'}}", "{Name:'minecraft:piston_extension',Properties:{facing:'south',type:'sticky'}}"); ++ register(588, "{Name:'minecraft:moving_piston',Properties:{facing:'west',type:'sticky'}}", "{Name:'minecraft:piston_extension',Properties:{facing:'west',type:'sticky'}}"); ++ register(589, "{Name:'minecraft:moving_piston',Properties:{facing:'east',type:'sticky'}}", "{Name:'minecraft:piston_extension',Properties:{facing:'east',type:'sticky'}}"); ++ register(592, "{Name:'minecraft:dandelion'}", "{Name:'minecraft:yellow_flower',Properties:{type:'dandelion'}}"); ++ register(608, "{Name:'minecraft:poppy'}", "{Name:'minecraft:red_flower',Properties:{type:'poppy'}}"); ++ register(609, "{Name:'minecraft:blue_orchid'}", "{Name:'minecraft:red_flower',Properties:{type:'blue_orchid'}}"); ++ register(610, "{Name:'minecraft:allium'}", "{Name:'minecraft:red_flower',Properties:{type:'allium'}}"); ++ register(611, "{Name:'minecraft:azure_bluet'}", "{Name:'minecraft:red_flower',Properties:{type:'houstonia'}}"); ++ register(612, "{Name:'minecraft:red_tulip'}", "{Name:'minecraft:red_flower',Properties:{type:'red_tulip'}}"); ++ register(613, "{Name:'minecraft:orange_tulip'}", "{Name:'minecraft:red_flower',Properties:{type:'orange_tulip'}}"); ++ register(614, "{Name:'minecraft:white_tulip'}", "{Name:'minecraft:red_flower',Properties:{type:'white_tulip'}}"); ++ register(615, "{Name:'minecraft:pink_tulip'}", "{Name:'minecraft:red_flower',Properties:{type:'pink_tulip'}}"); ++ register(616, "{Name:'minecraft:oxeye_daisy'}", "{Name:'minecraft:red_flower',Properties:{type:'oxeye_daisy'}}"); ++ register(624, "{Name:'minecraft:brown_mushroom'}", "{Name:'minecraft:brown_mushroom'}"); ++ register(640, "{Name:'minecraft:red_mushroom'}", "{Name:'minecraft:red_mushroom'}"); ++ register(656, "{Name:'minecraft:gold_block'}", "{Name:'minecraft:gold_block'}"); ++ register(672, "{Name:'minecraft:iron_block'}", "{Name:'minecraft:iron_block'}"); ++ register(688, "{Name:'minecraft:stone_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_stone_slab',Properties:{seamless:'false',variant:'stone'}}"); ++ register(689, "{Name:'minecraft:sandstone_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_stone_slab',Properties:{seamless:'false',variant:'sandstone'}}"); ++ register(690, "{Name:'minecraft:petrified_oak_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_stone_slab',Properties:{seamless:'false',variant:'wood_old'}}"); ++ register(691, "{Name:'minecraft:cobblestone_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_stone_slab',Properties:{seamless:'false',variant:'cobblestone'}}"); ++ register(692, "{Name:'minecraft:brick_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_stone_slab',Properties:{seamless:'false',variant:'brick'}}"); ++ register(693, "{Name:'minecraft:stone_brick_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_stone_slab',Properties:{seamless:'false',variant:'stone_brick'}}"); ++ register(694, "{Name:'minecraft:nether_brick_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_stone_slab',Properties:{seamless:'false',variant:'nether_brick'}}"); ++ register(695, "{Name:'minecraft:quartz_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_stone_slab',Properties:{seamless:'false',variant:'quartz'}}"); ++ register(696, "{Name:'minecraft:smooth_stone'}", "{Name:'minecraft:double_stone_slab',Properties:{seamless:'true',variant:'stone'}}"); ++ register(697, "{Name:'minecraft:smooth_sandstone'}", "{Name:'minecraft:double_stone_slab',Properties:{seamless:'true',variant:'sandstone'}}"); ++ register(698, "{Name:'minecraft:petrified_oak_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_stone_slab',Properties:{seamless:'true',variant:'wood_old'}}"); ++ register(699, "{Name:'minecraft:cobblestone_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_stone_slab',Properties:{seamless:'true',variant:'cobblestone'}}"); ++ register(700, "{Name:'minecraft:brick_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_stone_slab',Properties:{seamless:'true',variant:'brick'}}"); ++ register(701, "{Name:'minecraft:stone_brick_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_stone_slab',Properties:{seamless:'true',variant:'stone_brick'}}"); ++ register(702, "{Name:'minecraft:nether_brick_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_stone_slab',Properties:{seamless:'true',variant:'nether_brick'}}"); ++ register(703, "{Name:'minecraft:smooth_quartz'}", "{Name:'minecraft:double_stone_slab',Properties:{seamless:'true',variant:'quartz'}}"); ++ register(704, "{Name:'minecraft:stone_slab',Properties:{type:'bottom'}}", "{Name:'minecraft:stone_slab',Properties:{half:'bottom',variant:'stone'}}"); ++ register(705, "{Name:'minecraft:sandstone_slab',Properties:{type:'bottom'}}", "{Name:'minecraft:stone_slab',Properties:{half:'bottom',variant:'sandstone'}}"); ++ register(706, "{Name:'minecraft:petrified_oak_slab',Properties:{type:'bottom'}}", "{Name:'minecraft:stone_slab',Properties:{half:'bottom',variant:'wood_old'}}"); ++ register(707, "{Name:'minecraft:cobblestone_slab',Properties:{type:'bottom'}}", "{Name:'minecraft:stone_slab',Properties:{half:'bottom',variant:'cobblestone'}}"); ++ register(708, "{Name:'minecraft:brick_slab',Properties:{type:'bottom'}}", "{Name:'minecraft:stone_slab',Properties:{half:'bottom',variant:'brick'}}"); ++ register(709, "{Name:'minecraft:stone_brick_slab',Properties:{type:'bottom'}}", "{Name:'minecraft:stone_slab',Properties:{half:'bottom',variant:'stone_brick'}}"); ++ register(710, "{Name:'minecraft:nether_brick_slab',Properties:{type:'bottom'}}", "{Name:'minecraft:stone_slab',Properties:{half:'bottom',variant:'nether_brick'}}"); ++ register(711, "{Name:'minecraft:quartz_slab',Properties:{type:'bottom'}}", "{Name:'minecraft:stone_slab',Properties:{half:'bottom',variant:'quartz'}}"); ++ register(712, "{Name:'minecraft:stone_slab',Properties:{type:'top'}}", "{Name:'minecraft:stone_slab',Properties:{half:'top',variant:'stone'}}"); ++ register(713, "{Name:'minecraft:sandstone_slab',Properties:{type:'top'}}", "{Name:'minecraft:stone_slab',Properties:{half:'top',variant:'sandstone'}}"); ++ register(714, "{Name:'minecraft:petrified_oak_slab',Properties:{type:'top'}}", "{Name:'minecraft:stone_slab',Properties:{half:'top',variant:'wood_old'}}"); ++ register(715, "{Name:'minecraft:cobblestone_slab',Properties:{type:'top'}}", "{Name:'minecraft:stone_slab',Properties:{half:'top',variant:'cobblestone'}}"); ++ register(716, "{Name:'minecraft:brick_slab',Properties:{type:'top'}}", "{Name:'minecraft:stone_slab',Properties:{half:'top',variant:'brick'}}"); ++ register(717, "{Name:'minecraft:stone_brick_slab',Properties:{type:'top'}}", "{Name:'minecraft:stone_slab',Properties:{half:'top',variant:'stone_brick'}}"); ++ register(718, "{Name:'minecraft:nether_brick_slab',Properties:{type:'top'}}", "{Name:'minecraft:stone_slab',Properties:{half:'top',variant:'nether_brick'}}"); ++ register(719, "{Name:'minecraft:quartz_slab',Properties:{type:'top'}}", "{Name:'minecraft:stone_slab',Properties:{half:'top',variant:'quartz'}}"); ++ register(720, "{Name:'minecraft:bricks'}", "{Name:'minecraft:brick_block'}"); ++ register(736, "{Name:'minecraft:tnt',Properties:{unstable:'false'}}", "{Name:'minecraft:tnt',Properties:{explode:'false'}}"); ++ register(737, "{Name:'minecraft:tnt',Properties:{unstable:'true'}}", "{Name:'minecraft:tnt',Properties:{explode:'true'}}"); ++ register(752, "{Name:'minecraft:bookshelf'}", "{Name:'minecraft:bookshelf'}"); ++ register(768, "{Name:'minecraft:mossy_cobblestone'}", "{Name:'minecraft:mossy_cobblestone'}"); ++ register(784, "{Name:'minecraft:obsidian'}", "{Name:'minecraft:obsidian'}"); ++ register(801, "{Name:'minecraft:wall_torch',Properties:{facing:'east'}}", "{Name:'minecraft:torch',Properties:{facing:'east'}}"); ++ register(802, "{Name:'minecraft:wall_torch',Properties:{facing:'west'}}", "{Name:'minecraft:torch',Properties:{facing:'west'}}"); ++ register(803, "{Name:'minecraft:wall_torch',Properties:{facing:'south'}}", "{Name:'minecraft:torch',Properties:{facing:'south'}}"); ++ register(804, "{Name:'minecraft:wall_torch',Properties:{facing:'north'}}", "{Name:'minecraft:torch',Properties:{facing:'north'}}"); ++ register(805, "{Name:'minecraft:torch'}", "{Name:'minecraft:torch',Properties:{facing:'up'}}"); ++ register(816, "{Name:'minecraft:fire',Properties:{age:'0',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'0',east:'true',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(817, "{Name:'minecraft:fire',Properties:{age:'1',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'1',east:'true',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(818, "{Name:'minecraft:fire',Properties:{age:'2',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'2',east:'true',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(819, "{Name:'minecraft:fire',Properties:{age:'3',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'3',east:'true',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(820, "{Name:'minecraft:fire',Properties:{age:'4',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'4',east:'true',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(821, "{Name:'minecraft:fire',Properties:{age:'5',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'5',east:'true',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(822, "{Name:'minecraft:fire',Properties:{age:'6',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'6',east:'true',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(823, "{Name:'minecraft:fire',Properties:{age:'7',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'7',east:'true',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(824, "{Name:'minecraft:fire',Properties:{age:'8',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'8',east:'true',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(825, "{Name:'minecraft:fire',Properties:{age:'9',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'9',east:'true',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(826, "{Name:'minecraft:fire',Properties:{age:'10',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'10',east:'true',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(827, "{Name:'minecraft:fire',Properties:{age:'11',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'11',east:'true',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(828, "{Name:'minecraft:fire',Properties:{age:'12',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'12',east:'true',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(829, "{Name:'minecraft:fire',Properties:{age:'13',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'13',east:'true',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(830, "{Name:'minecraft:fire',Properties:{age:'14',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'14',east:'true',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(831, "{Name:'minecraft:fire',Properties:{age:'15',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:fire',Properties:{age:'15',east:'true',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(832, "{Name:'minecraft:mob_spawner'}", "{Name:'minecraft:mob_spawner'}"); ++ register(848, "{Name:'minecraft:oak_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}"); ++ register(849, "{Name:'minecraft:oak_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}"); ++ register(850, "{Name:'minecraft:oak_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}"); ++ register(851, "{Name:'minecraft:oak_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}"); ++ register(852, "{Name:'minecraft:oak_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'east',half:'top',shape:'inner_left'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'east',half:'top',shape:'inner_right'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'east',half:'top',shape:'outer_left'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'east',half:'top',shape:'outer_right'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}"); ++ register(853, "{Name:'minecraft:oak_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'west',half:'top',shape:'inner_left'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'west',half:'top',shape:'inner_right'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'west',half:'top',shape:'outer_left'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'west',half:'top',shape:'outer_right'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}"); ++ register(854, "{Name:'minecraft:oak_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'south',half:'top',shape:'inner_left'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'south',half:'top',shape:'inner_right'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'south',half:'top',shape:'outer_left'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'south',half:'top',shape:'outer_right'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}"); ++ register(855, "{Name:'minecraft:oak_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'north',half:'top',shape:'inner_left'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'north',half:'top',shape:'inner_right'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'north',half:'top',shape:'outer_left'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'north',half:'top',shape:'outer_right'}}", "{Name:'minecraft:oak_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}"); ++ register(866, "{Name:'minecraft:chest',Properties:{facing:'north',type:'single'}}", "{Name:'minecraft:chest',Properties:{facing:'north'}}"); ++ register(867, "{Name:'minecraft:chest',Properties:{facing:'south',type:'single'}}", "{Name:'minecraft:chest',Properties:{facing:'south'}}"); ++ register(868, "{Name:'minecraft:chest',Properties:{facing:'west',type:'single'}}", "{Name:'minecraft:chest',Properties:{facing:'west'}}"); ++ register(869, "{Name:'minecraft:chest',Properties:{facing:'east',type:'single'}}", "{Name:'minecraft:chest',Properties:{facing:'east'}}"); ++ register(880, "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'0',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'0',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'0',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'0',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'0',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'0',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'0',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'0',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'0',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'0',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'0',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'0',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'0',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'0',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'0',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'0',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'0',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'0',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'0',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'0',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'0',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'0',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'0',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'0',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'0',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'0',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'0',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'0',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'0',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'0',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'0',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'0',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'0',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'0',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'0',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'0',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'0',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'0',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'0',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'0',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'0',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'0',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'0',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'0',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'0',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'0',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'0',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'0',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'0',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'0',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'0',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'0',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'0',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'0',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'0',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'0',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'0',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'0',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'0',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'0',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'0',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'0',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'0',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'0',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'0',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'0',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'0',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'0',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'0',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'0',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'0',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'0',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'0',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'0',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'0',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'0',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'0',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'0',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'0',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'0',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'0',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'0',south:'up',west:'up'}}"); ++ register(881, "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'1',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'1',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'1',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'1',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'1',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'1',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'1',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'1',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'1',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'1',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'1',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'1',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'1',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'1',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'1',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'1',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'1',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'1',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'1',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'1',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'1',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'1',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'1',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'1',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'1',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'1',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'1',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'1',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'1',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'1',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'1',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'1',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'1',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'1',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'1',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'1',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'1',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'1',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'1',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'1',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'1',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'1',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'1',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'1',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'1',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'1',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'1',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'1',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'1',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'1',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'1',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'1',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'1',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'1',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'1',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'1',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'1',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'1',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'1',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'1',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'1',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'1',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'1',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'1',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'1',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'1',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'1',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'1',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'1',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'1',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'1',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'1',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'1',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'1',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'1',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'1',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'1',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'1',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'1',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'1',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'1',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'1',south:'up',west:'up'}}"); ++ register(882, "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'2',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'2',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'2',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'2',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'2',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'2',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'2',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'2',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'2',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'2',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'2',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'2',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'2',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'2',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'2',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'2',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'2',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'2',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'2',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'2',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'2',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'2',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'2',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'2',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'2',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'2',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'2',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'2',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'2',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'2',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'2',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'2',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'2',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'2',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'2',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'2',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'2',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'2',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'2',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'2',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'2',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'2',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'2',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'2',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'2',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'2',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'2',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'2',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'2',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'2',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'2',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'2',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'2',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'2',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'2',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'2',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'2',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'2',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'2',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'2',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'2',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'2',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'2',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'2',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'2',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'2',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'2',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'2',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'2',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'2',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'2',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'2',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'2',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'2',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'2',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'2',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'2',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'2',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'2',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'2',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'2',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'2',south:'up',west:'up'}}"); ++ register(883, "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'3',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'3',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'3',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'3',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'3',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'3',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'3',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'3',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'3',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'3',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'3',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'3',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'3',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'3',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'3',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'3',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'3',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'3',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'3',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'3',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'3',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'3',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'3',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'3',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'3',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'3',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'3',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'3',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'3',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'3',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'3',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'3',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'3',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'3',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'3',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'3',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'3',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'3',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'3',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'3',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'3',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'3',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'3',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'3',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'3',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'3',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'3',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'3',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'3',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'3',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'3',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'3',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'3',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'3',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'3',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'3',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'3',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'3',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'3',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'3',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'3',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'3',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'3',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'3',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'3',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'3',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'3',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'3',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'3',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'3',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'3',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'3',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'3',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'3',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'3',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'3',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'3',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'3',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'3',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'3',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'3',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'3',south:'up',west:'up'}}"); ++ register(884, "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'4',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'4',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'4',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'4',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'4',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'4',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'4',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'4',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'4',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'4',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'4',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'4',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'4',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'4',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'4',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'4',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'4',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'4',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'4',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'4',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'4',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'4',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'4',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'4',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'4',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'4',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'4',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'4',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'4',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'4',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'4',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'4',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'4',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'4',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'4',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'4',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'4',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'4',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'4',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'4',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'4',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'4',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'4',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'4',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'4',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'4',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'4',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'4',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'4',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'4',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'4',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'4',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'4',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'4',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'4',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'4',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'4',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'4',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'4',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'4',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'4',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'4',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'4',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'4',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'4',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'4',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'4',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'4',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'4',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'4',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'4',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'4',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'4',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'4',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'4',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'4',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'4',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'4',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'4',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'4',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'4',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'4',south:'up',west:'up'}}"); ++ register(885, "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'5',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'5',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'5',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'5',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'5',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'5',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'5',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'5',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'5',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'5',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'5',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'5',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'5',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'5',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'5',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'5',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'5',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'5',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'5',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'5',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'5',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'5',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'5',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'5',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'5',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'5',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'5',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'5',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'5',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'5',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'5',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'5',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'5',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'5',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'5',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'5',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'5',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'5',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'5',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'5',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'5',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'5',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'5',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'5',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'5',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'5',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'5',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'5',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'5',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'5',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'5',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'5',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'5',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'5',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'5',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'5',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'5',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'5',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'5',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'5',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'5',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'5',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'5',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'5',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'5',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'5',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'5',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'5',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'5',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'5',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'5',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'5',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'5',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'5',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'5',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'5',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'5',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'5',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'5',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'5',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'5',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'5',south:'up',west:'up'}}"); ++ register(886, "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'6',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'6',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'6',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'6',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'6',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'6',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'6',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'6',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'6',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'6',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'6',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'6',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'6',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'6',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'6',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'6',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'6',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'6',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'6',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'6',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'6',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'6',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'6',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'6',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'6',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'6',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'6',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'6',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'6',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'6',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'6',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'6',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'6',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'6',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'6',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'6',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'6',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'6',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'6',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'6',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'6',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'6',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'6',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'6',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'6',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'6',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'6',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'6',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'6',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'6',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'6',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'6',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'6',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'6',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'6',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'6',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'6',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'6',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'6',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'6',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'6',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'6',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'6',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'6',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'6',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'6',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'6',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'6',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'6',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'6',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'6',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'6',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'6',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'6',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'6',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'6',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'6',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'6',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'6',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'6',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'6',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'6',south:'up',west:'up'}}"); ++ register(887, "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'7',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'7',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'7',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'7',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'7',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'7',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'7',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'7',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'7',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'7',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'7',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'7',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'7',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'7',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'7',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'7',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'7',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'7',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'7',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'7',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'7',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'7',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'7',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'7',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'7',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'7',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'7',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'7',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'7',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'7',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'7',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'7',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'7',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'7',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'7',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'7',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'7',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'7',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'7',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'7',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'7',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'7',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'7',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'7',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'7',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'7',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'7',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'7',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'7',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'7',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'7',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'7',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'7',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'7',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'7',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'7',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'7',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'7',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'7',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'7',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'7',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'7',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'7',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'7',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'7',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'7',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'7',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'7',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'7',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'7',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'7',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'7',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'7',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'7',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'7',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'7',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'7',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'7',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'7',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'7',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'7',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'7',south:'up',west:'up'}}"); ++ register(888, "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'8',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'8',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'8',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'8',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'8',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'8',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'8',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'8',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'8',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'8',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'8',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'8',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'8',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'8',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'8',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'8',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'8',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'8',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'8',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'8',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'8',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'8',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'8',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'8',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'8',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'8',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'8',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'8',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'8',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'8',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'8',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'8',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'8',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'8',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'8',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'8',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'8',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'8',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'8',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'8',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'8',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'8',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'8',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'8',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'8',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'8',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'8',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'8',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'8',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'8',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'8',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'8',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'8',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'8',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'8',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'8',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'8',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'8',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'8',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'8',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'8',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'8',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'8',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'8',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'8',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'8',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'8',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'8',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'8',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'8',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'8',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'8',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'8',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'8',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'8',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'8',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'8',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'8',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'8',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'8',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'8',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'8',south:'up',west:'up'}}"); ++ register(889, "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'9',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'9',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'9',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'9',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'9',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'9',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'9',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'9',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'9',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'9',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'9',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'9',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'9',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'9',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'9',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'9',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'9',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'9',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'9',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'9',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'9',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'9',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'9',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'9',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'9',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'9',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'9',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'9',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'9',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'9',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'9',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'9',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'9',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'9',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'9',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'9',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'9',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'9',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'9',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'9',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'9',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'9',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'9',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'9',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'9',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'9',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'9',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'9',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'9',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'9',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'9',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'9',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'9',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'9',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'9',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'9',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'9',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'9',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'9',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'9',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'9',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'9',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'9',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'9',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'9',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'9',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'9',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'9',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'9',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'9',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'9',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'9',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'9',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'9',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'9',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'9',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'9',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'9',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'9',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'9',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'9',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'9',south:'up',west:'up'}}"); ++ register(890, "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'10',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'10',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'10',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'10',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'10',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'10',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'10',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'10',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'10',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'10',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'10',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'10',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'10',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'10',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'10',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'10',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'10',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'10',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'10',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'10',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'10',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'10',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'10',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'10',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'10',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'10',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'10',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'10',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'10',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'10',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'10',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'10',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'10',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'10',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'10',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'10',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'10',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'10',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'10',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'10',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'10',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'10',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'10',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'10',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'10',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'10',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'10',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'10',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'10',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'10',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'10',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'10',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'10',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'10',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'10',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'10',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'10',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'10',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'10',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'10',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'10',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'10',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'10',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'10',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'10',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'10',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'10',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'10',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'10',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'10',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'10',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'10',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'10',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'10',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'10',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'10',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'10',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'10',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'10',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'10',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'10',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'10',south:'up',west:'up'}}"); ++ register(891, "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'11',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'11',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'11',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'11',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'11',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'11',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'11',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'11',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'11',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'11',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'11',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'11',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'11',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'11',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'11',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'11',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'11',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'11',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'11',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'11',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'11',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'11',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'11',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'11',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'11',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'11',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'11',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'11',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'11',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'11',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'11',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'11',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'11',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'11',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'11',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'11',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'11',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'11',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'11',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'11',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'11',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'11',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'11',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'11',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'11',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'11',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'11',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'11',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'11',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'11',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'11',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'11',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'11',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'11',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'11',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'11',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'11',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'11',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'11',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'11',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'11',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'11',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'11',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'11',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'11',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'11',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'11',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'11',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'11',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'11',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'11',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'11',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'11',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'11',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'11',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'11',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'11',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'11',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'11',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'11',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'11',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'11',south:'up',west:'up'}}"); ++ register(892, "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'12',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'12',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'12',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'12',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'12',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'12',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'12',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'12',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'12',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'12',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'12',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'12',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'12',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'12',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'12',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'12',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'12',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'12',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'12',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'12',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'12',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'12',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'12',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'12',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'12',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'12',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'12',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'12',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'12',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'12',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'12',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'12',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'12',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'12',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'12',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'12',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'12',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'12',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'12',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'12',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'12',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'12',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'12',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'12',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'12',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'12',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'12',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'12',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'12',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'12',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'12',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'12',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'12',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'12',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'12',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'12',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'12',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'12',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'12',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'12',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'12',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'12',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'12',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'12',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'12',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'12',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'12',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'12',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'12',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'12',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'12',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'12',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'12',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'12',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'12',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'12',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'12',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'12',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'12',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'12',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'12',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'12',south:'up',west:'up'}}"); ++ register(893, "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'13',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'13',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'13',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'13',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'13',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'13',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'13',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'13',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'13',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'13',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'13',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'13',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'13',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'13',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'13',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'13',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'13',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'13',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'13',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'13',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'13',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'13',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'13',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'13',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'13',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'13',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'13',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'13',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'13',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'13',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'13',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'13',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'13',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'13',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'13',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'13',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'13',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'13',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'13',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'13',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'13',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'13',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'13',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'13',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'13',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'13',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'13',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'13',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'13',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'13',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'13',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'13',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'13',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'13',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'13',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'13',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'13',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'13',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'13',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'13',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'13',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'13',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'13',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'13',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'13',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'13',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'13',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'13',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'13',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'13',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'13',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'13',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'13',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'13',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'13',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'13',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'13',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'13',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'13',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'13',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'13',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'13',south:'up',west:'up'}}"); ++ register(894, "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'14',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'14',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'14',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'14',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'14',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'14',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'14',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'14',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'14',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'14',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'14',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'14',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'14',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'14',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'14',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'14',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'14',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'14',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'14',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'14',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'14',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'14',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'14',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'14',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'14',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'14',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'14',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'14',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'14',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'14',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'14',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'14',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'14',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'14',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'14',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'14',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'14',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'14',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'14',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'14',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'14',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'14',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'14',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'14',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'14',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'14',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'14',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'14',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'14',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'14',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'14',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'14',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'14',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'14',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'14',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'14',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'14',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'14',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'14',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'14',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'14',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'14',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'14',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'14',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'14',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'14',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'14',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'14',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'14',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'14',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'14',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'14',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'14',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'14',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'14',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'14',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'14',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'14',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'14',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'14',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'14',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'14',south:'up',west:'up'}}"); ++ register(895, "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'15',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'15',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'15',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'15',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'15',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'15',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'15',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'15',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'15',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'none',power:'15',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'15',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'15',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'15',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'15',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'15',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'15',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'15',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'15',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'side',power:'15',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'15',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'15',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'15',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'15',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'15',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'15',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'15',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'15',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'none',north:'up',power:'15',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'15',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'15',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'15',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'15',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'15',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'15',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'15',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'15',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'none',power:'15',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'15',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'15',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'15',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'15',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'15',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'15',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'15',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'15',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'side',power:'15',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'15',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'15',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'15',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'15',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'15',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'15',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'15',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'15',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'side',north:'up',power:'15',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'15',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'15',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'15',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'15',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'15',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'15',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'15',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'15',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'none',power:'15',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'15',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'15',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'15',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'15',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'15',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'15',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'15',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'15',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'side',power:'15',south:'up',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'15',south:'none',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'15',south:'none',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'15',south:'none',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'15',south:'side',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'15',south:'side',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'15',south:'side',west:'up'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'15',south:'up',west:'none'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'15',south:'up',west:'side'}}", "{Name:'minecraft:redstone_wire',Properties:{east:'up',north:'up',power:'15',south:'up',west:'up'}}"); ++ register(896, "{Name:'minecraft:diamond_ore'}", "{Name:'minecraft:diamond_ore'}"); ++ register(912, "{Name:'minecraft:diamond_block'}", "{Name:'minecraft:diamond_block'}"); ++ register(928, "{Name:'minecraft:crafting_table'}", "{Name:'minecraft:crafting_table'}"); ++ register(944, "{Name:'minecraft:wheat',Properties:{age:'0'}}", "{Name:'minecraft:wheat',Properties:{age:'0'}}"); ++ register(945, "{Name:'minecraft:wheat',Properties:{age:'1'}}", "{Name:'minecraft:wheat',Properties:{age:'1'}}"); ++ register(946, "{Name:'minecraft:wheat',Properties:{age:'2'}}", "{Name:'minecraft:wheat',Properties:{age:'2'}}"); ++ register(947, "{Name:'minecraft:wheat',Properties:{age:'3'}}", "{Name:'minecraft:wheat',Properties:{age:'3'}}"); ++ register(948, "{Name:'minecraft:wheat',Properties:{age:'4'}}", "{Name:'minecraft:wheat',Properties:{age:'4'}}"); ++ register(949, "{Name:'minecraft:wheat',Properties:{age:'5'}}", "{Name:'minecraft:wheat',Properties:{age:'5'}}"); ++ register(950, "{Name:'minecraft:wheat',Properties:{age:'6'}}", "{Name:'minecraft:wheat',Properties:{age:'6'}}"); ++ register(951, "{Name:'minecraft:wheat',Properties:{age:'7'}}", "{Name:'minecraft:wheat',Properties:{age:'7'}}"); ++ register(960, "{Name:'minecraft:farmland',Properties:{moisture:'0'}}", "{Name:'minecraft:farmland',Properties:{moisture:'0'}}"); ++ register(961, "{Name:'minecraft:farmland',Properties:{moisture:'1'}}", "{Name:'minecraft:farmland',Properties:{moisture:'1'}}"); ++ register(962, "{Name:'minecraft:farmland',Properties:{moisture:'2'}}", "{Name:'minecraft:farmland',Properties:{moisture:'2'}}"); ++ register(963, "{Name:'minecraft:farmland',Properties:{moisture:'3'}}", "{Name:'minecraft:farmland',Properties:{moisture:'3'}}"); ++ register(964, "{Name:'minecraft:farmland',Properties:{moisture:'4'}}", "{Name:'minecraft:farmland',Properties:{moisture:'4'}}"); ++ register(965, "{Name:'minecraft:farmland',Properties:{moisture:'5'}}", "{Name:'minecraft:farmland',Properties:{moisture:'5'}}"); ++ register(966, "{Name:'minecraft:farmland',Properties:{moisture:'6'}}", "{Name:'minecraft:farmland',Properties:{moisture:'6'}}"); ++ register(967, "{Name:'minecraft:farmland',Properties:{moisture:'7'}}", "{Name:'minecraft:farmland',Properties:{moisture:'7'}}"); ++ register(978, "{Name:'minecraft:furnace',Properties:{facing:'north',lit:'false'}}", "{Name:'minecraft:furnace',Properties:{facing:'north'}}"); ++ register(979, "{Name:'minecraft:furnace',Properties:{facing:'south',lit:'false'}}", "{Name:'minecraft:furnace',Properties:{facing:'south'}}"); ++ register(980, "{Name:'minecraft:furnace',Properties:{facing:'west',lit:'false'}}", "{Name:'minecraft:furnace',Properties:{facing:'west'}}"); ++ register(981, "{Name:'minecraft:furnace',Properties:{facing:'east',lit:'false'}}", "{Name:'minecraft:furnace',Properties:{facing:'east'}}"); ++ register(994, "{Name:'minecraft:furnace',Properties:{facing:'north',lit:'true'}}", "{Name:'minecraft:lit_furnace',Properties:{facing:'north'}}"); ++ register(995, "{Name:'minecraft:furnace',Properties:{facing:'south',lit:'true'}}", "{Name:'minecraft:lit_furnace',Properties:{facing:'south'}}"); ++ register(996, "{Name:'minecraft:furnace',Properties:{facing:'west',lit:'true'}}", "{Name:'minecraft:lit_furnace',Properties:{facing:'west'}}"); ++ register(997, "{Name:'minecraft:furnace',Properties:{facing:'east',lit:'true'}}", "{Name:'minecraft:lit_furnace',Properties:{facing:'east'}}"); ++ register(1008, "{Name:'minecraft:sign',Properties:{rotation:'0'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'0'}}"); ++ register(1009, "{Name:'minecraft:sign',Properties:{rotation:'1'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'1'}}"); ++ register(1010, "{Name:'minecraft:sign',Properties:{rotation:'2'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'2'}}"); ++ register(1011, "{Name:'minecraft:sign',Properties:{rotation:'3'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'3'}}"); ++ register(1012, "{Name:'minecraft:sign',Properties:{rotation:'4'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'4'}}"); ++ register(1013, "{Name:'minecraft:sign',Properties:{rotation:'5'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'5'}}"); ++ register(1014, "{Name:'minecraft:sign',Properties:{rotation:'6'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'6'}}"); ++ register(1015, "{Name:'minecraft:sign',Properties:{rotation:'7'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'7'}}"); ++ register(1016, "{Name:'minecraft:sign',Properties:{rotation:'8'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'8'}}"); ++ register(1017, "{Name:'minecraft:sign',Properties:{rotation:'9'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'9'}}"); ++ register(1018, "{Name:'minecraft:sign',Properties:{rotation:'10'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'10'}}"); ++ register(1019, "{Name:'minecraft:sign',Properties:{rotation:'11'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'11'}}"); ++ register(1020, "{Name:'minecraft:sign',Properties:{rotation:'12'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'12'}}"); ++ register(1021, "{Name:'minecraft:sign',Properties:{rotation:'13'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'13'}}"); ++ register(1022, "{Name:'minecraft:sign',Properties:{rotation:'14'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'14'}}"); ++ register(1023, "{Name:'minecraft:sign',Properties:{rotation:'15'}}", "{Name:'minecraft:standing_sign',Properties:{rotation:'15'}}"); ++ register(1024, "{Name:'minecraft:oak_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(1025, "{Name:'minecraft:oak_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(1026, "{Name:'minecraft:oak_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(1027, "{Name:'minecraft:oak_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(1028, "{Name:'minecraft:oak_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(1029, "{Name:'minecraft:oak_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(1030, "{Name:'minecraft:oak_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(1031, "{Name:'minecraft:oak_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(1032, "{Name:'minecraft:oak_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'true',powered:'false'}}"); ++ register(1033, "{Name:'minecraft:oak_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'true',powered:'false'}}"); ++ register(1034, "{Name:'minecraft:oak_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'true',powered:'true'}}"); ++ register(1035, "{Name:'minecraft:oak_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:wooden_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'true',powered:'true'}}"); ++ register(1036, "{Name:'minecraft:oak_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'true',powered:'false'}}"); ++ register(1037, "{Name:'minecraft:oak_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'true',powered:'false'}}"); ++ register(1038, "{Name:'minecraft:oak_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'true',powered:'false'}}"); ++ register(1039, "{Name:'minecraft:oak_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'true',powered:'false'}}"); ++ register(1042, "{Name:'minecraft:ladder',Properties:{facing:'north'}}", "{Name:'minecraft:ladder',Properties:{facing:'north'}}"); ++ register(1043, "{Name:'minecraft:ladder',Properties:{facing:'south'}}", "{Name:'minecraft:ladder',Properties:{facing:'south'}}"); ++ register(1044, "{Name:'minecraft:ladder',Properties:{facing:'west'}}", "{Name:'minecraft:ladder',Properties:{facing:'west'}}"); ++ register(1045, "{Name:'minecraft:ladder',Properties:{facing:'east'}}", "{Name:'minecraft:ladder',Properties:{facing:'east'}}"); ++ register(1056, "{Name:'minecraft:rail',Properties:{shape:'north_south'}}", "{Name:'minecraft:rail',Properties:{shape:'north_south'}}"); ++ register(1057, "{Name:'minecraft:rail',Properties:{shape:'east_west'}}", "{Name:'minecraft:rail',Properties:{shape:'east_west'}}"); ++ register(1058, "{Name:'minecraft:rail',Properties:{shape:'ascending_east'}}", "{Name:'minecraft:rail',Properties:{shape:'ascending_east'}}"); ++ register(1059, "{Name:'minecraft:rail',Properties:{shape:'ascending_west'}}", "{Name:'minecraft:rail',Properties:{shape:'ascending_west'}}"); ++ register(1060, "{Name:'minecraft:rail',Properties:{shape:'ascending_north'}}", "{Name:'minecraft:rail',Properties:{shape:'ascending_north'}}"); ++ register(1061, "{Name:'minecraft:rail',Properties:{shape:'ascending_south'}}", "{Name:'minecraft:rail',Properties:{shape:'ascending_south'}}"); ++ register(1062, "{Name:'minecraft:rail',Properties:{shape:'south_east'}}", "{Name:'minecraft:rail',Properties:{shape:'south_east'}}"); ++ register(1063, "{Name:'minecraft:rail',Properties:{shape:'south_west'}}", "{Name:'minecraft:rail',Properties:{shape:'south_west'}}"); ++ register(1064, "{Name:'minecraft:rail',Properties:{shape:'north_west'}}", "{Name:'minecraft:rail',Properties:{shape:'north_west'}}"); ++ register(1065, "{Name:'minecraft:rail',Properties:{shape:'north_east'}}", "{Name:'minecraft:rail',Properties:{shape:'north_east'}}"); ++ register(1072, "{Name:'minecraft:cobblestone_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}"); ++ register(1073, "{Name:'minecraft:cobblestone_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}"); ++ register(1074, "{Name:'minecraft:cobblestone_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}"); ++ register(1075, "{Name:'minecraft:cobblestone_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}"); ++ register(1076, "{Name:'minecraft:cobblestone_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'east',half:'top',shape:'inner_left'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'east',half:'top',shape:'inner_right'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'east',half:'top',shape:'outer_left'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'east',half:'top',shape:'outer_right'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}"); ++ register(1077, "{Name:'minecraft:cobblestone_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'west',half:'top',shape:'inner_left'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'west',half:'top',shape:'inner_right'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'west',half:'top',shape:'outer_left'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'west',half:'top',shape:'outer_right'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}"); ++ register(1078, "{Name:'minecraft:cobblestone_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'south',half:'top',shape:'inner_left'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'south',half:'top',shape:'inner_right'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'south',half:'top',shape:'outer_left'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'south',half:'top',shape:'outer_right'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}"); ++ register(1079, "{Name:'minecraft:cobblestone_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'north',half:'top',shape:'inner_left'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'north',half:'top',shape:'inner_right'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'north',half:'top',shape:'outer_left'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'north',half:'top',shape:'outer_right'}}", "{Name:'minecraft:stone_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}"); ++ register(1090, "{Name:'minecraft:wall_sign',Properties:{facing:'north'}}", "{Name:'minecraft:wall_sign',Properties:{facing:'north'}}"); ++ register(1091, "{Name:'minecraft:wall_sign',Properties:{facing:'south'}}", "{Name:'minecraft:wall_sign',Properties:{facing:'south'}}"); ++ register(1092, "{Name:'minecraft:wall_sign',Properties:{facing:'west'}}", "{Name:'minecraft:wall_sign',Properties:{facing:'west'}}"); ++ register(1093, "{Name:'minecraft:wall_sign',Properties:{facing:'east'}}", "{Name:'minecraft:wall_sign',Properties:{facing:'east'}}"); ++ register(1104, "{Name:'minecraft:lever',Properties:{face:'ceiling',facing:'west',powered:'false'}}", "{Name:'minecraft:lever',Properties:{facing:'down_x',powered:'false'}}"); ++ register(1105, "{Name:'minecraft:lever',Properties:{face:'wall',facing:'east',powered:'false'}}", "{Name:'minecraft:lever',Properties:{facing:'east',powered:'false'}}"); ++ register(1106, "{Name:'minecraft:lever',Properties:{face:'wall',facing:'west',powered:'false'}}", "{Name:'minecraft:lever',Properties:{facing:'west',powered:'false'}}"); ++ register(1107, "{Name:'minecraft:lever',Properties:{face:'wall',facing:'south',powered:'false'}}", "{Name:'minecraft:lever',Properties:{facing:'south',powered:'false'}}"); ++ register(1108, "{Name:'minecraft:lever',Properties:{face:'wall',facing:'north',powered:'false'}}", "{Name:'minecraft:lever',Properties:{facing:'north',powered:'false'}}"); ++ register(1109, "{Name:'minecraft:lever',Properties:{face:'floor',facing:'north',powered:'false'}}", "{Name:'minecraft:lever',Properties:{facing:'up_z',powered:'false'}}"); ++ register(1110, "{Name:'minecraft:lever',Properties:{face:'floor',facing:'west',powered:'false'}}", "{Name:'minecraft:lever',Properties:{facing:'up_x',powered:'false'}}"); ++ register(1111, "{Name:'minecraft:lever',Properties:{face:'ceiling',facing:'north',powered:'false'}}", "{Name:'minecraft:lever',Properties:{facing:'down_z',powered:'false'}}"); ++ register(1112, "{Name:'minecraft:lever',Properties:{face:'ceiling',facing:'west',powered:'true'}}", "{Name:'minecraft:lever',Properties:{facing:'down_x',powered:'true'}}"); ++ register(1113, "{Name:'minecraft:lever',Properties:{face:'wall',facing:'east',powered:'true'}}", "{Name:'minecraft:lever',Properties:{facing:'east',powered:'true'}}"); ++ register(1114, "{Name:'minecraft:lever',Properties:{face:'wall',facing:'west',powered:'true'}}", "{Name:'minecraft:lever',Properties:{facing:'west',powered:'true'}}"); ++ register(1115, "{Name:'minecraft:lever',Properties:{face:'wall',facing:'south',powered:'true'}}", "{Name:'minecraft:lever',Properties:{facing:'south',powered:'true'}}"); ++ register(1116, "{Name:'minecraft:lever',Properties:{face:'wall',facing:'north',powered:'true'}}", "{Name:'minecraft:lever',Properties:{facing:'north',powered:'true'}}"); ++ register(1117, "{Name:'minecraft:lever',Properties:{face:'floor',facing:'north',powered:'true'}}", "{Name:'minecraft:lever',Properties:{facing:'up_z',powered:'true'}}"); ++ register(1118, "{Name:'minecraft:lever',Properties:{face:'floor',facing:'west',powered:'true'}}", "{Name:'minecraft:lever',Properties:{facing:'up_x',powered:'true'}}"); ++ register(1119, "{Name:'minecraft:lever',Properties:{face:'ceiling',facing:'north',powered:'true'}}", "{Name:'minecraft:lever',Properties:{facing:'down_z',powered:'true'}}"); ++ register(1120, "{Name:'minecraft:stone_pressure_plate',Properties:{powered:'false'}}", "{Name:'minecraft:stone_pressure_plate',Properties:{powered:'false'}}"); ++ register(1121, "{Name:'minecraft:stone_pressure_plate',Properties:{powered:'true'}}", "{Name:'minecraft:stone_pressure_plate',Properties:{powered:'true'}}"); ++ register(1136, "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(1137, "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(1138, "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(1139, "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(1140, "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(1141, "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(1142, "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(1143, "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(1144, "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'true',powered:'false'}}"); ++ register(1145, "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'true',powered:'false'}}"); ++ register(1146, "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'true',powered:'true'}}"); ++ register(1147, "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'true',powered:'true'}}"); ++ register(1148, "{Name:'minecraft:iron_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'true',powered:'false'}}"); ++ register(1149, "{Name:'minecraft:iron_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'true',powered:'false'}}"); ++ register(1150, "{Name:'minecraft:iron_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'true',powered:'false'}}"); ++ register(1151, "{Name:'minecraft:iron_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'true',powered:'false'}}"); ++ register(1152, "{Name:'minecraft:oak_pressure_plate',Properties:{powered:'false'}}", "{Name:'minecraft:wooden_pressure_plate',Properties:{powered:'false'}}"); ++ register(1153, "{Name:'minecraft:oak_pressure_plate',Properties:{powered:'true'}}", "{Name:'minecraft:wooden_pressure_plate',Properties:{powered:'true'}}"); ++ register(1168, "{Name:'minecraft:redstone_ore',Properties:{lit:'false'}}", "{Name:'minecraft:redstone_ore'}"); ++ register(1184, "{Name:'minecraft:redstone_ore',Properties:{lit:'true'}}", "{Name:'minecraft:lit_redstone_ore'}"); ++ register(1201, "{Name:'minecraft:redstone_wall_torch',Properties:{facing:'east',lit:'false'}}", "{Name:'minecraft:unlit_redstone_torch',Properties:{facing:'east'}}"); ++ register(1202, "{Name:'minecraft:redstone_wall_torch',Properties:{facing:'west',lit:'false'}}", "{Name:'minecraft:unlit_redstone_torch',Properties:{facing:'west'}}"); ++ register(1203, "{Name:'minecraft:redstone_wall_torch',Properties:{facing:'south',lit:'false'}}", "{Name:'minecraft:unlit_redstone_torch',Properties:{facing:'south'}}"); ++ register(1204, "{Name:'minecraft:redstone_wall_torch',Properties:{facing:'north',lit:'false'}}", "{Name:'minecraft:unlit_redstone_torch',Properties:{facing:'north'}}"); ++ register(1205, "{Name:'minecraft:redstone_torch',Properties:{lit:'false'}}", "{Name:'minecraft:unlit_redstone_torch',Properties:{facing:'up'}}"); ++ register(1217, "{Name:'minecraft:redstone_wall_torch',Properties:{facing:'east',lit:'true'}}", "{Name:'minecraft:redstone_torch',Properties:{facing:'east'}}"); ++ register(1218, "{Name:'minecraft:redstone_wall_torch',Properties:{facing:'west',lit:'true'}}", "{Name:'minecraft:redstone_torch',Properties:{facing:'west'}}"); ++ register(1219, "{Name:'minecraft:redstone_wall_torch',Properties:{facing:'south',lit:'true'}}", "{Name:'minecraft:redstone_torch',Properties:{facing:'south'}}"); ++ register(1220, "{Name:'minecraft:redstone_wall_torch',Properties:{facing:'north',lit:'true'}}", "{Name:'minecraft:redstone_torch',Properties:{facing:'north'}}"); ++ register(1221, "{Name:'minecraft:redstone_torch',Properties:{lit:'true'}}", "{Name:'minecraft:redstone_torch',Properties:{facing:'up'}}"); ++ register(1232, "{Name:'minecraft:stone_button',Properties:{face:'ceiling',facing:'north',powered:'false'}}", "{Name:'minecraft:stone_button',Properties:{facing:'down',powered:'false'}}"); ++ register(1233, "{Name:'minecraft:stone_button',Properties:{face:'wall',facing:'east',powered:'false'}}", "{Name:'minecraft:stone_button',Properties:{facing:'east',powered:'false'}}"); ++ register(1234, "{Name:'minecraft:stone_button',Properties:{face:'wall',facing:'west',powered:'false'}}", "{Name:'minecraft:stone_button',Properties:{facing:'west',powered:'false'}}"); ++ register(1235, "{Name:'minecraft:stone_button',Properties:{face:'wall',facing:'south',powered:'false'}}", "{Name:'minecraft:stone_button',Properties:{facing:'south',powered:'false'}}"); ++ register(1236, "{Name:'minecraft:stone_button',Properties:{face:'wall',facing:'north',powered:'false'}}", "{Name:'minecraft:stone_button',Properties:{facing:'north',powered:'false'}}"); ++ register(1237, "{Name:'minecraft:stone_button',Properties:{face:'floor',facing:'north',powered:'false'}}", "{Name:'minecraft:stone_button',Properties:{facing:'up',powered:'false'}}"); ++ register(1240, "{Name:'minecraft:stone_button',Properties:{face:'ceiling',facing:'north',powered:'true'}}", "{Name:'minecraft:stone_button',Properties:{facing:'down',powered:'true'}}"); ++ register(1241, "{Name:'minecraft:stone_button',Properties:{face:'wall',facing:'east',powered:'true'}}", "{Name:'minecraft:stone_button',Properties:{facing:'east',powered:'true'}}"); ++ register(1242, "{Name:'minecraft:stone_button',Properties:{face:'wall',facing:'west',powered:'true'}}", "{Name:'minecraft:stone_button',Properties:{facing:'west',powered:'true'}}"); ++ register(1243, "{Name:'minecraft:stone_button',Properties:{face:'wall',facing:'south',powered:'true'}}", "{Name:'minecraft:stone_button',Properties:{facing:'south',powered:'true'}}"); ++ register(1244, "{Name:'minecraft:stone_button',Properties:{face:'wall',facing:'north',powered:'true'}}", "{Name:'minecraft:stone_button',Properties:{facing:'north',powered:'true'}}"); ++ register(1245, "{Name:'minecraft:stone_button',Properties:{face:'floor',facing:'north',powered:'true'}}", "{Name:'minecraft:stone_button',Properties:{facing:'up',powered:'true'}}"); ++ register(1248, "{Name:'minecraft:snow',Properties:{layers:'1'}}", "{Name:'minecraft:snow_layer',Properties:{layers:'1'}}"); ++ register(1249, "{Name:'minecraft:snow',Properties:{layers:'2'}}", "{Name:'minecraft:snow_layer',Properties:{layers:'2'}}"); ++ register(1250, "{Name:'minecraft:snow',Properties:{layers:'3'}}", "{Name:'minecraft:snow_layer',Properties:{layers:'3'}}"); ++ register(1251, "{Name:'minecraft:snow',Properties:{layers:'4'}}", "{Name:'minecraft:snow_layer',Properties:{layers:'4'}}"); ++ register(1252, "{Name:'minecraft:snow',Properties:{layers:'5'}}", "{Name:'minecraft:snow_layer',Properties:{layers:'5'}}"); ++ register(1253, "{Name:'minecraft:snow',Properties:{layers:'6'}}", "{Name:'minecraft:snow_layer',Properties:{layers:'6'}}"); ++ register(1254, "{Name:'minecraft:snow',Properties:{layers:'7'}}", "{Name:'minecraft:snow_layer',Properties:{layers:'7'}}"); ++ register(1255, "{Name:'minecraft:snow',Properties:{layers:'8'}}", "{Name:'minecraft:snow_layer',Properties:{layers:'8'}}"); ++ register(1264, "{Name:'minecraft:ice'}", "{Name:'minecraft:ice'}"); ++ register(1280, "{Name:'minecraft:snow_block'}", "{Name:'minecraft:snow'}"); ++ register(1296, "{Name:'minecraft:cactus',Properties:{age:'0'}}", "{Name:'minecraft:cactus',Properties:{age:'0'}}"); ++ register(1297, "{Name:'minecraft:cactus',Properties:{age:'1'}}", "{Name:'minecraft:cactus',Properties:{age:'1'}}"); ++ register(1298, "{Name:'minecraft:cactus',Properties:{age:'2'}}", "{Name:'minecraft:cactus',Properties:{age:'2'}}"); ++ register(1299, "{Name:'minecraft:cactus',Properties:{age:'3'}}", "{Name:'minecraft:cactus',Properties:{age:'3'}}"); ++ register(1300, "{Name:'minecraft:cactus',Properties:{age:'4'}}", "{Name:'minecraft:cactus',Properties:{age:'4'}}"); ++ register(1301, "{Name:'minecraft:cactus',Properties:{age:'5'}}", "{Name:'minecraft:cactus',Properties:{age:'5'}}"); ++ register(1302, "{Name:'minecraft:cactus',Properties:{age:'6'}}", "{Name:'minecraft:cactus',Properties:{age:'6'}}"); ++ register(1303, "{Name:'minecraft:cactus',Properties:{age:'7'}}", "{Name:'minecraft:cactus',Properties:{age:'7'}}"); ++ register(1304, "{Name:'minecraft:cactus',Properties:{age:'8'}}", "{Name:'minecraft:cactus',Properties:{age:'8'}}"); ++ register(1305, "{Name:'minecraft:cactus',Properties:{age:'9'}}", "{Name:'minecraft:cactus',Properties:{age:'9'}}"); ++ register(1306, "{Name:'minecraft:cactus',Properties:{age:'10'}}", "{Name:'minecraft:cactus',Properties:{age:'10'}}"); ++ register(1307, "{Name:'minecraft:cactus',Properties:{age:'11'}}", "{Name:'minecraft:cactus',Properties:{age:'11'}}"); ++ register(1308, "{Name:'minecraft:cactus',Properties:{age:'12'}}", "{Name:'minecraft:cactus',Properties:{age:'12'}}"); ++ register(1309, "{Name:'minecraft:cactus',Properties:{age:'13'}}", "{Name:'minecraft:cactus',Properties:{age:'13'}}"); ++ register(1310, "{Name:'minecraft:cactus',Properties:{age:'14'}}", "{Name:'minecraft:cactus',Properties:{age:'14'}}"); ++ register(1311, "{Name:'minecraft:cactus',Properties:{age:'15'}}", "{Name:'minecraft:cactus',Properties:{age:'15'}}"); ++ register(1312, "{Name:'minecraft:clay'}", "{Name:'minecraft:clay'}"); ++ register(1328, "{Name:'minecraft:sugar_cane',Properties:{age:'0'}}", "{Name:'minecraft:reeds',Properties:{age:'0'}}"); ++ register(1329, "{Name:'minecraft:sugar_cane',Properties:{age:'1'}}", "{Name:'minecraft:reeds',Properties:{age:'1'}}"); ++ register(1330, "{Name:'minecraft:sugar_cane',Properties:{age:'2'}}", "{Name:'minecraft:reeds',Properties:{age:'2'}}"); ++ register(1331, "{Name:'minecraft:sugar_cane',Properties:{age:'3'}}", "{Name:'minecraft:reeds',Properties:{age:'3'}}"); ++ register(1332, "{Name:'minecraft:sugar_cane',Properties:{age:'4'}}", "{Name:'minecraft:reeds',Properties:{age:'4'}}"); ++ register(1333, "{Name:'minecraft:sugar_cane',Properties:{age:'5'}}", "{Name:'minecraft:reeds',Properties:{age:'5'}}"); ++ register(1334, "{Name:'minecraft:sugar_cane',Properties:{age:'6'}}", "{Name:'minecraft:reeds',Properties:{age:'6'}}"); ++ register(1335, "{Name:'minecraft:sugar_cane',Properties:{age:'7'}}", "{Name:'minecraft:reeds',Properties:{age:'7'}}"); ++ register(1336, "{Name:'minecraft:sugar_cane',Properties:{age:'8'}}", "{Name:'minecraft:reeds',Properties:{age:'8'}}"); ++ register(1337, "{Name:'minecraft:sugar_cane',Properties:{age:'9'}}", "{Name:'minecraft:reeds',Properties:{age:'9'}}"); ++ register(1338, "{Name:'minecraft:sugar_cane',Properties:{age:'10'}}", "{Name:'minecraft:reeds',Properties:{age:'10'}}"); ++ register(1339, "{Name:'minecraft:sugar_cane',Properties:{age:'11'}}", "{Name:'minecraft:reeds',Properties:{age:'11'}}"); ++ register(1340, "{Name:'minecraft:sugar_cane',Properties:{age:'12'}}", "{Name:'minecraft:reeds',Properties:{age:'12'}}"); ++ register(1341, "{Name:'minecraft:sugar_cane',Properties:{age:'13'}}", "{Name:'minecraft:reeds',Properties:{age:'13'}}"); ++ register(1342, "{Name:'minecraft:sugar_cane',Properties:{age:'14'}}", "{Name:'minecraft:reeds',Properties:{age:'14'}}"); ++ register(1343, "{Name:'minecraft:sugar_cane',Properties:{age:'15'}}", "{Name:'minecraft:reeds',Properties:{age:'15'}}"); ++ register(1344, "{Name:'minecraft:jukebox',Properties:{has_record:'false'}}", "{Name:'minecraft:jukebox',Properties:{has_record:'false'}}"); ++ register(1345, "{Name:'minecraft:jukebox',Properties:{has_record:'true'}}", "{Name:'minecraft:jukebox',Properties:{has_record:'true'}}"); ++ register(1360, "{Name:'minecraft:oak_fence',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:fence',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:fence',Properties:{east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:fence',Properties:{east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:fence',Properties:{east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:fence',Properties:{east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:fence',Properties:{east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:fence',Properties:{east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:fence',Properties:{east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:fence',Properties:{east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:fence',Properties:{east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:fence',Properties:{east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:fence',Properties:{east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:fence',Properties:{east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:fence',Properties:{east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:fence',Properties:{east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:fence',Properties:{east:'true',north:'true',south:'true',west:'true'}}"); ++ register(1376, "{Name:'minecraft:carved_pumpkin',Properties:{facing:'south'}}", "{Name:'minecraft:pumpkin',Properties:{facing:'south'}}"); ++ register(1377, "{Name:'minecraft:carved_pumpkin',Properties:{facing:'west'}}", "{Name:'minecraft:pumpkin',Properties:{facing:'west'}}"); ++ register(1378, "{Name:'minecraft:carved_pumpkin',Properties:{facing:'north'}}", "{Name:'minecraft:pumpkin',Properties:{facing:'north'}}"); ++ register(1379, "{Name:'minecraft:carved_pumpkin',Properties:{facing:'east'}}", "{Name:'minecraft:pumpkin',Properties:{facing:'east'}}"); ++ register(1392, "{Name:'minecraft:netherrack'}", "{Name:'minecraft:netherrack'}"); ++ register(1408, "{Name:'minecraft:soul_sand'}", "{Name:'minecraft:soul_sand'}"); ++ register(1424, "{Name:'minecraft:glowstone'}", "{Name:'minecraft:glowstone'}"); ++ register(1441, "{Name:'minecraft:portal',Properties:{axis:'x'}}", "{Name:'minecraft:portal',Properties:{axis:'x'}}"); ++ register(1442, "{Name:'minecraft:portal',Properties:{axis:'z'}}", "{Name:'minecraft:portal',Properties:{axis:'z'}}"); ++ register(1456, "{Name:'minecraft:jack_o_lantern',Properties:{facing:'south'}}", "{Name:'minecraft:lit_pumpkin',Properties:{facing:'south'}}"); ++ register(1457, "{Name:'minecraft:jack_o_lantern',Properties:{facing:'west'}}", "{Name:'minecraft:lit_pumpkin',Properties:{facing:'west'}}"); ++ register(1458, "{Name:'minecraft:jack_o_lantern',Properties:{facing:'north'}}", "{Name:'minecraft:lit_pumpkin',Properties:{facing:'north'}}"); ++ register(1459, "{Name:'minecraft:jack_o_lantern',Properties:{facing:'east'}}", "{Name:'minecraft:lit_pumpkin',Properties:{facing:'east'}}"); ++ register(1472, "{Name:'minecraft:cake',Properties:{bites:'0'}}", "{Name:'minecraft:cake',Properties:{bites:'0'}}"); ++ register(1473, "{Name:'minecraft:cake',Properties:{bites:'1'}}", "{Name:'minecraft:cake',Properties:{bites:'1'}}"); ++ register(1474, "{Name:'minecraft:cake',Properties:{bites:'2'}}", "{Name:'minecraft:cake',Properties:{bites:'2'}}"); ++ register(1475, "{Name:'minecraft:cake',Properties:{bites:'3'}}", "{Name:'minecraft:cake',Properties:{bites:'3'}}"); ++ register(1476, "{Name:'minecraft:cake',Properties:{bites:'4'}}", "{Name:'minecraft:cake',Properties:{bites:'4'}}"); ++ register(1477, "{Name:'minecraft:cake',Properties:{bites:'5'}}", "{Name:'minecraft:cake',Properties:{bites:'5'}}"); ++ register(1478, "{Name:'minecraft:cake',Properties:{bites:'6'}}", "{Name:'minecraft:cake',Properties:{bites:'6'}}"); ++ register(1488, "{Name:'minecraft:repeater',Properties:{delay:'1',facing:'south',locked:'false',powered:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'1',facing:'south',locked:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'1',facing:'south',locked:'true'}}"); ++ register(1489, "{Name:'minecraft:repeater',Properties:{delay:'1',facing:'west',locked:'false',powered:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'1',facing:'west',locked:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'1',facing:'west',locked:'true'}}"); ++ register(1490, "{Name:'minecraft:repeater',Properties:{delay:'1',facing:'north',locked:'false',powered:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'1',facing:'north',locked:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'1',facing:'north',locked:'true'}}"); ++ register(1491, "{Name:'minecraft:repeater',Properties:{delay:'1',facing:'east',locked:'false',powered:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'1',facing:'east',locked:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'1',facing:'east',locked:'true'}}"); ++ register(1492, "{Name:'minecraft:repeater',Properties:{delay:'2',facing:'south',locked:'false',powered:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'2',facing:'south',locked:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'2',facing:'south',locked:'true'}}"); ++ register(1493, "{Name:'minecraft:repeater',Properties:{delay:'2',facing:'west',locked:'false',powered:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'2',facing:'west',locked:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'2',facing:'west',locked:'true'}}"); ++ register(1494, "{Name:'minecraft:repeater',Properties:{delay:'2',facing:'north',locked:'false',powered:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'2',facing:'north',locked:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'2',facing:'north',locked:'true'}}"); ++ register(1495, "{Name:'minecraft:repeater',Properties:{delay:'2',facing:'east',locked:'false',powered:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'2',facing:'east',locked:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'2',facing:'east',locked:'true'}}"); ++ register(1496, "{Name:'minecraft:repeater',Properties:{delay:'3',facing:'south',locked:'false',powered:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'3',facing:'south',locked:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'3',facing:'south',locked:'true'}}"); ++ register(1497, "{Name:'minecraft:repeater',Properties:{delay:'3',facing:'west',locked:'false',powered:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'3',facing:'west',locked:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'3',facing:'west',locked:'true'}}"); ++ register(1498, "{Name:'minecraft:repeater',Properties:{delay:'3',facing:'north',locked:'false',powered:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'3',facing:'north',locked:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'3',facing:'north',locked:'true'}}"); ++ register(1499, "{Name:'minecraft:repeater',Properties:{delay:'3',facing:'east',locked:'false',powered:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'3',facing:'east',locked:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'3',facing:'east',locked:'true'}}"); ++ register(1500, "{Name:'minecraft:repeater',Properties:{delay:'4',facing:'south',locked:'false',powered:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'4',facing:'south',locked:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'4',facing:'south',locked:'true'}}"); ++ register(1501, "{Name:'minecraft:repeater',Properties:{delay:'4',facing:'west',locked:'false',powered:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'4',facing:'west',locked:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'4',facing:'west',locked:'true'}}"); ++ register(1502, "{Name:'minecraft:repeater',Properties:{delay:'4',facing:'north',locked:'false',powered:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'4',facing:'north',locked:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'4',facing:'north',locked:'true'}}"); ++ register(1503, "{Name:'minecraft:repeater',Properties:{delay:'4',facing:'east',locked:'false',powered:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'4',facing:'east',locked:'false'}}", "{Name:'minecraft:unpowered_repeater',Properties:{delay:'4',facing:'east',locked:'true'}}"); ++ register(1504, "{Name:'minecraft:repeater',Properties:{delay:'1',facing:'south',locked:'false',powered:'true'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'1',facing:'south',locked:'false'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'1',facing:'south',locked:'true'}}"); ++ register(1505, "{Name:'minecraft:repeater',Properties:{delay:'1',facing:'west',locked:'false',powered:'true'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'1',facing:'west',locked:'false'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'1',facing:'west',locked:'true'}}"); ++ register(1506, "{Name:'minecraft:repeater',Properties:{delay:'1',facing:'north',locked:'false',powered:'true'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'1',facing:'north',locked:'false'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'1',facing:'north',locked:'true'}}"); ++ register(1507, "{Name:'minecraft:repeater',Properties:{delay:'1',facing:'east',locked:'false',powered:'true'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'1',facing:'east',locked:'false'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'1',facing:'east',locked:'true'}}"); ++ register(1508, "{Name:'minecraft:repeater',Properties:{delay:'2',facing:'south',locked:'false',powered:'true'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'2',facing:'south',locked:'false'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'2',facing:'south',locked:'true'}}"); ++ register(1509, "{Name:'minecraft:repeater',Properties:{delay:'2',facing:'west',locked:'false',powered:'true'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'2',facing:'west',locked:'false'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'2',facing:'west',locked:'true'}}"); ++ register(1510, "{Name:'minecraft:repeater',Properties:{delay:'2',facing:'north',locked:'false',powered:'true'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'2',facing:'north',locked:'false'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'2',facing:'north',locked:'true'}}"); ++ register(1511, "{Name:'minecraft:repeater',Properties:{delay:'2',facing:'east',locked:'false',powered:'true'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'2',facing:'east',locked:'false'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'2',facing:'east',locked:'true'}}"); ++ register(1512, "{Name:'minecraft:repeater',Properties:{delay:'3',facing:'south',locked:'false',powered:'true'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'3',facing:'south',locked:'false'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'3',facing:'south',locked:'true'}}"); ++ register(1513, "{Name:'minecraft:repeater',Properties:{delay:'3',facing:'west',locked:'false',powered:'true'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'3',facing:'west',locked:'false'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'3',facing:'west',locked:'true'}}"); ++ register(1514, "{Name:'minecraft:repeater',Properties:{delay:'3',facing:'north',locked:'false',powered:'true'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'3',facing:'north',locked:'false'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'3',facing:'north',locked:'true'}}"); ++ register(1515, "{Name:'minecraft:repeater',Properties:{delay:'3',facing:'east',locked:'false',powered:'true'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'3',facing:'east',locked:'false'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'3',facing:'east',locked:'true'}}"); ++ register(1516, "{Name:'minecraft:repeater',Properties:{delay:'4',facing:'south',locked:'false',powered:'true'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'4',facing:'south',locked:'false'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'4',facing:'south',locked:'true'}}"); ++ register(1517, "{Name:'minecraft:repeater',Properties:{delay:'4',facing:'west',locked:'false',powered:'true'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'4',facing:'west',locked:'false'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'4',facing:'west',locked:'true'}}"); ++ register(1518, "{Name:'minecraft:repeater',Properties:{delay:'4',facing:'north',locked:'false',powered:'true'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'4',facing:'north',locked:'false'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'4',facing:'north',locked:'true'}}"); ++ register(1519, "{Name:'minecraft:repeater',Properties:{delay:'4',facing:'east',locked:'false',powered:'true'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'4',facing:'east',locked:'false'}}", "{Name:'minecraft:powered_repeater',Properties:{delay:'4',facing:'east',locked:'true'}}"); ++ register(1520, "{Name:'minecraft:white_stained_glass'}", "{Name:'minecraft:stained_glass',Properties:{color:'white'}}"); ++ register(1521, "{Name:'minecraft:orange_stained_glass'}", "{Name:'minecraft:stained_glass',Properties:{color:'orange'}}"); ++ register(1522, "{Name:'minecraft:magenta_stained_glass'}", "{Name:'minecraft:stained_glass',Properties:{color:'magenta'}}"); ++ register(1523, "{Name:'minecraft:light_blue_stained_glass'}", "{Name:'minecraft:stained_glass',Properties:{color:'light_blue'}}"); ++ register(1524, "{Name:'minecraft:yellow_stained_glass'}", "{Name:'minecraft:stained_glass',Properties:{color:'yellow'}}"); ++ register(1525, "{Name:'minecraft:lime_stained_glass'}", "{Name:'minecraft:stained_glass',Properties:{color:'lime'}}"); ++ register(1526, "{Name:'minecraft:pink_stained_glass'}", "{Name:'minecraft:stained_glass',Properties:{color:'pink'}}"); ++ register(1527, "{Name:'minecraft:gray_stained_glass'}", "{Name:'minecraft:stained_glass',Properties:{color:'gray'}}"); ++ register(1528, "{Name:'minecraft:light_gray_stained_glass'}", "{Name:'minecraft:stained_glass',Properties:{color:'silver'}}"); ++ register(1529, "{Name:'minecraft:cyan_stained_glass'}", "{Name:'minecraft:stained_glass',Properties:{color:'cyan'}}"); ++ register(1530, "{Name:'minecraft:purple_stained_glass'}", "{Name:'minecraft:stained_glass',Properties:{color:'purple'}}"); ++ register(1531, "{Name:'minecraft:blue_stained_glass'}", "{Name:'minecraft:stained_glass',Properties:{color:'blue'}}"); ++ register(1532, "{Name:'minecraft:brown_stained_glass'}", "{Name:'minecraft:stained_glass',Properties:{color:'brown'}}"); ++ register(1533, "{Name:'minecraft:green_stained_glass'}", "{Name:'minecraft:stained_glass',Properties:{color:'green'}}"); ++ register(1534, "{Name:'minecraft:red_stained_glass'}", "{Name:'minecraft:stained_glass',Properties:{color:'red'}}"); ++ register(1535, "{Name:'minecraft:black_stained_glass'}", "{Name:'minecraft:stained_glass',Properties:{color:'black'}}"); ++ register(1536, "{Name:'minecraft:oak_trapdoor',Properties:{facing:'north',half:'bottom',open:'false'}}", "{Name:'minecraft:trapdoor',Properties:{facing:'north',half:'bottom',open:'false'}}"); ++ register(1537, "{Name:'minecraft:oak_trapdoor',Properties:{facing:'south',half:'bottom',open:'false'}}", "{Name:'minecraft:trapdoor',Properties:{facing:'south',half:'bottom',open:'false'}}"); ++ register(1538, "{Name:'minecraft:oak_trapdoor',Properties:{facing:'west',half:'bottom',open:'false'}}", "{Name:'minecraft:trapdoor',Properties:{facing:'west',half:'bottom',open:'false'}}"); ++ register(1539, "{Name:'minecraft:oak_trapdoor',Properties:{facing:'east',half:'bottom',open:'false'}}", "{Name:'minecraft:trapdoor',Properties:{facing:'east',half:'bottom',open:'false'}}"); ++ register(1540, "{Name:'minecraft:oak_trapdoor',Properties:{facing:'north',half:'bottom',open:'true'}}", "{Name:'minecraft:trapdoor',Properties:{facing:'north',half:'bottom',open:'true'}}"); ++ register(1541, "{Name:'minecraft:oak_trapdoor',Properties:{facing:'south',half:'bottom',open:'true'}}", "{Name:'minecraft:trapdoor',Properties:{facing:'south',half:'bottom',open:'true'}}"); ++ register(1542, "{Name:'minecraft:oak_trapdoor',Properties:{facing:'west',half:'bottom',open:'true'}}", "{Name:'minecraft:trapdoor',Properties:{facing:'west',half:'bottom',open:'true'}}"); ++ register(1543, "{Name:'minecraft:oak_trapdoor',Properties:{facing:'east',half:'bottom',open:'true'}}", "{Name:'minecraft:trapdoor',Properties:{facing:'east',half:'bottom',open:'true'}}"); ++ register(1544, "{Name:'minecraft:oak_trapdoor',Properties:{facing:'north',half:'top',open:'false'}}", "{Name:'minecraft:trapdoor',Properties:{facing:'north',half:'top',open:'false'}}"); ++ register(1545, "{Name:'minecraft:oak_trapdoor',Properties:{facing:'south',half:'top',open:'false'}}", "{Name:'minecraft:trapdoor',Properties:{facing:'south',half:'top',open:'false'}}"); ++ register(1546, "{Name:'minecraft:oak_trapdoor',Properties:{facing:'west',half:'top',open:'false'}}", "{Name:'minecraft:trapdoor',Properties:{facing:'west',half:'top',open:'false'}}"); ++ register(1547, "{Name:'minecraft:oak_trapdoor',Properties:{facing:'east',half:'top',open:'false'}}", "{Name:'minecraft:trapdoor',Properties:{facing:'east',half:'top',open:'false'}}"); ++ register(1548, "{Name:'minecraft:oak_trapdoor',Properties:{facing:'north',half:'top',open:'true'}}", "{Name:'minecraft:trapdoor',Properties:{facing:'north',half:'top',open:'true'}}"); ++ register(1549, "{Name:'minecraft:oak_trapdoor',Properties:{facing:'south',half:'top',open:'true'}}", "{Name:'minecraft:trapdoor',Properties:{facing:'south',half:'top',open:'true'}}"); ++ register(1550, "{Name:'minecraft:oak_trapdoor',Properties:{facing:'west',half:'top',open:'true'}}", "{Name:'minecraft:trapdoor',Properties:{facing:'west',half:'top',open:'true'}}"); ++ register(1551, "{Name:'minecraft:oak_trapdoor',Properties:{facing:'east',half:'top',open:'true'}}", "{Name:'minecraft:trapdoor',Properties:{facing:'east',half:'top',open:'true'}}"); ++ register(1552, "{Name:'minecraft:infested_stone'}", "{Name:'minecraft:monster_egg',Properties:{variant:'stone'}}"); ++ register(1553, "{Name:'minecraft:infested_cobblestone'}", "{Name:'minecraft:monster_egg',Properties:{variant:'cobblestone'}}"); ++ register(1554, "{Name:'minecraft:infested_stone_bricks'}", "{Name:'minecraft:monster_egg',Properties:{variant:'stone_brick'}}"); ++ register(1555, "{Name:'minecraft:infested_mossy_stone_bricks'}", "{Name:'minecraft:monster_egg',Properties:{variant:'mossy_brick'}}"); ++ register(1556, "{Name:'minecraft:infested_cracked_stone_bricks'}", "{Name:'minecraft:monster_egg',Properties:{variant:'cracked_brick'}}"); ++ register(1557, "{Name:'minecraft:infested_chiseled_stone_bricks'}", "{Name:'minecraft:monster_egg',Properties:{variant:'chiseled_brick'}}"); ++ register(1568, "{Name:'minecraft:stone_bricks'}", "{Name:'minecraft:stonebrick',Properties:{variant:'stonebrick'}}"); ++ register(1569, "{Name:'minecraft:mossy_stone_bricks'}", "{Name:'minecraft:stonebrick',Properties:{variant:'mossy_stonebrick'}}"); ++ register(1570, "{Name:'minecraft:cracked_stone_bricks'}", "{Name:'minecraft:stonebrick',Properties:{variant:'cracked_stonebrick'}}"); ++ register(1571, "{Name:'minecraft:chiseled_stone_bricks'}", "{Name:'minecraft:stonebrick',Properties:{variant:'chiseled_stonebrick'}}"); ++ register(1584, "{Name:'minecraft:brown_mushroom_block',Properties:{north:'false',east:'false',south:'false',west:'false',up:'false',down:'false'}}", "{Name:'minecraft:brown_mushroom_block',Properties:{variant:'all_inside'}}"); ++ register(1585, "{Name:'minecraft:brown_mushroom_block',Properties:{north:'true',east:'false',south:'false',west:'true',up:'true',down:'false'}}", "{Name:'minecraft:brown_mushroom_block',Properties:{variant:'north_west'}}"); ++ register(1586, "{Name:'minecraft:brown_mushroom_block',Properties:{north:'true',east:'false',south:'false',west:'false',up:'true',down:'false'}}", "{Name:'minecraft:brown_mushroom_block',Properties:{variant:'north'}}"); ++ register(1587, "{Name:'minecraft:brown_mushroom_block',Properties:{north:'true',east:'true',south:'false',west:'false',up:'true',down:'false'}}", "{Name:'minecraft:brown_mushroom_block',Properties:{variant:'north_east'}}"); ++ register(1588, "{Name:'minecraft:brown_mushroom_block',Properties:{north:'false',east:'false',south:'false',west:'true',up:'true',down:'false'}}", "{Name:'minecraft:brown_mushroom_block',Properties:{variant:'west'}}"); ++ register(1589, "{Name:'minecraft:brown_mushroom_block',Properties:{north:'false',east:'false',south:'false',west:'false',up:'true',down:'false'}}", "{Name:'minecraft:brown_mushroom_block',Properties:{variant:'center'}}"); ++ register(1590, "{Name:'minecraft:brown_mushroom_block',Properties:{north:'false',east:'true',south:'false',west:'false',up:'true',down:'false'}}", "{Name:'minecraft:brown_mushroom_block',Properties:{variant:'east'}}"); ++ register(1591, "{Name:'minecraft:brown_mushroom_block',Properties:{north:'false',east:'false',south:'true',west:'true',up:'true',down:'false'}}", "{Name:'minecraft:brown_mushroom_block',Properties:{variant:'south_west'}}"); ++ register(1592, "{Name:'minecraft:brown_mushroom_block',Properties:{north:'false',east:'false',south:'true',west:'false',up:'true',down:'false'}}", "{Name:'minecraft:brown_mushroom_block',Properties:{variant:'south'}}"); ++ register(1593, "{Name:'minecraft:brown_mushroom_block',Properties:{north:'false',east:'true',south:'true',west:'false',up:'true',down:'false'}}", "{Name:'minecraft:brown_mushroom_block',Properties:{variant:'south_east'}}"); ++ register(1594, "{Name:'minecraft:mushroom_stem',Properties:{north:'true',east:'true',south:'true',west:'true',up:'false',down:'false'}}", "{Name:'minecraft:brown_mushroom_block',Properties:{variant:'stem'}}"); ++ register(1595, "{Name:'minecraft:brown_mushroom_block',Properties:{north:'false',east:'false',south:'false',west:'false',up:'false',down:'false'}}"); ++ register(1596, "{Name:'minecraft:brown_mushroom_block',Properties:{north:'false',east:'false',south:'false',west:'false',up:'false',down:'false'}}"); ++ register(1597, "{Name:'minecraft:brown_mushroom_block',Properties:{north:'false',east:'false',south:'false',west:'false',up:'false',down:'false'}}"); ++ register(1598, "{Name:'minecraft:brown_mushroom_block',Properties:{north:'true',east:'true',south:'true',west:'true',up:'true',down:'true'}}", "{Name:'minecraft:brown_mushroom_block',Properties:{variant:'all_outside'}}"); ++ register(1599, "{Name:'minecraft:mushroom_stem',Properties:{north:'true',east:'true',south:'true',west:'true',up:'true',down:'true'}}", "{Name:'minecraft:brown_mushroom_block',Properties:{variant:'all_stem'}}"); ++ register(1600, "{Name:'minecraft:red_mushroom_block',Properties:{north:'false',east:'false',south:'false',west:'false',up:'false',down:'false'}}", "{Name:'minecraft:red_mushroom_block',Properties:{variant:'all_inside'}}"); ++ register(1601, "{Name:'minecraft:red_mushroom_block',Properties:{north:'true',east:'false',south:'false',west:'true',up:'true',down:'false'}}", "{Name:'minecraft:red_mushroom_block',Properties:{variant:'north_west'}}"); ++ register(1602, "{Name:'minecraft:red_mushroom_block',Properties:{north:'true',east:'false',south:'false',west:'false',up:'true',down:'false'}}", "{Name:'minecraft:red_mushroom_block',Properties:{variant:'north'}}"); ++ register(1603, "{Name:'minecraft:red_mushroom_block',Properties:{north:'true',east:'true',south:'false',west:'false',up:'true',down:'false'}}", "{Name:'minecraft:red_mushroom_block',Properties:{variant:'north_east'}}"); ++ register(1604, "{Name:'minecraft:red_mushroom_block',Properties:{north:'false',east:'false',south:'false',west:'true',up:'true',down:'false'}}", "{Name:'minecraft:red_mushroom_block',Properties:{variant:'west'}}"); ++ register(1605, "{Name:'minecraft:red_mushroom_block',Properties:{north:'false',east:'false',south:'false',west:'false',up:'true',down:'false'}}", "{Name:'minecraft:red_mushroom_block',Properties:{variant:'center'}}"); ++ register(1606, "{Name:'minecraft:red_mushroom_block',Properties:{north:'false',east:'true',south:'false',west:'false',up:'true',down:'false'}}", "{Name:'minecraft:red_mushroom_block',Properties:{variant:'east'}}"); ++ register(1607, "{Name:'minecraft:red_mushroom_block',Properties:{north:'false',east:'false',south:'true',west:'true',up:'true',down:'false'}}", "{Name:'minecraft:red_mushroom_block',Properties:{variant:'south_west'}}"); ++ register(1608, "{Name:'minecraft:red_mushroom_block',Properties:{north:'false',east:'false',south:'true',west:'false',up:'true',down:'false'}}", "{Name:'minecraft:red_mushroom_block',Properties:{variant:'south'}}"); ++ register(1609, "{Name:'minecraft:red_mushroom_block',Properties:{north:'false',east:'true',south:'true',west:'false',up:'true',down:'false'}}", "{Name:'minecraft:red_mushroom_block',Properties:{variant:'south_east'}}"); ++ register(1610, "{Name:'minecraft:mushroom_stem',Properties:{north:'true',east:'true',south:'true',west:'true',up:'false',down:'false'}}", "{Name:'minecraft:red_mushroom_block',Properties:{variant:'stem'}}"); ++ register(1611, "{Name:'minecraft:red_mushroom_block',Properties:{north:'false',east:'false',south:'false',west:'false',up:'false',down:'false'}}"); ++ register(1612, "{Name:'minecraft:red_mushroom_block',Properties:{north:'false',east:'false',south:'false',west:'false',up:'false',down:'false'}}"); ++ register(1613, "{Name:'minecraft:red_mushroom_block',Properties:{north:'false',east:'false',south:'false',west:'false',up:'false',down:'false'}}"); ++ register(1614, "{Name:'minecraft:red_mushroom_block',Properties:{north:'true',east:'true',south:'true',west:'true',up:'true',down:'true'}}", "{Name:'minecraft:red_mushroom_block',Properties:{variant:'all_outside'}}"); ++ register(1615, "{Name:'minecraft:mushroom_stem',Properties:{north:'true',east:'true',south:'true',west:'true',up:'true',down:'true'}}", "{Name:'minecraft:red_mushroom_block',Properties:{variant:'all_stem'}}"); ++ register(1616, "{Name:'minecraft:iron_bars',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:iron_bars',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:iron_bars',Properties:{east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:iron_bars',Properties:{east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:iron_bars',Properties:{east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:iron_bars',Properties:{east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:iron_bars',Properties:{east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:iron_bars',Properties:{east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:iron_bars',Properties:{east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:iron_bars',Properties:{east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:iron_bars',Properties:{east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:iron_bars',Properties:{east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:iron_bars',Properties:{east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:iron_bars',Properties:{east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:iron_bars',Properties:{east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:iron_bars',Properties:{east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:iron_bars',Properties:{east:'true',north:'true',south:'true',west:'true'}}"); ++ register(1632, "{Name:'minecraft:glass_pane',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:glass_pane',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:glass_pane',Properties:{east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:glass_pane',Properties:{east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:glass_pane',Properties:{east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:glass_pane',Properties:{east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:glass_pane',Properties:{east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:glass_pane',Properties:{east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:glass_pane',Properties:{east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:glass_pane',Properties:{east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:glass_pane',Properties:{east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:glass_pane',Properties:{east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:glass_pane',Properties:{east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:glass_pane',Properties:{east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:glass_pane',Properties:{east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:glass_pane',Properties:{east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:glass_pane',Properties:{east:'true',north:'true',south:'true',west:'true'}}"); ++ register(1648, "{Name:'minecraft:melon_block'}", "{Name:'minecraft:melon_block'}"); ++ register(1664, "{Name:'minecraft:pumpkin_stem',Properties:{age:'0'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'0',facing:'east'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'0',facing:'north'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'0',facing:'south'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'0',facing:'up'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'0',facing:'west'}}"); ++ register(1665, "{Name:'minecraft:pumpkin_stem',Properties:{age:'1'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'1',facing:'east'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'1',facing:'north'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'1',facing:'south'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'1',facing:'up'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'1',facing:'west'}}"); ++ register(1666, "{Name:'minecraft:pumpkin_stem',Properties:{age:'2'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'2',facing:'east'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'2',facing:'north'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'2',facing:'south'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'2',facing:'up'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'2',facing:'west'}}"); ++ register(1667, "{Name:'minecraft:pumpkin_stem',Properties:{age:'3'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'3',facing:'east'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'3',facing:'north'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'3',facing:'south'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'3',facing:'up'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'3',facing:'west'}}"); ++ register(1668, "{Name:'minecraft:pumpkin_stem',Properties:{age:'4'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'4',facing:'east'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'4',facing:'north'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'4',facing:'south'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'4',facing:'up'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'4',facing:'west'}}"); ++ register(1669, "{Name:'minecraft:pumpkin_stem',Properties:{age:'5'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'5',facing:'east'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'5',facing:'north'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'5',facing:'south'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'5',facing:'up'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'5',facing:'west'}}"); ++ register(1670, "{Name:'minecraft:pumpkin_stem',Properties:{age:'6'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'6',facing:'east'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'6',facing:'north'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'6',facing:'south'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'6',facing:'up'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'6',facing:'west'}}"); ++ register(1671, "{Name:'minecraft:pumpkin_stem',Properties:{age:'7'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'7',facing:'east'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'7',facing:'north'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'7',facing:'south'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'7',facing:'up'}}", "{Name:'minecraft:pumpkin_stem',Properties:{age:'7',facing:'west'}}"); ++ register(1680, "{Name:'minecraft:melon_stem',Properties:{age:'0'}}", "{Name:'minecraft:melon_stem',Properties:{age:'0',facing:'east'}}", "{Name:'minecraft:melon_stem',Properties:{age:'0',facing:'north'}}", "{Name:'minecraft:melon_stem',Properties:{age:'0',facing:'south'}}", "{Name:'minecraft:melon_stem',Properties:{age:'0',facing:'up'}}", "{Name:'minecraft:melon_stem',Properties:{age:'0',facing:'west'}}"); ++ register(1681, "{Name:'minecraft:melon_stem',Properties:{age:'1'}}", "{Name:'minecraft:melon_stem',Properties:{age:'1',facing:'east'}}", "{Name:'minecraft:melon_stem',Properties:{age:'1',facing:'north'}}", "{Name:'minecraft:melon_stem',Properties:{age:'1',facing:'south'}}", "{Name:'minecraft:melon_stem',Properties:{age:'1',facing:'up'}}", "{Name:'minecraft:melon_stem',Properties:{age:'1',facing:'west'}}"); ++ register(1682, "{Name:'minecraft:melon_stem',Properties:{age:'2'}}", "{Name:'minecraft:melon_stem',Properties:{age:'2',facing:'east'}}", "{Name:'minecraft:melon_stem',Properties:{age:'2',facing:'north'}}", "{Name:'minecraft:melon_stem',Properties:{age:'2',facing:'south'}}", "{Name:'minecraft:melon_stem',Properties:{age:'2',facing:'up'}}", "{Name:'minecraft:melon_stem',Properties:{age:'2',facing:'west'}}"); ++ register(1683, "{Name:'minecraft:melon_stem',Properties:{age:'3'}}", "{Name:'minecraft:melon_stem',Properties:{age:'3',facing:'east'}}", "{Name:'minecraft:melon_stem',Properties:{age:'3',facing:'north'}}", "{Name:'minecraft:melon_stem',Properties:{age:'3',facing:'south'}}", "{Name:'minecraft:melon_stem',Properties:{age:'3',facing:'up'}}", "{Name:'minecraft:melon_stem',Properties:{age:'3',facing:'west'}}"); ++ register(1684, "{Name:'minecraft:melon_stem',Properties:{age:'4'}}", "{Name:'minecraft:melon_stem',Properties:{age:'4',facing:'east'}}", "{Name:'minecraft:melon_stem',Properties:{age:'4',facing:'north'}}", "{Name:'minecraft:melon_stem',Properties:{age:'4',facing:'south'}}", "{Name:'minecraft:melon_stem',Properties:{age:'4',facing:'up'}}", "{Name:'minecraft:melon_stem',Properties:{age:'4',facing:'west'}}"); ++ register(1685, "{Name:'minecraft:melon_stem',Properties:{age:'5'}}", "{Name:'minecraft:melon_stem',Properties:{age:'5',facing:'east'}}", "{Name:'minecraft:melon_stem',Properties:{age:'5',facing:'north'}}", "{Name:'minecraft:melon_stem',Properties:{age:'5',facing:'south'}}", "{Name:'minecraft:melon_stem',Properties:{age:'5',facing:'up'}}", "{Name:'minecraft:melon_stem',Properties:{age:'5',facing:'west'}}"); ++ register(1686, "{Name:'minecraft:melon_stem',Properties:{age:'6'}}", "{Name:'minecraft:melon_stem',Properties:{age:'6',facing:'east'}}", "{Name:'minecraft:melon_stem',Properties:{age:'6',facing:'north'}}", "{Name:'minecraft:melon_stem',Properties:{age:'6',facing:'south'}}", "{Name:'minecraft:melon_stem',Properties:{age:'6',facing:'up'}}", "{Name:'minecraft:melon_stem',Properties:{age:'6',facing:'west'}}"); ++ register(1687, "{Name:'minecraft:melon_stem',Properties:{age:'7'}}", "{Name:'minecraft:melon_stem',Properties:{age:'7',facing:'east'}}", "{Name:'minecraft:melon_stem',Properties:{age:'7',facing:'north'}}", "{Name:'minecraft:melon_stem',Properties:{age:'7',facing:'south'}}", "{Name:'minecraft:melon_stem',Properties:{age:'7',facing:'up'}}", "{Name:'minecraft:melon_stem',Properties:{age:'7',facing:'west'}}"); ++ register(1696, "{Name:'minecraft:vine',Properties:{east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:vine',Properties:{east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:vine',Properties:{east:'false',north:'false',south:'false',up:'true',west:'false'}}"); ++ register(1697, "{Name:'minecraft:vine',Properties:{east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:vine',Properties:{east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:vine',Properties:{east:'false',north:'false',south:'true',up:'true',west:'false'}}"); ++ register(1698, "{Name:'minecraft:vine',Properties:{east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:vine',Properties:{east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:vine',Properties:{east:'false',north:'false',south:'false',up:'true',west:'true'}}"); ++ register(1699, "{Name:'minecraft:vine',Properties:{east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:vine',Properties:{east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:vine',Properties:{east:'false',north:'false',south:'true',up:'true',west:'true'}}"); ++ register(1700, "{Name:'minecraft:vine',Properties:{east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:vine',Properties:{east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:vine',Properties:{east:'false',north:'true',south:'false',up:'true',west:'false'}}"); ++ register(1701, "{Name:'minecraft:vine',Properties:{east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:vine',Properties:{east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:vine',Properties:{east:'false',north:'true',south:'true',up:'true',west:'false'}}"); ++ register(1702, "{Name:'minecraft:vine',Properties:{east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:vine',Properties:{east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:vine',Properties:{east:'false',north:'true',south:'false',up:'true',west:'true'}}"); ++ register(1703, "{Name:'minecraft:vine',Properties:{east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:vine',Properties:{east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:vine',Properties:{east:'false',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(1704, "{Name:'minecraft:vine',Properties:{east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:vine',Properties:{east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:vine',Properties:{east:'true',north:'false',south:'false',up:'true',west:'false'}}"); ++ register(1705, "{Name:'minecraft:vine',Properties:{east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:vine',Properties:{east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:vine',Properties:{east:'true',north:'false',south:'true',up:'true',west:'false'}}"); ++ register(1706, "{Name:'minecraft:vine',Properties:{east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:vine',Properties:{east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:vine',Properties:{east:'true',north:'false',south:'false',up:'true',west:'true'}}"); ++ register(1707, "{Name:'minecraft:vine',Properties:{east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:vine',Properties:{east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:vine',Properties:{east:'true',north:'false',south:'true',up:'true',west:'true'}}"); ++ register(1708, "{Name:'minecraft:vine',Properties:{east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:vine',Properties:{east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:vine',Properties:{east:'true',north:'true',south:'false',up:'true',west:'false'}}"); ++ register(1709, "{Name:'minecraft:vine',Properties:{east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:vine',Properties:{east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:vine',Properties:{east:'true',north:'true',south:'true',up:'true',west:'false'}}"); ++ register(1710, "{Name:'minecraft:vine',Properties:{east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:vine',Properties:{east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:vine',Properties:{east:'true',north:'true',south:'false',up:'true',west:'true'}}"); ++ register(1711, "{Name:'minecraft:vine',Properties:{east:'true',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:vine',Properties:{east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:vine',Properties:{east:'true',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(1712, "{Name:'minecraft:oak_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'south',in_wall:'true',open:'false',powered:'false'}}"); ++ register(1713, "{Name:'minecraft:oak_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'west',in_wall:'true',open:'false',powered:'false'}}"); ++ register(1714, "{Name:'minecraft:oak_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'north',in_wall:'true',open:'false',powered:'false'}}"); ++ register(1715, "{Name:'minecraft:oak_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'east',in_wall:'true',open:'false',powered:'false'}}"); ++ register(1716, "{Name:'minecraft:oak_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'south',in_wall:'true',open:'true',powered:'false'}}"); ++ register(1717, "{Name:'minecraft:oak_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'west',in_wall:'true',open:'true',powered:'false'}}"); ++ register(1718, "{Name:'minecraft:oak_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'north',in_wall:'true',open:'true',powered:'false'}}"); ++ register(1719, "{Name:'minecraft:oak_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'east',in_wall:'true',open:'true',powered:'false'}}"); ++ register(1720, "{Name:'minecraft:oak_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'south',in_wall:'true',open:'false',powered:'true'}}"); ++ register(1721, "{Name:'minecraft:oak_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'west',in_wall:'true',open:'false',powered:'true'}}"); ++ register(1722, "{Name:'minecraft:oak_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'north',in_wall:'true',open:'false',powered:'true'}}"); ++ register(1723, "{Name:'minecraft:oak_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'east',in_wall:'true',open:'false',powered:'true'}}"); ++ register(1724, "{Name:'minecraft:oak_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'south',in_wall:'true',open:'true',powered:'true'}}"); ++ register(1725, "{Name:'minecraft:oak_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'west',in_wall:'true',open:'true',powered:'true'}}"); ++ register(1726, "{Name:'minecraft:oak_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'north',in_wall:'true',open:'true',powered:'true'}}"); ++ register(1727, "{Name:'minecraft:oak_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:fence_gate',Properties:{facing:'east',in_wall:'true',open:'true',powered:'true'}}"); ++ register(1728, "{Name:'minecraft:brick_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}"); ++ register(1729, "{Name:'minecraft:brick_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}"); ++ register(1730, "{Name:'minecraft:brick_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}"); ++ register(1731, "{Name:'minecraft:brick_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}"); ++ register(1732, "{Name:'minecraft:brick_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'east',half:'top',shape:'inner_left'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'east',half:'top',shape:'inner_right'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'east',half:'top',shape:'outer_left'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'east',half:'top',shape:'outer_right'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}"); ++ register(1733, "{Name:'minecraft:brick_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'west',half:'top',shape:'inner_left'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'west',half:'top',shape:'inner_right'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'west',half:'top',shape:'outer_left'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'west',half:'top',shape:'outer_right'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}"); ++ register(1734, "{Name:'minecraft:brick_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'south',half:'top',shape:'inner_left'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'south',half:'top',shape:'inner_right'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'south',half:'top',shape:'outer_left'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'south',half:'top',shape:'outer_right'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}"); ++ register(1735, "{Name:'minecraft:brick_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'north',half:'top',shape:'inner_left'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'north',half:'top',shape:'inner_right'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'north',half:'top',shape:'outer_left'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'north',half:'top',shape:'outer_right'}}", "{Name:'minecraft:brick_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}"); ++ register(1744, "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}"); ++ register(1745, "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}"); ++ register(1746, "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}"); ++ register(1747, "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}"); ++ register(1748, "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'east',half:'top',shape:'inner_left'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'east',half:'top',shape:'inner_right'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'east',half:'top',shape:'outer_left'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'east',half:'top',shape:'outer_right'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}"); ++ register(1749, "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'west',half:'top',shape:'inner_left'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'west',half:'top',shape:'inner_right'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'west',half:'top',shape:'outer_left'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'west',half:'top',shape:'outer_right'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}"); ++ register(1750, "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'south',half:'top',shape:'inner_left'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'south',half:'top',shape:'inner_right'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'south',half:'top',shape:'outer_left'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'south',half:'top',shape:'outer_right'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}"); ++ register(1751, "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'north',half:'top',shape:'inner_left'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'north',half:'top',shape:'inner_right'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'north',half:'top',shape:'outer_left'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'north',half:'top',shape:'outer_right'}}", "{Name:'minecraft:stone_brick_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}"); ++ register(1760, "{Name:'minecraft:mycelium',Properties:{snowy:'false'}}", "{Name:'minecraft:mycelium',Properties:{snowy:'false'}}", "{Name:'minecraft:mycelium',Properties:{snowy:'true'}}"); ++ register(1776, "{Name:'minecraft:lily_pad'}", "{Name:'minecraft:waterlily'}"); ++ register(1792, "{Name:'minecraft:nether_bricks'}", "{Name:'minecraft:nether_brick'}"); ++ register(1808, "{Name:'minecraft:nether_brick_fence',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:nether_brick_fence',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:nether_brick_fence',Properties:{east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:nether_brick_fence',Properties:{east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:nether_brick_fence',Properties:{east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:nether_brick_fence',Properties:{east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:nether_brick_fence',Properties:{east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:nether_brick_fence',Properties:{east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:nether_brick_fence',Properties:{east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:nether_brick_fence',Properties:{east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:nether_brick_fence',Properties:{east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:nether_brick_fence',Properties:{east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:nether_brick_fence',Properties:{east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:nether_brick_fence',Properties:{east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:nether_brick_fence',Properties:{east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:nether_brick_fence',Properties:{east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:nether_brick_fence',Properties:{east:'true',north:'true',south:'true',west:'true'}}"); ++ register(1824, "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}"); ++ register(1825, "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}"); ++ register(1826, "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}"); ++ register(1827, "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}"); ++ register(1828, "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'east',half:'top',shape:'inner_left'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'east',half:'top',shape:'inner_right'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'east',half:'top',shape:'outer_left'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'east',half:'top',shape:'outer_right'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}"); ++ register(1829, "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'west',half:'top',shape:'inner_left'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'west',half:'top',shape:'inner_right'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'west',half:'top',shape:'outer_left'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'west',half:'top',shape:'outer_right'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}"); ++ register(1830, "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'south',half:'top',shape:'inner_left'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'south',half:'top',shape:'inner_right'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'south',half:'top',shape:'outer_left'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'south',half:'top',shape:'outer_right'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}"); ++ register(1831, "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'north',half:'top',shape:'inner_left'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'north',half:'top',shape:'inner_right'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'north',half:'top',shape:'outer_left'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'north',half:'top',shape:'outer_right'}}", "{Name:'minecraft:nether_brick_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}"); ++ register(1840, "{Name:'minecraft:nether_wart',Properties:{age:'0'}}", "{Name:'minecraft:nether_wart',Properties:{age:'0'}}"); ++ register(1841, "{Name:'minecraft:nether_wart',Properties:{age:'1'}}", "{Name:'minecraft:nether_wart',Properties:{age:'1'}}"); ++ register(1842, "{Name:'minecraft:nether_wart',Properties:{age:'2'}}", "{Name:'minecraft:nether_wart',Properties:{age:'2'}}"); ++ register(1843, "{Name:'minecraft:nether_wart',Properties:{age:'3'}}", "{Name:'minecraft:nether_wart',Properties:{age:'3'}}"); ++ register(1856, "{Name:'minecraft:enchanting_table'}", "{Name:'minecraft:enchanting_table'}"); ++ register(1872, "{Name:'minecraft:brewing_stand',Properties:{has_bottle_0:'false',has_bottle_1:'false',has_bottle_2:'false'}}", "{Name:'minecraft:brewing_stand',Properties:{has_bottle_0:'false',has_bottle_1:'false',has_bottle_2:'false'}}"); ++ register(1873, "{Name:'minecraft:brewing_stand',Properties:{has_bottle_0:'true',has_bottle_1:'false',has_bottle_2:'false'}}", "{Name:'minecraft:brewing_stand',Properties:{has_bottle_0:'true',has_bottle_1:'false',has_bottle_2:'false'}}"); ++ register(1874, "{Name:'minecraft:brewing_stand',Properties:{has_bottle_0:'false',has_bottle_1:'true',has_bottle_2:'false'}}", "{Name:'minecraft:brewing_stand',Properties:{has_bottle_0:'false',has_bottle_1:'true',has_bottle_2:'false'}}"); ++ register(1875, "{Name:'minecraft:brewing_stand',Properties:{has_bottle_0:'true',has_bottle_1:'true',has_bottle_2:'false'}}", "{Name:'minecraft:brewing_stand',Properties:{has_bottle_0:'true',has_bottle_1:'true',has_bottle_2:'false'}}"); ++ register(1876, "{Name:'minecraft:brewing_stand',Properties:{has_bottle_0:'false',has_bottle_1:'false',has_bottle_2:'true'}}", "{Name:'minecraft:brewing_stand',Properties:{has_bottle_0:'false',has_bottle_1:'false',has_bottle_2:'true'}}"); ++ register(1877, "{Name:'minecraft:brewing_stand',Properties:{has_bottle_0:'true',has_bottle_1:'false',has_bottle_2:'true'}}", "{Name:'minecraft:brewing_stand',Properties:{has_bottle_0:'true',has_bottle_1:'false',has_bottle_2:'true'}}"); ++ register(1878, "{Name:'minecraft:brewing_stand',Properties:{has_bottle_0:'false',has_bottle_1:'true',has_bottle_2:'true'}}", "{Name:'minecraft:brewing_stand',Properties:{has_bottle_0:'false',has_bottle_1:'true',has_bottle_2:'true'}}"); ++ register(1879, "{Name:'minecraft:brewing_stand',Properties:{has_bottle_0:'true',has_bottle_1:'true',has_bottle_2:'true'}}", "{Name:'minecraft:brewing_stand',Properties:{has_bottle_0:'true',has_bottle_1:'true',has_bottle_2:'true'}}"); ++ register(1888, "{Name:'minecraft:cauldron',Properties:{level:'0'}}", "{Name:'minecraft:cauldron',Properties:{level:'0'}}"); ++ register(1889, "{Name:'minecraft:cauldron',Properties:{level:'1'}}", "{Name:'minecraft:cauldron',Properties:{level:'1'}}"); ++ register(1890, "{Name:'minecraft:cauldron',Properties:{level:'2'}}", "{Name:'minecraft:cauldron',Properties:{level:'2'}}"); ++ register(1891, "{Name:'minecraft:cauldron',Properties:{level:'3'}}", "{Name:'minecraft:cauldron',Properties:{level:'3'}}"); ++ register(1904, "{Name:'minecraft:end_portal'}", "{Name:'minecraft:end_portal'}"); ++ register(1920, "{Name:'minecraft:end_portal_frame',Properties:{eye:'false',facing:'south'}}", "{Name:'minecraft:end_portal_frame',Properties:{eye:'false',facing:'south'}}"); ++ register(1921, "{Name:'minecraft:end_portal_frame',Properties:{eye:'false',facing:'west'}}", "{Name:'minecraft:end_portal_frame',Properties:{eye:'false',facing:'west'}}"); ++ register(1922, "{Name:'minecraft:end_portal_frame',Properties:{eye:'false',facing:'north'}}", "{Name:'minecraft:end_portal_frame',Properties:{eye:'false',facing:'north'}}"); ++ register(1923, "{Name:'minecraft:end_portal_frame',Properties:{eye:'false',facing:'east'}}", "{Name:'minecraft:end_portal_frame',Properties:{eye:'false',facing:'east'}}"); ++ register(1924, "{Name:'minecraft:end_portal_frame',Properties:{eye:'true',facing:'south'}}", "{Name:'minecraft:end_portal_frame',Properties:{eye:'true',facing:'south'}}"); ++ register(1925, "{Name:'minecraft:end_portal_frame',Properties:{eye:'true',facing:'west'}}", "{Name:'minecraft:end_portal_frame',Properties:{eye:'true',facing:'west'}}"); ++ register(1926, "{Name:'minecraft:end_portal_frame',Properties:{eye:'true',facing:'north'}}", "{Name:'minecraft:end_portal_frame',Properties:{eye:'true',facing:'north'}}"); ++ register(1927, "{Name:'minecraft:end_portal_frame',Properties:{eye:'true',facing:'east'}}", "{Name:'minecraft:end_portal_frame',Properties:{eye:'true',facing:'east'}}"); ++ register(1936, "{Name:'minecraft:end_stone'}", "{Name:'minecraft:end_stone'}"); ++ register(1952, "{Name:'minecraft:dragon_egg'}", "{Name:'minecraft:dragon_egg'}"); ++ register(1968, "{Name:'minecraft:redstone_lamp',Properties:{lit:'false'}}", "{Name:'minecraft:redstone_lamp'}"); ++ register(1984, "{Name:'minecraft:redstone_lamp',Properties:{lit:'true'}}", "{Name:'minecraft:lit_redstone_lamp'}"); ++ register(2000, "{Name:'minecraft:oak_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_wooden_slab',Properties:{variant:'oak'}}"); ++ register(2001, "{Name:'minecraft:spruce_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_wooden_slab',Properties:{variant:'spruce'}}"); ++ register(2002, "{Name:'minecraft:birch_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_wooden_slab',Properties:{variant:'birch'}}"); ++ register(2003, "{Name:'minecraft:jungle_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_wooden_slab',Properties:{variant:'jungle'}}"); ++ register(2004, "{Name:'minecraft:acacia_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_wooden_slab',Properties:{variant:'acacia'}}"); ++ register(2005, "{Name:'minecraft:dark_oak_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_wooden_slab',Properties:{variant:'dark_oak'}}"); ++ register(2016, "{Name:'minecraft:oak_slab',Properties:{type:'bottom'}}", "{Name:'minecraft:wooden_slab',Properties:{half:'bottom',variant:'oak'}}"); ++ register(2017, "{Name:'minecraft:spruce_slab',Properties:{type:'bottom'}}", "{Name:'minecraft:wooden_slab',Properties:{half:'bottom',variant:'spruce'}}"); ++ register(2018, "{Name:'minecraft:birch_slab',Properties:{type:'bottom'}}", "{Name:'minecraft:wooden_slab',Properties:{half:'bottom',variant:'birch'}}"); ++ register(2019, "{Name:'minecraft:jungle_slab',Properties:{type:'bottom'}}", "{Name:'minecraft:wooden_slab',Properties:{half:'bottom',variant:'jungle'}}"); ++ register(2020, "{Name:'minecraft:acacia_slab',Properties:{type:'bottom'}}", "{Name:'minecraft:wooden_slab',Properties:{half:'bottom',variant:'acacia'}}"); ++ register(2021, "{Name:'minecraft:dark_oak_slab',Properties:{type:'bottom'}}", "{Name:'minecraft:wooden_slab',Properties:{half:'bottom',variant:'dark_oak'}}"); ++ register(2024, "{Name:'minecraft:oak_slab',Properties:{type:'top'}}", "{Name:'minecraft:wooden_slab',Properties:{half:'top',variant:'oak'}}"); ++ register(2025, "{Name:'minecraft:spruce_slab',Properties:{type:'top'}}", "{Name:'minecraft:wooden_slab',Properties:{half:'top',variant:'spruce'}}"); ++ register(2026, "{Name:'minecraft:birch_slab',Properties:{type:'top'}}", "{Name:'minecraft:wooden_slab',Properties:{half:'top',variant:'birch'}}"); ++ register(2027, "{Name:'minecraft:jungle_slab',Properties:{type:'top'}}", "{Name:'minecraft:wooden_slab',Properties:{half:'top',variant:'jungle'}}"); ++ register(2028, "{Name:'minecraft:acacia_slab',Properties:{type:'top'}}", "{Name:'minecraft:wooden_slab',Properties:{half:'top',variant:'acacia'}}"); ++ register(2029, "{Name:'minecraft:dark_oak_slab',Properties:{type:'top'}}", "{Name:'minecraft:wooden_slab',Properties:{half:'top',variant:'dark_oak'}}"); ++ register(2032, "{Name:'minecraft:cocoa',Properties:{age:'0',facing:'south'}}", "{Name:'minecraft:cocoa',Properties:{age:'0',facing:'south'}}"); ++ register(2033, "{Name:'minecraft:cocoa',Properties:{age:'0',facing:'west'}}", "{Name:'minecraft:cocoa',Properties:{age:'0',facing:'west'}}"); ++ register(2034, "{Name:'minecraft:cocoa',Properties:{age:'0',facing:'north'}}", "{Name:'minecraft:cocoa',Properties:{age:'0',facing:'north'}}"); ++ register(2035, "{Name:'minecraft:cocoa',Properties:{age:'0',facing:'east'}}", "{Name:'minecraft:cocoa',Properties:{age:'0',facing:'east'}}"); ++ register(2036, "{Name:'minecraft:cocoa',Properties:{age:'1',facing:'south'}}", "{Name:'minecraft:cocoa',Properties:{age:'1',facing:'south'}}"); ++ register(2037, "{Name:'minecraft:cocoa',Properties:{age:'1',facing:'west'}}", "{Name:'minecraft:cocoa',Properties:{age:'1',facing:'west'}}"); ++ register(2038, "{Name:'minecraft:cocoa',Properties:{age:'1',facing:'north'}}", "{Name:'minecraft:cocoa',Properties:{age:'1',facing:'north'}}"); ++ register(2039, "{Name:'minecraft:cocoa',Properties:{age:'1',facing:'east'}}", "{Name:'minecraft:cocoa',Properties:{age:'1',facing:'east'}}"); ++ register(2040, "{Name:'minecraft:cocoa',Properties:{age:'2',facing:'south'}}", "{Name:'minecraft:cocoa',Properties:{age:'2',facing:'south'}}"); ++ register(2041, "{Name:'minecraft:cocoa',Properties:{age:'2',facing:'west'}}", "{Name:'minecraft:cocoa',Properties:{age:'2',facing:'west'}}"); ++ register(2042, "{Name:'minecraft:cocoa',Properties:{age:'2',facing:'north'}}", "{Name:'minecraft:cocoa',Properties:{age:'2',facing:'north'}}"); ++ register(2043, "{Name:'minecraft:cocoa',Properties:{age:'2',facing:'east'}}", "{Name:'minecraft:cocoa',Properties:{age:'2',facing:'east'}}"); ++ register(2048, "{Name:'minecraft:sandstone_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}"); ++ register(2049, "{Name:'minecraft:sandstone_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}"); ++ register(2050, "{Name:'minecraft:sandstone_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}"); ++ register(2051, "{Name:'minecraft:sandstone_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}"); ++ register(2052, "{Name:'minecraft:sandstone_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'east',half:'top',shape:'inner_left'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'east',half:'top',shape:'inner_right'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'east',half:'top',shape:'outer_left'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'east',half:'top',shape:'outer_right'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}"); ++ register(2053, "{Name:'minecraft:sandstone_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'west',half:'top',shape:'inner_left'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'west',half:'top',shape:'inner_right'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'west',half:'top',shape:'outer_left'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'west',half:'top',shape:'outer_right'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}"); ++ register(2054, "{Name:'minecraft:sandstone_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'south',half:'top',shape:'inner_left'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'south',half:'top',shape:'inner_right'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'south',half:'top',shape:'outer_left'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'south',half:'top',shape:'outer_right'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}"); ++ register(2055, "{Name:'minecraft:sandstone_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'north',half:'top',shape:'inner_left'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'north',half:'top',shape:'inner_right'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'north',half:'top',shape:'outer_left'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'north',half:'top',shape:'outer_right'}}", "{Name:'minecraft:sandstone_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}"); ++ register(2064, "{Name:'minecraft:emerald_ore'}", "{Name:'minecraft:emerald_ore'}"); ++ register(2082, "{Name:'minecraft:ender_chest',Properties:{facing:'north'}}", "{Name:'minecraft:ender_chest',Properties:{facing:'north'}}"); ++ register(2083, "{Name:'minecraft:ender_chest',Properties:{facing:'south'}}", "{Name:'minecraft:ender_chest',Properties:{facing:'south'}}"); ++ register(2084, "{Name:'minecraft:ender_chest',Properties:{facing:'west'}}", "{Name:'minecraft:ender_chest',Properties:{facing:'west'}}"); ++ register(2085, "{Name:'minecraft:ender_chest',Properties:{facing:'east'}}", "{Name:'minecraft:ender_chest',Properties:{facing:'east'}}"); ++ register(2096, "{Name:'minecraft:tripwire_hook',Properties:{attached:'false',facing:'south',powered:'false'}}", "{Name:'minecraft:tripwire_hook',Properties:{attached:'false',facing:'south',powered:'false'}}"); ++ register(2097, "{Name:'minecraft:tripwire_hook',Properties:{attached:'false',facing:'west',powered:'false'}}", "{Name:'minecraft:tripwire_hook',Properties:{attached:'false',facing:'west',powered:'false'}}"); ++ register(2098, "{Name:'minecraft:tripwire_hook',Properties:{attached:'false',facing:'north',powered:'false'}}", "{Name:'minecraft:tripwire_hook',Properties:{attached:'false',facing:'north',powered:'false'}}"); ++ register(2099, "{Name:'minecraft:tripwire_hook',Properties:{attached:'false',facing:'east',powered:'false'}}", "{Name:'minecraft:tripwire_hook',Properties:{attached:'false',facing:'east',powered:'false'}}"); ++ register(2100, "{Name:'minecraft:tripwire_hook',Properties:{attached:'true',facing:'south',powered:'false'}}", "{Name:'minecraft:tripwire_hook',Properties:{attached:'true',facing:'south',powered:'false'}}"); ++ register(2101, "{Name:'minecraft:tripwire_hook',Properties:{attached:'true',facing:'west',powered:'false'}}", "{Name:'minecraft:tripwire_hook',Properties:{attached:'true',facing:'west',powered:'false'}}"); ++ register(2102, "{Name:'minecraft:tripwire_hook',Properties:{attached:'true',facing:'north',powered:'false'}}", "{Name:'minecraft:tripwire_hook',Properties:{attached:'true',facing:'north',powered:'false'}}"); ++ register(2103, "{Name:'minecraft:tripwire_hook',Properties:{attached:'true',facing:'east',powered:'false'}}", "{Name:'minecraft:tripwire_hook',Properties:{attached:'true',facing:'east',powered:'false'}}"); ++ register(2104, "{Name:'minecraft:tripwire_hook',Properties:{attached:'false',facing:'south',powered:'true'}}", "{Name:'minecraft:tripwire_hook',Properties:{attached:'false',facing:'south',powered:'true'}}"); ++ register(2105, "{Name:'minecraft:tripwire_hook',Properties:{attached:'false',facing:'west',powered:'true'}}", "{Name:'minecraft:tripwire_hook',Properties:{attached:'false',facing:'west',powered:'true'}}"); ++ register(2106, "{Name:'minecraft:tripwire_hook',Properties:{attached:'false',facing:'north',powered:'true'}}", "{Name:'minecraft:tripwire_hook',Properties:{attached:'false',facing:'north',powered:'true'}}"); ++ register(2107, "{Name:'minecraft:tripwire_hook',Properties:{attached:'false',facing:'east',powered:'true'}}", "{Name:'minecraft:tripwire_hook',Properties:{attached:'false',facing:'east',powered:'true'}}"); ++ register(2108, "{Name:'minecraft:tripwire_hook',Properties:{attached:'true',facing:'south',powered:'true'}}", "{Name:'minecraft:tripwire_hook',Properties:{attached:'true',facing:'south',powered:'true'}}"); ++ register(2109, "{Name:'minecraft:tripwire_hook',Properties:{attached:'true',facing:'west',powered:'true'}}", "{Name:'minecraft:tripwire_hook',Properties:{attached:'true',facing:'west',powered:'true'}}"); ++ register(2110, "{Name:'minecraft:tripwire_hook',Properties:{attached:'true',facing:'north',powered:'true'}}", "{Name:'minecraft:tripwire_hook',Properties:{attached:'true',facing:'north',powered:'true'}}"); ++ register(2111, "{Name:'minecraft:tripwire_hook',Properties:{attached:'true',facing:'east',powered:'true'}}", "{Name:'minecraft:tripwire_hook',Properties:{attached:'true',facing:'east',powered:'true'}}"); ++ register(2112, "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'false',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'false',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'false',powered:'false',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'false',powered:'false',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'false',powered:'false',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'true',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'true',powered:'false',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'true',powered:'false',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'true',powered:'false',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'true',north:'false',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'true',north:'false',powered:'false',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'true',north:'false',powered:'false',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'true',north:'false',powered:'false',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'true',north:'true',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'true',north:'true',powered:'false',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'true',north:'true',powered:'false',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'true',north:'true',powered:'false',south:'true',west:'true'}}"); ++ register(2113, "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'false',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'false',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'false',powered:'true',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'false',powered:'true',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'false',powered:'true',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'true',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'true',powered:'true',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'true',powered:'true',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'true',powered:'true',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'true',north:'false',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'true',north:'false',powered:'true',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'true',north:'false',powered:'true',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'true',north:'false',powered:'true',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'true',north:'true',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'true',north:'true',powered:'true',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'true',north:'true',powered:'true',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'true',north:'true',powered:'true',south:'true',west:'true'}}"); ++ register(2114, "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'false',powered:'false',south:'false',west:'false'}}"); ++ register(2115, "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'false',east:'false',north:'false',powered:'true',south:'false',west:'false'}}"); ++ register(2116, "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'false',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'false',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'false',powered:'false',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'false',powered:'false',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'false',powered:'false',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'true',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'true',powered:'false',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'true',powered:'false',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'true',powered:'false',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'true',north:'false',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'true',north:'false',powered:'false',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'true',north:'false',powered:'false',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'true',north:'false',powered:'false',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'true',north:'true',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'true',north:'true',powered:'false',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'true',north:'true',powered:'false',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'true',north:'true',powered:'false',south:'true',west:'true'}}"); ++ register(2117, "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'false',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'false',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'false',powered:'true',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'false',powered:'true',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'false',powered:'true',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'true',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'true',powered:'true',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'true',powered:'true',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'true',powered:'true',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'true',north:'false',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'true',north:'false',powered:'true',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'true',north:'false',powered:'true',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'true',north:'false',powered:'true',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'true',north:'true',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'true',north:'true',powered:'true',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'true',north:'true',powered:'true',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'true',north:'true',powered:'true',south:'true',west:'true'}}"); ++ register(2118, "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'false',powered:'false',south:'false',west:'false'}}"); ++ register(2119, "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'false',east:'false',north:'false',powered:'true',south:'false',west:'false'}}"); ++ register(2120, "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'false',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'false',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'false',powered:'false',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'false',powered:'false',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'false',powered:'false',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'true',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'true',powered:'false',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'true',powered:'false',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'true',powered:'false',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'true',north:'false',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'true',north:'false',powered:'false',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'true',north:'false',powered:'false',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'true',north:'false',powered:'false',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'true',north:'true',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'true',north:'true',powered:'false',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'true',north:'true',powered:'false',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'true',north:'true',powered:'false',south:'true',west:'true'}}"); ++ register(2121, "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'false',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'false',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'false',powered:'true',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'false',powered:'true',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'false',powered:'true',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'true',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'true',powered:'true',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'true',powered:'true',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'true',powered:'true',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'true',north:'false',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'true',north:'false',powered:'true',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'true',north:'false',powered:'true',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'true',north:'false',powered:'true',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'true',north:'true',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'true',north:'true',powered:'true',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'true',north:'true',powered:'true',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'true',north:'true',powered:'true',south:'true',west:'true'}}"); ++ register(2122, "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'false',powered:'false',south:'false',west:'false'}}"); ++ register(2123, "{Name:'minecraft:tripwire',Properties:{attached:'false',disarmed:'true',east:'false',north:'false',powered:'true',south:'false',west:'false'}}"); ++ register(2124, "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'false',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'false',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'false',powered:'false',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'false',powered:'false',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'false',powered:'false',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'true',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'true',powered:'false',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'true',powered:'false',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'true',powered:'false',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'true',north:'false',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'true',north:'false',powered:'false',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'true',north:'false',powered:'false',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'true',north:'false',powered:'false',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'true',north:'true',powered:'false',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'true',north:'true',powered:'false',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'true',north:'true',powered:'false',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'true',north:'true',powered:'false',south:'true',west:'true'}}"); ++ register(2125, "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'false',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'false',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'false',powered:'true',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'false',powered:'true',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'false',powered:'true',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'true',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'true',powered:'true',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'true',powered:'true',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'true',powered:'true',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'true',north:'false',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'true',north:'false',powered:'true',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'true',north:'false',powered:'true',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'true',north:'false',powered:'true',south:'true',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'true',north:'true',powered:'true',south:'false',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'true',north:'true',powered:'true',south:'false',west:'true'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'true',north:'true',powered:'true',south:'true',west:'false'}}", "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'true',north:'true',powered:'true',south:'true',west:'true'}}"); ++ register(2126, "{Name:'minecraft:tripwire',Properties:{attached:'true',disarmed:'true',east:'false',north:'false',powered:'false',south:'false',west:'false'}}"); ++ register(2128, "{Name:'minecraft:emerald_block'}", "{Name:'minecraft:emerald_block'}"); ++ register(2144, "{Name:'minecraft:spruce_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}"); ++ register(2145, "{Name:'minecraft:spruce_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}"); ++ register(2146, "{Name:'minecraft:spruce_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}"); ++ register(2147, "{Name:'minecraft:spruce_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}"); ++ register(2148, "{Name:'minecraft:spruce_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'east',half:'top',shape:'inner_left'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'east',half:'top',shape:'inner_right'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'east',half:'top',shape:'outer_left'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'east',half:'top',shape:'outer_right'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}"); ++ register(2149, "{Name:'minecraft:spruce_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'west',half:'top',shape:'inner_left'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'west',half:'top',shape:'inner_right'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'west',half:'top',shape:'outer_left'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'west',half:'top',shape:'outer_right'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}"); ++ register(2150, "{Name:'minecraft:spruce_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'south',half:'top',shape:'inner_left'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'south',half:'top',shape:'inner_right'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'south',half:'top',shape:'outer_left'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'south',half:'top',shape:'outer_right'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}"); ++ register(2151, "{Name:'minecraft:spruce_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'north',half:'top',shape:'inner_left'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'north',half:'top',shape:'inner_right'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'north',half:'top',shape:'outer_left'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'north',half:'top',shape:'outer_right'}}", "{Name:'minecraft:spruce_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}"); ++ register(2160, "{Name:'minecraft:birch_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}"); ++ register(2161, "{Name:'minecraft:birch_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}"); ++ register(2162, "{Name:'minecraft:birch_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}"); ++ register(2163, "{Name:'minecraft:birch_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}"); ++ register(2164, "{Name:'minecraft:birch_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'east',half:'top',shape:'inner_left'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'east',half:'top',shape:'inner_right'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'east',half:'top',shape:'outer_left'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'east',half:'top',shape:'outer_right'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}"); ++ register(2165, "{Name:'minecraft:birch_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'west',half:'top',shape:'inner_left'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'west',half:'top',shape:'inner_right'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'west',half:'top',shape:'outer_left'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'west',half:'top',shape:'outer_right'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}"); ++ register(2166, "{Name:'minecraft:birch_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'south',half:'top',shape:'inner_left'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'south',half:'top',shape:'inner_right'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'south',half:'top',shape:'outer_left'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'south',half:'top',shape:'outer_right'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}"); ++ register(2167, "{Name:'minecraft:birch_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'north',half:'top',shape:'inner_left'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'north',half:'top',shape:'inner_right'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'north',half:'top',shape:'outer_left'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'north',half:'top',shape:'outer_right'}}", "{Name:'minecraft:birch_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}"); ++ register(2176, "{Name:'minecraft:jungle_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}"); ++ register(2177, "{Name:'minecraft:jungle_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}"); ++ register(2178, "{Name:'minecraft:jungle_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}"); ++ register(2179, "{Name:'minecraft:jungle_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}"); ++ register(2180, "{Name:'minecraft:jungle_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'east',half:'top',shape:'inner_left'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'east',half:'top',shape:'inner_right'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'east',half:'top',shape:'outer_left'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'east',half:'top',shape:'outer_right'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}"); ++ register(2181, "{Name:'minecraft:jungle_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'west',half:'top',shape:'inner_left'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'west',half:'top',shape:'inner_right'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'west',half:'top',shape:'outer_left'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'west',half:'top',shape:'outer_right'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}"); ++ register(2182, "{Name:'minecraft:jungle_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'south',half:'top',shape:'inner_left'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'south',half:'top',shape:'inner_right'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'south',half:'top',shape:'outer_left'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'south',half:'top',shape:'outer_right'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}"); ++ register(2183, "{Name:'minecraft:jungle_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'north',half:'top',shape:'inner_left'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'north',half:'top',shape:'inner_right'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'north',half:'top',shape:'outer_left'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'north',half:'top',shape:'outer_right'}}", "{Name:'minecraft:jungle_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}"); ++ register(2192, "{Name:'minecraft:command_block',Properties:{conditional:'false',facing:'down'}}", "{Name:'minecraft:command_block',Properties:{conditional:'false',facing:'down'}}"); ++ register(2193, "{Name:'minecraft:command_block',Properties:{conditional:'false',facing:'up'}}", "{Name:'minecraft:command_block',Properties:{conditional:'false',facing:'up'}}"); ++ register(2194, "{Name:'minecraft:command_block',Properties:{conditional:'false',facing:'north'}}", "{Name:'minecraft:command_block',Properties:{conditional:'false',facing:'north'}}"); ++ register(2195, "{Name:'minecraft:command_block',Properties:{conditional:'false',facing:'south'}}", "{Name:'minecraft:command_block',Properties:{conditional:'false',facing:'south'}}"); ++ register(2196, "{Name:'minecraft:command_block',Properties:{conditional:'false',facing:'west'}}", "{Name:'minecraft:command_block',Properties:{conditional:'false',facing:'west'}}"); ++ register(2197, "{Name:'minecraft:command_block',Properties:{conditional:'false',facing:'east'}}", "{Name:'minecraft:command_block',Properties:{conditional:'false',facing:'east'}}"); ++ register(2200, "{Name:'minecraft:command_block',Properties:{conditional:'true',facing:'down'}}", "{Name:'minecraft:command_block',Properties:{conditional:'true',facing:'down'}}"); ++ register(2201, "{Name:'minecraft:command_block',Properties:{conditional:'true',facing:'up'}}", "{Name:'minecraft:command_block',Properties:{conditional:'true',facing:'up'}}"); ++ register(2202, "{Name:'minecraft:command_block',Properties:{conditional:'true',facing:'north'}}", "{Name:'minecraft:command_block',Properties:{conditional:'true',facing:'north'}}"); ++ register(2203, "{Name:'minecraft:command_block',Properties:{conditional:'true',facing:'south'}}", "{Name:'minecraft:command_block',Properties:{conditional:'true',facing:'south'}}"); ++ register(2204, "{Name:'minecraft:command_block',Properties:{conditional:'true',facing:'west'}}", "{Name:'minecraft:command_block',Properties:{conditional:'true',facing:'west'}}"); ++ register(2205, "{Name:'minecraft:command_block',Properties:{conditional:'true',facing:'east'}}", "{Name:'minecraft:command_block',Properties:{conditional:'true',facing:'east'}}"); ++ register(2208, "{Name:'minecraft:beacon'}", "{Name:'minecraft:beacon'}"); ++ register(2224, "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'false',south:'false',up:'false',variant:'cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'false',south:'false',up:'false',variant:'cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'false',south:'false',up:'true',variant:'cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'false',south:'false',up:'true',variant:'cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'false',south:'true',up:'false',variant:'cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'false',south:'true',up:'false',variant:'cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'false',south:'true',up:'true',variant:'cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'false',south:'true',up:'true',variant:'cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'true',south:'false',up:'false',variant:'cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'true',south:'false',up:'false',variant:'cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'true',south:'false',up:'true',variant:'cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'true',south:'false',up:'true',variant:'cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'true',south:'true',up:'false',variant:'cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'true',south:'true',up:'false',variant:'cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'true',south:'true',up:'true',variant:'cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'true',south:'true',up:'true',variant:'cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'false',south:'false',up:'false',variant:'cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'false',south:'false',up:'false',variant:'cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'false',south:'false',up:'true',variant:'cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'false',south:'false',up:'true',variant:'cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'false',south:'true',up:'false',variant:'cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'false',south:'true',up:'false',variant:'cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'false',south:'true',up:'true',variant:'cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'false',south:'true',up:'true',variant:'cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'true',south:'false',up:'false',variant:'cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'true',south:'false',up:'false',variant:'cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'true',south:'false',up:'true',variant:'cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'true',south:'false',up:'true',variant:'cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'true',south:'true',up:'false',variant:'cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'true',south:'true',up:'false',variant:'cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'true',south:'true',up:'true',variant:'cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'true',south:'true',up:'true',variant:'cobblestone',west:'true'}}"); ++ register(2225, "{Name:'minecraft:mossy_cobblestone_wall',Properties:{east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'false',south:'false',up:'false',variant:'mossy_cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'false',south:'false',up:'false',variant:'mossy_cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'false',south:'false',up:'true',variant:'mossy_cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'false',south:'false',up:'true',variant:'mossy_cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'false',south:'true',up:'false',variant:'mossy_cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'false',south:'true',up:'false',variant:'mossy_cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'false',south:'true',up:'true',variant:'mossy_cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'false',south:'true',up:'true',variant:'mossy_cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'true',south:'false',up:'false',variant:'mossy_cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'true',south:'false',up:'false',variant:'mossy_cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'true',south:'false',up:'true',variant:'mossy_cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'true',south:'false',up:'true',variant:'mossy_cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'true',south:'true',up:'false',variant:'mossy_cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'true',south:'true',up:'false',variant:'mossy_cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'true',south:'true',up:'true',variant:'mossy_cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'false',north:'true',south:'true',up:'true',variant:'mossy_cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'false',south:'false',up:'false',variant:'mossy_cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'false',south:'false',up:'false',variant:'mossy_cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'false',south:'false',up:'true',variant:'mossy_cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'false',south:'false',up:'true',variant:'mossy_cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'false',south:'true',up:'false',variant:'mossy_cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'false',south:'true',up:'false',variant:'mossy_cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'false',south:'true',up:'true',variant:'mossy_cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'false',south:'true',up:'true',variant:'mossy_cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'true',south:'false',up:'false',variant:'mossy_cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'true',south:'false',up:'false',variant:'mossy_cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'true',south:'false',up:'true',variant:'mossy_cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'true',south:'false',up:'true',variant:'mossy_cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'true',south:'true',up:'false',variant:'mossy_cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'true',south:'true',up:'false',variant:'mossy_cobblestone',west:'true'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'true',south:'true',up:'true',variant:'mossy_cobblestone',west:'false'}}", "{Name:'minecraft:cobblestone_wall',Properties:{east:'true',north:'true',south:'true',up:'true',variant:'mossy_cobblestone',west:'true'}}"); ++ // There are a few changes made to flower pot here, notably handling how legacy data is handled. ++ // The TE itself should contain the target item and from there the proper state can be determined. However, there are ++ // blocks that do not contain a TE. So we need to make sure there is a default to fall on. ++ // I simply followed the legacy handling from BlockFlowerPot from 1.8.8 to find what legacy data mapped to what. ++ // It's better than defaulting everything to a cactus. ++ register(2240, "{Name:'minecraft:flower_pot'}", "{Name:'minecraft:flower_pot',Properties:{contents:'acacia_sapling',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'allium',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'birch_sapling',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'blue_orchid',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'cactus',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dandelion',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dark_oak_sapling',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dead_bush',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'empty',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'fern',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'houstonia',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'jungle_sapling',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_brown',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_red',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oak_sapling',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'orange_tulip',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oxeye_daisy',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'pink_tulip',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'red_tulip',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'rose',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'spruce_sapling',legacy_data:'0'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'white_tulip',legacy_data:'0'}}"); ++ register(2241, "{Name:'minecraft:potted_poppy'}", "{Name:'minecraft:flower_pot',Properties:{contents:'acacia_sapling',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'allium',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'birch_sapling',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'blue_orchid',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'cactus',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dandelion',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dark_oak_sapling',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dead_bush',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'empty',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'fern',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'houstonia',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'jungle_sapling',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_brown',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_red',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oak_sapling',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'orange_tulip',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oxeye_daisy',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'pink_tulip',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'red_tulip',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'rose',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'spruce_sapling',legacy_data:'1'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'white_tulip',legacy_data:'1'}}"); ++ register(2242, "{Name:'minecraft:potted_dandelion'}", "{Name:'minecraft:flower_pot',Properties:{contents:'acacia_sapling',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'allium',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'birch_sapling',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'blue_orchid',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'cactus',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dandelion',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dark_oak_sapling',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dead_bush',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'empty',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'fern',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'houstonia',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'jungle_sapling',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_brown',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_red',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oak_sapling',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'orange_tulip',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oxeye_daisy',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'pink_tulip',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'red_tulip',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'rose',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'spruce_sapling',legacy_data:'2'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'white_tulip',legacy_data:'2'}}"); ++ register(2243, "{Name:'minecraft:potted_oak_sapling'}", "{Name:'minecraft:flower_pot',Properties:{contents:'acacia_sapling',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'allium',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'birch_sapling',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'blue_orchid',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'cactus',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dandelion',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dark_oak_sapling',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dead_bush',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'empty',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'fern',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'houstonia',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'jungle_sapling',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_brown',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_red',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oak_sapling',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'orange_tulip',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oxeye_daisy',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'pink_tulip',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'red_tulip',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'rose',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'spruce_sapling',legacy_data:'3'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'white_tulip',legacy_data:'3'}}"); ++ register(2244, "{Name:'minecraft:potted_spruce_sapling'}", "{Name:'minecraft:flower_pot',Properties:{contents:'acacia_sapling',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'allium',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'birch_sapling',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'blue_orchid',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'cactus',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dandelion',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dark_oak_sapling',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dead_bush',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'empty',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'fern',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'houstonia',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'jungle_sapling',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_brown',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_red',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oak_sapling',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'orange_tulip',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oxeye_daisy',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'pink_tulip',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'red_tulip',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'rose',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'spruce_sapling',legacy_data:'4'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'white_tulip',legacy_data:'4'}}"); ++ register(2245, "{Name:'minecraft:potted_birch_sapling'}", "{Name:'minecraft:flower_pot',Properties:{contents:'acacia_sapling',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'allium',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'birch_sapling',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'blue_orchid',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'cactus',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dandelion',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dark_oak_sapling',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dead_bush',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'empty',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'fern',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'houstonia',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'jungle_sapling',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_brown',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_red',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oak_sapling',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'orange_tulip',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oxeye_daisy',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'pink_tulip',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'red_tulip',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'rose',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'spruce_sapling',legacy_data:'5'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'white_tulip',legacy_data:'5'}}"); ++ register(2246, "{Name:'minecraft:potted_jungle_sapling'}", "{Name:'minecraft:flower_pot',Properties:{contents:'acacia_sapling',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'allium',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'birch_sapling',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'blue_orchid',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'cactus',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dandelion',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dark_oak_sapling',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dead_bush',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'empty',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'fern',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'houstonia',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'jungle_sapling',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_brown',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_red',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oak_sapling',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'orange_tulip',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oxeye_daisy',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'pink_tulip',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'red_tulip',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'rose',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'spruce_sapling',legacy_data:'6'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'white_tulip',legacy_data:'6'}}"); ++ register(2247, "{Name:'minecraft:potted_red_mushroom'}", "{Name:'minecraft:flower_pot',Properties:{contents:'acacia_sapling',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'allium',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'birch_sapling',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'blue_orchid',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'cactus',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dandelion',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dark_oak_sapling',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dead_bush',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'empty',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'fern',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'houstonia',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'jungle_sapling',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_brown',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_red',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oak_sapling',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'orange_tulip',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oxeye_daisy',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'pink_tulip',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'red_tulip',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'rose',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'spruce_sapling',legacy_data:'7'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'white_tulip',legacy_data:'7'}}"); ++ register(2248, "{Name:'minecraft:potted_brown_mushroom'}", "{Name:'minecraft:flower_pot',Properties:{contents:'acacia_sapling',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'allium',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'birch_sapling',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'blue_orchid',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'cactus',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dandelion',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dark_oak_sapling',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dead_bush',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'empty',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'fern',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'houstonia',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'jungle_sapling',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_brown',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_red',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oak_sapling',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'orange_tulip',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oxeye_daisy',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'pink_tulip',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'red_tulip',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'rose',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'spruce_sapling',legacy_data:'8'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'white_tulip',legacy_data:'8'}}"); ++ register(2249, "{Name:'minecraft:potted_cactus'}", "{Name:'minecraft:flower_pot',Properties:{contents:'acacia_sapling',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'allium',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'birch_sapling',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'blue_orchid',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'cactus',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dandelion',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dark_oak_sapling',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dead_bush',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'empty',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'fern',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'houstonia',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'jungle_sapling',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_brown',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_red',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oak_sapling',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'orange_tulip',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oxeye_daisy',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'pink_tulip',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'red_tulip',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'rose',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'spruce_sapling',legacy_data:'9'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'white_tulip',legacy_data:'9'}}"); ++ register(2250, "{Name:'minecraft:potted_dead_bush'}", "{Name:'minecraft:flower_pot',Properties:{contents:'acacia_sapling',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'allium',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'birch_sapling',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'blue_orchid',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'cactus',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dandelion',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dark_oak_sapling',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dead_bush',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'empty',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'fern',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'houstonia',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'jungle_sapling',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_brown',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_red',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oak_sapling',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'orange_tulip',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oxeye_daisy',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'pink_tulip',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'red_tulip',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'rose',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'spruce_sapling',legacy_data:'10'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'white_tulip',legacy_data:'10'}}"); ++ register(2251, "{Name:'minecraft:potted_fern'}", "{Name:'minecraft:flower_pot',Properties:{contents:'acacia_sapling',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'allium',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'birch_sapling',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'blue_orchid',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'cactus',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dandelion',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dark_oak_sapling',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dead_bush',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'empty',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'fern',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'houstonia',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'jungle_sapling',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_brown',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_red',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oak_sapling',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'orange_tulip',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oxeye_daisy',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'pink_tulip',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'red_tulip',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'rose',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'spruce_sapling',legacy_data:'11'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'white_tulip',legacy_data:'11'}}"); ++ register(2252, "{Name:'minecraft:potted_acacia_sapling'}", "{Name:'minecraft:flower_pot',Properties:{contents:'acacia_sapling',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'allium',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'birch_sapling',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'blue_orchid',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'cactus',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dandelion',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dark_oak_sapling',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dead_bush',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'empty',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'fern',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'houstonia',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'jungle_sapling',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_brown',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_red',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oak_sapling',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'orange_tulip',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oxeye_daisy',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'pink_tulip',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'red_tulip',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'rose',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'spruce_sapling',legacy_data:'12'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'white_tulip',legacy_data:'12'}}"); ++ register(2253, "{Name:'minecraft:potted_dark_oak_sapling'}", "{Name:'minecraft:flower_pot',Properties:{contents:'acacia_sapling',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'allium',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'birch_sapling',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'blue_orchid',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'cactus',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dandelion',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dark_oak_sapling',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dead_bush',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'empty',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'fern',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'houstonia',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'jungle_sapling',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_brown',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_red',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oak_sapling',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'orange_tulip',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oxeye_daisy',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'pink_tulip',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'red_tulip',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'rose',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'spruce_sapling',legacy_data:'13'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'white_tulip',legacy_data:'13'}}"); ++ register(2254, "{Name:'minecraft:flower_pot'}", "{Name:'minecraft:flower_pot',Properties:{contents:'acacia_sapling',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'allium',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'birch_sapling',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'blue_orchid',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'cactus',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dandelion',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dark_oak_sapling',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dead_bush',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'empty',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'fern',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'houstonia',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'jungle_sapling',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_brown',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_red',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oak_sapling',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'orange_tulip',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oxeye_daisy',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'pink_tulip',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'red_tulip',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'rose',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'spruce_sapling',legacy_data:'14'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'white_tulip',legacy_data:'14'}}"); ++ register(2255, "{Name:'minecraft:flower_pot'}", "{Name:'minecraft:flower_pot',Properties:{contents:'acacia_sapling',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'allium',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'birch_sapling',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'blue_orchid',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'cactus',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dandelion',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dark_oak_sapling',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'dead_bush',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'empty',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'fern',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'houstonia',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'jungle_sapling',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_brown',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'mushroom_red',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oak_sapling',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'orange_tulip',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'oxeye_daisy',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'pink_tulip',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'red_tulip',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'rose',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'spruce_sapling',legacy_data:'15'}}", "{Name:'minecraft:flower_pot',Properties:{contents:'white_tulip',legacy_data:'15'}}"); ++ register(2256, "{Name:'minecraft:carrots',Properties:{age:'0'}}", "{Name:'minecraft:carrots',Properties:{age:'0'}}"); ++ register(2257, "{Name:'minecraft:carrots',Properties:{age:'1'}}", "{Name:'minecraft:carrots',Properties:{age:'1'}}"); ++ register(2258, "{Name:'minecraft:carrots',Properties:{age:'2'}}", "{Name:'minecraft:carrots',Properties:{age:'2'}}"); ++ register(2259, "{Name:'minecraft:carrots',Properties:{age:'3'}}", "{Name:'minecraft:carrots',Properties:{age:'3'}}"); ++ register(2260, "{Name:'minecraft:carrots',Properties:{age:'4'}}", "{Name:'minecraft:carrots',Properties:{age:'4'}}"); ++ register(2261, "{Name:'minecraft:carrots',Properties:{age:'5'}}", "{Name:'minecraft:carrots',Properties:{age:'5'}}"); ++ register(2262, "{Name:'minecraft:carrots',Properties:{age:'6'}}", "{Name:'minecraft:carrots',Properties:{age:'6'}}"); ++ register(2263, "{Name:'minecraft:carrots',Properties:{age:'7'}}", "{Name:'minecraft:carrots',Properties:{age:'7'}}"); ++ register(2272, "{Name:'minecraft:potatoes',Properties:{age:'0'}}", "{Name:'minecraft:potatoes',Properties:{age:'0'}}"); ++ register(2273, "{Name:'minecraft:potatoes',Properties:{age:'1'}}", "{Name:'minecraft:potatoes',Properties:{age:'1'}}"); ++ register(2274, "{Name:'minecraft:potatoes',Properties:{age:'2'}}", "{Name:'minecraft:potatoes',Properties:{age:'2'}}"); ++ register(2275, "{Name:'minecraft:potatoes',Properties:{age:'3'}}", "{Name:'minecraft:potatoes',Properties:{age:'3'}}"); ++ register(2276, "{Name:'minecraft:potatoes',Properties:{age:'4'}}", "{Name:'minecraft:potatoes',Properties:{age:'4'}}"); ++ register(2277, "{Name:'minecraft:potatoes',Properties:{age:'5'}}", "{Name:'minecraft:potatoes',Properties:{age:'5'}}"); ++ register(2278, "{Name:'minecraft:potatoes',Properties:{age:'6'}}", "{Name:'minecraft:potatoes',Properties:{age:'6'}}"); ++ register(2279, "{Name:'minecraft:potatoes',Properties:{age:'7'}}", "{Name:'minecraft:potatoes',Properties:{age:'7'}}"); ++ register(2288, "{Name:'minecraft:oak_button',Properties:{face:'ceiling',facing:'north',powered:'false'}}", "{Name:'minecraft:wooden_button',Properties:{facing:'down',powered:'false'}}"); ++ register(2289, "{Name:'minecraft:oak_button',Properties:{face:'wall',facing:'east',powered:'false'}}", "{Name:'minecraft:wooden_button',Properties:{facing:'east',powered:'false'}}"); ++ register(2290, "{Name:'minecraft:oak_button',Properties:{face:'wall',facing:'west',powered:'false'}}", "{Name:'minecraft:wooden_button',Properties:{facing:'west',powered:'false'}}"); ++ register(2291, "{Name:'minecraft:oak_button',Properties:{face:'wall',facing:'south',powered:'false'}}", "{Name:'minecraft:wooden_button',Properties:{facing:'south',powered:'false'}}"); ++ register(2292, "{Name:'minecraft:oak_button',Properties:{face:'wall',facing:'north',powered:'false'}}", "{Name:'minecraft:wooden_button',Properties:{facing:'north',powered:'false'}}"); ++ register(2293, "{Name:'minecraft:oak_button',Properties:{face:'floor',facing:'north',powered:'false'}}", "{Name:'minecraft:wooden_button',Properties:{facing:'up',powered:'false'}}"); ++ register(2296, "{Name:'minecraft:oak_button',Properties:{face:'ceiling',facing:'north',powered:'true'}}", "{Name:'minecraft:wooden_button',Properties:{facing:'down',powered:'true'}}"); ++ register(2297, "{Name:'minecraft:oak_button',Properties:{face:'wall',facing:'east',powered:'true'}}", "{Name:'minecraft:wooden_button',Properties:{facing:'east',powered:'true'}}"); ++ register(2298, "{Name:'minecraft:oak_button',Properties:{face:'wall',facing:'west',powered:'true'}}", "{Name:'minecraft:wooden_button',Properties:{facing:'west',powered:'true'}}"); ++ register(2299, "{Name:'minecraft:oak_button',Properties:{face:'wall',facing:'south',powered:'true'}}", "{Name:'minecraft:wooden_button',Properties:{facing:'south',powered:'true'}}"); ++ register(2300, "{Name:'minecraft:oak_button',Properties:{face:'wall',facing:'north',powered:'true'}}", "{Name:'minecraft:wooden_button',Properties:{facing:'north',powered:'true'}}"); ++ register(2301, "{Name:'minecraft:oak_button',Properties:{face:'floor',facing:'north',powered:'true'}}", "{Name:'minecraft:wooden_button',Properties:{facing:'up',powered:'true'}}"); ++ register(2304, "{Name:'%%FILTER_ME%%',Properties:{facing:'down',nodrop:'false'}}", "{Name:'minecraft:skull',Properties:{facing:'down',nodrop:'false'}}"); ++ register(2305, "{Name:'%%FILTER_ME%%',Properties:{facing:'up',nodrop:'false'}}", "{Name:'minecraft:skull',Properties:{facing:'up',nodrop:'false'}}"); ++ register(2306, "{Name:'%%FILTER_ME%%',Properties:{facing:'north',nodrop:'false'}}", "{Name:'minecraft:skull',Properties:{facing:'north',nodrop:'false'}}"); ++ register(2307, "{Name:'%%FILTER_ME%%',Properties:{facing:'south',nodrop:'false'}}", "{Name:'minecraft:skull',Properties:{facing:'south',nodrop:'false'}}"); ++ register(2308, "{Name:'%%FILTER_ME%%',Properties:{facing:'west',nodrop:'false'}}", "{Name:'minecraft:skull',Properties:{facing:'west',nodrop:'false'}}"); ++ register(2309, "{Name:'%%FILTER_ME%%',Properties:{facing:'east',nodrop:'false'}}", "{Name:'minecraft:skull',Properties:{facing:'east',nodrop:'false'}}"); ++ register(2312, "{Name:'%%FILTER_ME%%',Properties:{facing:'down',nodrop:'true'}}", "{Name:'minecraft:skull',Properties:{facing:'down',nodrop:'true'}}"); ++ register(2313, "{Name:'%%FILTER_ME%%',Properties:{facing:'up',nodrop:'true'}}", "{Name:'minecraft:skull',Properties:{facing:'up',nodrop:'true'}}"); ++ register(2314, "{Name:'%%FILTER_ME%%',Properties:{facing:'north',nodrop:'true'}}", "{Name:'minecraft:skull',Properties:{facing:'north',nodrop:'true'}}"); ++ register(2315, "{Name:'%%FILTER_ME%%',Properties:{facing:'south',nodrop:'true'}}", "{Name:'minecraft:skull',Properties:{facing:'south',nodrop:'true'}}"); ++ register(2316, "{Name:'%%FILTER_ME%%',Properties:{facing:'west',nodrop:'true'}}", "{Name:'minecraft:skull',Properties:{facing:'west',nodrop:'true'}}"); ++ register(2317, "{Name:'%%FILTER_ME%%',Properties:{facing:'east',nodrop:'true'}}", "{Name:'minecraft:skull',Properties:{facing:'east',nodrop:'true'}}"); ++ register(2320, "{Name:'minecraft:anvil',Properties:{facing:'south'}}", "{Name:'minecraft:anvil',Properties:{damage:'0',facing:'south'}}"); ++ register(2321, "{Name:'minecraft:anvil',Properties:{facing:'west'}}", "{Name:'minecraft:anvil',Properties:{damage:'0',facing:'west'}}"); ++ register(2322, "{Name:'minecraft:anvil',Properties:{facing:'north'}}", "{Name:'minecraft:anvil',Properties:{damage:'0',facing:'north'}}"); ++ register(2323, "{Name:'minecraft:anvil',Properties:{facing:'east'}}", "{Name:'minecraft:anvil',Properties:{damage:'0',facing:'east'}}"); ++ register(2324, "{Name:'minecraft:chipped_anvil',Properties:{facing:'south'}}", "{Name:'minecraft:anvil',Properties:{damage:'1',facing:'south'}}"); ++ register(2325, "{Name:'minecraft:chipped_anvil',Properties:{facing:'west'}}", "{Name:'minecraft:anvil',Properties:{damage:'1',facing:'west'}}"); ++ register(2326, "{Name:'minecraft:chipped_anvil',Properties:{facing:'north'}}", "{Name:'minecraft:anvil',Properties:{damage:'1',facing:'north'}}"); ++ register(2327, "{Name:'minecraft:chipped_anvil',Properties:{facing:'east'}}", "{Name:'minecraft:anvil',Properties:{damage:'1',facing:'east'}}"); ++ register(2328, "{Name:'minecraft:damaged_anvil',Properties:{facing:'south'}}", "{Name:'minecraft:anvil',Properties:{damage:'2',facing:'south'}}"); ++ register(2329, "{Name:'minecraft:damaged_anvil',Properties:{facing:'west'}}", "{Name:'minecraft:anvil',Properties:{damage:'2',facing:'west'}}"); ++ register(2330, "{Name:'minecraft:damaged_anvil',Properties:{facing:'north'}}", "{Name:'minecraft:anvil',Properties:{damage:'2',facing:'north'}}"); ++ register(2331, "{Name:'minecraft:damaged_anvil',Properties:{facing:'east'}}", "{Name:'minecraft:anvil',Properties:{damage:'2',facing:'east'}}"); ++ register(2338, "{Name:'minecraft:trapped_chest',Properties:{facing:'north',type:'single'}}", "{Name:'minecraft:trapped_chest',Properties:{facing:'north'}}"); ++ register(2339, "{Name:'minecraft:trapped_chest',Properties:{facing:'south',type:'single'}}", "{Name:'minecraft:trapped_chest',Properties:{facing:'south'}}"); ++ register(2340, "{Name:'minecraft:trapped_chest',Properties:{facing:'west',type:'single'}}", "{Name:'minecraft:trapped_chest',Properties:{facing:'west'}}"); ++ register(2341, "{Name:'minecraft:trapped_chest',Properties:{facing:'east',type:'single'}}", "{Name:'minecraft:trapped_chest',Properties:{facing:'east'}}"); ++ register(2352, "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'0'}}", "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'0'}}"); ++ register(2353, "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'1'}}", "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'1'}}"); ++ register(2354, "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'2'}}", "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'2'}}"); ++ register(2355, "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'3'}}", "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'3'}}"); ++ register(2356, "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'4'}}", "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'4'}}"); ++ register(2357, "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'5'}}", "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'5'}}"); ++ register(2358, "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'6'}}", "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'6'}}"); ++ register(2359, "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'7'}}", "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'7'}}"); ++ register(2360, "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'8'}}", "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'8'}}"); ++ register(2361, "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'9'}}", "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'9'}}"); ++ register(2362, "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'10'}}", "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'10'}}"); ++ register(2363, "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'11'}}", "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'11'}}"); ++ register(2364, "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'12'}}", "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'12'}}"); ++ register(2365, "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'13'}}", "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'13'}}"); ++ register(2366, "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'14'}}", "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'14'}}"); ++ register(2367, "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'15'}}", "{Name:'minecraft:light_weighted_pressure_plate',Properties:{power:'15'}}"); ++ register(2368, "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'0'}}", "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'0'}}"); ++ register(2369, "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'1'}}", "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'1'}}"); ++ register(2370, "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'2'}}", "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'2'}}"); ++ register(2371, "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'3'}}", "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'3'}}"); ++ register(2372, "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'4'}}", "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'4'}}"); ++ register(2373, "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'5'}}", "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'5'}}"); ++ register(2374, "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'6'}}", "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'6'}}"); ++ register(2375, "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'7'}}", "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'7'}}"); ++ register(2376, "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'8'}}", "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'8'}}"); ++ register(2377, "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'9'}}", "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'9'}}"); ++ register(2378, "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'10'}}", "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'10'}}"); ++ register(2379, "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'11'}}", "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'11'}}"); ++ register(2380, "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'12'}}", "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'12'}}"); ++ register(2381, "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'13'}}", "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'13'}}"); ++ register(2382, "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'14'}}", "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'14'}}"); ++ register(2383, "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'15'}}", "{Name:'minecraft:heavy_weighted_pressure_plate',Properties:{power:'15'}}"); ++ register(2384, "{Name:'minecraft:comparator',Properties:{facing:'south',mode:'compare',powered:'false'}}", "{Name:'minecraft:unpowered_comparator',Properties:{facing:'south',mode:'compare',powered:'false'}}"); ++ register(2385, "{Name:'minecraft:comparator',Properties:{facing:'west',mode:'compare',powered:'false'}}", "{Name:'minecraft:unpowered_comparator',Properties:{facing:'west',mode:'compare',powered:'false'}}"); ++ register(2386, "{Name:'minecraft:comparator',Properties:{facing:'north',mode:'compare',powered:'false'}}", "{Name:'minecraft:unpowered_comparator',Properties:{facing:'north',mode:'compare',powered:'false'}}"); ++ register(2387, "{Name:'minecraft:comparator',Properties:{facing:'east',mode:'compare',powered:'false'}}", "{Name:'minecraft:unpowered_comparator',Properties:{facing:'east',mode:'compare',powered:'false'}}"); ++ register(2388, "{Name:'minecraft:comparator',Properties:{facing:'south',mode:'subtract',powered:'false'}}", "{Name:'minecraft:unpowered_comparator',Properties:{facing:'south',mode:'subtract',powered:'false'}}"); ++ register(2389, "{Name:'minecraft:comparator',Properties:{facing:'west',mode:'subtract',powered:'false'}}", "{Name:'minecraft:unpowered_comparator',Properties:{facing:'west',mode:'subtract',powered:'false'}}"); ++ register(2390, "{Name:'minecraft:comparator',Properties:{facing:'north',mode:'subtract',powered:'false'}}", "{Name:'minecraft:unpowered_comparator',Properties:{facing:'north',mode:'subtract',powered:'false'}}"); ++ register(2391, "{Name:'minecraft:comparator',Properties:{facing:'east',mode:'subtract',powered:'false'}}", "{Name:'minecraft:unpowered_comparator',Properties:{facing:'east',mode:'subtract',powered:'false'}}"); ++ register(2392, "{Name:'minecraft:comparator',Properties:{facing:'south',mode:'compare',powered:'true'}}", "{Name:'minecraft:unpowered_comparator',Properties:{facing:'south',mode:'compare',powered:'true'}}"); ++ register(2393, "{Name:'minecraft:comparator',Properties:{facing:'west',mode:'compare',powered:'true'}}", "{Name:'minecraft:unpowered_comparator',Properties:{facing:'west',mode:'compare',powered:'true'}}"); ++ register(2394, "{Name:'minecraft:comparator',Properties:{facing:'north',mode:'compare',powered:'true'}}", "{Name:'minecraft:unpowered_comparator',Properties:{facing:'north',mode:'compare',powered:'true'}}"); ++ register(2395, "{Name:'minecraft:comparator',Properties:{facing:'east',mode:'compare',powered:'true'}}", "{Name:'minecraft:unpowered_comparator',Properties:{facing:'east',mode:'compare',powered:'true'}}"); ++ register(2396, "{Name:'minecraft:comparator',Properties:{facing:'south',mode:'subtract',powered:'true'}}", "{Name:'minecraft:unpowered_comparator',Properties:{facing:'south',mode:'subtract',powered:'true'}}"); ++ register(2397, "{Name:'minecraft:comparator',Properties:{facing:'west',mode:'subtract',powered:'true'}}", "{Name:'minecraft:unpowered_comparator',Properties:{facing:'west',mode:'subtract',powered:'true'}}"); ++ register(2398, "{Name:'minecraft:comparator',Properties:{facing:'north',mode:'subtract',powered:'true'}}", "{Name:'minecraft:unpowered_comparator',Properties:{facing:'north',mode:'subtract',powered:'true'}}"); ++ register(2399, "{Name:'minecraft:comparator',Properties:{facing:'east',mode:'subtract',powered:'true'}}", "{Name:'minecraft:unpowered_comparator',Properties:{facing:'east',mode:'subtract',powered:'true'}}"); ++ register(2400, "{Name:'minecraft:comparator',Properties:{facing:'south',mode:'compare',powered:'false'}}", "{Name:'minecraft:powered_comparator',Properties:{facing:'south',mode:'compare',powered:'false'}}"); ++ register(2401, "{Name:'minecraft:comparator',Properties:{facing:'west',mode:'compare',powered:'false'}}", "{Name:'minecraft:powered_comparator',Properties:{facing:'west',mode:'compare',powered:'false'}}"); ++ register(2402, "{Name:'minecraft:comparator',Properties:{facing:'north',mode:'compare',powered:'false'}}", "{Name:'minecraft:powered_comparator',Properties:{facing:'north',mode:'compare',powered:'false'}}"); ++ register(2403, "{Name:'minecraft:comparator',Properties:{facing:'east',mode:'compare',powered:'false'}}", "{Name:'minecraft:powered_comparator',Properties:{facing:'east',mode:'compare',powered:'false'}}"); ++ register(2404, "{Name:'minecraft:comparator',Properties:{facing:'south',mode:'subtract',powered:'false'}}", "{Name:'minecraft:powered_comparator',Properties:{facing:'south',mode:'subtract',powered:'false'}}"); ++ register(2405, "{Name:'minecraft:comparator',Properties:{facing:'west',mode:'subtract',powered:'false'}}", "{Name:'minecraft:powered_comparator',Properties:{facing:'west',mode:'subtract',powered:'false'}}"); ++ register(2406, "{Name:'minecraft:comparator',Properties:{facing:'north',mode:'subtract',powered:'false'}}", "{Name:'minecraft:powered_comparator',Properties:{facing:'north',mode:'subtract',powered:'false'}}"); ++ register(2407, "{Name:'minecraft:comparator',Properties:{facing:'east',mode:'subtract',powered:'false'}}", "{Name:'minecraft:powered_comparator',Properties:{facing:'east',mode:'subtract',powered:'false'}}"); ++ register(2408, "{Name:'minecraft:comparator',Properties:{facing:'south',mode:'compare',powered:'true'}}", "{Name:'minecraft:powered_comparator',Properties:{facing:'south',mode:'compare',powered:'true'}}"); ++ register(2409, "{Name:'minecraft:comparator',Properties:{facing:'west',mode:'compare',powered:'true'}}", "{Name:'minecraft:powered_comparator',Properties:{facing:'west',mode:'compare',powered:'true'}}"); ++ register(2410, "{Name:'minecraft:comparator',Properties:{facing:'north',mode:'compare',powered:'true'}}", "{Name:'minecraft:powered_comparator',Properties:{facing:'north',mode:'compare',powered:'true'}}"); ++ register(2411, "{Name:'minecraft:comparator',Properties:{facing:'east',mode:'compare',powered:'true'}}", "{Name:'minecraft:powered_comparator',Properties:{facing:'east',mode:'compare',powered:'true'}}"); ++ register(2412, "{Name:'minecraft:comparator',Properties:{facing:'south',mode:'subtract',powered:'true'}}", "{Name:'minecraft:powered_comparator',Properties:{facing:'south',mode:'subtract',powered:'true'}}"); ++ register(2413, "{Name:'minecraft:comparator',Properties:{facing:'west',mode:'subtract',powered:'true'}}", "{Name:'minecraft:powered_comparator',Properties:{facing:'west',mode:'subtract',powered:'true'}}"); ++ register(2414, "{Name:'minecraft:comparator',Properties:{facing:'north',mode:'subtract',powered:'true'}}", "{Name:'minecraft:powered_comparator',Properties:{facing:'north',mode:'subtract',powered:'true'}}"); ++ register(2415, "{Name:'minecraft:comparator',Properties:{facing:'east',mode:'subtract',powered:'true'}}", "{Name:'minecraft:powered_comparator',Properties:{facing:'east',mode:'subtract',powered:'true'}}"); ++ register(2416, "{Name:'minecraft:daylight_detector',Properties:{inverted:'false',power:'0'}}", "{Name:'minecraft:daylight_detector',Properties:{power:'0'}}"); ++ register(2417, "{Name:'minecraft:daylight_detector',Properties:{inverted:'false',power:'1'}}", "{Name:'minecraft:daylight_detector',Properties:{power:'1'}}"); ++ register(2418, "{Name:'minecraft:daylight_detector',Properties:{inverted:'false',power:'2'}}", "{Name:'minecraft:daylight_detector',Properties:{power:'2'}}"); ++ register(2419, "{Name:'minecraft:daylight_detector',Properties:{inverted:'false',power:'3'}}", "{Name:'minecraft:daylight_detector',Properties:{power:'3'}}"); ++ register(2420, "{Name:'minecraft:daylight_detector',Properties:{inverted:'false',power:'4'}}", "{Name:'minecraft:daylight_detector',Properties:{power:'4'}}"); ++ register(2421, "{Name:'minecraft:daylight_detector',Properties:{inverted:'false',power:'5'}}", "{Name:'minecraft:daylight_detector',Properties:{power:'5'}}"); ++ register(2422, "{Name:'minecraft:daylight_detector',Properties:{inverted:'false',power:'6'}}", "{Name:'minecraft:daylight_detector',Properties:{power:'6'}}"); ++ register(2423, "{Name:'minecraft:daylight_detector',Properties:{inverted:'false',power:'7'}}", "{Name:'minecraft:daylight_detector',Properties:{power:'7'}}"); ++ register(2424, "{Name:'minecraft:daylight_detector',Properties:{inverted:'false',power:'8'}}", "{Name:'minecraft:daylight_detector',Properties:{power:'8'}}"); ++ register(2425, "{Name:'minecraft:daylight_detector',Properties:{inverted:'false',power:'9'}}", "{Name:'minecraft:daylight_detector',Properties:{power:'9'}}"); ++ register(2426, "{Name:'minecraft:daylight_detector',Properties:{inverted:'false',power:'10'}}", "{Name:'minecraft:daylight_detector',Properties:{power:'10'}}"); ++ register(2427, "{Name:'minecraft:daylight_detector',Properties:{inverted:'false',power:'11'}}", "{Name:'minecraft:daylight_detector',Properties:{power:'11'}}"); ++ register(2428, "{Name:'minecraft:daylight_detector',Properties:{inverted:'false',power:'12'}}", "{Name:'minecraft:daylight_detector',Properties:{power:'12'}}"); ++ register(2429, "{Name:'minecraft:daylight_detector',Properties:{inverted:'false',power:'13'}}", "{Name:'minecraft:daylight_detector',Properties:{power:'13'}}"); ++ register(2430, "{Name:'minecraft:daylight_detector',Properties:{inverted:'false',power:'14'}}", "{Name:'minecraft:daylight_detector',Properties:{power:'14'}}"); ++ register(2431, "{Name:'minecraft:daylight_detector',Properties:{inverted:'false',power:'15'}}", "{Name:'minecraft:daylight_detector',Properties:{power:'15'}}"); ++ register(2432, "{Name:'minecraft:redstone_block'}", "{Name:'minecraft:redstone_block'}"); ++ register(2448, "{Name:'minecraft:nether_quartz_ore'}", "{Name:'minecraft:quartz_ore'}"); ++ register(2464, "{Name:'minecraft:hopper',Properties:{enabled:'true',facing:'down'}}", "{Name:'minecraft:hopper',Properties:{enabled:'true',facing:'down'}}"); ++ register(2466, "{Name:'minecraft:hopper',Properties:{enabled:'true',facing:'north'}}", "{Name:'minecraft:hopper',Properties:{enabled:'true',facing:'north'}}"); ++ register(2467, "{Name:'minecraft:hopper',Properties:{enabled:'true',facing:'south'}}", "{Name:'minecraft:hopper',Properties:{enabled:'true',facing:'south'}}"); ++ register(2468, "{Name:'minecraft:hopper',Properties:{enabled:'true',facing:'west'}}", "{Name:'minecraft:hopper',Properties:{enabled:'true',facing:'west'}}"); ++ register(2469, "{Name:'minecraft:hopper',Properties:{enabled:'true',facing:'east'}}", "{Name:'minecraft:hopper',Properties:{enabled:'true',facing:'east'}}"); ++ register(2472, "{Name:'minecraft:hopper',Properties:{enabled:'false',facing:'down'}}", "{Name:'minecraft:hopper',Properties:{enabled:'false',facing:'down'}}"); ++ register(2474, "{Name:'minecraft:hopper',Properties:{enabled:'false',facing:'north'}}", "{Name:'minecraft:hopper',Properties:{enabled:'false',facing:'north'}}"); ++ register(2475, "{Name:'minecraft:hopper',Properties:{enabled:'false',facing:'south'}}", "{Name:'minecraft:hopper',Properties:{enabled:'false',facing:'south'}}"); ++ register(2476, "{Name:'minecraft:hopper',Properties:{enabled:'false',facing:'west'}}", "{Name:'minecraft:hopper',Properties:{enabled:'false',facing:'west'}}"); ++ register(2477, "{Name:'minecraft:hopper',Properties:{enabled:'false',facing:'east'}}", "{Name:'minecraft:hopper',Properties:{enabled:'false',facing:'east'}}"); ++ register(2480, "{Name:'minecraft:quartz_block'}", "{Name:'minecraft:quartz_block',Properties:{variant:'default'}}"); ++ register(2481, "{Name:'minecraft:chiseled_quartz_block'}", "{Name:'minecraft:quartz_block',Properties:{variant:'chiseled'}}"); ++ register(2482, "{Name:'minecraft:quartz_pillar',Properties:{axis:'y'}}", "{Name:'minecraft:quartz_block',Properties:{variant:'lines_y'}}"); ++ register(2483, "{Name:'minecraft:quartz_pillar',Properties:{axis:'x'}}", "{Name:'minecraft:quartz_block',Properties:{variant:'lines_x'}}"); ++ register(2484, "{Name:'minecraft:quartz_pillar',Properties:{axis:'z'}}", "{Name:'minecraft:quartz_block',Properties:{variant:'lines_z'}}"); ++ register(2496, "{Name:'minecraft:quartz_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}"); ++ register(2497, "{Name:'minecraft:quartz_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}"); ++ register(2498, "{Name:'minecraft:quartz_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}"); ++ register(2499, "{Name:'minecraft:quartz_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}"); ++ register(2500, "{Name:'minecraft:quartz_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'east',half:'top',shape:'inner_left'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'east',half:'top',shape:'inner_right'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'east',half:'top',shape:'outer_left'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'east',half:'top',shape:'outer_right'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}"); ++ register(2501, "{Name:'minecraft:quartz_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'west',half:'top',shape:'inner_left'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'west',half:'top',shape:'inner_right'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'west',half:'top',shape:'outer_left'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'west',half:'top',shape:'outer_right'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}"); ++ register(2502, "{Name:'minecraft:quartz_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'south',half:'top',shape:'inner_left'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'south',half:'top',shape:'inner_right'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'south',half:'top',shape:'outer_left'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'south',half:'top',shape:'outer_right'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}"); ++ register(2503, "{Name:'minecraft:quartz_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'north',half:'top',shape:'inner_left'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'north',half:'top',shape:'inner_right'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'north',half:'top',shape:'outer_left'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'north',half:'top',shape:'outer_right'}}", "{Name:'minecraft:quartz_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}"); ++ register(2512, "{Name:'minecraft:activator_rail',Properties:{powered:'false',shape:'north_south'}}", "{Name:'minecraft:activator_rail',Properties:{powered:'false',shape:'north_south'}}"); ++ register(2513, "{Name:'minecraft:activator_rail',Properties:{powered:'false',shape:'east_west'}}", "{Name:'minecraft:activator_rail',Properties:{powered:'false',shape:'east_west'}}"); ++ register(2514, "{Name:'minecraft:activator_rail',Properties:{powered:'false',shape:'ascending_east'}}", "{Name:'minecraft:activator_rail',Properties:{powered:'false',shape:'ascending_east'}}"); ++ register(2515, "{Name:'minecraft:activator_rail',Properties:{powered:'false',shape:'ascending_west'}}", "{Name:'minecraft:activator_rail',Properties:{powered:'false',shape:'ascending_west'}}"); ++ register(2516, "{Name:'minecraft:activator_rail',Properties:{powered:'false',shape:'ascending_north'}}", "{Name:'minecraft:activator_rail',Properties:{powered:'false',shape:'ascending_north'}}"); ++ register(2517, "{Name:'minecraft:activator_rail',Properties:{powered:'false',shape:'ascending_south'}}", "{Name:'minecraft:activator_rail',Properties:{powered:'false',shape:'ascending_south'}}"); ++ register(2520, "{Name:'minecraft:activator_rail',Properties:{powered:'true',shape:'north_south'}}", "{Name:'minecraft:activator_rail',Properties:{powered:'true',shape:'north_south'}}"); ++ register(2521, "{Name:'minecraft:activator_rail',Properties:{powered:'true',shape:'east_west'}}", "{Name:'minecraft:activator_rail',Properties:{powered:'true',shape:'east_west'}}"); ++ register(2522, "{Name:'minecraft:activator_rail',Properties:{powered:'true',shape:'ascending_east'}}", "{Name:'minecraft:activator_rail',Properties:{powered:'true',shape:'ascending_east'}}"); ++ register(2523, "{Name:'minecraft:activator_rail',Properties:{powered:'true',shape:'ascending_west'}}", "{Name:'minecraft:activator_rail',Properties:{powered:'true',shape:'ascending_west'}}"); ++ register(2524, "{Name:'minecraft:activator_rail',Properties:{powered:'true',shape:'ascending_north'}}", "{Name:'minecraft:activator_rail',Properties:{powered:'true',shape:'ascending_north'}}"); ++ register(2525, "{Name:'minecraft:activator_rail',Properties:{powered:'true',shape:'ascending_south'}}", "{Name:'minecraft:activator_rail',Properties:{powered:'true',shape:'ascending_south'}}"); ++ register(2528, "{Name:'minecraft:dropper',Properties:{facing:'down',triggered:'false'}}", "{Name:'minecraft:dropper',Properties:{facing:'down',triggered:'false'}}"); ++ register(2529, "{Name:'minecraft:dropper',Properties:{facing:'up',triggered:'false'}}", "{Name:'minecraft:dropper',Properties:{facing:'up',triggered:'false'}}"); ++ register(2530, "{Name:'minecraft:dropper',Properties:{facing:'north',triggered:'false'}}", "{Name:'minecraft:dropper',Properties:{facing:'north',triggered:'false'}}"); ++ register(2531, "{Name:'minecraft:dropper',Properties:{facing:'south',triggered:'false'}}", "{Name:'minecraft:dropper',Properties:{facing:'south',triggered:'false'}}"); ++ register(2532, "{Name:'minecraft:dropper',Properties:{facing:'west',triggered:'false'}}", "{Name:'minecraft:dropper',Properties:{facing:'west',triggered:'false'}}"); ++ register(2533, "{Name:'minecraft:dropper',Properties:{facing:'east',triggered:'false'}}", "{Name:'minecraft:dropper',Properties:{facing:'east',triggered:'false'}}"); ++ register(2536, "{Name:'minecraft:dropper',Properties:{facing:'down',triggered:'true'}}", "{Name:'minecraft:dropper',Properties:{facing:'down',triggered:'true'}}"); ++ register(2537, "{Name:'minecraft:dropper',Properties:{facing:'up',triggered:'true'}}", "{Name:'minecraft:dropper',Properties:{facing:'up',triggered:'true'}}"); ++ register(2538, "{Name:'minecraft:dropper',Properties:{facing:'north',triggered:'true'}}", "{Name:'minecraft:dropper',Properties:{facing:'north',triggered:'true'}}"); ++ register(2539, "{Name:'minecraft:dropper',Properties:{facing:'south',triggered:'true'}}", "{Name:'minecraft:dropper',Properties:{facing:'south',triggered:'true'}}"); ++ register(2540, "{Name:'minecraft:dropper',Properties:{facing:'west',triggered:'true'}}", "{Name:'minecraft:dropper',Properties:{facing:'west',triggered:'true'}}"); ++ register(2541, "{Name:'minecraft:dropper',Properties:{facing:'east',triggered:'true'}}", "{Name:'minecraft:dropper',Properties:{facing:'east',triggered:'true'}}"); ++ register(2544, "{Name:'minecraft:white_terracotta'}", "{Name:'minecraft:stained_hardened_clay',Properties:{color:'white'}}"); ++ register(2545, "{Name:'minecraft:orange_terracotta'}", "{Name:'minecraft:stained_hardened_clay',Properties:{color:'orange'}}"); ++ register(2546, "{Name:'minecraft:magenta_terracotta'}", "{Name:'minecraft:stained_hardened_clay',Properties:{color:'magenta'}}"); ++ register(2547, "{Name:'minecraft:light_blue_terracotta'}", "{Name:'minecraft:stained_hardened_clay',Properties:{color:'light_blue'}}"); ++ register(2548, "{Name:'minecraft:yellow_terracotta'}", "{Name:'minecraft:stained_hardened_clay',Properties:{color:'yellow'}}"); ++ register(2549, "{Name:'minecraft:lime_terracotta'}", "{Name:'minecraft:stained_hardened_clay',Properties:{color:'lime'}}"); ++ register(2550, "{Name:'minecraft:pink_terracotta'}", "{Name:'minecraft:stained_hardened_clay',Properties:{color:'pink'}}"); ++ register(2551, "{Name:'minecraft:gray_terracotta'}", "{Name:'minecraft:stained_hardened_clay',Properties:{color:'gray'}}"); ++ register(2552, "{Name:'minecraft:light_gray_terracotta'}", "{Name:'minecraft:stained_hardened_clay',Properties:{color:'silver'}}"); ++ register(2553, "{Name:'minecraft:cyan_terracotta'}", "{Name:'minecraft:stained_hardened_clay',Properties:{color:'cyan'}}"); ++ register(2554, "{Name:'minecraft:purple_terracotta'}", "{Name:'minecraft:stained_hardened_clay',Properties:{color:'purple'}}"); ++ register(2555, "{Name:'minecraft:blue_terracotta'}", "{Name:'minecraft:stained_hardened_clay',Properties:{color:'blue'}}"); ++ register(2556, "{Name:'minecraft:brown_terracotta'}", "{Name:'minecraft:stained_hardened_clay',Properties:{color:'brown'}}"); ++ register(2557, "{Name:'minecraft:green_terracotta'}", "{Name:'minecraft:stained_hardened_clay',Properties:{color:'green'}}"); ++ register(2558, "{Name:'minecraft:red_terracotta'}", "{Name:'minecraft:stained_hardened_clay',Properties:{color:'red'}}"); ++ register(2559, "{Name:'minecraft:black_terracotta'}", "{Name:'minecraft:stained_hardened_clay',Properties:{color:'black'}}"); ++ register(2560, "{Name:'minecraft:white_stained_glass_pane',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'white',east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'white',east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'white',east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'white',east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'white',east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'white',east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'white',east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'white',east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'white',east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'white',east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'white',east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'white',east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'white',east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'white',east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'white',east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'white',east:'true',north:'true',south:'true',west:'true'}}"); ++ register(2561, "{Name:'minecraft:orange_stained_glass_pane',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'orange',east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'orange',east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'orange',east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'orange',east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'orange',east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'orange',east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'orange',east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'orange',east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'orange',east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'orange',east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'orange',east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'orange',east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'orange',east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'orange',east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'orange',east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'orange',east:'true',north:'true',south:'true',west:'true'}}"); ++ register(2562, "{Name:'minecraft:magenta_stained_glass_pane',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'magenta',east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'magenta',east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'magenta',east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'magenta',east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'magenta',east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'magenta',east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'magenta',east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'magenta',east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'magenta',east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'magenta',east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'magenta',east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'magenta',east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'magenta',east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'magenta',east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'magenta',east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'magenta',east:'true',north:'true',south:'true',west:'true'}}"); ++ register(2563, "{Name:'minecraft:light_blue_stained_glass_pane',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'light_blue',east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'light_blue',east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'light_blue',east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'light_blue',east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'light_blue',east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'light_blue',east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'light_blue',east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'light_blue',east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'light_blue',east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'light_blue',east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'light_blue',east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'light_blue',east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'light_blue',east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'light_blue',east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'light_blue',east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'light_blue',east:'true',north:'true',south:'true',west:'true'}}"); ++ register(2564, "{Name:'minecraft:yellow_stained_glass_pane',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'yellow',east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'yellow',east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'yellow',east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'yellow',east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'yellow',east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'yellow',east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'yellow',east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'yellow',east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'yellow',east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'yellow',east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'yellow',east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'yellow',east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'yellow',east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'yellow',east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'yellow',east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'yellow',east:'true',north:'true',south:'true',west:'true'}}"); ++ register(2565, "{Name:'minecraft:lime_stained_glass_pane',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'lime',east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'lime',east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'lime',east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'lime',east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'lime',east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'lime',east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'lime',east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'lime',east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'lime',east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'lime',east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'lime',east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'lime',east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'lime',east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'lime',east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'lime',east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'lime',east:'true',north:'true',south:'true',west:'true'}}"); ++ register(2566, "{Name:'minecraft:pink_stained_glass_pane',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'pink',east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'pink',east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'pink',east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'pink',east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'pink',east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'pink',east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'pink',east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'pink',east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'pink',east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'pink',east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'pink',east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'pink',east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'pink',east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'pink',east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'pink',east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'pink',east:'true',north:'true',south:'true',west:'true'}}"); ++ register(2567, "{Name:'minecraft:gray_stained_glass_pane',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'gray',east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'gray',east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'gray',east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'gray',east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'gray',east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'gray',east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'gray',east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'gray',east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'gray',east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'gray',east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'gray',east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'gray',east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'gray',east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'gray',east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'gray',east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'gray',east:'true',north:'true',south:'true',west:'true'}}"); ++ register(2568, "{Name:'minecraft:light_gray_stained_glass_pane',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'silver',east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'silver',east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'silver',east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'silver',east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'silver',east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'silver',east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'silver',east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'silver',east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'silver',east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'silver',east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'silver',east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'silver',east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'silver',east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'silver',east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'silver',east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'silver',east:'true',north:'true',south:'true',west:'true'}}"); ++ register(2569, "{Name:'minecraft:cyan_stained_glass_pane',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'cyan',east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'cyan',east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'cyan',east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'cyan',east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'cyan',east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'cyan',east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'cyan',east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'cyan',east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'cyan',east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'cyan',east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'cyan',east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'cyan',east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'cyan',east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'cyan',east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'cyan',east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'cyan',east:'true',north:'true',south:'true',west:'true'}}"); ++ register(2570, "{Name:'minecraft:purple_stained_glass_pane',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'purple',east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'purple',east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'purple',east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'purple',east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'purple',east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'purple',east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'purple',east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'purple',east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'purple',east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'purple',east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'purple',east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'purple',east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'purple',east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'purple',east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'purple',east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'purple',east:'true',north:'true',south:'true',west:'true'}}"); ++ register(2571, "{Name:'minecraft:blue_stained_glass_pane',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'blue',east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'blue',east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'blue',east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'blue',east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'blue',east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'blue',east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'blue',east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'blue',east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'blue',east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'blue',east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'blue',east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'blue',east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'blue',east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'blue',east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'blue',east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'blue',east:'true',north:'true',south:'true',west:'true'}}"); ++ register(2572, "{Name:'minecraft:brown_stained_glass_pane',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'brown',east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'brown',east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'brown',east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'brown',east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'brown',east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'brown',east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'brown',east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'brown',east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'brown',east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'brown',east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'brown',east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'brown',east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'brown',east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'brown',east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'brown',east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'brown',east:'true',north:'true',south:'true',west:'true'}}"); ++ register(2573, "{Name:'minecraft:green_stained_glass_pane',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'green',east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'green',east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'green',east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'green',east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'green',east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'green',east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'green',east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'green',east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'green',east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'green',east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'green',east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'green',east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'green',east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'green',east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'green',east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'green',east:'true',north:'true',south:'true',west:'true'}}"); ++ register(2574, "{Name:'minecraft:red_stained_glass_pane',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'red',east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'red',east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'red',east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'red',east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'red',east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'red',east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'red',east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'red',east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'red',east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'red',east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'red',east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'red',east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'red',east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'red',east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'red',east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'red',east:'true',north:'true',south:'true',west:'true'}}"); ++ register(2575, "{Name:'minecraft:black_stained_glass_pane',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'black',east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'black',east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'black',east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'black',east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'black',east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'black',east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'black',east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'black',east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'black',east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'black',east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'black',east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'black',east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'black',east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'black',east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'black',east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:stained_glass_pane',Properties:{color:'black',east:'true',north:'true',south:'true',west:'true'}}"); ++ register(2576, "{Name:'minecraft:acacia_leaves',Properties:{check_decay:'false',decayable:'true'}}", "{Name:'minecraft:leaves2',Properties:{check_decay:'false',decayable:'true',variant:'acacia'}}"); ++ register(2577, "{Name:'minecraft:dark_oak_leaves',Properties:{check_decay:'false',decayable:'true'}}", "{Name:'minecraft:leaves2',Properties:{check_decay:'false',decayable:'true',variant:'dark_oak'}}"); ++ register(2580, "{Name:'minecraft:acacia_leaves',Properties:{check_decay:'false',decayable:'false'}}", "{Name:'minecraft:leaves2',Properties:{check_decay:'false',decayable:'false',variant:'acacia'}}"); ++ register(2581, "{Name:'minecraft:dark_oak_leaves',Properties:{check_decay:'false',decayable:'false'}}", "{Name:'minecraft:leaves2',Properties:{check_decay:'false',decayable:'false',variant:'dark_oak'}}"); ++ register(2584, "{Name:'minecraft:acacia_leaves',Properties:{check_decay:'true',decayable:'true'}}", "{Name:'minecraft:leaves2',Properties:{check_decay:'true',decayable:'true',variant:'acacia'}}"); ++ register(2585, "{Name:'minecraft:dark_oak_leaves',Properties:{check_decay:'true',decayable:'true'}}", "{Name:'minecraft:leaves2',Properties:{check_decay:'true',decayable:'true',variant:'dark_oak'}}"); ++ register(2588, "{Name:'minecraft:acacia_leaves',Properties:{check_decay:'true',decayable:'false'}}", "{Name:'minecraft:leaves2',Properties:{check_decay:'true',decayable:'false',variant:'acacia'}}"); ++ register(2589, "{Name:'minecraft:dark_oak_leaves',Properties:{check_decay:'true',decayable:'false'}}", "{Name:'minecraft:leaves2',Properties:{check_decay:'true',decayable:'false',variant:'dark_oak'}}"); ++ register(2592, "{Name:'minecraft:acacia_log',Properties:{axis:'y'}}", "{Name:'minecraft:log2',Properties:{axis:'y',variant:'acacia'}}"); ++ register(2593, "{Name:'minecraft:dark_oak_log',Properties:{axis:'y'}}", "{Name:'minecraft:log2',Properties:{axis:'y',variant:'dark_oak'}}"); ++ register(2596, "{Name:'minecraft:acacia_log',Properties:{axis:'x'}}", "{Name:'minecraft:log2',Properties:{axis:'x',variant:'acacia'}}"); ++ register(2597, "{Name:'minecraft:dark_oak_log',Properties:{axis:'x'}}", "{Name:'minecraft:log2',Properties:{axis:'x',variant:'dark_oak'}}"); ++ register(2600, "{Name:'minecraft:acacia_log',Properties:{axis:'z'}}", "{Name:'minecraft:log2',Properties:{axis:'z',variant:'acacia'}}"); ++ register(2601, "{Name:'minecraft:dark_oak_log',Properties:{axis:'z'}}", "{Name:'minecraft:log2',Properties:{axis:'z',variant:'dark_oak'}}"); ++ register(2604, "{Name:'minecraft:acacia_bark'}", "{Name:'minecraft:log2',Properties:{axis:'none',variant:'acacia'}}"); ++ register(2605, "{Name:'minecraft:dark_oak_bark'}", "{Name:'minecraft:log2',Properties:{axis:'none',variant:'dark_oak'}}"); ++ register(2608, "{Name:'minecraft:acacia_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}"); ++ register(2609, "{Name:'minecraft:acacia_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}"); ++ register(2610, "{Name:'minecraft:acacia_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}"); ++ register(2611, "{Name:'minecraft:acacia_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}"); ++ register(2612, "{Name:'minecraft:acacia_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'east',half:'top',shape:'inner_left'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'east',half:'top',shape:'inner_right'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'east',half:'top',shape:'outer_left'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'east',half:'top',shape:'outer_right'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}"); ++ register(2613, "{Name:'minecraft:acacia_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'west',half:'top',shape:'inner_left'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'west',half:'top',shape:'inner_right'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'west',half:'top',shape:'outer_left'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'west',half:'top',shape:'outer_right'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}"); ++ register(2614, "{Name:'minecraft:acacia_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'south',half:'top',shape:'inner_left'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'south',half:'top',shape:'inner_right'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'south',half:'top',shape:'outer_left'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'south',half:'top',shape:'outer_right'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}"); ++ register(2615, "{Name:'minecraft:acacia_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'north',half:'top',shape:'inner_left'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'north',half:'top',shape:'inner_right'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'north',half:'top',shape:'outer_left'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'north',half:'top',shape:'outer_right'}}", "{Name:'minecraft:acacia_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}"); ++ register(2624, "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}"); ++ register(2625, "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}"); ++ register(2626, "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}"); ++ register(2627, "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}"); ++ register(2628, "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'east',half:'top',shape:'inner_left'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'east',half:'top',shape:'inner_right'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'east',half:'top',shape:'outer_left'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'east',half:'top',shape:'outer_right'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}"); ++ register(2629, "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'west',half:'top',shape:'inner_left'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'west',half:'top',shape:'inner_right'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'west',half:'top',shape:'outer_left'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'west',half:'top',shape:'outer_right'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}"); ++ register(2630, "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'south',half:'top',shape:'inner_left'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'south',half:'top',shape:'inner_right'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'south',half:'top',shape:'outer_left'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'south',half:'top',shape:'outer_right'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}"); ++ register(2631, "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'north',half:'top',shape:'inner_left'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'north',half:'top',shape:'inner_right'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'north',half:'top',shape:'outer_left'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'north',half:'top',shape:'outer_right'}}", "{Name:'minecraft:dark_oak_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}"); ++ register(2640, "{Name:'minecraft:slime_block'}", "{Name:'minecraft:slime'}"); ++ register(2656, "{Name:'minecraft:barrier'}", "{Name:'minecraft:barrier'}"); ++ register(2672, "{Name:'minecraft:iron_trapdoor',Properties:{facing:'north',half:'bottom',open:'false'}}", "{Name:'minecraft:iron_trapdoor',Properties:{facing:'north',half:'bottom',open:'false'}}"); ++ register(2673, "{Name:'minecraft:iron_trapdoor',Properties:{facing:'south',half:'bottom',open:'false'}}", "{Name:'minecraft:iron_trapdoor',Properties:{facing:'south',half:'bottom',open:'false'}}"); ++ register(2674, "{Name:'minecraft:iron_trapdoor',Properties:{facing:'west',half:'bottom',open:'false'}}", "{Name:'minecraft:iron_trapdoor',Properties:{facing:'west',half:'bottom',open:'false'}}"); ++ register(2675, "{Name:'minecraft:iron_trapdoor',Properties:{facing:'east',half:'bottom',open:'false'}}", "{Name:'minecraft:iron_trapdoor',Properties:{facing:'east',half:'bottom',open:'false'}}"); ++ register(2676, "{Name:'minecraft:iron_trapdoor',Properties:{facing:'north',half:'bottom',open:'true'}}", "{Name:'minecraft:iron_trapdoor',Properties:{facing:'north',half:'bottom',open:'true'}}"); ++ register(2677, "{Name:'minecraft:iron_trapdoor',Properties:{facing:'south',half:'bottom',open:'true'}}", "{Name:'minecraft:iron_trapdoor',Properties:{facing:'south',half:'bottom',open:'true'}}"); ++ register(2678, "{Name:'minecraft:iron_trapdoor',Properties:{facing:'west',half:'bottom',open:'true'}}", "{Name:'minecraft:iron_trapdoor',Properties:{facing:'west',half:'bottom',open:'true'}}"); ++ register(2679, "{Name:'minecraft:iron_trapdoor',Properties:{facing:'east',half:'bottom',open:'true'}}", "{Name:'minecraft:iron_trapdoor',Properties:{facing:'east',half:'bottom',open:'true'}}"); ++ register(2680, "{Name:'minecraft:iron_trapdoor',Properties:{facing:'north',half:'top',open:'false'}}", "{Name:'minecraft:iron_trapdoor',Properties:{facing:'north',half:'top',open:'false'}}"); ++ register(2681, "{Name:'minecraft:iron_trapdoor',Properties:{facing:'south',half:'top',open:'false'}}", "{Name:'minecraft:iron_trapdoor',Properties:{facing:'south',half:'top',open:'false'}}"); ++ register(2682, "{Name:'minecraft:iron_trapdoor',Properties:{facing:'west',half:'top',open:'false'}}", "{Name:'minecraft:iron_trapdoor',Properties:{facing:'west',half:'top',open:'false'}}"); ++ register(2683, "{Name:'minecraft:iron_trapdoor',Properties:{facing:'east',half:'top',open:'false'}}", "{Name:'minecraft:iron_trapdoor',Properties:{facing:'east',half:'top',open:'false'}}"); ++ register(2684, "{Name:'minecraft:iron_trapdoor',Properties:{facing:'north',half:'top',open:'true'}}", "{Name:'minecraft:iron_trapdoor',Properties:{facing:'north',half:'top',open:'true'}}"); ++ register(2685, "{Name:'minecraft:iron_trapdoor',Properties:{facing:'south',half:'top',open:'true'}}", "{Name:'minecraft:iron_trapdoor',Properties:{facing:'south',half:'top',open:'true'}}"); ++ register(2686, "{Name:'minecraft:iron_trapdoor',Properties:{facing:'west',half:'top',open:'true'}}", "{Name:'minecraft:iron_trapdoor',Properties:{facing:'west',half:'top',open:'true'}}"); ++ register(2687, "{Name:'minecraft:iron_trapdoor',Properties:{facing:'east',half:'top',open:'true'}}", "{Name:'minecraft:iron_trapdoor',Properties:{facing:'east',half:'top',open:'true'}}"); ++ register(2688, "{Name:'minecraft:prismarine'}", "{Name:'minecraft:prismarine',Properties:{variant:'prismarine'}}"); ++ register(2689, "{Name:'minecraft:prismarine_bricks'}", "{Name:'minecraft:prismarine',Properties:{variant:'prismarine_bricks'}}"); ++ register(2690, "{Name:'minecraft:dark_prismarine'}", "{Name:'minecraft:prismarine',Properties:{variant:'dark_prismarine'}}"); ++ register(2704, "{Name:'minecraft:sea_lantern'}", "{Name:'minecraft:sea_lantern'}"); ++ register(2720, "{Name:'minecraft:hay_block',Properties:{axis:'y'}}", "{Name:'minecraft:hay_block',Properties:{axis:'y'}}"); ++ register(2724, "{Name:'minecraft:hay_block',Properties:{axis:'x'}}", "{Name:'minecraft:hay_block',Properties:{axis:'x'}}"); ++ register(2728, "{Name:'minecraft:hay_block',Properties:{axis:'z'}}", "{Name:'minecraft:hay_block',Properties:{axis:'z'}}"); ++ register(2736, "{Name:'minecraft:white_carpet'}", "{Name:'minecraft:carpet',Properties:{color:'white'}}"); ++ register(2737, "{Name:'minecraft:orange_carpet'}", "{Name:'minecraft:carpet',Properties:{color:'orange'}}"); ++ register(2738, "{Name:'minecraft:magenta_carpet'}", "{Name:'minecraft:carpet',Properties:{color:'magenta'}}"); ++ register(2739, "{Name:'minecraft:light_blue_carpet'}", "{Name:'minecraft:carpet',Properties:{color:'light_blue'}}"); ++ register(2740, "{Name:'minecraft:yellow_carpet'}", "{Name:'minecraft:carpet',Properties:{color:'yellow'}}"); ++ register(2741, "{Name:'minecraft:lime_carpet'}", "{Name:'minecraft:carpet',Properties:{color:'lime'}}"); ++ register(2742, "{Name:'minecraft:pink_carpet'}", "{Name:'minecraft:carpet',Properties:{color:'pink'}}"); ++ register(2743, "{Name:'minecraft:gray_carpet'}", "{Name:'minecraft:carpet',Properties:{color:'gray'}}"); ++ register(2744, "{Name:'minecraft:light_gray_carpet'}", "{Name:'minecraft:carpet',Properties:{color:'silver'}}"); ++ register(2745, "{Name:'minecraft:cyan_carpet'}", "{Name:'minecraft:carpet',Properties:{color:'cyan'}}"); ++ register(2746, "{Name:'minecraft:purple_carpet'}", "{Name:'minecraft:carpet',Properties:{color:'purple'}}"); ++ register(2747, "{Name:'minecraft:blue_carpet'}", "{Name:'minecraft:carpet',Properties:{color:'blue'}}"); ++ register(2748, "{Name:'minecraft:brown_carpet'}", "{Name:'minecraft:carpet',Properties:{color:'brown'}}"); ++ register(2749, "{Name:'minecraft:green_carpet'}", "{Name:'minecraft:carpet',Properties:{color:'green'}}"); ++ register(2750, "{Name:'minecraft:red_carpet'}", "{Name:'minecraft:carpet',Properties:{color:'red'}}"); ++ register(2751, "{Name:'minecraft:black_carpet'}", "{Name:'minecraft:carpet',Properties:{color:'black'}}"); ++ register(2752, "{Name:'minecraft:terracotta'}", "{Name:'minecraft:hardened_clay'}"); ++ register(2768, "{Name:'minecraft:coal_block'}", "{Name:'minecraft:coal_block'}"); ++ register(2784, "{Name:'minecraft:packed_ice'}", "{Name:'minecraft:packed_ice'}"); ++ register(2800, "{Name:'minecraft:sunflower',Properties:{half:'lower'}}", "{Name:'minecraft:double_plant',Properties:{facing:'east',half:'lower',variant:'sunflower'}}", "{Name:'minecraft:double_plant',Properties:{facing:'north',half:'lower',variant:'sunflower'}}", "{Name:'minecraft:double_plant',Properties:{facing:'south',half:'lower',variant:'sunflower'}}", "{Name:'minecraft:double_plant',Properties:{facing:'west',half:'lower',variant:'sunflower'}}"); ++ register(2801, "{Name:'minecraft:lilac',Properties:{half:'lower'}}", "{Name:'minecraft:double_plant',Properties:{facing:'east',half:'lower',variant:'syringa'}}", "{Name:'minecraft:double_plant',Properties:{facing:'north',half:'lower',variant:'syringa'}}", "{Name:'minecraft:double_plant',Properties:{facing:'south',half:'lower',variant:'syringa'}}", "{Name:'minecraft:double_plant',Properties:{facing:'west',half:'lower',variant:'syringa'}}"); ++ register(2802, "{Name:'minecraft:tall_grass',Properties:{half:'lower'}}", "{Name:'minecraft:double_plant',Properties:{facing:'east',half:'lower',variant:'double_grass'}}", "{Name:'minecraft:double_plant',Properties:{facing:'north',half:'lower',variant:'double_grass'}}", "{Name:'minecraft:double_plant',Properties:{facing:'south',half:'lower',variant:'double_grass'}}", "{Name:'minecraft:double_plant',Properties:{facing:'west',half:'lower',variant:'double_grass'}}"); ++ register(2803, "{Name:'minecraft:large_fern',Properties:{half:'lower'}}", "{Name:'minecraft:double_plant',Properties:{facing:'east',half:'lower',variant:'double_fern'}}", "{Name:'minecraft:double_plant',Properties:{facing:'north',half:'lower',variant:'double_fern'}}", "{Name:'minecraft:double_plant',Properties:{facing:'south',half:'lower',variant:'double_fern'}}", "{Name:'minecraft:double_plant',Properties:{facing:'west',half:'lower',variant:'double_fern'}}"); ++ register(2804, "{Name:'minecraft:rose_bush',Properties:{half:'lower'}}", "{Name:'minecraft:double_plant',Properties:{facing:'east',half:'lower',variant:'double_rose'}}", "{Name:'minecraft:double_plant',Properties:{facing:'north',half:'lower',variant:'double_rose'}}", "{Name:'minecraft:double_plant',Properties:{facing:'south',half:'lower',variant:'double_rose'}}", "{Name:'minecraft:double_plant',Properties:{facing:'west',half:'lower',variant:'double_rose'}}"); ++ register(2805, "{Name:'minecraft:peony',Properties:{half:'lower'}}", "{Name:'minecraft:double_plant',Properties:{facing:'east',half:'lower',variant:'paeonia'}}", "{Name:'minecraft:double_plant',Properties:{facing:'north',half:'lower',variant:'paeonia'}}", "{Name:'minecraft:double_plant',Properties:{facing:'south',half:'lower',variant:'paeonia'}}", "{Name:'minecraft:double_plant',Properties:{facing:'west',half:'lower',variant:'paeonia'}}"); ++ register(2808, "{Name:'minecraft:peony',Properties:{half:'upper'}}", "{Name:'minecraft:double_plant',Properties:{facing:'south',half:'upper',variant:'double_fern'}}", "{Name:'minecraft:double_plant',Properties:{facing:'south',half:'upper',variant:'double_grass'}}", "{Name:'minecraft:double_plant',Properties:{facing:'south',half:'upper',variant:'double_rose'}}", "{Name:'minecraft:double_plant',Properties:{facing:'south',half:'upper',variant:'paeonia'}}", "{Name:'minecraft:double_plant',Properties:{facing:'south',half:'upper',variant:'sunflower'}}", "{Name:'minecraft:double_plant',Properties:{facing:'south',half:'upper',variant:'syringa'}}"); ++ register(2809, "{Name:'minecraft:peony',Properties:{half:'upper'}}", "{Name:'minecraft:double_plant',Properties:{facing:'west',half:'upper',variant:'double_fern'}}", "{Name:'minecraft:double_plant',Properties:{facing:'west',half:'upper',variant:'double_grass'}}", "{Name:'minecraft:double_plant',Properties:{facing:'west',half:'upper',variant:'double_rose'}}", "{Name:'minecraft:double_plant',Properties:{facing:'west',half:'upper',variant:'paeonia'}}", "{Name:'minecraft:double_plant',Properties:{facing:'west',half:'upper',variant:'sunflower'}}", "{Name:'minecraft:double_plant',Properties:{facing:'west',half:'upper',variant:'syringa'}}"); ++ register(2810, "{Name:'minecraft:peony',Properties:{half:'upper'}}", "{Name:'minecraft:double_plant',Properties:{facing:'north',half:'upper',variant:'double_fern'}}", "{Name:'minecraft:double_plant',Properties:{facing:'north',half:'upper',variant:'double_grass'}}", "{Name:'minecraft:double_plant',Properties:{facing:'north',half:'upper',variant:'double_rose'}}", "{Name:'minecraft:double_plant',Properties:{facing:'north',half:'upper',variant:'paeonia'}}", "{Name:'minecraft:double_plant',Properties:{facing:'north',half:'upper',variant:'sunflower'}}", "{Name:'minecraft:double_plant',Properties:{facing:'north',half:'upper',variant:'syringa'}}"); ++ register(2811, "{Name:'minecraft:peony',Properties:{half:'upper'}}", "{Name:'minecraft:double_plant',Properties:{facing:'east',half:'upper',variant:'double_fern'}}", "{Name:'minecraft:double_plant',Properties:{facing:'east',half:'upper',variant:'double_grass'}}", "{Name:'minecraft:double_plant',Properties:{facing:'east',half:'upper',variant:'double_rose'}}", "{Name:'minecraft:double_plant',Properties:{facing:'east',half:'upper',variant:'paeonia'}}", "{Name:'minecraft:double_plant',Properties:{facing:'east',half:'upper',variant:'sunflower'}}", "{Name:'minecraft:double_plant',Properties:{facing:'east',half:'upper',variant:'syringa'}}"); ++ register(2816, "{Name:'minecraft:white_banner',Properties:{rotation:'0'}}", "{Name:'minecraft:standing_banner',Properties:{rotation:'0'}}"); ++ register(2817, "{Name:'minecraft:white_banner',Properties:{rotation:'1'}}", "{Name:'minecraft:standing_banner',Properties:{rotation:'1'}}"); ++ register(2818, "{Name:'minecraft:white_banner',Properties:{rotation:'2'}}", "{Name:'minecraft:standing_banner',Properties:{rotation:'2'}}"); ++ register(2819, "{Name:'minecraft:white_banner',Properties:{rotation:'3'}}", "{Name:'minecraft:standing_banner',Properties:{rotation:'3'}}"); ++ register(2820, "{Name:'minecraft:white_banner',Properties:{rotation:'4'}}", "{Name:'minecraft:standing_banner',Properties:{rotation:'4'}}"); ++ register(2821, "{Name:'minecraft:white_banner',Properties:{rotation:'5'}}", "{Name:'minecraft:standing_banner',Properties:{rotation:'5'}}"); ++ register(2822, "{Name:'minecraft:white_banner',Properties:{rotation:'6'}}", "{Name:'minecraft:standing_banner',Properties:{rotation:'6'}}"); ++ register(2823, "{Name:'minecraft:white_banner',Properties:{rotation:'7'}}", "{Name:'minecraft:standing_banner',Properties:{rotation:'7'}}"); ++ register(2824, "{Name:'minecraft:white_banner',Properties:{rotation:'8'}}", "{Name:'minecraft:standing_banner',Properties:{rotation:'8'}}"); ++ register(2825, "{Name:'minecraft:white_banner',Properties:{rotation:'9'}}", "{Name:'minecraft:standing_banner',Properties:{rotation:'9'}}"); ++ register(2826, "{Name:'minecraft:white_banner',Properties:{rotation:'10'}}", "{Name:'minecraft:standing_banner',Properties:{rotation:'10'}}"); ++ register(2827, "{Name:'minecraft:white_banner',Properties:{rotation:'11'}}", "{Name:'minecraft:standing_banner',Properties:{rotation:'11'}}"); ++ register(2828, "{Name:'minecraft:white_banner',Properties:{rotation:'12'}}", "{Name:'minecraft:standing_banner',Properties:{rotation:'12'}}"); ++ register(2829, "{Name:'minecraft:white_banner',Properties:{rotation:'13'}}", "{Name:'minecraft:standing_banner',Properties:{rotation:'13'}}"); ++ register(2830, "{Name:'minecraft:white_banner',Properties:{rotation:'14'}}", "{Name:'minecraft:standing_banner',Properties:{rotation:'14'}}"); ++ register(2831, "{Name:'minecraft:white_banner',Properties:{rotation:'15'}}", "{Name:'minecraft:standing_banner',Properties:{rotation:'15'}}"); ++ register(2834, "{Name:'minecraft:white_wall_banner',Properties:{facing:'north'}}", "{Name:'minecraft:wall_banner',Properties:{facing:'north'}}"); ++ register(2835, "{Name:'minecraft:white_wall_banner',Properties:{facing:'south'}}", "{Name:'minecraft:wall_banner',Properties:{facing:'south'}}"); ++ register(2836, "{Name:'minecraft:white_wall_banner',Properties:{facing:'west'}}", "{Name:'minecraft:wall_banner',Properties:{facing:'west'}}"); ++ register(2837, "{Name:'minecraft:white_wall_banner',Properties:{facing:'east'}}", "{Name:'minecraft:wall_banner',Properties:{facing:'east'}}"); ++ register(2848, "{Name:'minecraft:daylight_detector',Properties:{inverted:'true',power:'0'}}", "{Name:'minecraft:daylight_detector_inverted',Properties:{power:'0'}}"); ++ register(2849, "{Name:'minecraft:daylight_detector',Properties:{inverted:'true',power:'1'}}", "{Name:'minecraft:daylight_detector_inverted',Properties:{power:'1'}}"); ++ register(2850, "{Name:'minecraft:daylight_detector',Properties:{inverted:'true',power:'2'}}", "{Name:'minecraft:daylight_detector_inverted',Properties:{power:'2'}}"); ++ register(2851, "{Name:'minecraft:daylight_detector',Properties:{inverted:'true',power:'3'}}", "{Name:'minecraft:daylight_detector_inverted',Properties:{power:'3'}}"); ++ register(2852, "{Name:'minecraft:daylight_detector',Properties:{inverted:'true',power:'4'}}", "{Name:'minecraft:daylight_detector_inverted',Properties:{power:'4'}}"); ++ register(2853, "{Name:'minecraft:daylight_detector',Properties:{inverted:'true',power:'5'}}", "{Name:'minecraft:daylight_detector_inverted',Properties:{power:'5'}}"); ++ register(2854, "{Name:'minecraft:daylight_detector',Properties:{inverted:'true',power:'6'}}", "{Name:'minecraft:daylight_detector_inverted',Properties:{power:'6'}}"); ++ register(2855, "{Name:'minecraft:daylight_detector',Properties:{inverted:'true',power:'7'}}", "{Name:'minecraft:daylight_detector_inverted',Properties:{power:'7'}}"); ++ register(2856, "{Name:'minecraft:daylight_detector',Properties:{inverted:'true',power:'8'}}", "{Name:'minecraft:daylight_detector_inverted',Properties:{power:'8'}}"); ++ register(2857, "{Name:'minecraft:daylight_detector',Properties:{inverted:'true',power:'9'}}", "{Name:'minecraft:daylight_detector_inverted',Properties:{power:'9'}}"); ++ register(2858, "{Name:'minecraft:daylight_detector',Properties:{inverted:'true',power:'10'}}", "{Name:'minecraft:daylight_detector_inverted',Properties:{power:'10'}}"); ++ register(2859, "{Name:'minecraft:daylight_detector',Properties:{inverted:'true',power:'11'}}", "{Name:'minecraft:daylight_detector_inverted',Properties:{power:'11'}}"); ++ register(2860, "{Name:'minecraft:daylight_detector',Properties:{inverted:'true',power:'12'}}", "{Name:'minecraft:daylight_detector_inverted',Properties:{power:'12'}}"); ++ register(2861, "{Name:'minecraft:daylight_detector',Properties:{inverted:'true',power:'13'}}", "{Name:'minecraft:daylight_detector_inverted',Properties:{power:'13'}}"); ++ register(2862, "{Name:'minecraft:daylight_detector',Properties:{inverted:'true',power:'14'}}", "{Name:'minecraft:daylight_detector_inverted',Properties:{power:'14'}}"); ++ register(2863, "{Name:'minecraft:daylight_detector',Properties:{inverted:'true',power:'15'}}", "{Name:'minecraft:daylight_detector_inverted',Properties:{power:'15'}}"); ++ register(2864, "{Name:'minecraft:red_sandstone'}", "{Name:'minecraft:red_sandstone',Properties:{type:'red_sandstone'}}"); ++ register(2865, "{Name:'minecraft:chiseled_red_sandstone'}", "{Name:'minecraft:red_sandstone',Properties:{type:'chiseled_red_sandstone'}}"); ++ register(2866, "{Name:'minecraft:cut_red_sandstone'}", "{Name:'minecraft:red_sandstone',Properties:{type:'smooth_red_sandstone'}}"); ++ register(2880, "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}"); ++ register(2881, "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}"); ++ register(2882, "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}"); ++ register(2883, "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}"); ++ register(2884, "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'east',half:'top',shape:'inner_left'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'east',half:'top',shape:'inner_right'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'east',half:'top',shape:'outer_left'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'east',half:'top',shape:'outer_right'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}"); ++ register(2885, "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'west',half:'top',shape:'inner_left'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'west',half:'top',shape:'inner_right'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'west',half:'top',shape:'outer_left'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'west',half:'top',shape:'outer_right'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}"); ++ register(2886, "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'south',half:'top',shape:'inner_left'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'south',half:'top',shape:'inner_right'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'south',half:'top',shape:'outer_left'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'south',half:'top',shape:'outer_right'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}"); ++ register(2887, "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'north',half:'top',shape:'inner_left'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'north',half:'top',shape:'inner_right'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'north',half:'top',shape:'outer_left'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'north',half:'top',shape:'outer_right'}}", "{Name:'minecraft:red_sandstone_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}"); ++ register(2896, "{Name:'minecraft:red_sandstone_slab',Properties:{type:'double'}}", "{Name:'minecraft:double_stone_slab2',Properties:{seamless:'false',variant:'red_sandstone'}}"); ++ register(2904, "{Name:'minecraft:smooth_red_sandstone'}", "{Name:'minecraft:double_stone_slab2',Properties:{seamless:'true',variant:'red_sandstone'}}"); ++ register(2912, "{Name:'minecraft:red_sandstone_slab',Properties:{type:'bottom'}}", "{Name:'minecraft:stone_slab2',Properties:{half:'bottom',variant:'red_sandstone'}}"); ++ register(2920, "{Name:'minecraft:red_sandstone_slab',Properties:{type:'top'}}", "{Name:'minecraft:stone_slab2',Properties:{half:'top',variant:'red_sandstone'}}"); ++ register(2928, "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'south',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2929, "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'west',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2930, "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'north',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2931, "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'east',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2932, "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'south',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2933, "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'west',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2934, "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'north',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2935, "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'east',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2936, "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'south',in_wall:'true',open:'false',powered:'true'}}"); ++ register(2937, "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'west',in_wall:'true',open:'false',powered:'true'}}"); ++ register(2938, "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'north',in_wall:'true',open:'false',powered:'true'}}"); ++ register(2939, "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'east',in_wall:'true',open:'false',powered:'true'}}"); ++ register(2940, "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'south',in_wall:'true',open:'true',powered:'true'}}"); ++ register(2941, "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'west',in_wall:'true',open:'true',powered:'true'}}"); ++ register(2942, "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'north',in_wall:'true',open:'true',powered:'true'}}"); ++ register(2943, "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:spruce_fence_gate',Properties:{facing:'east',in_wall:'true',open:'true',powered:'true'}}"); ++ register(2944, "{Name:'minecraft:birch_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'south',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2945, "{Name:'minecraft:birch_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'west',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2946, "{Name:'minecraft:birch_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'north',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2947, "{Name:'minecraft:birch_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'east',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2948, "{Name:'minecraft:birch_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'south',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2949, "{Name:'minecraft:birch_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'west',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2950, "{Name:'minecraft:birch_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'north',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2951, "{Name:'minecraft:birch_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'east',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2952, "{Name:'minecraft:birch_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'south',in_wall:'true',open:'false',powered:'true'}}"); ++ register(2953, "{Name:'minecraft:birch_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'west',in_wall:'true',open:'false',powered:'true'}}"); ++ register(2954, "{Name:'minecraft:birch_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'north',in_wall:'true',open:'false',powered:'true'}}"); ++ register(2955, "{Name:'minecraft:birch_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'east',in_wall:'true',open:'false',powered:'true'}}"); ++ register(2956, "{Name:'minecraft:birch_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'south',in_wall:'true',open:'true',powered:'true'}}"); ++ register(2957, "{Name:'minecraft:birch_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'west',in_wall:'true',open:'true',powered:'true'}}"); ++ register(2958, "{Name:'minecraft:birch_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'north',in_wall:'true',open:'true',powered:'true'}}"); ++ register(2959, "{Name:'minecraft:birch_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:birch_fence_gate',Properties:{facing:'east',in_wall:'true',open:'true',powered:'true'}}"); ++ register(2960, "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'south',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2961, "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'west',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2962, "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'north',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2963, "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'east',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2964, "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'south',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2965, "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'west',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2966, "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'north',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2967, "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'east',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2968, "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'south',in_wall:'true',open:'false',powered:'true'}}"); ++ register(2969, "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'west',in_wall:'true',open:'false',powered:'true'}}"); ++ register(2970, "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'north',in_wall:'true',open:'false',powered:'true'}}"); ++ register(2971, "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'east',in_wall:'true',open:'false',powered:'true'}}"); ++ register(2972, "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'south',in_wall:'true',open:'true',powered:'true'}}"); ++ register(2973, "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'west',in_wall:'true',open:'true',powered:'true'}}"); ++ register(2974, "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'north',in_wall:'true',open:'true',powered:'true'}}"); ++ register(2975, "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:jungle_fence_gate',Properties:{facing:'east',in_wall:'true',open:'true',powered:'true'}}"); ++ register(2976, "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'south',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2977, "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'west',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2978, "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'north',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2979, "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'east',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2980, "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'south',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2981, "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'west',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2982, "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'north',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2983, "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'east',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2984, "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'south',in_wall:'true',open:'false',powered:'true'}}"); ++ register(2985, "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'west',in_wall:'true',open:'false',powered:'true'}}"); ++ register(2986, "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'north',in_wall:'true',open:'false',powered:'true'}}"); ++ register(2987, "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'east',in_wall:'true',open:'false',powered:'true'}}"); ++ register(2988, "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'south',in_wall:'true',open:'true',powered:'true'}}"); ++ register(2989, "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'west',in_wall:'true',open:'true',powered:'true'}}"); ++ register(2990, "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'north',in_wall:'true',open:'true',powered:'true'}}"); ++ register(2991, "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:dark_oak_fence_gate',Properties:{facing:'east',in_wall:'true',open:'true',powered:'true'}}"); ++ register(2992, "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'south',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2993, "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'west',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2994, "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'north',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2995, "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'east',in_wall:'true',open:'false',powered:'false'}}"); ++ register(2996, "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'south',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2997, "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'west',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2998, "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'north',in_wall:'true',open:'true',powered:'false'}}"); ++ register(2999, "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'east',in_wall:'true',open:'true',powered:'false'}}"); ++ register(3000, "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'south',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'south',in_wall:'true',open:'false',powered:'true'}}"); ++ register(3001, "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'west',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'west',in_wall:'true',open:'false',powered:'true'}}"); ++ register(3002, "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'north',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'north',in_wall:'true',open:'false',powered:'true'}}"); ++ register(3003, "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'east',in_wall:'false',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'east',in_wall:'true',open:'false',powered:'true'}}"); ++ register(3004, "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'south',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'south',in_wall:'true',open:'true',powered:'true'}}"); ++ register(3005, "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'west',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'west',in_wall:'true',open:'true',powered:'true'}}"); ++ register(3006, "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'north',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'north',in_wall:'true',open:'true',powered:'true'}}"); ++ register(3007, "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'east',in_wall:'false',open:'true',powered:'true'}}", "{Name:'minecraft:acacia_fence_gate',Properties:{facing:'east',in_wall:'true',open:'true',powered:'true'}}"); ++ register(3008, "{Name:'minecraft:spruce_fence',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:spruce_fence',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:spruce_fence',Properties:{east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:spruce_fence',Properties:{east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:spruce_fence',Properties:{east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:spruce_fence',Properties:{east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:spruce_fence',Properties:{east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:spruce_fence',Properties:{east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:spruce_fence',Properties:{east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:spruce_fence',Properties:{east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:spruce_fence',Properties:{east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:spruce_fence',Properties:{east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:spruce_fence',Properties:{east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:spruce_fence',Properties:{east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:spruce_fence',Properties:{east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:spruce_fence',Properties:{east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:spruce_fence',Properties:{east:'true',north:'true',south:'true',west:'true'}}"); ++ register(3024, "{Name:'minecraft:birch_fence',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:birch_fence',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:birch_fence',Properties:{east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:birch_fence',Properties:{east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:birch_fence',Properties:{east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:birch_fence',Properties:{east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:birch_fence',Properties:{east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:birch_fence',Properties:{east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:birch_fence',Properties:{east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:birch_fence',Properties:{east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:birch_fence',Properties:{east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:birch_fence',Properties:{east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:birch_fence',Properties:{east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:birch_fence',Properties:{east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:birch_fence',Properties:{east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:birch_fence',Properties:{east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:birch_fence',Properties:{east:'true',north:'true',south:'true',west:'true'}}"); ++ register(3040, "{Name:'minecraft:jungle_fence',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:jungle_fence',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:jungle_fence',Properties:{east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:jungle_fence',Properties:{east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:jungle_fence',Properties:{east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:jungle_fence',Properties:{east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:jungle_fence',Properties:{east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:jungle_fence',Properties:{east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:jungle_fence',Properties:{east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:jungle_fence',Properties:{east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:jungle_fence',Properties:{east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:jungle_fence',Properties:{east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:jungle_fence',Properties:{east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:jungle_fence',Properties:{east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:jungle_fence',Properties:{east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:jungle_fence',Properties:{east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:jungle_fence',Properties:{east:'true',north:'true',south:'true',west:'true'}}"); ++ register(3056, "{Name:'minecraft:dark_oak_fence',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:dark_oak_fence',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:dark_oak_fence',Properties:{east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:dark_oak_fence',Properties:{east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:dark_oak_fence',Properties:{east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:dark_oak_fence',Properties:{east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:dark_oak_fence',Properties:{east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:dark_oak_fence',Properties:{east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:dark_oak_fence',Properties:{east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:dark_oak_fence',Properties:{east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:dark_oak_fence',Properties:{east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:dark_oak_fence',Properties:{east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:dark_oak_fence',Properties:{east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:dark_oak_fence',Properties:{east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:dark_oak_fence',Properties:{east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:dark_oak_fence',Properties:{east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:dark_oak_fence',Properties:{east:'true',north:'true',south:'true',west:'true'}}"); ++ register(3072, "{Name:'minecraft:acacia_fence',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:acacia_fence',Properties:{east:'false',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:acacia_fence',Properties:{east:'false',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:acacia_fence',Properties:{east:'false',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:acacia_fence',Properties:{east:'false',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:acacia_fence',Properties:{east:'false',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:acacia_fence',Properties:{east:'false',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:acacia_fence',Properties:{east:'false',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:acacia_fence',Properties:{east:'false',north:'true',south:'true',west:'true'}}", "{Name:'minecraft:acacia_fence',Properties:{east:'true',north:'false',south:'false',west:'false'}}", "{Name:'minecraft:acacia_fence',Properties:{east:'true',north:'false',south:'false',west:'true'}}", "{Name:'minecraft:acacia_fence',Properties:{east:'true',north:'false',south:'true',west:'false'}}", "{Name:'minecraft:acacia_fence',Properties:{east:'true',north:'false',south:'true',west:'true'}}", "{Name:'minecraft:acacia_fence',Properties:{east:'true',north:'true',south:'false',west:'false'}}", "{Name:'minecraft:acacia_fence',Properties:{east:'true',north:'true',south:'false',west:'true'}}", "{Name:'minecraft:acacia_fence',Properties:{east:'true',north:'true',south:'true',west:'false'}}", "{Name:'minecraft:acacia_fence',Properties:{east:'true',north:'true',south:'true',west:'true'}}"); ++ register(3088, "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3089, "{Name:'minecraft:spruce_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3090, "{Name:'minecraft:spruce_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3091, "{Name:'minecraft:spruce_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3092, "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3093, "{Name:'minecraft:spruce_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3094, "{Name:'minecraft:spruce_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3095, "{Name:'minecraft:spruce_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3096, "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'true',powered:'false'}}"); ++ register(3097, "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'true',powered:'false'}}"); ++ register(3098, "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'true',powered:'true'}}"); ++ register(3099, "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:spruce_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'true',powered:'true'}}"); ++ register(3104, "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3105, "{Name:'minecraft:birch_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3106, "{Name:'minecraft:birch_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3107, "{Name:'minecraft:birch_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3108, "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3109, "{Name:'minecraft:birch_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3110, "{Name:'minecraft:birch_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3111, "{Name:'minecraft:birch_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3112, "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'true',powered:'false'}}"); ++ register(3113, "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:birch_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'true',powered:'false'}}"); ++ register(3114, "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'true',powered:'true'}}"); ++ register(3115, "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:birch_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'true',powered:'true'}}"); ++ register(3120, "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3121, "{Name:'minecraft:jungle_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3122, "{Name:'minecraft:jungle_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3123, "{Name:'minecraft:jungle_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3124, "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3125, "{Name:'minecraft:jungle_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3126, "{Name:'minecraft:jungle_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3127, "{Name:'minecraft:jungle_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3128, "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'true',powered:'false'}}"); ++ register(3129, "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'true',powered:'false'}}"); ++ register(3130, "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'true',powered:'true'}}"); ++ register(3131, "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:jungle_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'true',powered:'true'}}"); ++ register(3136, "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3137, "{Name:'minecraft:acacia_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3138, "{Name:'minecraft:acacia_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3139, "{Name:'minecraft:acacia_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3140, "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3141, "{Name:'minecraft:acacia_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3142, "{Name:'minecraft:acacia_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3143, "{Name:'minecraft:acacia_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3144, "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'true',powered:'false'}}"); ++ register(3145, "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'true',powered:'false'}}"); ++ register(3146, "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'true',powered:'true'}}"); ++ register(3147, "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:acacia_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'true',powered:'true'}}"); ++ register(3152, "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3153, "{Name:'minecraft:dark_oak_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3154, "{Name:'minecraft:dark_oak_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3155, "{Name:'minecraft:dark_oak_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'false',powered:'true'}}"); ++ register(3156, "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3157, "{Name:'minecraft:dark_oak_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'south',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'south',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3158, "{Name:'minecraft:dark_oak_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'west',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'west',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3159, "{Name:'minecraft:dark_oak_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'north',half:'lower',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'north',half:'lower',hinge:'right',open:'true',powered:'true'}}"); ++ register(3160, "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'true',powered:'false'}}"); ++ register(3161, "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'true',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'false',powered:'false'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'true',powered:'false'}}"); ++ register(3162, "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'north',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'south',half:'upper',hinge:'left',open:'true',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'west',half:'upper',hinge:'left',open:'true',powered:'true'}}"); ++ register(3163, "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'east',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'north',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'south',half:'upper',hinge:'right',open:'true',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'false',powered:'true'}}", "{Name:'minecraft:dark_oak_door',Properties:{facing:'west',half:'upper',hinge:'right',open:'true',powered:'true'}}"); ++ register(3168, "{Name:'minecraft:end_rod',Properties:{facing:'down'}}", "{Name:'minecraft:end_rod',Properties:{facing:'down'}}"); ++ register(3169, "{Name:'minecraft:end_rod',Properties:{facing:'up'}}", "{Name:'minecraft:end_rod',Properties:{facing:'up'}}"); ++ register(3170, "{Name:'minecraft:end_rod',Properties:{facing:'north'}}", "{Name:'minecraft:end_rod',Properties:{facing:'north'}}"); ++ register(3171, "{Name:'minecraft:end_rod',Properties:{facing:'south'}}", "{Name:'minecraft:end_rod',Properties:{facing:'south'}}"); ++ register(3172, "{Name:'minecraft:end_rod',Properties:{facing:'west'}}", "{Name:'minecraft:end_rod',Properties:{facing:'west'}}"); ++ register(3173, "{Name:'minecraft:end_rod',Properties:{facing:'east'}}", "{Name:'minecraft:end_rod',Properties:{facing:'east'}}"); ++ register(3184, "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'false',east:'true',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'false',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'false',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'false',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'false',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'false',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'false',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'false',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'false',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'false',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'false',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'false',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'false',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'false',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'false',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'false',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'false',north:'true',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'true',north:'false',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'true',north:'false',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'true',north:'false',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'true',north:'false',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'true',north:'false',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'true',north:'false',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'true',north:'false',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'true',north:'false',south:'true',up:'true',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'true',north:'true',south:'false',up:'false',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'true',north:'true',south:'false',up:'false',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'true',north:'true',south:'false',up:'true',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'true',north:'true',south:'false',up:'true',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'true',north:'true',south:'true',up:'false',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'true',north:'true',south:'true',up:'false',west:'true'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'true',north:'true',south:'true',up:'true',west:'false'}}", "{Name:'minecraft:chorus_plant',Properties:{down:'true',east:'true',north:'true',south:'true',up:'true',west:'true'}}"); ++ register(3200, "{Name:'minecraft:chorus_flower',Properties:{age:'0'}}", "{Name:'minecraft:chorus_flower',Properties:{age:'0'}}"); ++ register(3201, "{Name:'minecraft:chorus_flower',Properties:{age:'1'}}", "{Name:'minecraft:chorus_flower',Properties:{age:'1'}}"); ++ register(3202, "{Name:'minecraft:chorus_flower',Properties:{age:'2'}}", "{Name:'minecraft:chorus_flower',Properties:{age:'2'}}"); ++ register(3203, "{Name:'minecraft:chorus_flower',Properties:{age:'3'}}", "{Name:'minecraft:chorus_flower',Properties:{age:'3'}}"); ++ register(3204, "{Name:'minecraft:chorus_flower',Properties:{age:'4'}}", "{Name:'minecraft:chorus_flower',Properties:{age:'4'}}"); ++ register(3205, "{Name:'minecraft:chorus_flower',Properties:{age:'5'}}", "{Name:'minecraft:chorus_flower',Properties:{age:'5'}}"); ++ register(3216, "{Name:'minecraft:purpur_block'}", "{Name:'minecraft:purpur_block'}"); ++ register(3232, "{Name:'minecraft:purpur_pillar',Properties:{axis:'y'}}", "{Name:'minecraft:purpur_pillar',Properties:{axis:'y'}}"); ++ register(3236, "{Name:'minecraft:purpur_pillar',Properties:{axis:'x'}}", "{Name:'minecraft:purpur_pillar',Properties:{axis:'x'}}"); ++ register(3240, "{Name:'minecraft:purpur_pillar',Properties:{axis:'z'}}", "{Name:'minecraft:purpur_pillar',Properties:{axis:'z'}}"); ++ register(3248, "{Name:'minecraft:purpur_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'east',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'east',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'east',half:'bottom',shape:'straight'}}"); ++ register(3249, "{Name:'minecraft:purpur_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'west',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'west',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'west',half:'bottom',shape:'straight'}}"); ++ register(3250, "{Name:'minecraft:purpur_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'south',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'south',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'south',half:'bottom',shape:'straight'}}"); ++ register(3251, "{Name:'minecraft:purpur_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_left'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'north',half:'bottom',shape:'inner_right'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_left'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'north',half:'bottom',shape:'outer_right'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'north',half:'bottom',shape:'straight'}}"); ++ register(3252, "{Name:'minecraft:purpur_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'east',half:'top',shape:'inner_left'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'east',half:'top',shape:'inner_right'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'east',half:'top',shape:'outer_left'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'east',half:'top',shape:'outer_right'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'east',half:'top',shape:'straight'}}"); ++ register(3253, "{Name:'minecraft:purpur_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'west',half:'top',shape:'inner_left'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'west',half:'top',shape:'inner_right'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'west',half:'top',shape:'outer_left'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'west',half:'top',shape:'outer_right'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'west',half:'top',shape:'straight'}}"); ++ register(3254, "{Name:'minecraft:purpur_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'south',half:'top',shape:'inner_left'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'south',half:'top',shape:'inner_right'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'south',half:'top',shape:'outer_left'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'south',half:'top',shape:'outer_right'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'south',half:'top',shape:'straight'}}"); ++ register(3255, "{Name:'minecraft:purpur_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'north',half:'top',shape:'inner_left'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'north',half:'top',shape:'inner_right'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'north',half:'top',shape:'outer_left'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'north',half:'top',shape:'outer_right'}}", "{Name:'minecraft:purpur_stairs',Properties:{facing:'north',half:'top',shape:'straight'}}"); ++ register(3264, "{Name:'minecraft:purpur_slab',Properties:{type:'double'}}", "{Name:'minecraft:purpur_double_slab',Properties:{variant:'default'}}"); ++ register(3280, "{Name:'minecraft:purpur_slab',Properties:{type:'bottom'}}", "{Name:'minecraft:purpur_slab',Properties:{half:'bottom',variant:'default'}}"); ++ register(3288, "{Name:'minecraft:purpur_slab',Properties:{type:'top'}}", "{Name:'minecraft:purpur_slab',Properties:{half:'top',variant:'default'}}"); ++ register(3296, "{Name:'minecraft:end_stone_bricks'}", "{Name:'minecraft:end_bricks'}"); ++ register(3312, "{Name:'minecraft:beetroots',Properties:{age:'0'}}", "{Name:'minecraft:beetroots',Properties:{age:'0'}}"); ++ register(3313, "{Name:'minecraft:beetroots',Properties:{age:'1'}}", "{Name:'minecraft:beetroots',Properties:{age:'1'}}"); ++ register(3314, "{Name:'minecraft:beetroots',Properties:{age:'2'}}", "{Name:'minecraft:beetroots',Properties:{age:'2'}}"); ++ register(3315, "{Name:'minecraft:beetroots',Properties:{age:'3'}}", "{Name:'minecraft:beetroots',Properties:{age:'3'}}"); ++ register(3328, "{Name:'minecraft:grass_path'}", "{Name:'minecraft:grass_path'}"); ++ register(3344, "{Name:'minecraft:end_gateway'}", "{Name:'minecraft:end_gateway'}"); ++ register(3360, "{Name:'minecraft:repeating_command_block',Properties:{conditional:'false',facing:'down'}}", "{Name:'minecraft:repeating_command_block',Properties:{conditional:'false',facing:'down'}}"); ++ register(3361, "{Name:'minecraft:repeating_command_block',Properties:{conditional:'false',facing:'up'}}", "{Name:'minecraft:repeating_command_block',Properties:{conditional:'false',facing:'up'}}"); ++ register(3362, "{Name:'minecraft:repeating_command_block',Properties:{conditional:'false',facing:'north'}}", "{Name:'minecraft:repeating_command_block',Properties:{conditional:'false',facing:'north'}}"); ++ register(3363, "{Name:'minecraft:repeating_command_block',Properties:{conditional:'false',facing:'south'}}", "{Name:'minecraft:repeating_command_block',Properties:{conditional:'false',facing:'south'}}"); ++ register(3364, "{Name:'minecraft:repeating_command_block',Properties:{conditional:'false',facing:'west'}}", "{Name:'minecraft:repeating_command_block',Properties:{conditional:'false',facing:'west'}}"); ++ register(3365, "{Name:'minecraft:repeating_command_block',Properties:{conditional:'false',facing:'east'}}", "{Name:'minecraft:repeating_command_block',Properties:{conditional:'false',facing:'east'}}"); ++ register(3368, "{Name:'minecraft:repeating_command_block',Properties:{conditional:'true',facing:'down'}}", "{Name:'minecraft:repeating_command_block',Properties:{conditional:'true',facing:'down'}}"); ++ register(3369, "{Name:'minecraft:repeating_command_block',Properties:{conditional:'true',facing:'up'}}", "{Name:'minecraft:repeating_command_block',Properties:{conditional:'true',facing:'up'}}"); ++ register(3370, "{Name:'minecraft:repeating_command_block',Properties:{conditional:'true',facing:'north'}}", "{Name:'minecraft:repeating_command_block',Properties:{conditional:'true',facing:'north'}}"); ++ register(3371, "{Name:'minecraft:repeating_command_block',Properties:{conditional:'true',facing:'south'}}", "{Name:'minecraft:repeating_command_block',Properties:{conditional:'true',facing:'south'}}"); ++ register(3372, "{Name:'minecraft:repeating_command_block',Properties:{conditional:'true',facing:'west'}}", "{Name:'minecraft:repeating_command_block',Properties:{conditional:'true',facing:'west'}}"); ++ register(3373, "{Name:'minecraft:repeating_command_block',Properties:{conditional:'true',facing:'east'}}", "{Name:'minecraft:repeating_command_block',Properties:{conditional:'true',facing:'east'}}"); ++ register(3376, "{Name:'minecraft:chain_command_block',Properties:{conditional:'false',facing:'down'}}", "{Name:'minecraft:chain_command_block',Properties:{conditional:'false',facing:'down'}}"); ++ register(3377, "{Name:'minecraft:chain_command_block',Properties:{conditional:'false',facing:'up'}}", "{Name:'minecraft:chain_command_block',Properties:{conditional:'false',facing:'up'}}"); ++ register(3378, "{Name:'minecraft:chain_command_block',Properties:{conditional:'false',facing:'north'}}", "{Name:'minecraft:chain_command_block',Properties:{conditional:'false',facing:'north'}}"); ++ register(3379, "{Name:'minecraft:chain_command_block',Properties:{conditional:'false',facing:'south'}}", "{Name:'minecraft:chain_command_block',Properties:{conditional:'false',facing:'south'}}"); ++ register(3380, "{Name:'minecraft:chain_command_block',Properties:{conditional:'false',facing:'west'}}", "{Name:'minecraft:chain_command_block',Properties:{conditional:'false',facing:'west'}}"); ++ register(3381, "{Name:'minecraft:chain_command_block',Properties:{conditional:'false',facing:'east'}}", "{Name:'minecraft:chain_command_block',Properties:{conditional:'false',facing:'east'}}"); ++ register(3384, "{Name:'minecraft:chain_command_block',Properties:{conditional:'true',facing:'down'}}", "{Name:'minecraft:chain_command_block',Properties:{conditional:'true',facing:'down'}}"); ++ register(3385, "{Name:'minecraft:chain_command_block',Properties:{conditional:'true',facing:'up'}}", "{Name:'minecraft:chain_command_block',Properties:{conditional:'true',facing:'up'}}"); ++ register(3386, "{Name:'minecraft:chain_command_block',Properties:{conditional:'true',facing:'north'}}", "{Name:'minecraft:chain_command_block',Properties:{conditional:'true',facing:'north'}}"); ++ register(3387, "{Name:'minecraft:chain_command_block',Properties:{conditional:'true',facing:'south'}}", "{Name:'minecraft:chain_command_block',Properties:{conditional:'true',facing:'south'}}"); ++ register(3388, "{Name:'minecraft:chain_command_block',Properties:{conditional:'true',facing:'west'}}", "{Name:'minecraft:chain_command_block',Properties:{conditional:'true',facing:'west'}}"); ++ register(3389, "{Name:'minecraft:chain_command_block',Properties:{conditional:'true',facing:'east'}}", "{Name:'minecraft:chain_command_block',Properties:{conditional:'true',facing:'east'}}"); ++ register(3392, "{Name:'minecraft:frosted_ice',Properties:{age:'0'}}", "{Name:'minecraft:frosted_ice',Properties:{age:'0'}}"); ++ register(3393, "{Name:'minecraft:frosted_ice',Properties:{age:'1'}}", "{Name:'minecraft:frosted_ice',Properties:{age:'1'}}"); ++ register(3394, "{Name:'minecraft:frosted_ice',Properties:{age:'2'}}", "{Name:'minecraft:frosted_ice',Properties:{age:'2'}}"); ++ register(3395, "{Name:'minecraft:frosted_ice',Properties:{age:'3'}}", "{Name:'minecraft:frosted_ice',Properties:{age:'3'}}"); ++ register(3408, "{Name:'minecraft:magma_block'}", "{Name:'minecraft:magma'}"); ++ register(3424, "{Name:'minecraft:nether_wart_block'}", "{Name:'minecraft:nether_wart_block'}"); ++ register(3440, "{Name:'minecraft:red_nether_bricks'}", "{Name:'minecraft:red_nether_brick'}"); ++ register(3456, "{Name:'minecraft:bone_block',Properties:{axis:'y'}}", "{Name:'minecraft:bone_block',Properties:{axis:'y'}}"); ++ register(3460, "{Name:'minecraft:bone_block',Properties:{axis:'x'}}", "{Name:'minecraft:bone_block',Properties:{axis:'x'}}"); ++ register(3464, "{Name:'minecraft:bone_block',Properties:{axis:'z'}}", "{Name:'minecraft:bone_block',Properties:{axis:'z'}}"); ++ register(3472, "{Name:'minecraft:structure_void'}", "{Name:'minecraft:structure_void'}"); ++ register(3488, "{Name:'minecraft:observer',Properties:{facing:'down',powered:'false'}}", "{Name:'minecraft:observer',Properties:{facing:'down',powered:'false'}}"); ++ register(3489, "{Name:'minecraft:observer',Properties:{facing:'up',powered:'false'}}", "{Name:'minecraft:observer',Properties:{facing:'up',powered:'false'}}"); ++ register(3490, "{Name:'minecraft:observer',Properties:{facing:'north',powered:'false'}}", "{Name:'minecraft:observer',Properties:{facing:'north',powered:'false'}}"); ++ register(3491, "{Name:'minecraft:observer',Properties:{facing:'south',powered:'false'}}", "{Name:'minecraft:observer',Properties:{facing:'south',powered:'false'}}"); ++ register(3492, "{Name:'minecraft:observer',Properties:{facing:'west',powered:'false'}}", "{Name:'minecraft:observer',Properties:{facing:'west',powered:'false'}}"); ++ register(3493, "{Name:'minecraft:observer',Properties:{facing:'east',powered:'false'}}", "{Name:'minecraft:observer',Properties:{facing:'east',powered:'false'}}"); ++ register(3496, "{Name:'minecraft:observer',Properties:{facing:'down',powered:'true'}}", "{Name:'minecraft:observer',Properties:{facing:'down',powered:'true'}}"); ++ register(3497, "{Name:'minecraft:observer',Properties:{facing:'up',powered:'true'}}", "{Name:'minecraft:observer',Properties:{facing:'up',powered:'true'}}"); ++ register(3498, "{Name:'minecraft:observer',Properties:{facing:'north',powered:'true'}}", "{Name:'minecraft:observer',Properties:{facing:'north',powered:'true'}}"); ++ register(3499, "{Name:'minecraft:observer',Properties:{facing:'south',powered:'true'}}", "{Name:'minecraft:observer',Properties:{facing:'south',powered:'true'}}"); ++ register(3500, "{Name:'minecraft:observer',Properties:{facing:'west',powered:'true'}}", "{Name:'minecraft:observer',Properties:{facing:'west',powered:'true'}}"); ++ register(3501, "{Name:'minecraft:observer',Properties:{facing:'east',powered:'true'}}", "{Name:'minecraft:observer',Properties:{facing:'east',powered:'true'}}"); ++ register(3504, "{Name:'minecraft:white_shulker_box',Properties:{facing:'down'}}", "{Name:'minecraft:white_shulker_box',Properties:{facing:'down'}}"); ++ register(3505, "{Name:'minecraft:white_shulker_box',Properties:{facing:'up'}}", "{Name:'minecraft:white_shulker_box',Properties:{facing:'up'}}"); ++ register(3506, "{Name:'minecraft:white_shulker_box',Properties:{facing:'north'}}", "{Name:'minecraft:white_shulker_box',Properties:{facing:'north'}}"); ++ register(3507, "{Name:'minecraft:white_shulker_box',Properties:{facing:'south'}}", "{Name:'minecraft:white_shulker_box',Properties:{facing:'south'}}"); ++ register(3508, "{Name:'minecraft:white_shulker_box',Properties:{facing:'west'}}", "{Name:'minecraft:white_shulker_box',Properties:{facing:'west'}}"); ++ register(3509, "{Name:'minecraft:white_shulker_box',Properties:{facing:'east'}}", "{Name:'minecraft:white_shulker_box',Properties:{facing:'east'}}"); ++ register(3520, "{Name:'minecraft:orange_shulker_box',Properties:{facing:'down'}}", "{Name:'minecraft:orange_shulker_box',Properties:{facing:'down'}}"); ++ register(3521, "{Name:'minecraft:orange_shulker_box',Properties:{facing:'up'}}", "{Name:'minecraft:orange_shulker_box',Properties:{facing:'up'}}"); ++ register(3522, "{Name:'minecraft:orange_shulker_box',Properties:{facing:'north'}}", "{Name:'minecraft:orange_shulker_box',Properties:{facing:'north'}}"); ++ register(3523, "{Name:'minecraft:orange_shulker_box',Properties:{facing:'south'}}", "{Name:'minecraft:orange_shulker_box',Properties:{facing:'south'}}"); ++ register(3524, "{Name:'minecraft:orange_shulker_box',Properties:{facing:'west'}}", "{Name:'minecraft:orange_shulker_box',Properties:{facing:'west'}}"); ++ register(3525, "{Name:'minecraft:orange_shulker_box',Properties:{facing:'east'}}", "{Name:'minecraft:orange_shulker_box',Properties:{facing:'east'}}"); ++ register(3536, "{Name:'minecraft:magenta_shulker_box',Properties:{facing:'down'}}", "{Name:'minecraft:magenta_shulker_box',Properties:{facing:'down'}}"); ++ register(3537, "{Name:'minecraft:magenta_shulker_box',Properties:{facing:'up'}}", "{Name:'minecraft:magenta_shulker_box',Properties:{facing:'up'}}"); ++ register(3538, "{Name:'minecraft:magenta_shulker_box',Properties:{facing:'north'}}", "{Name:'minecraft:magenta_shulker_box',Properties:{facing:'north'}}"); ++ register(3539, "{Name:'minecraft:magenta_shulker_box',Properties:{facing:'south'}}", "{Name:'minecraft:magenta_shulker_box',Properties:{facing:'south'}}"); ++ register(3540, "{Name:'minecraft:magenta_shulker_box',Properties:{facing:'west'}}", "{Name:'minecraft:magenta_shulker_box',Properties:{facing:'west'}}"); ++ register(3541, "{Name:'minecraft:magenta_shulker_box',Properties:{facing:'east'}}", "{Name:'minecraft:magenta_shulker_box',Properties:{facing:'east'}}"); ++ register(3552, "{Name:'minecraft:light_blue_shulker_box',Properties:{facing:'down'}}", "{Name:'minecraft:light_blue_shulker_box',Properties:{facing:'down'}}"); ++ register(3553, "{Name:'minecraft:light_blue_shulker_box',Properties:{facing:'up'}}", "{Name:'minecraft:light_blue_shulker_box',Properties:{facing:'up'}}"); ++ register(3554, "{Name:'minecraft:light_blue_shulker_box',Properties:{facing:'north'}}", "{Name:'minecraft:light_blue_shulker_box',Properties:{facing:'north'}}"); ++ register(3555, "{Name:'minecraft:light_blue_shulker_box',Properties:{facing:'south'}}", "{Name:'minecraft:light_blue_shulker_box',Properties:{facing:'south'}}"); ++ register(3556, "{Name:'minecraft:light_blue_shulker_box',Properties:{facing:'west'}}", "{Name:'minecraft:light_blue_shulker_box',Properties:{facing:'west'}}"); ++ register(3557, "{Name:'minecraft:light_blue_shulker_box',Properties:{facing:'east'}}", "{Name:'minecraft:light_blue_shulker_box',Properties:{facing:'east'}}"); ++ register(3568, "{Name:'minecraft:yellow_shulker_box',Properties:{facing:'down'}}", "{Name:'minecraft:yellow_shulker_box',Properties:{facing:'down'}}"); ++ register(3569, "{Name:'minecraft:yellow_shulker_box',Properties:{facing:'up'}}", "{Name:'minecraft:yellow_shulker_box',Properties:{facing:'up'}}"); ++ register(3570, "{Name:'minecraft:yellow_shulker_box',Properties:{facing:'north'}}", "{Name:'minecraft:yellow_shulker_box',Properties:{facing:'north'}}"); ++ register(3571, "{Name:'minecraft:yellow_shulker_box',Properties:{facing:'south'}}", "{Name:'minecraft:yellow_shulker_box',Properties:{facing:'south'}}"); ++ register(3572, "{Name:'minecraft:yellow_shulker_box',Properties:{facing:'west'}}", "{Name:'minecraft:yellow_shulker_box',Properties:{facing:'west'}}"); ++ register(3573, "{Name:'minecraft:yellow_shulker_box',Properties:{facing:'east'}}", "{Name:'minecraft:yellow_shulker_box',Properties:{facing:'east'}}"); ++ register(3584, "{Name:'minecraft:lime_shulker_box',Properties:{facing:'down'}}", "{Name:'minecraft:lime_shulker_box',Properties:{facing:'down'}}"); ++ register(3585, "{Name:'minecraft:lime_shulker_box',Properties:{facing:'up'}}", "{Name:'minecraft:lime_shulker_box',Properties:{facing:'up'}}"); ++ register(3586, "{Name:'minecraft:lime_shulker_box',Properties:{facing:'north'}}", "{Name:'minecraft:lime_shulker_box',Properties:{facing:'north'}}"); ++ register(3587, "{Name:'minecraft:lime_shulker_box',Properties:{facing:'south'}}", "{Name:'minecraft:lime_shulker_box',Properties:{facing:'south'}}"); ++ register(3588, "{Name:'minecraft:lime_shulker_box',Properties:{facing:'west'}}", "{Name:'minecraft:lime_shulker_box',Properties:{facing:'west'}}"); ++ register(3589, "{Name:'minecraft:lime_shulker_box',Properties:{facing:'east'}}", "{Name:'minecraft:lime_shulker_box',Properties:{facing:'east'}}"); ++ register(3600, "{Name:'minecraft:pink_shulker_box',Properties:{facing:'down'}}", "{Name:'minecraft:pink_shulker_box',Properties:{facing:'down'}}"); ++ register(3601, "{Name:'minecraft:pink_shulker_box',Properties:{facing:'up'}}", "{Name:'minecraft:pink_shulker_box',Properties:{facing:'up'}}"); ++ register(3602, "{Name:'minecraft:pink_shulker_box',Properties:{facing:'north'}}", "{Name:'minecraft:pink_shulker_box',Properties:{facing:'north'}}"); ++ register(3603, "{Name:'minecraft:pink_shulker_box',Properties:{facing:'south'}}", "{Name:'minecraft:pink_shulker_box',Properties:{facing:'south'}}"); ++ register(3604, "{Name:'minecraft:pink_shulker_box',Properties:{facing:'west'}}", "{Name:'minecraft:pink_shulker_box',Properties:{facing:'west'}}"); ++ register(3605, "{Name:'minecraft:pink_shulker_box',Properties:{facing:'east'}}", "{Name:'minecraft:pink_shulker_box',Properties:{facing:'east'}}"); ++ register(3616, "{Name:'minecraft:gray_shulker_box',Properties:{facing:'down'}}", "{Name:'minecraft:gray_shulker_box',Properties:{facing:'down'}}"); ++ register(3617, "{Name:'minecraft:gray_shulker_box',Properties:{facing:'up'}}", "{Name:'minecraft:gray_shulker_box',Properties:{facing:'up'}}"); ++ register(3618, "{Name:'minecraft:gray_shulker_box',Properties:{facing:'north'}}", "{Name:'minecraft:gray_shulker_box',Properties:{facing:'north'}}"); ++ register(3619, "{Name:'minecraft:gray_shulker_box',Properties:{facing:'south'}}", "{Name:'minecraft:gray_shulker_box',Properties:{facing:'south'}}"); ++ register(3620, "{Name:'minecraft:gray_shulker_box',Properties:{facing:'west'}}", "{Name:'minecraft:gray_shulker_box',Properties:{facing:'west'}}"); ++ register(3621, "{Name:'minecraft:gray_shulker_box',Properties:{facing:'east'}}", "{Name:'minecraft:gray_shulker_box',Properties:{facing:'east'}}"); ++ register(3632, "{Name:'minecraft:light_gray_shulker_box',Properties:{facing:'down'}}", "{Name:'minecraft:silver_shulker_box',Properties:{facing:'down'}}"); ++ register(3633, "{Name:'minecraft:light_gray_shulker_box',Properties:{facing:'up'}}", "{Name:'minecraft:silver_shulker_box',Properties:{facing:'up'}}"); ++ register(3634, "{Name:'minecraft:light_gray_shulker_box',Properties:{facing:'north'}}", "{Name:'minecraft:silver_shulker_box',Properties:{facing:'north'}}"); ++ register(3635, "{Name:'minecraft:light_gray_shulker_box',Properties:{facing:'south'}}", "{Name:'minecraft:silver_shulker_box',Properties:{facing:'south'}}"); ++ register(3636, "{Name:'minecraft:light_gray_shulker_box',Properties:{facing:'west'}}", "{Name:'minecraft:silver_shulker_box',Properties:{facing:'west'}}"); ++ register(3637, "{Name:'minecraft:light_gray_shulker_box',Properties:{facing:'east'}}", "{Name:'minecraft:silver_shulker_box',Properties:{facing:'east'}}"); ++ register(3648, "{Name:'minecraft:cyan_shulker_box',Properties:{facing:'down'}}", "{Name:'minecraft:cyan_shulker_box',Properties:{facing:'down'}}"); ++ register(3649, "{Name:'minecraft:cyan_shulker_box',Properties:{facing:'up'}}", "{Name:'minecraft:cyan_shulker_box',Properties:{facing:'up'}}"); ++ register(3650, "{Name:'minecraft:cyan_shulker_box',Properties:{facing:'north'}}", "{Name:'minecraft:cyan_shulker_box',Properties:{facing:'north'}}"); ++ register(3651, "{Name:'minecraft:cyan_shulker_box',Properties:{facing:'south'}}", "{Name:'minecraft:cyan_shulker_box',Properties:{facing:'south'}}"); ++ register(3652, "{Name:'minecraft:cyan_shulker_box',Properties:{facing:'west'}}", "{Name:'minecraft:cyan_shulker_box',Properties:{facing:'west'}}"); ++ register(3653, "{Name:'minecraft:cyan_shulker_box',Properties:{facing:'east'}}", "{Name:'minecraft:cyan_shulker_box',Properties:{facing:'east'}}"); ++ register(3664, "{Name:'minecraft:purple_shulker_box',Properties:{facing:'down'}}", "{Name:'minecraft:purple_shulker_box',Properties:{facing:'down'}}"); ++ register(3665, "{Name:'minecraft:purple_shulker_box',Properties:{facing:'up'}}", "{Name:'minecraft:purple_shulker_box',Properties:{facing:'up'}}"); ++ register(3666, "{Name:'minecraft:purple_shulker_box',Properties:{facing:'north'}}", "{Name:'minecraft:purple_shulker_box',Properties:{facing:'north'}}"); ++ register(3667, "{Name:'minecraft:purple_shulker_box',Properties:{facing:'south'}}", "{Name:'minecraft:purple_shulker_box',Properties:{facing:'south'}}"); ++ register(3668, "{Name:'minecraft:purple_shulker_box',Properties:{facing:'west'}}", "{Name:'minecraft:purple_shulker_box',Properties:{facing:'west'}}"); ++ register(3669, "{Name:'minecraft:purple_shulker_box',Properties:{facing:'east'}}", "{Name:'minecraft:purple_shulker_box',Properties:{facing:'east'}}"); ++ register(3680, "{Name:'minecraft:blue_shulker_box',Properties:{facing:'down'}}", "{Name:'minecraft:blue_shulker_box',Properties:{facing:'down'}}"); ++ register(3681, "{Name:'minecraft:blue_shulker_box',Properties:{facing:'up'}}", "{Name:'minecraft:blue_shulker_box',Properties:{facing:'up'}}"); ++ register(3682, "{Name:'minecraft:blue_shulker_box',Properties:{facing:'north'}}", "{Name:'minecraft:blue_shulker_box',Properties:{facing:'north'}}"); ++ register(3683, "{Name:'minecraft:blue_shulker_box',Properties:{facing:'south'}}", "{Name:'minecraft:blue_shulker_box',Properties:{facing:'south'}}"); ++ register(3684, "{Name:'minecraft:blue_shulker_box',Properties:{facing:'west'}}", "{Name:'minecraft:blue_shulker_box',Properties:{facing:'west'}}"); ++ register(3685, "{Name:'minecraft:blue_shulker_box',Properties:{facing:'east'}}", "{Name:'minecraft:blue_shulker_box',Properties:{facing:'east'}}"); ++ register(3696, "{Name:'minecraft:brown_shulker_box',Properties:{facing:'down'}}", "{Name:'minecraft:brown_shulker_box',Properties:{facing:'down'}}"); ++ register(3697, "{Name:'minecraft:brown_shulker_box',Properties:{facing:'up'}}", "{Name:'minecraft:brown_shulker_box',Properties:{facing:'up'}}"); ++ register(3698, "{Name:'minecraft:brown_shulker_box',Properties:{facing:'north'}}", "{Name:'minecraft:brown_shulker_box',Properties:{facing:'north'}}"); ++ register(3699, "{Name:'minecraft:brown_shulker_box',Properties:{facing:'south'}}", "{Name:'minecraft:brown_shulker_box',Properties:{facing:'south'}}"); ++ register(3700, "{Name:'minecraft:brown_shulker_box',Properties:{facing:'west'}}", "{Name:'minecraft:brown_shulker_box',Properties:{facing:'west'}}"); ++ register(3701, "{Name:'minecraft:brown_shulker_box',Properties:{facing:'east'}}", "{Name:'minecraft:brown_shulker_box',Properties:{facing:'east'}}"); ++ register(3712, "{Name:'minecraft:green_shulker_box',Properties:{facing:'down'}}", "{Name:'minecraft:green_shulker_box',Properties:{facing:'down'}}"); ++ register(3713, "{Name:'minecraft:green_shulker_box',Properties:{facing:'up'}}", "{Name:'minecraft:green_shulker_box',Properties:{facing:'up'}}"); ++ register(3714, "{Name:'minecraft:green_shulker_box',Properties:{facing:'north'}}", "{Name:'minecraft:green_shulker_box',Properties:{facing:'north'}}"); ++ register(3715, "{Name:'minecraft:green_shulker_box',Properties:{facing:'south'}}", "{Name:'minecraft:green_shulker_box',Properties:{facing:'south'}}"); ++ register(3716, "{Name:'minecraft:green_shulker_box',Properties:{facing:'west'}}", "{Name:'minecraft:green_shulker_box',Properties:{facing:'west'}}"); ++ register(3717, "{Name:'minecraft:green_shulker_box',Properties:{facing:'east'}}", "{Name:'minecraft:green_shulker_box',Properties:{facing:'east'}}"); ++ register(3728, "{Name:'minecraft:red_shulker_box',Properties:{facing:'down'}}", "{Name:'minecraft:red_shulker_box',Properties:{facing:'down'}}"); ++ register(3729, "{Name:'minecraft:red_shulker_box',Properties:{facing:'up'}}", "{Name:'minecraft:red_shulker_box',Properties:{facing:'up'}}"); ++ register(3730, "{Name:'minecraft:red_shulker_box',Properties:{facing:'north'}}", "{Name:'minecraft:red_shulker_box',Properties:{facing:'north'}}"); ++ register(3731, "{Name:'minecraft:red_shulker_box',Properties:{facing:'south'}}", "{Name:'minecraft:red_shulker_box',Properties:{facing:'south'}}"); ++ register(3732, "{Name:'minecraft:red_shulker_box',Properties:{facing:'west'}}", "{Name:'minecraft:red_shulker_box',Properties:{facing:'west'}}"); ++ register(3733, "{Name:'minecraft:red_shulker_box',Properties:{facing:'east'}}", "{Name:'minecraft:red_shulker_box',Properties:{facing:'east'}}"); ++ register(3744, "{Name:'minecraft:black_shulker_box',Properties:{facing:'down'}}", "{Name:'minecraft:black_shulker_box',Properties:{facing:'down'}}"); ++ register(3745, "{Name:'minecraft:black_shulker_box',Properties:{facing:'up'}}", "{Name:'minecraft:black_shulker_box',Properties:{facing:'up'}}"); ++ register(3746, "{Name:'minecraft:black_shulker_box',Properties:{facing:'north'}}", "{Name:'minecraft:black_shulker_box',Properties:{facing:'north'}}"); ++ register(3747, "{Name:'minecraft:black_shulker_box',Properties:{facing:'south'}}", "{Name:'minecraft:black_shulker_box',Properties:{facing:'south'}}"); ++ register(3748, "{Name:'minecraft:black_shulker_box',Properties:{facing:'west'}}", "{Name:'minecraft:black_shulker_box',Properties:{facing:'west'}}"); ++ register(3749, "{Name:'minecraft:black_shulker_box',Properties:{facing:'east'}}", "{Name:'minecraft:black_shulker_box',Properties:{facing:'east'}}"); ++ register(3760, "{Name:'minecraft:white_glazed_terracotta',Properties:{facing:'south'}}", "{Name:'minecraft:white_glazed_terracotta',Properties:{facing:'south'}}"); ++ register(3761, "{Name:'minecraft:white_glazed_terracotta',Properties:{facing:'west'}}", "{Name:'minecraft:white_glazed_terracotta',Properties:{facing:'west'}}"); ++ register(3762, "{Name:'minecraft:white_glazed_terracotta',Properties:{facing:'north'}}", "{Name:'minecraft:white_glazed_terracotta',Properties:{facing:'north'}}"); ++ register(3763, "{Name:'minecraft:white_glazed_terracotta',Properties:{facing:'east'}}", "{Name:'minecraft:white_glazed_terracotta',Properties:{facing:'east'}}"); ++ register(3776, "{Name:'minecraft:orange_glazed_terracotta',Properties:{facing:'south'}}", "{Name:'minecraft:orange_glazed_terracotta',Properties:{facing:'south'}}"); ++ register(3777, "{Name:'minecraft:orange_glazed_terracotta',Properties:{facing:'west'}}", "{Name:'minecraft:orange_glazed_terracotta',Properties:{facing:'west'}}"); ++ register(3778, "{Name:'minecraft:orange_glazed_terracotta',Properties:{facing:'north'}}", "{Name:'minecraft:orange_glazed_terracotta',Properties:{facing:'north'}}"); ++ register(3779, "{Name:'minecraft:orange_glazed_terracotta',Properties:{facing:'east'}}", "{Name:'minecraft:orange_glazed_terracotta',Properties:{facing:'east'}}"); ++ register(3792, "{Name:'minecraft:magenta_glazed_terracotta',Properties:{facing:'south'}}", "{Name:'minecraft:magenta_glazed_terracotta',Properties:{facing:'south'}}"); ++ register(3793, "{Name:'minecraft:magenta_glazed_terracotta',Properties:{facing:'west'}}", "{Name:'minecraft:magenta_glazed_terracotta',Properties:{facing:'west'}}"); ++ register(3794, "{Name:'minecraft:magenta_glazed_terracotta',Properties:{facing:'north'}}", "{Name:'minecraft:magenta_glazed_terracotta',Properties:{facing:'north'}}"); ++ register(3795, "{Name:'minecraft:magenta_glazed_terracotta',Properties:{facing:'east'}}", "{Name:'minecraft:magenta_glazed_terracotta',Properties:{facing:'east'}}"); ++ register(3808, "{Name:'minecraft:light_blue_glazed_terracotta',Properties:{facing:'south'}}", "{Name:'minecraft:light_blue_glazed_terracotta',Properties:{facing:'south'}}"); ++ register(3809, "{Name:'minecraft:light_blue_glazed_terracotta',Properties:{facing:'west'}}", "{Name:'minecraft:light_blue_glazed_terracotta',Properties:{facing:'west'}}"); ++ register(3810, "{Name:'minecraft:light_blue_glazed_terracotta',Properties:{facing:'north'}}", "{Name:'minecraft:light_blue_glazed_terracotta',Properties:{facing:'north'}}"); ++ register(3811, "{Name:'minecraft:light_blue_glazed_terracotta',Properties:{facing:'east'}}", "{Name:'minecraft:light_blue_glazed_terracotta',Properties:{facing:'east'}}"); ++ register(3824, "{Name:'minecraft:yellow_glazed_terracotta',Properties:{facing:'south'}}", "{Name:'minecraft:yellow_glazed_terracotta',Properties:{facing:'south'}}"); ++ register(3825, "{Name:'minecraft:yellow_glazed_terracotta',Properties:{facing:'west'}}", "{Name:'minecraft:yellow_glazed_terracotta',Properties:{facing:'west'}}"); ++ register(3826, "{Name:'minecraft:yellow_glazed_terracotta',Properties:{facing:'north'}}", "{Name:'minecraft:yellow_glazed_terracotta',Properties:{facing:'north'}}"); ++ register(3827, "{Name:'minecraft:yellow_glazed_terracotta',Properties:{facing:'east'}}", "{Name:'minecraft:yellow_glazed_terracotta',Properties:{facing:'east'}}"); ++ register(3840, "{Name:'minecraft:lime_glazed_terracotta',Properties:{facing:'south'}}", "{Name:'minecraft:lime_glazed_terracotta',Properties:{facing:'south'}}"); ++ register(3841, "{Name:'minecraft:lime_glazed_terracotta',Properties:{facing:'west'}}", "{Name:'minecraft:lime_glazed_terracotta',Properties:{facing:'west'}}"); ++ register(3842, "{Name:'minecraft:lime_glazed_terracotta',Properties:{facing:'north'}}", "{Name:'minecraft:lime_glazed_terracotta',Properties:{facing:'north'}}"); ++ register(3843, "{Name:'minecraft:lime_glazed_terracotta',Properties:{facing:'east'}}", "{Name:'minecraft:lime_glazed_terracotta',Properties:{facing:'east'}}"); ++ register(3856, "{Name:'minecraft:pink_glazed_terracotta',Properties:{facing:'south'}}", "{Name:'minecraft:pink_glazed_terracotta',Properties:{facing:'south'}}"); ++ register(3857, "{Name:'minecraft:pink_glazed_terracotta',Properties:{facing:'west'}}", "{Name:'minecraft:pink_glazed_terracotta',Properties:{facing:'west'}}"); ++ register(3858, "{Name:'minecraft:pink_glazed_terracotta',Properties:{facing:'north'}}", "{Name:'minecraft:pink_glazed_terracotta',Properties:{facing:'north'}}"); ++ register(3859, "{Name:'minecraft:pink_glazed_terracotta',Properties:{facing:'east'}}", "{Name:'minecraft:pink_glazed_terracotta',Properties:{facing:'east'}}"); ++ register(3872, "{Name:'minecraft:gray_glazed_terracotta',Properties:{facing:'south'}}", "{Name:'minecraft:gray_glazed_terracotta',Properties:{facing:'south'}}"); ++ register(3873, "{Name:'minecraft:gray_glazed_terracotta',Properties:{facing:'west'}}", "{Name:'minecraft:gray_glazed_terracotta',Properties:{facing:'west'}}"); ++ register(3874, "{Name:'minecraft:gray_glazed_terracotta',Properties:{facing:'north'}}", "{Name:'minecraft:gray_glazed_terracotta',Properties:{facing:'north'}}"); ++ register(3875, "{Name:'minecraft:gray_glazed_terracotta',Properties:{facing:'east'}}", "{Name:'minecraft:gray_glazed_terracotta',Properties:{facing:'east'}}"); ++ register(3888, "{Name:'minecraft:light_gray_glazed_terracotta',Properties:{facing:'south'}}", "{Name:'minecraft:silver_glazed_terracotta',Properties:{facing:'south'}}"); ++ register(3889, "{Name:'minecraft:light_gray_glazed_terracotta',Properties:{facing:'west'}}", "{Name:'minecraft:silver_glazed_terracotta',Properties:{facing:'west'}}"); ++ register(3890, "{Name:'minecraft:light_gray_glazed_terracotta',Properties:{facing:'north'}}", "{Name:'minecraft:silver_glazed_terracotta',Properties:{facing:'north'}}"); ++ register(3891, "{Name:'minecraft:light_gray_glazed_terracotta',Properties:{facing:'east'}}", "{Name:'minecraft:silver_glazed_terracotta',Properties:{facing:'east'}}"); ++ register(3904, "{Name:'minecraft:cyan_glazed_terracotta',Properties:{facing:'south'}}", "{Name:'minecraft:cyan_glazed_terracotta',Properties:{facing:'south'}}"); ++ register(3905, "{Name:'minecraft:cyan_glazed_terracotta',Properties:{facing:'west'}}", "{Name:'minecraft:cyan_glazed_terracotta',Properties:{facing:'west'}}"); ++ register(3906, "{Name:'minecraft:cyan_glazed_terracotta',Properties:{facing:'north'}}", "{Name:'minecraft:cyan_glazed_terracotta',Properties:{facing:'north'}}"); ++ register(3907, "{Name:'minecraft:cyan_glazed_terracotta',Properties:{facing:'east'}}", "{Name:'minecraft:cyan_glazed_terracotta',Properties:{facing:'east'}}"); ++ register(3920, "{Name:'minecraft:purple_glazed_terracotta',Properties:{facing:'south'}}", "{Name:'minecraft:purple_glazed_terracotta',Properties:{facing:'south'}}"); ++ register(3921, "{Name:'minecraft:purple_glazed_terracotta',Properties:{facing:'west'}}", "{Name:'minecraft:purple_glazed_terracotta',Properties:{facing:'west'}}"); ++ register(3922, "{Name:'minecraft:purple_glazed_terracotta',Properties:{facing:'north'}}", "{Name:'minecraft:purple_glazed_terracotta',Properties:{facing:'north'}}"); ++ register(3923, "{Name:'minecraft:purple_glazed_terracotta',Properties:{facing:'east'}}", "{Name:'minecraft:purple_glazed_terracotta',Properties:{facing:'east'}}"); ++ register(3936, "{Name:'minecraft:blue_glazed_terracotta',Properties:{facing:'south'}}", "{Name:'minecraft:blue_glazed_terracotta',Properties:{facing:'south'}}"); ++ register(3937, "{Name:'minecraft:blue_glazed_terracotta',Properties:{facing:'west'}}", "{Name:'minecraft:blue_glazed_terracotta',Properties:{facing:'west'}}"); ++ register(3938, "{Name:'minecraft:blue_glazed_terracotta',Properties:{facing:'north'}}", "{Name:'minecraft:blue_glazed_terracotta',Properties:{facing:'north'}}"); ++ register(3939, "{Name:'minecraft:blue_glazed_terracotta',Properties:{facing:'east'}}", "{Name:'minecraft:blue_glazed_terracotta',Properties:{facing:'east'}}"); ++ register(3952, "{Name:'minecraft:brown_glazed_terracotta',Properties:{facing:'south'}}", "{Name:'minecraft:brown_glazed_terracotta',Properties:{facing:'south'}}"); ++ register(3953, "{Name:'minecraft:brown_glazed_terracotta',Properties:{facing:'west'}}", "{Name:'minecraft:brown_glazed_terracotta',Properties:{facing:'west'}}"); ++ register(3954, "{Name:'minecraft:brown_glazed_terracotta',Properties:{facing:'north'}}", "{Name:'minecraft:brown_glazed_terracotta',Properties:{facing:'north'}}"); ++ register(3955, "{Name:'minecraft:brown_glazed_terracotta',Properties:{facing:'east'}}", "{Name:'minecraft:brown_glazed_terracotta',Properties:{facing:'east'}}"); ++ register(3968, "{Name:'minecraft:green_glazed_terracotta',Properties:{facing:'south'}}", "{Name:'minecraft:green_glazed_terracotta',Properties:{facing:'south'}}"); ++ register(3969, "{Name:'minecraft:green_glazed_terracotta',Properties:{facing:'west'}}", "{Name:'minecraft:green_glazed_terracotta',Properties:{facing:'west'}}"); ++ register(3970, "{Name:'minecraft:green_glazed_terracotta',Properties:{facing:'north'}}", "{Name:'minecraft:green_glazed_terracotta',Properties:{facing:'north'}}"); ++ register(3971, "{Name:'minecraft:green_glazed_terracotta',Properties:{facing:'east'}}", "{Name:'minecraft:green_glazed_terracotta',Properties:{facing:'east'}}"); ++ register(3984, "{Name:'minecraft:red_glazed_terracotta',Properties:{facing:'south'}}", "{Name:'minecraft:red_glazed_terracotta',Properties:{facing:'south'}}"); ++ register(3985, "{Name:'minecraft:red_glazed_terracotta',Properties:{facing:'west'}}", "{Name:'minecraft:red_glazed_terracotta',Properties:{facing:'west'}}"); ++ register(3986, "{Name:'minecraft:red_glazed_terracotta',Properties:{facing:'north'}}", "{Name:'minecraft:red_glazed_terracotta',Properties:{facing:'north'}}"); ++ register(3987, "{Name:'minecraft:red_glazed_terracotta',Properties:{facing:'east'}}", "{Name:'minecraft:red_glazed_terracotta',Properties:{facing:'east'}}"); ++ register(4000, "{Name:'minecraft:black_glazed_terracotta',Properties:{facing:'south'}}", "{Name:'minecraft:black_glazed_terracotta',Properties:{facing:'south'}}"); ++ register(4001, "{Name:'minecraft:black_glazed_terracotta',Properties:{facing:'west'}}", "{Name:'minecraft:black_glazed_terracotta',Properties:{facing:'west'}}"); ++ register(4002, "{Name:'minecraft:black_glazed_terracotta',Properties:{facing:'north'}}", "{Name:'minecraft:black_glazed_terracotta',Properties:{facing:'north'}}"); ++ register(4003, "{Name:'minecraft:black_glazed_terracotta',Properties:{facing:'east'}}", "{Name:'minecraft:black_glazed_terracotta',Properties:{facing:'east'}}"); ++ register(4016, "{Name:'minecraft:white_concrete'}", "{Name:'minecraft:concrete',Properties:{color:'white'}}"); ++ register(4017, "{Name:'minecraft:orange_concrete'}", "{Name:'minecraft:concrete',Properties:{color:'orange'}}"); ++ register(4018, "{Name:'minecraft:magenta_concrete'}", "{Name:'minecraft:concrete',Properties:{color:'magenta'}}"); ++ register(4019, "{Name:'minecraft:light_blue_concrete'}", "{Name:'minecraft:concrete',Properties:{color:'light_blue'}}"); ++ register(4020, "{Name:'minecraft:yellow_concrete'}", "{Name:'minecraft:concrete',Properties:{color:'yellow'}}"); ++ register(4021, "{Name:'minecraft:lime_concrete'}", "{Name:'minecraft:concrete',Properties:{color:'lime'}}"); ++ register(4022, "{Name:'minecraft:pink_concrete'}", "{Name:'minecraft:concrete',Properties:{color:'pink'}}"); ++ register(4023, "{Name:'minecraft:gray_concrete'}", "{Name:'minecraft:concrete',Properties:{color:'gray'}}"); ++ register(4024, "{Name:'minecraft:light_gray_concrete'}", "{Name:'minecraft:concrete',Properties:{color:'silver'}}"); ++ register(4025, "{Name:'minecraft:cyan_concrete'}", "{Name:'minecraft:concrete',Properties:{color:'cyan'}}"); ++ register(4026, "{Name:'minecraft:purple_concrete'}", "{Name:'minecraft:concrete',Properties:{color:'purple'}}"); ++ register(4027, "{Name:'minecraft:blue_concrete'}", "{Name:'minecraft:concrete',Properties:{color:'blue'}}"); ++ register(4028, "{Name:'minecraft:brown_concrete'}", "{Name:'minecraft:concrete',Properties:{color:'brown'}}"); ++ register(4029, "{Name:'minecraft:green_concrete'}", "{Name:'minecraft:concrete',Properties:{color:'green'}}"); ++ register(4030, "{Name:'minecraft:red_concrete'}", "{Name:'minecraft:concrete',Properties:{color:'red'}}"); ++ register(4031, "{Name:'minecraft:black_concrete'}", "{Name:'minecraft:concrete',Properties:{color:'black'}}"); ++ register(4032, "{Name:'minecraft:white_concrete_powder'}", "{Name:'minecraft:concrete_powder',Properties:{color:'white'}}"); ++ register(4033, "{Name:'minecraft:orange_concrete_powder'}", "{Name:'minecraft:concrete_powder',Properties:{color:'orange'}}"); ++ register(4034, "{Name:'minecraft:magenta_concrete_powder'}", "{Name:'minecraft:concrete_powder',Properties:{color:'magenta'}}"); ++ register(4035, "{Name:'minecraft:light_blue_concrete_powder'}", "{Name:'minecraft:concrete_powder',Properties:{color:'light_blue'}}"); ++ register(4036, "{Name:'minecraft:yellow_concrete_powder'}", "{Name:'minecraft:concrete_powder',Properties:{color:'yellow'}}"); ++ register(4037, "{Name:'minecraft:lime_concrete_powder'}", "{Name:'minecraft:concrete_powder',Properties:{color:'lime'}}"); ++ register(4038, "{Name:'minecraft:pink_concrete_powder'}", "{Name:'minecraft:concrete_powder',Properties:{color:'pink'}}"); ++ register(4039, "{Name:'minecraft:gray_concrete_powder'}", "{Name:'minecraft:concrete_powder',Properties:{color:'gray'}}"); ++ register(4040, "{Name:'minecraft:light_gray_concrete_powder'}", "{Name:'minecraft:concrete_powder',Properties:{color:'silver'}}"); ++ register(4041, "{Name:'minecraft:cyan_concrete_powder'}", "{Name:'minecraft:concrete_powder',Properties:{color:'cyan'}}"); ++ register(4042, "{Name:'minecraft:purple_concrete_powder'}", "{Name:'minecraft:concrete_powder',Properties:{color:'purple'}}"); ++ register(4043, "{Name:'minecraft:blue_concrete_powder'}", "{Name:'minecraft:concrete_powder',Properties:{color:'blue'}}"); ++ register(4044, "{Name:'minecraft:brown_concrete_powder'}", "{Name:'minecraft:concrete_powder',Properties:{color:'brown'}}"); ++ register(4045, "{Name:'minecraft:green_concrete_powder'}", "{Name:'minecraft:concrete_powder',Properties:{color:'green'}}"); ++ register(4046, "{Name:'minecraft:red_concrete_powder'}", "{Name:'minecraft:concrete_powder',Properties:{color:'red'}}"); ++ register(4047, "{Name:'minecraft:black_concrete_powder'}", "{Name:'minecraft:concrete_powder',Properties:{color:'black'}}"); ++ register(4080, "{Name:'minecraft:structure_block',Properties:{mode:'save'}}", "{Name:'minecraft:structure_block',Properties:{mode:'save'}}"); ++ register(4081, "{Name:'minecraft:structure_block',Properties:{mode:'load'}}", "{Name:'minecraft:structure_block',Properties:{mode:'load'}}"); ++ register(4082, "{Name:'minecraft:structure_block',Properties:{mode:'corner'}}", "{Name:'minecraft:structure_block',Properties:{mode:'corner'}}"); ++ register(4083, "{Name:'minecraft:structure_block',Properties:{mode:'data'}}", "{Name:'minecraft:structure_block',Properties:{mode:'data'}}"); ++ finalizeMaps(); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/helpers/HelperItemNameV102.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/helpers/HelperItemNameV102.java +new file mode 100644 +index 0000000000000000000000000000000000000000..7ce9cf645ccb2dc796b87858915dba1c3efc3d5b +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/helpers/HelperItemNameV102.java +@@ -0,0 +1,566 @@ ++package ca.spottedleaf.dataconverter.minecraft.converters.helpers; ++ ++import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; ++ ++public final class HelperItemNameV102 { ++ ++ // This class is responsible for mapping the id -> string update in itemstacks and potions ++ ++ private static final Int2ObjectOpenHashMap ITEM_NAMES = new Int2ObjectOpenHashMap() { ++ @Override ++ public String put(final int k, final String o) { ++ final String ret = super.put(k, o); ++ ++ if (ret != null) { ++ throw new IllegalStateException("Mapping already exists for " + k + ": prev: " + ret + ", new: " + o); ++ } ++ ++ return ret; ++ } ++ }; ++ ++ static { ++ ITEM_NAMES.put(0, "minecraft:air"); ++ ITEM_NAMES.put(1, "minecraft:stone"); ++ ITEM_NAMES.put(2, "minecraft:grass"); ++ ITEM_NAMES.put(3, "minecraft:dirt"); ++ ITEM_NAMES.put(4, "minecraft:cobblestone"); ++ ITEM_NAMES.put(5, "minecraft:planks"); ++ ITEM_NAMES.put(6, "minecraft:sapling"); ++ ITEM_NAMES.put(7, "minecraft:bedrock"); ++ ITEM_NAMES.put(8, "minecraft:flowing_water"); ++ ITEM_NAMES.put(9, "minecraft:water"); ++ ITEM_NAMES.put(10, "minecraft:flowing_lava"); ++ ITEM_NAMES.put(11, "minecraft:lava"); ++ ITEM_NAMES.put(12, "minecraft:sand"); ++ ITEM_NAMES.put(13, "minecraft:gravel"); ++ ITEM_NAMES.put(14, "minecraft:gold_ore"); ++ ITEM_NAMES.put(15, "minecraft:iron_ore"); ++ ITEM_NAMES.put(16, "minecraft:coal_ore"); ++ ITEM_NAMES.put(17, "minecraft:log"); ++ ITEM_NAMES.put(18, "minecraft:leaves"); ++ ITEM_NAMES.put(19, "minecraft:sponge"); ++ ITEM_NAMES.put(20, "minecraft:glass"); ++ ITEM_NAMES.put(21, "minecraft:lapis_ore"); ++ ITEM_NAMES.put(22, "minecraft:lapis_block"); ++ ITEM_NAMES.put(23, "minecraft:dispenser"); ++ ITEM_NAMES.put(24, "minecraft:sandstone"); ++ ITEM_NAMES.put(25, "minecraft:noteblock"); ++ ITEM_NAMES.put(27, "minecraft:golden_rail"); ++ ITEM_NAMES.put(28, "minecraft:detector_rail"); ++ ITEM_NAMES.put(29, "minecraft:sticky_piston"); ++ ITEM_NAMES.put(30, "minecraft:web"); ++ ITEM_NAMES.put(31, "minecraft:tallgrass"); ++ ITEM_NAMES.put(32, "minecraft:deadbush"); ++ ITEM_NAMES.put(33, "minecraft:piston"); ++ ITEM_NAMES.put(35, "minecraft:wool"); ++ ITEM_NAMES.put(37, "minecraft:yellow_flower"); ++ ITEM_NAMES.put(38, "minecraft:red_flower"); ++ ITEM_NAMES.put(39, "minecraft:brown_mushroom"); ++ ITEM_NAMES.put(40, "minecraft:red_mushroom"); ++ ITEM_NAMES.put(41, "minecraft:gold_block"); ++ ITEM_NAMES.put(42, "minecraft:iron_block"); ++ ITEM_NAMES.put(43, "minecraft:double_stone_slab"); ++ ITEM_NAMES.put(44, "minecraft:stone_slab"); ++ ITEM_NAMES.put(45, "minecraft:brick_block"); ++ ITEM_NAMES.put(46, "minecraft:tnt"); ++ ITEM_NAMES.put(47, "minecraft:bookshelf"); ++ ITEM_NAMES.put(48, "minecraft:mossy_cobblestone"); ++ ITEM_NAMES.put(49, "minecraft:obsidian"); ++ ITEM_NAMES.put(50, "minecraft:torch"); ++ ITEM_NAMES.put(51, "minecraft:fire"); ++ ITEM_NAMES.put(52, "minecraft:mob_spawner"); ++ ITEM_NAMES.put(53, "minecraft:oak_stairs"); ++ ITEM_NAMES.put(54, "minecraft:chest"); ++ ITEM_NAMES.put(56, "minecraft:diamond_ore"); ++ ITEM_NAMES.put(57, "minecraft:diamond_block"); ++ ITEM_NAMES.put(58, "minecraft:crafting_table"); ++ ITEM_NAMES.put(60, "minecraft:farmland"); ++ ITEM_NAMES.put(61, "minecraft:furnace"); ++ ITEM_NAMES.put(62, "minecraft:lit_furnace"); ++ ITEM_NAMES.put(65, "minecraft:ladder"); ++ ITEM_NAMES.put(66, "minecraft:rail"); ++ ITEM_NAMES.put(67, "minecraft:stone_stairs"); ++ ITEM_NAMES.put(69, "minecraft:lever"); ++ ITEM_NAMES.put(70, "minecraft:stone_pressure_plate"); ++ ITEM_NAMES.put(72, "minecraft:wooden_pressure_plate"); ++ ITEM_NAMES.put(73, "minecraft:redstone_ore"); ++ ITEM_NAMES.put(76, "minecraft:redstone_torch"); ++ ITEM_NAMES.put(77, "minecraft:stone_button"); ++ ITEM_NAMES.put(78, "minecraft:snow_layer"); ++ ITEM_NAMES.put(79, "minecraft:ice"); ++ ITEM_NAMES.put(80, "minecraft:snow"); ++ ITEM_NAMES.put(81, "minecraft:cactus"); ++ ITEM_NAMES.put(82, "minecraft:clay"); ++ ITEM_NAMES.put(84, "minecraft:jukebox"); ++ ITEM_NAMES.put(85, "minecraft:fence"); ++ ITEM_NAMES.put(86, "minecraft:pumpkin"); ++ ITEM_NAMES.put(87, "minecraft:netherrack"); ++ ITEM_NAMES.put(88, "minecraft:soul_sand"); ++ ITEM_NAMES.put(89, "minecraft:glowstone"); ++ ITEM_NAMES.put(90, "minecraft:portal"); ++ ITEM_NAMES.put(91, "minecraft:lit_pumpkin"); ++ ITEM_NAMES.put(95, "minecraft:stained_glass"); ++ ITEM_NAMES.put(96, "minecraft:trapdoor"); ++ ITEM_NAMES.put(97, "minecraft:monster_egg"); ++ ITEM_NAMES.put(98, "minecraft:stonebrick"); ++ ITEM_NAMES.put(99, "minecraft:brown_mushroom_block"); ++ ITEM_NAMES.put(100, "minecraft:red_mushroom_block"); ++ ITEM_NAMES.put(101, "minecraft:iron_bars"); ++ ITEM_NAMES.put(102, "minecraft:glass_pane"); ++ ITEM_NAMES.put(103, "minecraft:melon_block"); ++ ITEM_NAMES.put(106, "minecraft:vine"); ++ ITEM_NAMES.put(107, "minecraft:fence_gate"); ++ ITEM_NAMES.put(108, "minecraft:brick_stairs"); ++ ITEM_NAMES.put(109, "minecraft:stone_brick_stairs"); ++ ITEM_NAMES.put(110, "minecraft:mycelium"); ++ ITEM_NAMES.put(111, "minecraft:waterlily"); ++ ITEM_NAMES.put(112, "minecraft:nether_brick"); ++ ITEM_NAMES.put(113, "minecraft:nether_brick_fence"); ++ ITEM_NAMES.put(114, "minecraft:nether_brick_stairs"); ++ ITEM_NAMES.put(116, "minecraft:enchanting_table"); ++ ITEM_NAMES.put(119, "minecraft:end_portal"); ++ ITEM_NAMES.put(120, "minecraft:end_portal_frame"); ++ ITEM_NAMES.put(121, "minecraft:end_stone"); ++ ITEM_NAMES.put(122, "minecraft:dragon_egg"); ++ ITEM_NAMES.put(123, "minecraft:redstone_lamp"); ++ ITEM_NAMES.put(125, "minecraft:double_wooden_slab"); ++ ITEM_NAMES.put(126, "minecraft:wooden_slab"); ++ ITEM_NAMES.put(127, "minecraft:cocoa"); ++ ITEM_NAMES.put(128, "minecraft:sandstone_stairs"); ++ ITEM_NAMES.put(129, "minecraft:emerald_ore"); ++ ITEM_NAMES.put(130, "minecraft:ender_chest"); ++ ITEM_NAMES.put(131, "minecraft:tripwire_hook"); ++ ITEM_NAMES.put(133, "minecraft:emerald_block"); ++ ITEM_NAMES.put(134, "minecraft:spruce_stairs"); ++ ITEM_NAMES.put(135, "minecraft:birch_stairs"); ++ ITEM_NAMES.put(136, "minecraft:jungle_stairs"); ++ ITEM_NAMES.put(137, "minecraft:command_block"); ++ ITEM_NAMES.put(138, "minecraft:beacon"); ++ ITEM_NAMES.put(139, "minecraft:cobblestone_wall"); ++ ITEM_NAMES.put(141, "minecraft:carrots"); ++ ITEM_NAMES.put(142, "minecraft:potatoes"); ++ ITEM_NAMES.put(143, "minecraft:wooden_button"); ++ ITEM_NAMES.put(145, "minecraft:anvil"); ++ ITEM_NAMES.put(146, "minecraft:trapped_chest"); ++ ITEM_NAMES.put(147, "minecraft:light_weighted_pressure_plate"); ++ ITEM_NAMES.put(148, "minecraft:heavy_weighted_pressure_plate"); ++ ITEM_NAMES.put(151, "minecraft:daylight_detector"); ++ ITEM_NAMES.put(152, "minecraft:redstone_block"); ++ ITEM_NAMES.put(153, "minecraft:quartz_ore"); ++ ITEM_NAMES.put(154, "minecraft:hopper"); ++ ITEM_NAMES.put(155, "minecraft:quartz_block"); ++ ITEM_NAMES.put(156, "minecraft:quartz_stairs"); ++ ITEM_NAMES.put(157, "minecraft:activator_rail"); ++ ITEM_NAMES.put(158, "minecraft:dropper"); ++ ITEM_NAMES.put(159, "minecraft:stained_hardened_clay"); ++ ITEM_NAMES.put(160, "minecraft:stained_glass_pane"); ++ ITEM_NAMES.put(161, "minecraft:leaves2"); ++ ITEM_NAMES.put(162, "minecraft:log2"); ++ ITEM_NAMES.put(163, "minecraft:acacia_stairs"); ++ ITEM_NAMES.put(164, "minecraft:dark_oak_stairs"); ++ ITEM_NAMES.put(170, "minecraft:hay_block"); ++ ITEM_NAMES.put(171, "minecraft:carpet"); ++ ITEM_NAMES.put(172, "minecraft:hardened_clay"); ++ ITEM_NAMES.put(173, "minecraft:coal_block"); ++ ITEM_NAMES.put(174, "minecraft:packed_ice"); ++ ITEM_NAMES.put(175, "minecraft:double_plant"); ++ ITEM_NAMES.put(256, "minecraft:iron_shovel"); ++ ITEM_NAMES.put(257, "minecraft:iron_pickaxe"); ++ ITEM_NAMES.put(258, "minecraft:iron_axe"); ++ ITEM_NAMES.put(259, "minecraft:flint_and_steel"); ++ ITEM_NAMES.put(260, "minecraft:apple"); ++ ITEM_NAMES.put(261, "minecraft:bow"); ++ ITEM_NAMES.put(262, "minecraft:arrow"); ++ ITEM_NAMES.put(263, "minecraft:coal"); ++ ITEM_NAMES.put(264, "minecraft:diamond"); ++ ITEM_NAMES.put(265, "minecraft:iron_ingot"); ++ ITEM_NAMES.put(266, "minecraft:gold_ingot"); ++ ITEM_NAMES.put(267, "minecraft:iron_sword"); ++ ITEM_NAMES.put(268, "minecraft:wooden_sword"); ++ ITEM_NAMES.put(269, "minecraft:wooden_shovel"); ++ ITEM_NAMES.put(270, "minecraft:wooden_pickaxe"); ++ ITEM_NAMES.put(271, "minecraft:wooden_axe"); ++ ITEM_NAMES.put(272, "minecraft:stone_sword"); ++ ITEM_NAMES.put(273, "minecraft:stone_shovel"); ++ ITEM_NAMES.put(274, "minecraft:stone_pickaxe"); ++ ITEM_NAMES.put(275, "minecraft:stone_axe"); ++ ITEM_NAMES.put(276, "minecraft:diamond_sword"); ++ ITEM_NAMES.put(277, "minecraft:diamond_shovel"); ++ ITEM_NAMES.put(278, "minecraft:diamond_pickaxe"); ++ ITEM_NAMES.put(279, "minecraft:diamond_axe"); ++ ITEM_NAMES.put(280, "minecraft:stick"); ++ ITEM_NAMES.put(281, "minecraft:bowl"); ++ ITEM_NAMES.put(282, "minecraft:mushroom_stew"); ++ ITEM_NAMES.put(283, "minecraft:golden_sword"); ++ ITEM_NAMES.put(284, "minecraft:golden_shovel"); ++ ITEM_NAMES.put(285, "minecraft:golden_pickaxe"); ++ ITEM_NAMES.put(286, "minecraft:golden_axe"); ++ ITEM_NAMES.put(287, "minecraft:string"); ++ ITEM_NAMES.put(288, "minecraft:feather"); ++ ITEM_NAMES.put(289, "minecraft:gunpowder"); ++ ITEM_NAMES.put(290, "minecraft:wooden_hoe"); ++ ITEM_NAMES.put(291, "minecraft:stone_hoe"); ++ ITEM_NAMES.put(292, "minecraft:iron_hoe"); ++ ITEM_NAMES.put(293, "minecraft:diamond_hoe"); ++ ITEM_NAMES.put(294, "minecraft:golden_hoe"); ++ ITEM_NAMES.put(295, "minecraft:wheat_seeds"); ++ ITEM_NAMES.put(296, "minecraft:wheat"); ++ ITEM_NAMES.put(297, "minecraft:bread"); ++ ITEM_NAMES.put(298, "minecraft:leather_helmet"); ++ ITEM_NAMES.put(299, "minecraft:leather_chestplate"); ++ ITEM_NAMES.put(300, "minecraft:leather_leggings"); ++ ITEM_NAMES.put(301, "minecraft:leather_boots"); ++ ITEM_NAMES.put(302, "minecraft:chainmail_helmet"); ++ ITEM_NAMES.put(303, "minecraft:chainmail_chestplate"); ++ ITEM_NAMES.put(304, "minecraft:chainmail_leggings"); ++ ITEM_NAMES.put(305, "minecraft:chainmail_boots"); ++ ITEM_NAMES.put(306, "minecraft:iron_helmet"); ++ ITEM_NAMES.put(307, "minecraft:iron_chestplate"); ++ ITEM_NAMES.put(308, "minecraft:iron_leggings"); ++ ITEM_NAMES.put(309, "minecraft:iron_boots"); ++ ITEM_NAMES.put(310, "minecraft:diamond_helmet"); ++ ITEM_NAMES.put(311, "minecraft:diamond_chestplate"); ++ ITEM_NAMES.put(312, "minecraft:diamond_leggings"); ++ ITEM_NAMES.put(313, "minecraft:diamond_boots"); ++ ITEM_NAMES.put(314, "minecraft:golden_helmet"); ++ ITEM_NAMES.put(315, "minecraft:golden_chestplate"); ++ ITEM_NAMES.put(316, "minecraft:golden_leggings"); ++ ITEM_NAMES.put(317, "minecraft:golden_boots"); ++ ITEM_NAMES.put(318, "minecraft:flint"); ++ ITEM_NAMES.put(319, "minecraft:porkchop"); ++ ITEM_NAMES.put(320, "minecraft:cooked_porkchop"); ++ ITEM_NAMES.put(321, "minecraft:painting"); ++ ITEM_NAMES.put(322, "minecraft:golden_apple"); ++ ITEM_NAMES.put(323, "minecraft:sign"); ++ ITEM_NAMES.put(324, "minecraft:wooden_door"); ++ ITEM_NAMES.put(325, "minecraft:bucket"); ++ ITEM_NAMES.put(326, "minecraft:water_bucket"); ++ ITEM_NAMES.put(327, "minecraft:lava_bucket"); ++ ITEM_NAMES.put(328, "minecraft:minecart"); ++ ITEM_NAMES.put(329, "minecraft:saddle"); ++ ITEM_NAMES.put(330, "minecraft:iron_door"); ++ ITEM_NAMES.put(331, "minecraft:redstone"); ++ ITEM_NAMES.put(332, "minecraft:snowball"); ++ ITEM_NAMES.put(333, "minecraft:boat"); ++ ITEM_NAMES.put(334, "minecraft:leather"); ++ ITEM_NAMES.put(335, "minecraft:milk_bucket"); ++ ITEM_NAMES.put(336, "minecraft:brick"); ++ ITEM_NAMES.put(337, "minecraft:clay_ball"); ++ ITEM_NAMES.put(338, "minecraft:reeds"); ++ ITEM_NAMES.put(339, "minecraft:paper"); ++ ITEM_NAMES.put(340, "minecraft:book"); ++ ITEM_NAMES.put(341, "minecraft:slime_ball"); ++ ITEM_NAMES.put(342, "minecraft:chest_minecart"); ++ ITEM_NAMES.put(343, "minecraft:furnace_minecart"); ++ ITEM_NAMES.put(344, "minecraft:egg"); ++ ITEM_NAMES.put(345, "minecraft:compass"); ++ ITEM_NAMES.put(346, "minecraft:fishing_rod"); ++ ITEM_NAMES.put(347, "minecraft:clock"); ++ ITEM_NAMES.put(348, "minecraft:glowstone_dust"); ++ ITEM_NAMES.put(349, "minecraft:fish"); ++ ITEM_NAMES.put(350, "minecraft:cooked_fish"); // Fix typo, the game never recognized cooked_fished ++ ITEM_NAMES.put(351, "minecraft:dye"); ++ ITEM_NAMES.put(352, "minecraft:bone"); ++ ITEM_NAMES.put(353, "minecraft:sugar"); ++ ITEM_NAMES.put(354, "minecraft:cake"); ++ ITEM_NAMES.put(355, "minecraft:bed"); ++ ITEM_NAMES.put(356, "minecraft:repeater"); ++ ITEM_NAMES.put(357, "minecraft:cookie"); ++ ITEM_NAMES.put(358, "minecraft:filled_map"); ++ ITEM_NAMES.put(359, "minecraft:shears"); ++ ITEM_NAMES.put(360, "minecraft:melon"); ++ ITEM_NAMES.put(361, "minecraft:pumpkin_seeds"); ++ ITEM_NAMES.put(362, "minecraft:melon_seeds"); ++ ITEM_NAMES.put(363, "minecraft:beef"); ++ ITEM_NAMES.put(364, "minecraft:cooked_beef"); ++ ITEM_NAMES.put(365, "minecraft:chicken"); ++ ITEM_NAMES.put(366, "minecraft:cooked_chicken"); ++ ITEM_NAMES.put(367, "minecraft:rotten_flesh"); ++ ITEM_NAMES.put(368, "minecraft:ender_pearl"); ++ ITEM_NAMES.put(369, "minecraft:blaze_rod"); ++ ITEM_NAMES.put(370, "minecraft:ghast_tear"); ++ ITEM_NAMES.put(371, "minecraft:gold_nugget"); ++ ITEM_NAMES.put(372, "minecraft:nether_wart"); ++ ITEM_NAMES.put(373, "minecraft:potion"); ++ ITEM_NAMES.put(374, "minecraft:glass_bottle"); ++ ITEM_NAMES.put(375, "minecraft:spider_eye"); ++ ITEM_NAMES.put(376, "minecraft:fermented_spider_eye"); ++ ITEM_NAMES.put(377, "minecraft:blaze_powder"); ++ ITEM_NAMES.put(378, "minecraft:magma_cream"); ++ ITEM_NAMES.put(379, "minecraft:brewing_stand"); ++ ITEM_NAMES.put(380, "minecraft:cauldron"); ++ ITEM_NAMES.put(381, "minecraft:ender_eye"); ++ ITEM_NAMES.put(382, "minecraft:speckled_melon"); ++ ITEM_NAMES.put(383, "minecraft:spawn_egg"); ++ ITEM_NAMES.put(384, "minecraft:experience_bottle"); ++ ITEM_NAMES.put(385, "minecraft:fire_charge"); ++ ITEM_NAMES.put(386, "minecraft:writable_book"); ++ ITEM_NAMES.put(387, "minecraft:written_book"); ++ ITEM_NAMES.put(388, "minecraft:emerald"); ++ ITEM_NAMES.put(389, "minecraft:item_frame"); ++ ITEM_NAMES.put(390, "minecraft:flower_pot"); ++ ITEM_NAMES.put(391, "minecraft:carrot"); ++ ITEM_NAMES.put(392, "minecraft:potato"); ++ ITEM_NAMES.put(393, "minecraft:baked_potato"); ++ ITEM_NAMES.put(394, "minecraft:poisonous_potato"); ++ ITEM_NAMES.put(395, "minecraft:map"); ++ ITEM_NAMES.put(396, "minecraft:golden_carrot"); ++ ITEM_NAMES.put(397, "minecraft:skull"); ++ ITEM_NAMES.put(398, "minecraft:carrot_on_a_stick"); ++ ITEM_NAMES.put(399, "minecraft:nether_star"); ++ ITEM_NAMES.put(400, "minecraft:pumpkin_pie"); ++ ITEM_NAMES.put(401, "minecraft:fireworks"); ++ ITEM_NAMES.put(402, "minecraft:firework_charge"); ++ ITEM_NAMES.put(403, "minecraft:enchanted_book"); ++ ITEM_NAMES.put(404, "minecraft:comparator"); ++ ITEM_NAMES.put(405, "minecraft:netherbrick"); ++ ITEM_NAMES.put(406, "minecraft:quartz"); ++ ITEM_NAMES.put(407, "minecraft:tnt_minecart"); ++ ITEM_NAMES.put(408, "minecraft:hopper_minecart"); ++ ITEM_NAMES.put(417, "minecraft:iron_horse_armor"); ++ ITEM_NAMES.put(418, "minecraft:golden_horse_armor"); ++ ITEM_NAMES.put(419, "minecraft:diamond_horse_armor"); ++ ITEM_NAMES.put(420, "minecraft:lead"); ++ ITEM_NAMES.put(421, "minecraft:name_tag"); ++ ITEM_NAMES.put(422, "minecraft:command_block_minecart"); ++ ITEM_NAMES.put(2256, "minecraft:record_13"); ++ ITEM_NAMES.put(2257, "minecraft:record_cat"); ++ ITEM_NAMES.put(2258, "minecraft:record_blocks"); ++ ITEM_NAMES.put(2259, "minecraft:record_chirp"); ++ ITEM_NAMES.put(2260, "minecraft:record_far"); ++ ITEM_NAMES.put(2261, "minecraft:record_mall"); ++ ITEM_NAMES.put(2262, "minecraft:record_mellohi"); ++ ITEM_NAMES.put(2263, "minecraft:record_stal"); ++ ITEM_NAMES.put(2264, "minecraft:record_strad"); ++ ITEM_NAMES.put(2265, "minecraft:record_ward"); ++ ITEM_NAMES.put(2266, "minecraft:record_11"); ++ ITEM_NAMES.put(2267, "minecraft:record_wait"); ++ // https://github.com/starlis/empirecraft/commit/2da59d1901407fc0c135ef0458c0fe9b016570b3 ++ // It's likely that this is a result of old CB/Spigot behavior still writing ids into items as ints. ++ // These ids do not appear to be used by regular MC anyways, so I do not see the harm of porting it here. ++ // Extras can be added if needed ++ String[] extra = new String[4_000]; ++ // EMC start ++ extra[409] = "minecraft:prismarine_shard"; ++ extra[410] = "minecraft:prismarine_crystals"; ++ extra[411] = "minecraft:rabbit"; ++ extra[412] = "minecraft:cooked_rabbit"; ++ extra[413] = "minecraft:rabbit_stew"; ++ extra[414] = "minecraft:rabbit_foot"; ++ extra[415] = "minecraft:rabbit_hide"; ++ extra[416] = "minecraft:armor_stand"; ++ extra[423] = "minecraft:mutton"; ++ extra[424] = "minecraft:cooked_mutton"; ++ extra[425] = "minecraft:banner"; ++ extra[426] = "minecraft:end_crystal"; ++ extra[427] = "minecraft:spruce_door"; ++ extra[428] = "minecraft:birch_door"; ++ extra[429] = "minecraft:jungle_door"; ++ extra[430] = "minecraft:acacia_door"; ++ extra[431] = "minecraft:dark_oak_door"; ++ extra[432] = "minecraft:chorus_fruit"; ++ extra[433] = "minecraft:chorus_fruit_popped"; ++ extra[434] = "minecraft:beetroot"; ++ extra[435] = "minecraft:beetroot_seeds"; ++ extra[436] = "minecraft:beetroot_soup"; ++ extra[437] = "minecraft:dragon_breath"; ++ extra[438] = "minecraft:splash_potion"; ++ extra[439] = "minecraft:spectral_arrow"; ++ extra[440] = "minecraft:tipped_arrow"; ++ extra[441] = "minecraft:lingering_potion"; ++ extra[442] = "minecraft:shield"; ++ extra[443] = "minecraft:elytra"; ++ extra[444] = "minecraft:spruce_boat"; ++ extra[445] = "minecraft:birch_boat"; ++ extra[446] = "minecraft:jungle_boat"; ++ extra[447] = "minecraft:acacia_boat"; ++ extra[448] = "minecraft:dark_oak_boat"; ++ extra[449] = "minecraft:totem_of_undying"; ++ extra[450] = "minecraft:shulker_shell"; ++ extra[452] = "minecraft:iron_nugget"; ++ extra[453] = "minecraft:knowledge_book"; ++ // EMC end ++ ++ // dump extra into map ++ for (int i = 0; i < extra.length; ++i) { ++ if (extra[i] != null) { ++ ITEM_NAMES.put(i, extra[i]); ++ } ++ } ++ ++ // Add block ids into conversion as well ++ // Very old versions of the game handled them, but it seems 1.8.8 did not parse them at all, so no conversion ++ // was written. ++ // block ids are only skipped (set to AIR) if there is no 1-1 replacement item. ++ ITEM_NAMES.put(26, "minecraft:bed"); // bed block ++ ITEM_NAMES.put(34, ITEM_NAMES.get(0)); // skip (piston head block) ++ ITEM_NAMES.put(55, "minecraft:redstone"); // redstone wire block ++ ITEM_NAMES.put(59, ITEM_NAMES.get(0)); // skip (wheat crop block) ++ ITEM_NAMES.put(63, "minecraft:sign"); // standing sign ++ ITEM_NAMES.put(64, "minecraft:wooden_door"); // wooden door block ++ ITEM_NAMES.put(68, "minecraft:sign"); // wall sign ++ ITEM_NAMES.put(71, "minecraft:iron_door"); // iron door block ++ ITEM_NAMES.put(74, "minecraft:redstone_ore"); // lit redstone ore block ++ ITEM_NAMES.put(75, "minecraft:redstone_torch"); // unlit redstone torch ++ ITEM_NAMES.put(83, "minecraft:reeds"); // sugar cane block ++ ITEM_NAMES.put(92, "minecraft:cake"); // cake block ++ ITEM_NAMES.put(93, "minecraft:repeater"); // unpowered repeater block ++ ITEM_NAMES.put(94, "minecraft:repeater"); // powered repeater block ++ ITEM_NAMES.put(104, ITEM_NAMES.get(0)); // skip (pumpkin stem) ++ ITEM_NAMES.put(105, ITEM_NAMES.get(0)); // skip (melon stem) ++ ITEM_NAMES.put(115, "minecraft:nether_wart"); // nether wart block ++ ITEM_NAMES.put(117, "minecraft:brewing_stand"); // brewing stand block ++ ITEM_NAMES.put(118, "minecraft:cauldron"); // cauldron block ++ ITEM_NAMES.put(124, "minecraft:redstone_lamp"); // lit redstone lamp block ++ ITEM_NAMES.put(132, ITEM_NAMES.get(0)); // skip (tripwire wire block) ++ ITEM_NAMES.put(140, "minecraft:flower_pot"); // flower pot block ++ ITEM_NAMES.put(144, "minecraft:skull"); // skull block ++ ITEM_NAMES.put(149, "minecraft:comparator"); // unpowered comparator block ++ ITEM_NAMES.put(150, "minecraft:comparator"); // powered comparator block ++ // there are technically more, but at some point even older versions pre id -> name conversion didn't even load them. ++ // (all I know is 1.7.10 does not load them) ++ // and so given even the vanilla game wouldn't load them, there's no conversion path for them - they were never valid. ++ } ++ ++ private static final String[] POTION_NAMES = new String[128]; ++ static { ++ POTION_NAMES[0] = "minecraft:water"; ++ POTION_NAMES[1] = "minecraft:regeneration"; ++ POTION_NAMES[2] = "minecraft:swiftness"; ++ POTION_NAMES[3] = "minecraft:fire_resistance"; ++ POTION_NAMES[4] = "minecraft:poison"; ++ POTION_NAMES[5] = "minecraft:healing"; ++ POTION_NAMES[6] = "minecraft:night_vision"; ++ POTION_NAMES[7] = null; ++ POTION_NAMES[8] = "minecraft:weakness"; ++ POTION_NAMES[9] = "minecraft:strength"; ++ POTION_NAMES[10] = "minecraft:slowness"; ++ POTION_NAMES[11] = "minecraft:leaping"; ++ POTION_NAMES[12] = "minecraft:harming"; ++ POTION_NAMES[13] = "minecraft:water_breathing"; ++ POTION_NAMES[14] = "minecraft:invisibility"; ++ POTION_NAMES[15] = null; ++ POTION_NAMES[16] = "minecraft:awkward"; ++ POTION_NAMES[17] = "minecraft:regeneration"; ++ POTION_NAMES[18] = "minecraft:swiftness"; ++ POTION_NAMES[19] = "minecraft:fire_resistance"; ++ POTION_NAMES[20] = "minecraft:poison"; ++ POTION_NAMES[21] = "minecraft:healing"; ++ POTION_NAMES[22] = "minecraft:night_vision"; ++ POTION_NAMES[23] = null; ++ POTION_NAMES[24] = "minecraft:weakness"; ++ POTION_NAMES[25] = "minecraft:strength"; ++ POTION_NAMES[26] = "minecraft:slowness"; ++ POTION_NAMES[27] = "minecraft:leaping"; ++ POTION_NAMES[28] = "minecraft:harming"; ++ POTION_NAMES[29] = "minecraft:water_breathing"; ++ POTION_NAMES[30] = "minecraft:invisibility"; ++ POTION_NAMES[31] = null; ++ POTION_NAMES[32] = "minecraft:thick"; ++ POTION_NAMES[33] = "minecraft:strong_regeneration"; ++ POTION_NAMES[34] = "minecraft:strong_swiftness"; ++ POTION_NAMES[35] = "minecraft:fire_resistance"; ++ POTION_NAMES[36] = "minecraft:strong_poison"; ++ POTION_NAMES[37] = "minecraft:strong_healing"; ++ POTION_NAMES[38] = "minecraft:night_vision"; ++ POTION_NAMES[39] = null; ++ POTION_NAMES[40] = "minecraft:weakness"; ++ POTION_NAMES[41] = "minecraft:strong_strength"; ++ POTION_NAMES[42] = "minecraft:slowness"; ++ POTION_NAMES[43] = "minecraft:strong_leaping"; ++ POTION_NAMES[44] = "minecraft:strong_harming"; ++ POTION_NAMES[45] = "minecraft:water_breathing"; ++ POTION_NAMES[46] = "minecraft:invisibility"; ++ POTION_NAMES[47] = null; ++ POTION_NAMES[48] = null; ++ POTION_NAMES[49] = "minecraft:strong_regeneration"; ++ POTION_NAMES[50] = "minecraft:strong_swiftness"; ++ POTION_NAMES[51] = "minecraft:fire_resistance"; ++ POTION_NAMES[52] = "minecraft:strong_poison"; ++ POTION_NAMES[53] = "minecraft:strong_healing"; ++ POTION_NAMES[54] = "minecraft:night_vision"; ++ POTION_NAMES[55] = null; ++ POTION_NAMES[56] = "minecraft:weakness"; ++ POTION_NAMES[57] = "minecraft:strong_strength"; ++ POTION_NAMES[58] = "minecraft:slowness"; ++ POTION_NAMES[59] = "minecraft:strong_leaping"; ++ POTION_NAMES[60] = "minecraft:strong_harming"; ++ POTION_NAMES[61] = "minecraft:water_breathing"; ++ POTION_NAMES[62] = "minecraft:invisibility"; ++ POTION_NAMES[63] = null; ++ POTION_NAMES[64] = "minecraft:mundane"; ++ POTION_NAMES[65] = "minecraft:long_regeneration"; ++ POTION_NAMES[66] = "minecraft:long_swiftness"; ++ POTION_NAMES[67] = "minecraft:long_fire_resistance"; ++ POTION_NAMES[68] = "minecraft:long_poison"; ++ POTION_NAMES[69] = "minecraft:healing"; ++ POTION_NAMES[70] = "minecraft:long_night_vision"; ++ POTION_NAMES[71] = null; ++ POTION_NAMES[72] = "minecraft:long_weakness"; ++ POTION_NAMES[73] = "minecraft:long_strength"; ++ POTION_NAMES[74] = "minecraft:long_slowness"; ++ POTION_NAMES[75] = "minecraft:long_leaping"; ++ POTION_NAMES[76] = "minecraft:harming"; ++ POTION_NAMES[77] = "minecraft:long_water_breathing"; ++ POTION_NAMES[78] = "minecraft:long_invisibility"; ++ POTION_NAMES[79] = null; ++ POTION_NAMES[80] = "minecraft:awkward"; ++ POTION_NAMES[81] = "minecraft:long_regeneration"; ++ POTION_NAMES[82] = "minecraft:long_swiftness"; ++ POTION_NAMES[83] = "minecraft:long_fire_resistance"; ++ POTION_NAMES[84] = "minecraft:long_poison"; ++ POTION_NAMES[85] = "minecraft:healing"; ++ POTION_NAMES[86] = "minecraft:long_night_vision"; ++ POTION_NAMES[87] = null; ++ POTION_NAMES[88] = "minecraft:long_weakness"; ++ POTION_NAMES[89] = "minecraft:long_strength"; ++ POTION_NAMES[90] = "minecraft:long_slowness"; ++ POTION_NAMES[91] = "minecraft:long_leaping"; ++ POTION_NAMES[92] = "minecraft:harming"; ++ POTION_NAMES[93] = "minecraft:long_water_breathing"; ++ POTION_NAMES[94] = "minecraft:long_invisibility"; ++ POTION_NAMES[95] = null; ++ POTION_NAMES[96] = "minecraft:thick"; ++ POTION_NAMES[97] = "minecraft:regeneration"; ++ POTION_NAMES[98] = "minecraft:swiftness"; ++ POTION_NAMES[99] = "minecraft:long_fire_resistance"; ++ POTION_NAMES[100] = "minecraft:poison"; ++ POTION_NAMES[101] = "minecraft:strong_healing"; ++ POTION_NAMES[102] = "minecraft:long_night_vision"; ++ POTION_NAMES[103] = null; ++ POTION_NAMES[104] = "minecraft:long_weakness"; ++ POTION_NAMES[105] = "minecraft:strength"; ++ POTION_NAMES[106] = "minecraft:long_slowness"; ++ POTION_NAMES[107] = "minecraft:leaping"; ++ POTION_NAMES[108] = "minecraft:strong_harming"; ++ POTION_NAMES[109] = "minecraft:long_water_breathing"; ++ POTION_NAMES[110] = "minecraft:long_invisibility"; ++ POTION_NAMES[111] = null; ++ POTION_NAMES[112] = null; ++ POTION_NAMES[113] = "minecraft:regeneration"; ++ POTION_NAMES[114] = "minecraft:swiftness"; ++ POTION_NAMES[115] = "minecraft:long_fire_resistance"; ++ POTION_NAMES[116] = "minecraft:poison"; ++ POTION_NAMES[117] = "minecraft:strong_healing"; ++ POTION_NAMES[118] = "minecraft:long_night_vision"; ++ POTION_NAMES[119] = null; ++ POTION_NAMES[120] = "minecraft:long_weakness"; ++ POTION_NAMES[121] = "minecraft:strength"; ++ POTION_NAMES[122] = "minecraft:long_slowness"; ++ POTION_NAMES[123] = "minecraft:leaping"; ++ POTION_NAMES[124] = "minecraft:strong_harming"; ++ POTION_NAMES[125] = "minecraft:long_water_breathing"; ++ POTION_NAMES[126] = "minecraft:long_invisibility"; ++ POTION_NAMES[127] = null; ++ } ++ ++ // ret is nullable, you are supposed to log when it does not exist, NOT HIDE IT! ++ public static String getNameFromId(final int id) { ++ return ITEM_NAMES.get(id); ++ } ++ ++ public static String getPotionNameFromId(final short id) { ++ return POTION_NAMES[id & 127]; ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/helpers/HelperSpawnEggNameV105.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/helpers/HelperSpawnEggNameV105.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5008c6d28b7f9b730bfaf257a264edcb45c78487 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/helpers/HelperSpawnEggNameV105.java +@@ -0,0 +1,79 @@ ++package ca.spottedleaf.dataconverter.minecraft.converters.helpers; ++ ++public final class HelperSpawnEggNameV105 { ++ ++ private static final String[] ID_TO_STRING = new String[256]; ++ static { ++ ID_TO_STRING[1] = "Item"; ++ ID_TO_STRING[2] = "XPOrb"; ++ ID_TO_STRING[7] = "ThrownEgg"; ++ ID_TO_STRING[8] = "LeashKnot"; ++ ID_TO_STRING[9] = "Painting"; ++ ID_TO_STRING[10] = "Arrow"; ++ ID_TO_STRING[11] = "Snowball"; ++ ID_TO_STRING[12] = "Fireball"; ++ ID_TO_STRING[13] = "SmallFireball"; ++ ID_TO_STRING[14] = "ThrownEnderpearl"; ++ ID_TO_STRING[15] = "EyeOfEnderSignal"; ++ ID_TO_STRING[16] = "ThrownPotion"; ++ ID_TO_STRING[17] = "ThrownExpBottle"; ++ ID_TO_STRING[18] = "ItemFrame"; ++ ID_TO_STRING[19] = "WitherSkull"; ++ ID_TO_STRING[20] = "PrimedTnt"; ++ ID_TO_STRING[21] = "FallingSand"; ++ ID_TO_STRING[22] = "FireworksRocketEntity"; ++ ID_TO_STRING[23] = "TippedArrow"; ++ ID_TO_STRING[24] = "SpectralArrow"; ++ ID_TO_STRING[25] = "ShulkerBullet"; ++ ID_TO_STRING[26] = "DragonFireball"; ++ ID_TO_STRING[30] = "ArmorStand"; ++ ID_TO_STRING[41] = "Boat"; ++ ID_TO_STRING[42] = "MinecartRideable"; ++ ID_TO_STRING[43] = "MinecartChest"; ++ ID_TO_STRING[44] = "MinecartFurnace"; ++ ID_TO_STRING[45] = "MinecartTNT"; ++ ID_TO_STRING[46] = "MinecartHopper"; ++ ID_TO_STRING[47] = "MinecartSpawner"; ++ ID_TO_STRING[40] = "MinecartCommandBlock"; ++ ID_TO_STRING[48] = "Mob"; ++ ID_TO_STRING[49] = "Monster"; ++ ID_TO_STRING[50] = "Creeper"; ++ ID_TO_STRING[51] = "Skeleton"; ++ ID_TO_STRING[52] = "Spider"; ++ ID_TO_STRING[53] = "Giant"; ++ ID_TO_STRING[54] = "Zombie"; ++ ID_TO_STRING[55] = "Slime"; ++ ID_TO_STRING[56] = "Ghast"; ++ ID_TO_STRING[57] = "PigZombie"; ++ ID_TO_STRING[58] = "Enderman"; ++ ID_TO_STRING[59] = "CaveSpider"; ++ ID_TO_STRING[60] = "Silverfish"; ++ ID_TO_STRING[61] = "Blaze"; ++ ID_TO_STRING[62] = "LavaSlime"; ++ ID_TO_STRING[63] = "EnderDragon"; ++ ID_TO_STRING[64] = "WitherBoss"; ++ ID_TO_STRING[65] = "Bat"; ++ ID_TO_STRING[66] = "Witch"; ++ ID_TO_STRING[67] = "Endermite"; ++ ID_TO_STRING[68] = "Guardian"; ++ ID_TO_STRING[69] = "Shulker"; ++ ID_TO_STRING[90] = "Pig"; ++ ID_TO_STRING[91] = "Sheep"; ++ ID_TO_STRING[92] = "Cow"; ++ ID_TO_STRING[93] = "Chicken"; ++ ID_TO_STRING[94] = "Squid"; ++ ID_TO_STRING[95] = "Wolf"; ++ ID_TO_STRING[96] = "MushroomCow"; ++ ID_TO_STRING[97] = "SnowMan"; ++ ID_TO_STRING[98] = "Ozelot"; ++ ID_TO_STRING[99] = "VillagerGolem"; ++ ID_TO_STRING[100] = "EntityHorse"; ++ ID_TO_STRING[101] = "Rabbit"; ++ ID_TO_STRING[120] = "Villager"; ++ ID_TO_STRING[200] = "EnderCrystal"; ++ } ++ ++ public static String getSpawnNameFromId(final short id) { ++ return ID_TO_STRING[id & 255]; ++ } ++} +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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/itemname/ConverterAbstractItemRename.java +@@ -0,0 +1,18 @@ ++package ca.spottedleaf.dataconverter.minecraft.converters.itemname; ++ ++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.ConverterAbstractStringValueTypeRename; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import java.util.function.Function; ++ ++public final class ConverterAbstractItemRename { ++ ++ private ConverterAbstractItemRename() {} ++ ++ public static void register(final int version, final Function renamer) { ++ register(version, 0, renamer); ++ } ++ public static void register(final int version, final int subVersion, final Function renamer) { ++ ConverterAbstractStringValueTypeRename.register(version, subVersion, MCTypeRegistry.ITEM_NAME, renamer); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/itemstack/ConverterFlattenItemStack.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/itemstack/ConverterFlattenItemStack.java +new file mode 100644 +index 0000000000000000000000000000000000000000..48a1fdd4e8a9ba334ff264820b20e5f4224b3ce6 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/itemstack/ConverterFlattenItemStack.java +@@ -0,0 +1,461 @@ ++package ca.spottedleaf.dataconverter.minecraft.converters.itemstack; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.Types; ++import org.apache.logging.log4j.LogManager; ++import org.apache.logging.log4j.Logger; ++ ++import java.util.Arrays; ++import java.util.HashMap; ++import java.util.HashSet; ++import java.util.Map; ++import java.util.Set; ++ ++public final class ConverterFlattenItemStack extends DataConverter, MapType> { ++ ++ private static final Logger LOGGER = LogManager.getLogger(); ++ ++ // Map of "id.damage" -> "flattened id" ++ private static final Map FLATTEN_MAP = new HashMap<>(); ++ static { ++ FLATTEN_MAP.put("minecraft:stone.0", "minecraft:stone"); ++ FLATTEN_MAP.put("minecraft:stone.1", "minecraft:granite"); ++ FLATTEN_MAP.put("minecraft:stone.2", "minecraft:polished_granite"); ++ FLATTEN_MAP.put("minecraft:stone.3", "minecraft:diorite"); ++ FLATTEN_MAP.put("minecraft:stone.4", "minecraft:polished_diorite"); ++ FLATTEN_MAP.put("minecraft:stone.5", "minecraft:andesite"); ++ FLATTEN_MAP.put("minecraft:stone.6", "minecraft:polished_andesite"); ++ FLATTEN_MAP.put("minecraft:dirt.0", "minecraft:dirt"); ++ FLATTEN_MAP.put("minecraft:dirt.1", "minecraft:coarse_dirt"); ++ FLATTEN_MAP.put("minecraft:dirt.2", "minecraft:podzol"); ++ FLATTEN_MAP.put("minecraft:leaves.0", "minecraft:oak_leaves"); ++ FLATTEN_MAP.put("minecraft:leaves.1", "minecraft:spruce_leaves"); ++ FLATTEN_MAP.put("minecraft:leaves.2", "minecraft:birch_leaves"); ++ FLATTEN_MAP.put("minecraft:leaves.3", "minecraft:jungle_leaves"); ++ FLATTEN_MAP.put("minecraft:leaves2.0", "minecraft:acacia_leaves"); ++ FLATTEN_MAP.put("minecraft:leaves2.1", "minecraft:dark_oak_leaves"); ++ FLATTEN_MAP.put("minecraft:log.0", "minecraft:oak_log"); ++ FLATTEN_MAP.put("minecraft:log.1", "minecraft:spruce_log"); ++ FLATTEN_MAP.put("minecraft:log.2", "minecraft:birch_log"); ++ FLATTEN_MAP.put("minecraft:log.3", "minecraft:jungle_log"); ++ FLATTEN_MAP.put("minecraft:log2.0", "minecraft:acacia_log"); ++ FLATTEN_MAP.put("minecraft:log2.1", "minecraft:dark_oak_log"); ++ FLATTEN_MAP.put("minecraft:sapling.0", "minecraft:oak_sapling"); ++ FLATTEN_MAP.put("minecraft:sapling.1", "minecraft:spruce_sapling"); ++ FLATTEN_MAP.put("minecraft:sapling.2", "minecraft:birch_sapling"); ++ FLATTEN_MAP.put("minecraft:sapling.3", "minecraft:jungle_sapling"); ++ FLATTEN_MAP.put("minecraft:sapling.4", "minecraft:acacia_sapling"); ++ FLATTEN_MAP.put("minecraft:sapling.5", "minecraft:dark_oak_sapling"); ++ FLATTEN_MAP.put("minecraft:planks.0", "minecraft:oak_planks"); ++ FLATTEN_MAP.put("minecraft:planks.1", "minecraft:spruce_planks"); ++ FLATTEN_MAP.put("minecraft:planks.2", "minecraft:birch_planks"); ++ FLATTEN_MAP.put("minecraft:planks.3", "minecraft:jungle_planks"); ++ FLATTEN_MAP.put("minecraft:planks.4", "minecraft:acacia_planks"); ++ FLATTEN_MAP.put("minecraft:planks.5", "minecraft:dark_oak_planks"); ++ FLATTEN_MAP.put("minecraft:sand.0", "minecraft:sand"); ++ FLATTEN_MAP.put("minecraft:sand.1", "minecraft:red_sand"); ++ FLATTEN_MAP.put("minecraft:quartz_block.0", "minecraft:quartz_block"); ++ FLATTEN_MAP.put("minecraft:quartz_block.1", "minecraft:chiseled_quartz_block"); ++ FLATTEN_MAP.put("minecraft:quartz_block.2", "minecraft:quartz_pillar"); ++ FLATTEN_MAP.put("minecraft:anvil.0", "minecraft:anvil"); ++ FLATTEN_MAP.put("minecraft:anvil.1", "minecraft:chipped_anvil"); ++ FLATTEN_MAP.put("minecraft:anvil.2", "minecraft:damaged_anvil"); ++ FLATTEN_MAP.put("minecraft:wool.0", "minecraft:white_wool"); ++ FLATTEN_MAP.put("minecraft:wool.1", "minecraft:orange_wool"); ++ FLATTEN_MAP.put("minecraft:wool.2", "minecraft:magenta_wool"); ++ FLATTEN_MAP.put("minecraft:wool.3", "minecraft:light_blue_wool"); ++ FLATTEN_MAP.put("minecraft:wool.4", "minecraft:yellow_wool"); ++ FLATTEN_MAP.put("minecraft:wool.5", "minecraft:lime_wool"); ++ FLATTEN_MAP.put("minecraft:wool.6", "minecraft:pink_wool"); ++ FLATTEN_MAP.put("minecraft:wool.7", "minecraft:gray_wool"); ++ FLATTEN_MAP.put("minecraft:wool.8", "minecraft:light_gray_wool"); ++ FLATTEN_MAP.put("minecraft:wool.9", "minecraft:cyan_wool"); ++ FLATTEN_MAP.put("minecraft:wool.10", "minecraft:purple_wool"); ++ FLATTEN_MAP.put("minecraft:wool.11", "minecraft:blue_wool"); ++ FLATTEN_MAP.put("minecraft:wool.12", "minecraft:brown_wool"); ++ FLATTEN_MAP.put("minecraft:wool.13", "minecraft:green_wool"); ++ FLATTEN_MAP.put("minecraft:wool.14", "minecraft:red_wool"); ++ FLATTEN_MAP.put("minecraft:wool.15", "minecraft:black_wool"); ++ FLATTEN_MAP.put("minecraft:carpet.0", "minecraft:white_carpet"); ++ FLATTEN_MAP.put("minecraft:carpet.1", "minecraft:orange_carpet"); ++ FLATTEN_MAP.put("minecraft:carpet.2", "minecraft:magenta_carpet"); ++ FLATTEN_MAP.put("minecraft:carpet.3", "minecraft:light_blue_carpet"); ++ FLATTEN_MAP.put("minecraft:carpet.4", "minecraft:yellow_carpet"); ++ FLATTEN_MAP.put("minecraft:carpet.5", "minecraft:lime_carpet"); ++ FLATTEN_MAP.put("minecraft:carpet.6", "minecraft:pink_carpet"); ++ FLATTEN_MAP.put("minecraft:carpet.7", "minecraft:gray_carpet"); ++ FLATTEN_MAP.put("minecraft:carpet.8", "minecraft:light_gray_carpet"); ++ FLATTEN_MAP.put("minecraft:carpet.9", "minecraft:cyan_carpet"); ++ FLATTEN_MAP.put("minecraft:carpet.10", "minecraft:purple_carpet"); ++ FLATTEN_MAP.put("minecraft:carpet.11", "minecraft:blue_carpet"); ++ FLATTEN_MAP.put("minecraft:carpet.12", "minecraft:brown_carpet"); ++ FLATTEN_MAP.put("minecraft:carpet.13", "minecraft:green_carpet"); ++ FLATTEN_MAP.put("minecraft:carpet.14", "minecraft:red_carpet"); ++ FLATTEN_MAP.put("minecraft:carpet.15", "minecraft:black_carpet"); ++ FLATTEN_MAP.put("minecraft:hardened_clay.0", "minecraft:terracotta"); ++ FLATTEN_MAP.put("minecraft:stained_hardened_clay.0", "minecraft:white_terracotta"); ++ FLATTEN_MAP.put("minecraft:stained_hardened_clay.1", "minecraft:orange_terracotta"); ++ FLATTEN_MAP.put("minecraft:stained_hardened_clay.2", "minecraft:magenta_terracotta"); ++ FLATTEN_MAP.put("minecraft:stained_hardened_clay.3", "minecraft:light_blue_terracotta"); ++ FLATTEN_MAP.put("minecraft:stained_hardened_clay.4", "minecraft:yellow_terracotta"); ++ FLATTEN_MAP.put("minecraft:stained_hardened_clay.5", "minecraft:lime_terracotta"); ++ FLATTEN_MAP.put("minecraft:stained_hardened_clay.6", "minecraft:pink_terracotta"); ++ FLATTEN_MAP.put("minecraft:stained_hardened_clay.7", "minecraft:gray_terracotta"); ++ FLATTEN_MAP.put("minecraft:stained_hardened_clay.8", "minecraft:light_gray_terracotta"); ++ FLATTEN_MAP.put("minecraft:stained_hardened_clay.9", "minecraft:cyan_terracotta"); ++ FLATTEN_MAP.put("minecraft:stained_hardened_clay.10", "minecraft:purple_terracotta"); ++ FLATTEN_MAP.put("minecraft:stained_hardened_clay.11", "minecraft:blue_terracotta"); ++ FLATTEN_MAP.put("minecraft:stained_hardened_clay.12", "minecraft:brown_terracotta"); ++ FLATTEN_MAP.put("minecraft:stained_hardened_clay.13", "minecraft:green_terracotta"); ++ FLATTEN_MAP.put("minecraft:stained_hardened_clay.14", "minecraft:red_terracotta"); ++ FLATTEN_MAP.put("minecraft:stained_hardened_clay.15", "minecraft:black_terracotta"); ++ FLATTEN_MAP.put("minecraft:silver_glazed_terracotta.0", "minecraft:light_gray_glazed_terracotta"); ++ FLATTEN_MAP.put("minecraft:stained_glass.0", "minecraft:white_stained_glass"); ++ FLATTEN_MAP.put("minecraft:stained_glass.1", "minecraft:orange_stained_glass"); ++ FLATTEN_MAP.put("minecraft:stained_glass.2", "minecraft:magenta_stained_glass"); ++ FLATTEN_MAP.put("minecraft:stained_glass.3", "minecraft:light_blue_stained_glass"); ++ FLATTEN_MAP.put("minecraft:stained_glass.4", "minecraft:yellow_stained_glass"); ++ FLATTEN_MAP.put("minecraft:stained_glass.5", "minecraft:lime_stained_glass"); ++ FLATTEN_MAP.put("minecraft:stained_glass.6", "minecraft:pink_stained_glass"); ++ FLATTEN_MAP.put("minecraft:stained_glass.7", "minecraft:gray_stained_glass"); ++ FLATTEN_MAP.put("minecraft:stained_glass.8", "minecraft:light_gray_stained_glass"); ++ FLATTEN_MAP.put("minecraft:stained_glass.9", "minecraft:cyan_stained_glass"); ++ FLATTEN_MAP.put("minecraft:stained_glass.10", "minecraft:purple_stained_glass"); ++ FLATTEN_MAP.put("minecraft:stained_glass.11", "minecraft:blue_stained_glass"); ++ FLATTEN_MAP.put("minecraft:stained_glass.12", "minecraft:brown_stained_glass"); ++ FLATTEN_MAP.put("minecraft:stained_glass.13", "minecraft:green_stained_glass"); ++ FLATTEN_MAP.put("minecraft:stained_glass.14", "minecraft:red_stained_glass"); ++ FLATTEN_MAP.put("minecraft:stained_glass.15", "minecraft:black_stained_glass"); ++ FLATTEN_MAP.put("minecraft:stained_glass_pane.0", "minecraft:white_stained_glass_pane"); ++ FLATTEN_MAP.put("minecraft:stained_glass_pane.1", "minecraft:orange_stained_glass_pane"); ++ FLATTEN_MAP.put("minecraft:stained_glass_pane.2", "minecraft:magenta_stained_glass_pane"); ++ FLATTEN_MAP.put("minecraft:stained_glass_pane.3", "minecraft:light_blue_stained_glass_pane"); ++ FLATTEN_MAP.put("minecraft:stained_glass_pane.4", "minecraft:yellow_stained_glass_pane"); ++ FLATTEN_MAP.put("minecraft:stained_glass_pane.5", "minecraft:lime_stained_glass_pane"); ++ FLATTEN_MAP.put("minecraft:stained_glass_pane.6", "minecraft:pink_stained_glass_pane"); ++ FLATTEN_MAP.put("minecraft:stained_glass_pane.7", "minecraft:gray_stained_glass_pane"); ++ FLATTEN_MAP.put("minecraft:stained_glass_pane.8", "minecraft:light_gray_stained_glass_pane"); ++ FLATTEN_MAP.put("minecraft:stained_glass_pane.9", "minecraft:cyan_stained_glass_pane"); ++ FLATTEN_MAP.put("minecraft:stained_glass_pane.10", "minecraft:purple_stained_glass_pane"); ++ FLATTEN_MAP.put("minecraft:stained_glass_pane.11", "minecraft:blue_stained_glass_pane"); ++ FLATTEN_MAP.put("minecraft:stained_glass_pane.12", "minecraft:brown_stained_glass_pane"); ++ FLATTEN_MAP.put("minecraft:stained_glass_pane.13", "minecraft:green_stained_glass_pane"); ++ FLATTEN_MAP.put("minecraft:stained_glass_pane.14", "minecraft:red_stained_glass_pane"); ++ FLATTEN_MAP.put("minecraft:stained_glass_pane.15", "minecraft:black_stained_glass_pane"); ++ FLATTEN_MAP.put("minecraft:prismarine.0", "minecraft:prismarine"); ++ FLATTEN_MAP.put("minecraft:prismarine.1", "minecraft:prismarine_bricks"); ++ FLATTEN_MAP.put("minecraft:prismarine.2", "minecraft:dark_prismarine"); ++ FLATTEN_MAP.put("minecraft:concrete.0", "minecraft:white_concrete"); ++ FLATTEN_MAP.put("minecraft:concrete.1", "minecraft:orange_concrete"); ++ FLATTEN_MAP.put("minecraft:concrete.2", "minecraft:magenta_concrete"); ++ FLATTEN_MAP.put("minecraft:concrete.3", "minecraft:light_blue_concrete"); ++ FLATTEN_MAP.put("minecraft:concrete.4", "minecraft:yellow_concrete"); ++ FLATTEN_MAP.put("minecraft:concrete.5", "minecraft:lime_concrete"); ++ FLATTEN_MAP.put("minecraft:concrete.6", "minecraft:pink_concrete"); ++ FLATTEN_MAP.put("minecraft:concrete.7", "minecraft:gray_concrete"); ++ FLATTEN_MAP.put("minecraft:concrete.8", "minecraft:light_gray_concrete"); ++ FLATTEN_MAP.put("minecraft:concrete.9", "minecraft:cyan_concrete"); ++ FLATTEN_MAP.put("minecraft:concrete.10", "minecraft:purple_concrete"); ++ FLATTEN_MAP.put("minecraft:concrete.11", "minecraft:blue_concrete"); ++ FLATTEN_MAP.put("minecraft:concrete.12", "minecraft:brown_concrete"); ++ FLATTEN_MAP.put("minecraft:concrete.13", "minecraft:green_concrete"); ++ FLATTEN_MAP.put("minecraft:concrete.14", "minecraft:red_concrete"); ++ FLATTEN_MAP.put("minecraft:concrete.15", "minecraft:black_concrete"); ++ FLATTEN_MAP.put("minecraft:concrete_powder.0", "minecraft:white_concrete_powder"); ++ FLATTEN_MAP.put("minecraft:concrete_powder.1", "minecraft:orange_concrete_powder"); ++ FLATTEN_MAP.put("minecraft:concrete_powder.2", "minecraft:magenta_concrete_powder"); ++ FLATTEN_MAP.put("minecraft:concrete_powder.3", "minecraft:light_blue_concrete_powder"); ++ FLATTEN_MAP.put("minecraft:concrete_powder.4", "minecraft:yellow_concrete_powder"); ++ FLATTEN_MAP.put("minecraft:concrete_powder.5", "minecraft:lime_concrete_powder"); ++ FLATTEN_MAP.put("minecraft:concrete_powder.6", "minecraft:pink_concrete_powder"); ++ FLATTEN_MAP.put("minecraft:concrete_powder.7", "minecraft:gray_concrete_powder"); ++ FLATTEN_MAP.put("minecraft:concrete_powder.8", "minecraft:light_gray_concrete_powder"); ++ FLATTEN_MAP.put("minecraft:concrete_powder.9", "minecraft:cyan_concrete_powder"); ++ FLATTEN_MAP.put("minecraft:concrete_powder.10", "minecraft:purple_concrete_powder"); ++ FLATTEN_MAP.put("minecraft:concrete_powder.11", "minecraft:blue_concrete_powder"); ++ FLATTEN_MAP.put("minecraft:concrete_powder.12", "minecraft:brown_concrete_powder"); ++ FLATTEN_MAP.put("minecraft:concrete_powder.13", "minecraft:green_concrete_powder"); ++ FLATTEN_MAP.put("minecraft:concrete_powder.14", "minecraft:red_concrete_powder"); ++ FLATTEN_MAP.put("minecraft:concrete_powder.15", "minecraft:black_concrete_powder"); ++ FLATTEN_MAP.put("minecraft:cobblestone_wall.0", "minecraft:cobblestone_wall"); ++ FLATTEN_MAP.put("minecraft:cobblestone_wall.1", "minecraft:mossy_cobblestone_wall"); ++ FLATTEN_MAP.put("minecraft:sandstone.0", "minecraft:sandstone"); ++ FLATTEN_MAP.put("minecraft:sandstone.1", "minecraft:chiseled_sandstone"); ++ FLATTEN_MAP.put("minecraft:sandstone.2", "minecraft:cut_sandstone"); ++ FLATTEN_MAP.put("minecraft:red_sandstone.0", "minecraft:red_sandstone"); ++ FLATTEN_MAP.put("minecraft:red_sandstone.1", "minecraft:chiseled_red_sandstone"); ++ FLATTEN_MAP.put("minecraft:red_sandstone.2", "minecraft:cut_red_sandstone"); ++ FLATTEN_MAP.put("minecraft:stonebrick.0", "minecraft:stone_bricks"); ++ FLATTEN_MAP.put("minecraft:stonebrick.1", "minecraft:mossy_stone_bricks"); ++ FLATTEN_MAP.put("minecraft:stonebrick.2", "minecraft:cracked_stone_bricks"); ++ FLATTEN_MAP.put("minecraft:stonebrick.3", "minecraft:chiseled_stone_bricks"); ++ FLATTEN_MAP.put("minecraft:monster_egg.0", "minecraft:infested_stone"); ++ FLATTEN_MAP.put("minecraft:monster_egg.1", "minecraft:infested_cobblestone"); ++ FLATTEN_MAP.put("minecraft:monster_egg.2", "minecraft:infested_stone_bricks"); ++ FLATTEN_MAP.put("minecraft:monster_egg.3", "minecraft:infested_mossy_stone_bricks"); ++ FLATTEN_MAP.put("minecraft:monster_egg.4", "minecraft:infested_cracked_stone_bricks"); ++ FLATTEN_MAP.put("minecraft:monster_egg.5", "minecraft:infested_chiseled_stone_bricks"); ++ FLATTEN_MAP.put("minecraft:yellow_flower.0", "minecraft:dandelion"); ++ FLATTEN_MAP.put("minecraft:red_flower.0", "minecraft:poppy"); ++ FLATTEN_MAP.put("minecraft:red_flower.1", "minecraft:blue_orchid"); ++ FLATTEN_MAP.put("minecraft:red_flower.2", "minecraft:allium"); ++ FLATTEN_MAP.put("minecraft:red_flower.3", "minecraft:azure_bluet"); ++ FLATTEN_MAP.put("minecraft:red_flower.4", "minecraft:red_tulip"); ++ FLATTEN_MAP.put("minecraft:red_flower.5", "minecraft:orange_tulip"); ++ FLATTEN_MAP.put("minecraft:red_flower.6", "minecraft:white_tulip"); ++ FLATTEN_MAP.put("minecraft:red_flower.7", "minecraft:pink_tulip"); ++ FLATTEN_MAP.put("minecraft:red_flower.8", "minecraft:oxeye_daisy"); ++ FLATTEN_MAP.put("minecraft:double_plant.0", "minecraft:sunflower"); ++ FLATTEN_MAP.put("minecraft:double_plant.1", "minecraft:lilac"); ++ FLATTEN_MAP.put("minecraft:double_plant.2", "minecraft:tall_grass"); ++ FLATTEN_MAP.put("minecraft:double_plant.3", "minecraft:large_fern"); ++ FLATTEN_MAP.put("minecraft:double_plant.4", "minecraft:rose_bush"); ++ FLATTEN_MAP.put("minecraft:double_plant.5", "minecraft:peony"); ++ FLATTEN_MAP.put("minecraft:deadbush.0", "minecraft:dead_bush"); ++ FLATTEN_MAP.put("minecraft:tallgrass.0", "minecraft:dead_bush"); ++ FLATTEN_MAP.put("minecraft:tallgrass.1", "minecraft:grass"); ++ FLATTEN_MAP.put("minecraft:tallgrass.2", "minecraft:fern"); ++ FLATTEN_MAP.put("minecraft:sponge.0", "minecraft:sponge"); ++ FLATTEN_MAP.put("minecraft:sponge.1", "minecraft:wet_sponge"); ++ FLATTEN_MAP.put("minecraft:purpur_slab.0", "minecraft:purpur_slab"); ++ FLATTEN_MAP.put("minecraft:stone_slab.0", "minecraft:stone_slab"); ++ FLATTEN_MAP.put("minecraft:stone_slab.1", "minecraft:sandstone_slab"); ++ FLATTEN_MAP.put("minecraft:stone_slab.2", "minecraft:petrified_oak_slab"); ++ FLATTEN_MAP.put("minecraft:stone_slab.3", "minecraft:cobblestone_slab"); ++ FLATTEN_MAP.put("minecraft:stone_slab.4", "minecraft:brick_slab"); ++ FLATTEN_MAP.put("minecraft:stone_slab.5", "minecraft:stone_brick_slab"); ++ FLATTEN_MAP.put("minecraft:stone_slab.6", "minecraft:nether_brick_slab"); ++ FLATTEN_MAP.put("minecraft:stone_slab.7", "minecraft:quartz_slab"); ++ FLATTEN_MAP.put("minecraft:stone_slab2.0", "minecraft:red_sandstone_slab"); ++ FLATTEN_MAP.put("minecraft:wooden_slab.0", "minecraft:oak_slab"); ++ FLATTEN_MAP.put("minecraft:wooden_slab.1", "minecraft:spruce_slab"); ++ FLATTEN_MAP.put("minecraft:wooden_slab.2", "minecraft:birch_slab"); ++ FLATTEN_MAP.put("minecraft:wooden_slab.3", "minecraft:jungle_slab"); ++ FLATTEN_MAP.put("minecraft:wooden_slab.4", "minecraft:acacia_slab"); ++ FLATTEN_MAP.put("minecraft:wooden_slab.5", "minecraft:dark_oak_slab"); ++ FLATTEN_MAP.put("minecraft:coal.0", "minecraft:coal"); ++ FLATTEN_MAP.put("minecraft:coal.1", "minecraft:charcoal"); ++ FLATTEN_MAP.put("minecraft:fish.0", "minecraft:cod"); ++ FLATTEN_MAP.put("minecraft:fish.1", "minecraft:salmon"); ++ FLATTEN_MAP.put("minecraft:fish.2", "minecraft:clownfish"); ++ FLATTEN_MAP.put("minecraft:fish.3", "minecraft:pufferfish"); ++ FLATTEN_MAP.put("minecraft:cooked_fish.0", "minecraft:cooked_cod"); ++ FLATTEN_MAP.put("minecraft:cooked_fish.1", "minecraft:cooked_salmon"); ++ FLATTEN_MAP.put("minecraft:skull.0", "minecraft:skeleton_skull"); ++ FLATTEN_MAP.put("minecraft:skull.1", "minecraft:wither_skeleton_skull"); ++ FLATTEN_MAP.put("minecraft:skull.2", "minecraft:zombie_head"); ++ FLATTEN_MAP.put("minecraft:skull.3", "minecraft:player_head"); ++ FLATTEN_MAP.put("minecraft:skull.4", "minecraft:creeper_head"); ++ FLATTEN_MAP.put("minecraft:skull.5", "minecraft:dragon_head"); ++ FLATTEN_MAP.put("minecraft:golden_apple.0", "minecraft:golden_apple"); ++ FLATTEN_MAP.put("minecraft:golden_apple.1", "minecraft:enchanted_golden_apple"); ++ FLATTEN_MAP.put("minecraft:fireworks.0", "minecraft:firework_rocket"); ++ FLATTEN_MAP.put("minecraft:firework_charge.0", "minecraft:firework_star"); ++ FLATTEN_MAP.put("minecraft:dye.0", "minecraft:ink_sac"); ++ FLATTEN_MAP.put("minecraft:dye.1", "minecraft:rose_red"); ++ FLATTEN_MAP.put("minecraft:dye.2", "minecraft:cactus_green"); ++ FLATTEN_MAP.put("minecraft:dye.3", "minecraft:cocoa_beans"); ++ FLATTEN_MAP.put("minecraft:dye.4", "minecraft:lapis_lazuli"); ++ FLATTEN_MAP.put("minecraft:dye.5", "minecraft:purple_dye"); ++ FLATTEN_MAP.put("minecraft:dye.6", "minecraft:cyan_dye"); ++ FLATTEN_MAP.put("minecraft:dye.7", "minecraft:light_gray_dye"); ++ FLATTEN_MAP.put("minecraft:dye.8", "minecraft:gray_dye"); ++ FLATTEN_MAP.put("minecraft:dye.9", "minecraft:pink_dye"); ++ FLATTEN_MAP.put("minecraft:dye.10", "minecraft:lime_dye"); ++ FLATTEN_MAP.put("minecraft:dye.11", "minecraft:dandelion_yellow"); ++ FLATTEN_MAP.put("minecraft:dye.12", "minecraft:light_blue_dye"); ++ FLATTEN_MAP.put("minecraft:dye.13", "minecraft:magenta_dye"); ++ FLATTEN_MAP.put("minecraft:dye.14", "minecraft:orange_dye"); ++ FLATTEN_MAP.put("minecraft:dye.15", "minecraft:bone_meal"); ++ FLATTEN_MAP.put("minecraft:silver_shulker_box.0", "minecraft:light_gray_shulker_box"); ++ FLATTEN_MAP.put("minecraft:fence.0", "minecraft:oak_fence"); ++ FLATTEN_MAP.put("minecraft:fence_gate.0", "minecraft:oak_fence_gate"); ++ FLATTEN_MAP.put("minecraft:wooden_door.0", "minecraft:oak_door"); ++ FLATTEN_MAP.put("minecraft:boat.0", "minecraft:oak_boat"); ++ FLATTEN_MAP.put("minecraft:lit_pumpkin.0", "minecraft:jack_o_lantern"); ++ FLATTEN_MAP.put("minecraft:pumpkin.0", "minecraft:carved_pumpkin"); ++ FLATTEN_MAP.put("minecraft:trapdoor.0", "minecraft:oak_trapdoor"); ++ FLATTEN_MAP.put("minecraft:nether_brick.0", "minecraft:nether_bricks"); ++ FLATTEN_MAP.put("minecraft:red_nether_brick.0", "minecraft:red_nether_bricks"); ++ FLATTEN_MAP.put("minecraft:netherbrick.0", "minecraft:nether_brick"); ++ FLATTEN_MAP.put("minecraft:wooden_button.0", "minecraft:oak_button"); ++ FLATTEN_MAP.put("minecraft:wooden_pressure_plate.0", "minecraft:oak_pressure_plate"); ++ FLATTEN_MAP.put("minecraft:noteblock.0", "minecraft:note_block"); ++ FLATTEN_MAP.put("minecraft:bed.0", "minecraft:white_bed"); ++ FLATTEN_MAP.put("minecraft:bed.1", "minecraft:orange_bed"); ++ FLATTEN_MAP.put("minecraft:bed.2", "minecraft:magenta_bed"); ++ FLATTEN_MAP.put("minecraft:bed.3", "minecraft:light_blue_bed"); ++ FLATTEN_MAP.put("minecraft:bed.4", "minecraft:yellow_bed"); ++ FLATTEN_MAP.put("minecraft:bed.5", "minecraft:lime_bed"); ++ FLATTEN_MAP.put("minecraft:bed.6", "minecraft:pink_bed"); ++ FLATTEN_MAP.put("minecraft:bed.7", "minecraft:gray_bed"); ++ FLATTEN_MAP.put("minecraft:bed.8", "minecraft:light_gray_bed"); ++ FLATTEN_MAP.put("minecraft:bed.9", "minecraft:cyan_bed"); ++ FLATTEN_MAP.put("minecraft:bed.10", "minecraft:purple_bed"); ++ FLATTEN_MAP.put("minecraft:bed.11", "minecraft:blue_bed"); ++ FLATTEN_MAP.put("minecraft:bed.12", "minecraft:brown_bed"); ++ FLATTEN_MAP.put("minecraft:bed.13", "minecraft:green_bed"); ++ FLATTEN_MAP.put("minecraft:bed.14", "minecraft:red_bed"); ++ FLATTEN_MAP.put("minecraft:bed.15", "minecraft:black_bed"); ++ FLATTEN_MAP.put("minecraft:banner.15", "minecraft:white_banner"); ++ FLATTEN_MAP.put("minecraft:banner.14", "minecraft:orange_banner"); ++ FLATTEN_MAP.put("minecraft:banner.13", "minecraft:magenta_banner"); ++ FLATTEN_MAP.put("minecraft:banner.12", "minecraft:light_blue_banner"); ++ FLATTEN_MAP.put("minecraft:banner.11", "minecraft:yellow_banner"); ++ FLATTEN_MAP.put("minecraft:banner.10", "minecraft:lime_banner"); ++ FLATTEN_MAP.put("minecraft:banner.9", "minecraft:pink_banner"); ++ FLATTEN_MAP.put("minecraft:banner.8", "minecraft:gray_banner"); ++ FLATTEN_MAP.put("minecraft:banner.7", "minecraft:light_gray_banner"); ++ FLATTEN_MAP.put("minecraft:banner.6", "minecraft:cyan_banner"); ++ FLATTEN_MAP.put("minecraft:banner.5", "minecraft:purple_banner"); ++ FLATTEN_MAP.put("minecraft:banner.4", "minecraft:blue_banner"); ++ FLATTEN_MAP.put("minecraft:banner.3", "minecraft:brown_banner"); ++ FLATTEN_MAP.put("minecraft:banner.2", "minecraft:green_banner"); ++ FLATTEN_MAP.put("minecraft:banner.1", "minecraft:red_banner"); ++ FLATTEN_MAP.put("minecraft:banner.0", "minecraft:black_banner"); ++ FLATTEN_MAP.put("minecraft:grass.0", "minecraft:grass_block"); ++ FLATTEN_MAP.put("minecraft:brick_block.0", "minecraft:bricks"); ++ FLATTEN_MAP.put("minecraft:end_bricks.0", "minecraft:end_stone_bricks"); ++ FLATTEN_MAP.put("minecraft:golden_rail.0", "minecraft:powered_rail"); ++ FLATTEN_MAP.put("minecraft:magma.0", "minecraft:magma_block"); ++ FLATTEN_MAP.put("minecraft:quartz_ore.0", "minecraft:nether_quartz_ore"); ++ FLATTEN_MAP.put("minecraft:reeds.0", "minecraft:sugar_cane"); ++ FLATTEN_MAP.put("minecraft:slime.0", "minecraft:slime_block"); ++ FLATTEN_MAP.put("minecraft:stone_stairs.0", "minecraft:cobblestone_stairs"); ++ FLATTEN_MAP.put("minecraft:waterlily.0", "minecraft:lily_pad"); ++ FLATTEN_MAP.put("minecraft:web.0", "minecraft:cobweb"); ++ FLATTEN_MAP.put("minecraft:snow.0", "minecraft:snow_block"); ++ FLATTEN_MAP.put("minecraft:snow_layer.0", "minecraft:snow"); ++ FLATTEN_MAP.put("minecraft:record_11.0", "minecraft:music_disc_11"); ++ FLATTEN_MAP.put("minecraft:record_13.0", "minecraft:music_disc_13"); ++ FLATTEN_MAP.put("minecraft:record_blocks.0", "minecraft:music_disc_blocks"); ++ FLATTEN_MAP.put("minecraft:record_cat.0", "minecraft:music_disc_cat"); ++ FLATTEN_MAP.put("minecraft:record_chirp.0", "minecraft:music_disc_chirp"); ++ FLATTEN_MAP.put("minecraft:record_far.0", "minecraft:music_disc_far"); ++ FLATTEN_MAP.put("minecraft:record_mall.0", "minecraft:music_disc_mall"); ++ FLATTEN_MAP.put("minecraft:record_mellohi.0", "minecraft:music_disc_mellohi"); ++ FLATTEN_MAP.put("minecraft:record_stal.0", "minecraft:music_disc_stal"); ++ FLATTEN_MAP.put("minecraft:record_strad.0", "minecraft:music_disc_strad"); ++ FLATTEN_MAP.put("minecraft:record_wait.0", "minecraft:music_disc_wait"); ++ FLATTEN_MAP.put("minecraft:record_ward.0", "minecraft:music_disc_ward"); ++ } ++ ++ // maps out ids requiring flattening ++ private static final Set IDS_REQUIRING_FLATTENING = new HashSet<>(); ++ static { ++ for (final String key : FLATTEN_MAP.keySet()) { ++ IDS_REQUIRING_FLATTENING.add(key.substring(0, key.indexOf('.'))); ++ } ++ } ++ ++ // Damage tag is moved from the ItemStack base tag to the ItemStack tag, and we only want to migrate that ++ // for items that actually require it for damage purposes (Remember, old damage was used to differentiate item types) ++ // It should be noted that this ID set should not be included in the flattening map, because damage for these items ++ // is actual damage and not a subtype specifier ++ private static final Set ITEMS_WITH_DAMAGE = new HashSet<>(Arrays.asList( ++ "minecraft:bow", ++ "minecraft:carrot_on_a_stick", ++ "minecraft:chainmail_boots", ++ "minecraft:chainmail_chestplate", ++ "minecraft:chainmail_helmet", ++ "minecraft:chainmail_leggings", ++ "minecraft:diamond_axe", ++ "minecraft:diamond_boots", ++ "minecraft:diamond_chestplate", ++ "minecraft:diamond_helmet", ++ "minecraft:diamond_hoe", ++ "minecraft:diamond_leggings", ++ "minecraft:diamond_pickaxe", ++ "minecraft:diamond_shovel", ++ "minecraft:diamond_sword", ++ "minecraft:elytra", ++ "minecraft:fishing_rod", ++ "minecraft:flint_and_steel", ++ "minecraft:golden_axe", ++ "minecraft:golden_boots", ++ "minecraft:golden_chestplate", ++ "minecraft:golden_helmet", ++ "minecraft:golden_hoe", ++ "minecraft:golden_leggings", ++ "minecraft:golden_pickaxe", ++ "minecraft:golden_shovel", ++ "minecraft:golden_sword", ++ "minecraft:iron_axe", ++ "minecraft:iron_boots", ++ "minecraft:iron_chestplate", ++ "minecraft:iron_helmet", ++ "minecraft:iron_hoe", ++ "minecraft:iron_leggings", ++ "minecraft:iron_pickaxe", ++ "minecraft:iron_shovel", ++ "minecraft:iron_sword", ++ "minecraft:leather_boots", ++ "minecraft:leather_chestplate", ++ "minecraft:leather_helmet", ++ "minecraft:leather_leggings", ++ "minecraft:shears", ++ "minecraft:shield", ++ "minecraft:stone_axe", ++ "minecraft:stone_hoe", ++ "minecraft:stone_pickaxe", ++ "minecraft:stone_shovel", ++ "minecraft:stone_sword", ++ "minecraft:wooden_axe", ++ "minecraft:wooden_hoe", ++ "minecraft:wooden_pickaxe", ++ "minecraft:wooden_shovel", ++ "minecraft:wooden_sword" ++ )); ++ ++ public ConverterFlattenItemStack() { ++ super(MCVersions.V17W47A, 4); ++ } ++ ++ public static String flattenItem(final String oldName, final int data) { ++ if (IDS_REQUIRING_FLATTENING.contains(oldName)) { ++ final String flattened = FLATTEN_MAP.get(oldName + '.' + data); ++ return flattened == null ? FLATTEN_MAP.get(oldName.concat(".0")) : flattened; ++ } else { ++ return null; ++ } ++ } ++ ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final String id = data.getString("id"); ++ ++ if (id == null) { ++ return null; ++ } ++ ++ final int damage = data.getInt("Damage"); ++ data.remove("Damage"); ++ ++ if (IDS_REQUIRING_FLATTENING.contains(id)) { ++ String remap = FLATTEN_MAP.get(id + '.' + damage); ++ if (remap == null) { ++ remap = FLATTEN_MAP.get(id.concat(".0")); ++ // this shouldn't be null ++ } ++ if (remap != null) { ++ data.setString("id", remap); ++ } else { ++ LOGGER.warn("Item '" + id + "' requires flattening but found no mapping for it! (ConverterFlattenItemStack)"); ++ } ++ } ++ ++ if (damage != 0 && ITEMS_WITH_DAMAGE.contains(id)) { ++ // migrate damage ++ MapType tag = data.getMap("tag"); ++ if (tag == null) { ++ tag = Types.NBT.createEmptyMap(); ++ data.setMap("tag", tag); ++ } ++ tag.setInt("Damage", damage); ++ } ++ ++ return null; ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/itemstack/ConverterFlattenSpawnEgg.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/itemstack/ConverterFlattenSpawnEgg.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d88b12e6b9e381ba614dc04599a44e472a37ca03 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/itemstack/ConverterFlattenSpawnEgg.java +@@ -0,0 +1,83 @@ ++package ca.spottedleaf.dataconverter.minecraft.converters.itemstack; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.types.MapType; ++import java.util.HashMap; ++import java.util.Map; ++ ++public final class ConverterFlattenSpawnEgg extends DataConverter, MapType> { ++ ++ private static final Map ENTITY_ID_TO_NEW_EGG_ID = new HashMap<>(); ++ static { ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:bat", "minecraft:bat_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:blaze", "minecraft:blaze_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:cave_spider", "minecraft:cave_spider_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:chicken", "minecraft:chicken_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:cow", "minecraft:cow_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:creeper", "minecraft:creeper_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:donkey", "minecraft:donkey_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:elder_guardian", "minecraft:elder_guardian_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:enderman", "minecraft:enderman_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:endermite", "minecraft:endermite_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:evocation_illager", "minecraft:evocation_illager_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:ghast", "minecraft:ghast_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:guardian", "minecraft:guardian_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:horse", "minecraft:horse_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:husk", "minecraft:husk_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:llama", "minecraft:llama_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:magma_cube", "minecraft:magma_cube_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:mooshroom", "minecraft:mooshroom_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:mule", "minecraft:mule_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:ocelot", "minecraft:ocelot_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:pufferfish", "minecraft:pufferfish_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:parrot", "minecraft:parrot_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:pig", "minecraft:pig_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:polar_bear", "minecraft:polar_bear_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:rabbit", "minecraft:rabbit_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:sheep", "minecraft:sheep_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:shulker", "minecraft:shulker_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:silverfish", "minecraft:silverfish_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:skeleton", "minecraft:skeleton_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:skeleton_horse", "minecraft:skeleton_horse_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:slime", "minecraft:slime_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:spider", "minecraft:spider_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:squid", "minecraft:squid_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:stray", "minecraft:stray_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:turtle", "minecraft:turtle_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:vex", "minecraft:vex_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:villager", "minecraft:villager_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:vindication_illager", "minecraft:vindication_illager_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:witch", "minecraft:witch_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:wither_skeleton", "minecraft:wither_skeleton_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:wolf", "minecraft:wolf_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:zombie", "minecraft:zombie_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:zombie_horse", "minecraft:zombie_horse_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:zombie_pigman", "minecraft:zombie_pigman_spawn_egg"); ++ ENTITY_ID_TO_NEW_EGG_ID.put("minecraft:zombie_villager", "minecraft:zombie_villager_spawn_egg"); ++ } ++ ++ public ConverterFlattenSpawnEgg() { ++ super(MCVersions.V17W47A,5); ++ } ++ ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType tag = data.getMap("tag"); ++ if (tag == null) { ++ return null; ++ } ++ ++ final MapType entityTag = tag.getMap("EntityTag"); ++ if (entityTag == null) { ++ return null; ++ } ++ ++ final String id = entityTag.getString("id"); ++ if (id != null) { ++ data.setString("id", ENTITY_ID_TO_NEW_EGG_ID.getOrDefault(id, "minecraft:pig_spawn_egg")); ++ } ++ ++ return null; ++ } ++} +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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/options/ConverterAbstractOptionsRename.java +@@ -0,0 +1,33 @@ ++package ca.spottedleaf.dataconverter.minecraft.converters.options; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.types.MapType; ++import java.util.ArrayList; ++import java.util.function.Function; ++ ++public final class ConverterAbstractOptionsRename { ++ ++ private ConverterAbstractOptionsRename() {} ++ ++ public static void register(final int version, final Function renamer) { ++ register(version, 0, renamer); ++ } ++ ++ public static void register(final int version, final int subVersion, final Function renamer) { ++ 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); ++ } ++ } ++ return null; ++ } ++ }); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/poi/ConverterAbstractPOIRename.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/poi/ConverterAbstractPOIRename.java +new file mode 100644 +index 0000000000000000000000000000000000000000..57e210bf2bb189b15a32899011c4800b19668a5e +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/poi/ConverterAbstractPOIRename.java +@@ -0,0 +1,53 @@ ++package ca.spottedleaf.dataconverter.minecraft.converters.poi; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++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 java.util.function.Function; ++ ++public final class ConverterAbstractPOIRename { ++ ++ private ConverterAbstractPOIRename() {} ++ ++ public static void register(final int version, final Function renamer) { ++ register(version, 0, renamer); ++ } ++ ++ public static void register(final int version, final int subVersion, final Function renamer) { ++ MCTypeRegistry.POI_CHUNK.addStructureConverter(new DataConverter<>(version, subVersion) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType sections = data.getMap("Sections"); ++ if (sections == null) { ++ return null; ++ } ++ ++ for (final String key : sections.keys()) { ++ final MapType section = sections.getMap(key); ++ ++ final ListType records = section.getList("Records", ObjectType.MAP); ++ ++ if (records == null) { ++ continue; ++ } ++ ++ for (int i = 0, len = records.size(); i < len; ++i) { ++ final MapType record = records.getMap(i); ++ ++ final String type = record.getString("type"); ++ if (type != null) { ++ final String converted = renamer.apply(type); ++ if (converted != null) { ++ record.setString("type", converted); ++ } ++ } ++ } ++ } ++ ++ return null; ++ } ++ }); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/recipe/ConverterAbstractRecipeRename.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/recipe/ConverterAbstractRecipeRename.java +new file mode 100644 +index 0000000000000000000000000000000000000000..8f35cbbd78a629712f9ae3cd5d180269f015a11d +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/recipe/ConverterAbstractRecipeRename.java +@@ -0,0 +1,18 @@ ++package ca.spottedleaf.dataconverter.minecraft.converters.recipe; ++ ++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.ConverterAbstractStringValueTypeRename; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import java.util.function.Function; ++ ++public final class ConverterAbstractRecipeRename { ++ ++ private ConverterAbstractRecipeRename() {} ++ ++ public static void register(final int version, final Function renamer) { ++ register(version, 0, renamer); ++ } ++ ++ public static void register(final int version, final int subVersion, final Function renamer) { ++ ConverterAbstractStringValueTypeRename.register(version, subVersion, MCTypeRegistry.RECIPE, renamer); ++ } ++} +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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/stats/ConverterAbstractStatsRename.java +@@ -0,0 +1,75 @@ ++package ca.spottedleaf.dataconverter.minecraft.converters.stats; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.types.MapType; ++import java.util.ArrayList; ++import java.util.function.Function; ++ ++public final class ConverterAbstractStatsRename { ++ ++ private ConverterAbstractStatsRename() {} ++ ++ public static void register(final int version, final Function renamer) { ++ register(version, 0, renamer); ++ } ++ ++ public static void register(final int version, final int subVersion, final Function renamer) { ++ MCTypeRegistry.OBJECTIVE.addStructureConverter(new DataConverter<>(version, subVersion) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType criteriaType = data.getMap("CriteriaType"); ++ if (criteriaType == null) { ++ return null; ++ } ++ ++ final String type = criteriaType.getString("type"); ++ if (!"minecraft:custom".equals(type)) { ++ return null; ++ } ++ ++ final String id = criteriaType.getString("id"); ++ if (id == null) { ++ return null; ++ } ++ ++ final String rename = renamer.apply(id); ++ if (rename != null) { ++ criteriaType.setString("id", rename); ++ } ++ ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.STATS.addStructureConverter(new DataConverter<>(version, subVersion) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType stats = data.getMap("stats"); ++ ++ if (stats == null) { ++ return null; ++ } ++ ++ final MapType custom = stats.getMap("minecraft:custom"); ++ if (custom == null) { ++ 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); ++ } ++ ++ return null; ++ } ++ }); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/stats/ConverterFlattenStats.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/stats/ConverterFlattenStats.java +new file mode 100644 +index 0000000000000000000000000000000000000000..99d2c2c84820295be1f8bb0b43784e58f51a46dd +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/converters/stats/ConverterFlattenStats.java +@@ -0,0 +1,94 @@ ++package ca.spottedleaf.dataconverter.minecraft.converters.stats; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.HelperBlockFlatteningV1450; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemstack.ConverterFlattenItemStack; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.Types; ++import com.google.common.collect.ImmutableMap; ++import com.google.common.collect.ImmutableSet; ++import org.apache.commons.lang3.StringUtils; ++import java.util.Map; ++import java.util.Set; ++ ++public final class ConverterFlattenStats extends DataConverter, MapType> { ++ ++ private static final Set SKIP = ImmutableSet.builder().add("stat.craftItem.minecraft.spawn_egg").add("stat.useItem.minecraft.spawn_egg").add("stat.breakItem.minecraft.spawn_egg").add("stat.pickup.minecraft.spawn_egg").add("stat.drop.minecraft.spawn_egg").build(); ++ private static final Map CUSTOM_MAP = ImmutableMap.builder().put("stat.leaveGame", "minecraft:leave_game").put("stat.playOneMinute", "minecraft:play_one_minute").put("stat.timeSinceDeath", "minecraft:time_since_death").put("stat.sneakTime", "minecraft:sneak_time").put("stat.walkOneCm", "minecraft:walk_one_cm").put("stat.crouchOneCm", "minecraft:crouch_one_cm").put("stat.sprintOneCm", "minecraft:sprint_one_cm").put("stat.swimOneCm", "minecraft:swim_one_cm").put("stat.fallOneCm", "minecraft:fall_one_cm").put("stat.climbOneCm", "minecraft:climb_one_cm").put("stat.flyOneCm", "minecraft:fly_one_cm").put("stat.diveOneCm", "minecraft:dive_one_cm").put("stat.minecartOneCm", "minecraft:minecart_one_cm").put("stat.boatOneCm", "minecraft:boat_one_cm").put("stat.pigOneCm", "minecraft:pig_one_cm").put("stat.horseOneCm", "minecraft:horse_one_cm").put("stat.aviateOneCm", "minecraft:aviate_one_cm").put("stat.jump", "minecraft:jump").put("stat.drop", "minecraft:drop").put("stat.damageDealt", "minecraft:damage_dealt").put("stat.damageTaken", "minecraft:damage_taken").put("stat.deaths", "minecraft:deaths").put("stat.mobKills", "minecraft:mob_kills").put("stat.animalsBred", "minecraft:animals_bred").put("stat.playerKills", "minecraft:player_kills").put("stat.fishCaught", "minecraft:fish_caught").put("stat.talkedToVillager", "minecraft:talked_to_villager").put("stat.tradedWithVillager", "minecraft:traded_with_villager").put("stat.cakeSlicesEaten", "minecraft:eat_cake_slice").put("stat.cauldronFilled", "minecraft:fill_cauldron").put("stat.cauldronUsed", "minecraft:use_cauldron").put("stat.armorCleaned", "minecraft:clean_armor").put("stat.bannerCleaned", "minecraft:clean_banner").put("stat.brewingstandInteraction", "minecraft:interact_with_brewingstand").put("stat.beaconInteraction", "minecraft:interact_with_beacon").put("stat.dropperInspected", "minecraft:inspect_dropper").put("stat.hopperInspected", "minecraft:inspect_hopper").put("stat.dispenserInspected", "minecraft:inspect_dispenser").put("stat.noteblockPlayed", "minecraft:play_noteblock").put("stat.noteblockTuned", "minecraft:tune_noteblock").put("stat.flowerPotted", "minecraft:pot_flower").put("stat.trappedChestTriggered", "minecraft:trigger_trapped_chest").put("stat.enderchestOpened", "minecraft:open_enderchest").put("stat.itemEnchanted", "minecraft:enchant_item").put("stat.recordPlayed", "minecraft:play_record").put("stat.furnaceInteraction", "minecraft:interact_with_furnace").put("stat.craftingTableInteraction", "minecraft:interact_with_crafting_table").put("stat.chestOpened", "minecraft:open_chest").put("stat.sleepInBed", "minecraft:sleep_in_bed").put("stat.shulkerBoxOpened", "minecraft:open_shulker_box").build(); ++ private static final Map ITEM_KEYS = ImmutableMap.builder().put("stat.craftItem", "minecraft:crafted").put("stat.useItem", "minecraft:used").put("stat.breakItem", "minecraft:broken").put("stat.pickup", "minecraft:picked_up").put("stat.drop", "minecraft:dropped").build(); ++ private static final Map ENTITY_KEYS = ImmutableMap.builder().put("stat.entityKilledBy", "minecraft:killed_by").put("stat.killEntity", "minecraft:killed").build(); ++ private static final Map ENTITIES = ImmutableMap.builder().put("Bat", "minecraft:bat").put("Blaze", "minecraft:blaze").put("CaveSpider", "minecraft:cave_spider").put("Chicken", "minecraft:chicken").put("Cow", "minecraft:cow").put("Creeper", "minecraft:creeper").put("Donkey", "minecraft:donkey").put("ElderGuardian", "minecraft:elder_guardian").put("Enderman", "minecraft:enderman").put("Endermite", "minecraft:endermite").put("EvocationIllager", "minecraft:evocation_illager").put("Ghast", "minecraft:ghast").put("Guardian", "minecraft:guardian").put("Horse", "minecraft:horse").put("Husk", "minecraft:husk").put("Llama", "minecraft:llama").put("LavaSlime", "minecraft:magma_cube").put("MushroomCow", "minecraft:mooshroom").put("Mule", "minecraft:mule").put("Ozelot", "minecraft:ocelot").put("Parrot", "minecraft:parrot").put("Pig", "minecraft:pig").put("PolarBear", "minecraft:polar_bear").put("Rabbit", "minecraft:rabbit").put("Sheep", "minecraft:sheep").put("Shulker", "minecraft:shulker").put("Silverfish", "minecraft:silverfish").put("SkeletonHorse", "minecraft:skeleton_horse").put("Skeleton", "minecraft:skeleton").put("Slime", "minecraft:slime").put("Spider", "minecraft:spider").put("Squid", "minecraft:squid").put("Stray", "minecraft:stray").put("Vex", "minecraft:vex").put("Villager", "minecraft:villager").put("VindicationIllager", "minecraft:vindication_illager").put("Witch", "minecraft:witch").put("WitherSkeleton", "minecraft:wither_skeleton").put("Wolf", "minecraft:wolf").put("ZombieHorse", "minecraft:zombie_horse").put("PigZombie", "minecraft:zombie_pigman").put("ZombieVillager", "minecraft:zombie_villager").put("Zombie", "minecraft:zombie").build(); ++ ++ public ConverterFlattenStats() { ++ super(MCVersions.V17W47A, 6); ++ } ++ ++ private static String upgradeItem(final String itemName) { ++ return ConverterFlattenItemStack.flattenItem(itemName, 0); ++ } ++ ++ private static String upgradeBlock(final String block) { ++ return HelperBlockFlatteningV1450.getNewBlockName(block); ++ } ++ ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType stats = Types.NBT.createEmptyMap(); ++ ++ for (final String statKey : data.keys()) { ++ final Number value = data.getNumber(statKey); ++ if (value == null) { ++ continue; ++ } ++ ++ if (SKIP.contains(statKey)) { ++ continue; ++ } ++ ++ final String statType; ++ final String newStatKey; ++ ++ if (CUSTOM_MAP.containsKey(statKey)) { ++ statType = "minecraft:custom"; ++ newStatKey = CUSTOM_MAP.get(statKey); ++ } else { ++ final int i = StringUtils.ordinalIndexOf(statKey, ".", 2); ++ if (i < 0) { ++ continue; ++ } ++ ++ final String key = statKey.substring(0, i); ++ ++ if ("stat.mineBlock".equals(key)) { ++ statType = "minecraft:mined"; ++ newStatKey = upgradeBlock(statKey.substring(i + 1).replace('.', ':')); ++ } else if (ITEM_KEYS.containsKey(key)) { ++ statType = ITEM_KEYS.get(key); ++ final String item = statKey.substring(i + 1).replace('.', ':'); ++ final String upgradedItem = upgradeItem(item); ++ newStatKey = upgradedItem == null ? item : upgradedItem; ++ } else if (ENTITY_KEYS.containsKey(key)) { ++ statType = ENTITY_KEYS.get(key); ++ final String entity = statKey.substring(i + 1).replace('.', ':'); ++ newStatKey = ENTITIES.getOrDefault(entity, entity); ++ } else { ++ continue; ++ } ++ } ++ ++ MapType statTypeMap = stats.getMap(statType); ++ if (statTypeMap == null) { ++ stats.setMap(statType, statTypeMap = Types.NBT.createEmptyMap()); ++ } ++ ++ statTypeMap.setGeneric(newStatKey, value); ++ } ++ ++ data.clear(); ++ ++ data.setMap("stats", stats); ++ ++ return null; ++ } ++} +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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/datatypes/IDDataType.java +@@ -0,0 +1,163 @@ ++package ca.spottedleaf.dataconverter.minecraft.datatypes; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.converters.datatypes.DataHook; ++import ca.spottedleaf.dataconverter.converters.datatypes.DataWalker; ++import ca.spottedleaf.dataconverter.minecraft.MCVersionRegistry; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.util.Long2ObjectArraySortedMap; ++ ++import java.util.ArrayList; ++import java.util.HashMap; ++import java.util.List; ++import java.util.Map; ++ ++public class IDDataType extends MCDataType { ++ ++ protected final Map>>> walkersById = new HashMap<>(); ++ ++ public IDDataType(final String name) { ++ super(name); ++ } ++ ++ public void addConverterForId(final String id, final DataConverter, MapType> converter) { ++ this.addStructureConverter(new DataConverter<>(converter.getToVersion(), converter.getVersionStep()) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (!id.equals(data.getString("id"))) { ++ return null; ++ } ++ return converter.convert(data, sourceVersion, toVersion); ++ } ++ }); ++ } ++ ++ public void addWalker(final int minVersion, final String id, final DataWalker walker) { ++ this.addWalker(minVersion, 0, id, walker); ++ } ++ ++ public void addWalker(final int minVersion, final int versionStep, final String id, final DataWalker walker) { ++ this.walkersById.computeIfAbsent(id, (final String keyInMap) -> { ++ return new Long2ObjectArraySortedMap<>(); ++ }).computeIfAbsent(DataConverter.encodeVersions(minVersion, versionStep), (final long keyInMap) -> { ++ return new ArrayList<>(); ++ }).add(walker); ++ } ++ ++ public void copyWalkers(final int minVersion, final String fromId, final String toId) { ++ this.copyWalkers(minVersion, 0, fromId, toId); ++ } ++ ++ public void copyWalkers(final int minVersion, final int versionStep, final String fromId, final String toId) { ++ final long version = DataConverter.encodeVersions(minVersion, versionStep); ++ final Long2ObjectArraySortedMap>> walkersForId = this.walkersById.get(fromId); ++ if (walkersForId == null) { ++ return; ++ } ++ ++ final List> nearest = walkersForId.getFloor(version); ++ ++ if (nearest == null) { ++ return; ++ } ++ ++ for (final DataWalker walker : nearest) { ++ this.addWalker(minVersion, versionStep, toId, walker); ++ } ++ } ++ ++ @Override ++ public MapType convert(MapType data, final long fromVersion, final long toVersion) { ++ MapType ret = null; ++ ++ final List, MapType>> converters = this.structureConverters; ++ for (int i = 0, len = converters.size(); i < len; ++i) { ++ final DataConverter, MapType> converter = converters.get(i); ++ final long converterVersion = converter.getEncodedVersion(); ++ ++ if (converterVersion <= fromVersion) { ++ continue; ++ } ++ ++ if (converterVersion > toVersion) { ++ break; ++ } ++ ++ final List, MapType>> hooks = this.structureHooks.getFloor(converterVersion); ++ ++ if (hooks != null) { ++ for (int k = 0, klen = hooks.size(); k < klen; ++k) { ++ final MapType replace = hooks.get(k).preHook(data, fromVersion, toVersion); ++ if (replace != null) { ++ ret = data = replace; ++ } ++ } ++ } ++ ++ final MapType replace = converter.convert(data, fromVersion, toVersion); ++ if (replace != null) { ++ ret = data = replace; ++ } ++ ++ if (hooks != null) { ++ for (int klen = hooks.size(), k = klen - 1; k >= 0; --k) { ++ final MapType postReplace = hooks.get(k).postHook(data, fromVersion, toVersion); ++ if (postReplace != null) { ++ ret = data = postReplace; ++ } ++ } ++ } ++ } ++ ++ final List, MapType>> hooks = this.structureHooks.getFloor(toVersion); ++ ++ // run pre hooks ++ ++ if (hooks != null) { ++ for (int k = 0, klen = hooks.size(); k < klen; ++k) { ++ final MapType replace = hooks.get(k).preHook(data, fromVersion, toVersion); ++ if (replace != null) { ++ ret = data = replace; ++ } ++ } ++ } ++ ++ // run all walkers ++ ++ final List> walkers = this.structureWalkers.getFloor(toVersion); ++ if (walkers != null) { ++ for (int i = 0, len = walkers.size(); i < len; ++i) { ++ final MapType replace = walkers.get(i).walk(data, fromVersion, toVersion); ++ if (replace != null) { ++ ret = data = replace; ++ } ++ } ++ } ++ ++ final Long2ObjectArraySortedMap>> walkersByVersion = this.walkersById.get(data.getString("id")); ++ if (walkersByVersion != null) { ++ final List> walkersForId = walkersByVersion.getFloor(toVersion); ++ if (walkersForId != null) { ++ for (int i = 0, len = walkersForId.size(); i < len; ++i) { ++ final MapType replace = walkersForId.get(i).walk(data, fromVersion, toVersion); ++ if (replace != null) { ++ ret = data = replace; ++ } ++ } ++ } ++ } ++ ++ // run post hooks ++ ++ if (hooks != null) { ++ for (int klen = hooks.size(), k = klen - 1; k >= 0; --k) { ++ final MapType postReplace = hooks.get(k).postHook(data, fromVersion, toVersion); ++ if (postReplace != null) { ++ ret = data = postReplace; ++ } ++ } ++ } ++ ++ return ret; ++ } ++} +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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/datatypes/MCDataType.java +@@ -0,0 +1,126 @@ ++package ca.spottedleaf.dataconverter.minecraft.datatypes; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.converters.datatypes.DataHook; ++import ca.spottedleaf.dataconverter.converters.datatypes.DataType; ++import ca.spottedleaf.dataconverter.converters.datatypes.DataWalker; ++import ca.spottedleaf.dataconverter.minecraft.MCVersionRegistry; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.util.Long2ObjectArraySortedMap; ++import java.util.ArrayList; ++import java.util.List; ++ ++public class MCDataType extends DataType, MapType> { ++ ++ public final String name; ++ ++ protected final ArrayList, MapType>> structureConverters = new ArrayList<>(); ++ protected final Long2ObjectArraySortedMap>> structureWalkers = new Long2ObjectArraySortedMap<>(); ++ protected final Long2ObjectArraySortedMap, MapType>>> structureHooks = new Long2ObjectArraySortedMap<>(); ++ ++ public MCDataType(final String name) { ++ this.name = name; ++ } ++ ++ public void addStructureConverter(final DataConverter, MapType> converter) { ++ MCVersionRegistry.checkVersion(converter.getEncodedVersion()); ++ this.structureConverters.add(converter); ++ this.structureConverters.sort(DataConverter.LOWEST_VERSION_COMPARATOR); ++ } ++ ++ public void addStructureWalker(final int minVersion, final DataWalker walker) { ++ this.addStructureWalker(minVersion, 0, walker); ++ } ++ ++ public void addStructureWalker(final int minVersion, final int versionStep, final DataWalker walker) { ++ this.structureWalkers.computeIfAbsent(DataConverter.encodeVersions(minVersion, versionStep), (final long keyInMap) -> { ++ return new ArrayList<>(); ++ }).add(walker); ++ } ++ ++ public void addStructureHook(final int minVersion, final DataHook, MapType> hook) { ++ this.addStructureHook(minVersion, 0, hook); ++ } ++ ++ public void addStructureHook(final int minVersion, final int versionStep, final DataHook, MapType> hook) { ++ this.structureHooks.computeIfAbsent(DataConverter.encodeVersions(minVersion, versionStep), (final long keyInMap) -> { ++ return new ArrayList<>(); ++ }).add(hook); ++ } ++ ++ @Override ++ public MapType convert(MapType data, final long fromVersion, final long toVersion) { ++ MapType ret = null; ++ ++ final List, MapType>> converters = this.structureConverters; ++ for (int i = 0, len = converters.size(); i < len; ++i) { ++ final DataConverter, MapType> converter = converters.get(i); ++ final long converterVersion = converter.getEncodedVersion(); ++ ++ if (converterVersion <= fromVersion) { ++ continue; ++ } ++ ++ if (converterVersion > toVersion) { ++ break; ++ } ++ ++ final List, MapType>> hooks = this.structureHooks.getFloor(converterVersion); ++ ++ if (hooks != null) { ++ for (int k = 0, klen = hooks.size(); k < klen; ++k) { ++ final MapType replace = hooks.get(k).preHook(data, fromVersion, toVersion); ++ if (replace != null) { ++ ret = data = replace; ++ } ++ } ++ } ++ ++ final MapType replace = converter.convert(data, fromVersion, toVersion); ++ if (replace != null) { ++ ret = data = replace; ++ } ++ ++ if (hooks != null) { ++ for (int klen = hooks.size(), k = klen - 1; k >= 0; --k) { ++ final MapType postReplace = hooks.get(k).postHook(data, fromVersion, toVersion); ++ if (postReplace != null) { ++ ret = data = postReplace; ++ } ++ } ++ } ++ } ++ ++ final List, MapType>> hooks = this.structureHooks.getFloor(toVersion); ++ ++ if (hooks != null) { ++ for (int k = 0, klen = hooks.size(); k < klen; ++k) { ++ final MapType replace = hooks.get(k).preHook(data, fromVersion, toVersion); ++ if (replace != null) { ++ ret = data = replace; ++ } ++ } ++ } ++ ++ final List> walkers = this.structureWalkers.getFloor(toVersion); ++ if (walkers != null) { ++ for (int i = 0, len = walkers.size(); i < len; ++i) { ++ final MapType replace = walkers.get(i).walk(data, fromVersion, toVersion); ++ if (replace != null) { ++ ret = data = replace; ++ } ++ } ++ } ++ ++ if (hooks != null) { ++ for (int klen = hooks.size(), k = klen - 1; k >= 0; --k) { ++ final MapType postReplace = hooks.get(k).postHook(data, fromVersion, toVersion); ++ if (postReplace != null) { ++ ret = data = postReplace; ++ } ++ } ++ } ++ ++ return ret; ++ } ++} +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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/datatypes/MCTypeRegistry.java +@@ -0,0 +1,316 @@ ++package ca.spottedleaf.dataconverter.minecraft.datatypes; ++ ++import ca.spottedleaf.dataconverter.minecraft.versions.V100; ++import ca.spottedleaf.dataconverter.minecraft.versions.V101; ++import ca.spottedleaf.dataconverter.minecraft.versions.V102; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1022; ++import ca.spottedleaf.dataconverter.minecraft.versions.V105; ++import ca.spottedleaf.dataconverter.minecraft.versions.V106; ++import ca.spottedleaf.dataconverter.minecraft.versions.V107; ++import ca.spottedleaf.dataconverter.minecraft.versions.V108; ++import ca.spottedleaf.dataconverter.minecraft.versions.V109; ++import ca.spottedleaf.dataconverter.minecraft.versions.V110; ++import ca.spottedleaf.dataconverter.minecraft.versions.V111; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1125; ++import ca.spottedleaf.dataconverter.minecraft.versions.V113; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1344; ++import ca.spottedleaf.dataconverter.minecraft.versions.V135; ++import ca.spottedleaf.dataconverter.minecraft.versions.V143; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1446; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1450; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1451; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1456; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1458; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1460; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1466; ++import ca.spottedleaf.dataconverter.minecraft.versions.V147; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1470; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1474; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1475; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1480; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1483; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1484; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1486; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1487; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1488; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1490; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1492; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1494; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1496; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1500; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1501; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1502; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1506; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1510; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1514; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1515; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1624; ++import ca.spottedleaf.dataconverter.minecraft.versions.V165; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1800; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1801; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1802; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1803; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1904; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1905; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1906; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1911; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1917; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1918; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1920; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1925; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1928; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1929; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1931; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1936; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1946; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1948; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1953; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1955; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1961; ++import ca.spottedleaf.dataconverter.minecraft.versions.V1963; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2100; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2202; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2209; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2211; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2218; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2501; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2502; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2503; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2505; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2508; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2509; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2511; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2514; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2516; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2518; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2519; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2522; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2523; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2527; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2528; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2529; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2531; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2533; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2535; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2550; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2551; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2552; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2553; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2558; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2568; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2671; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2679; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2680; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2686; ++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.V2696; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2700; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2701; ++import ca.spottedleaf.dataconverter.minecraft.versions.V2702; ++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.V501; ++import ca.spottedleaf.dataconverter.minecraft.versions.V502; ++import ca.spottedleaf.dataconverter.minecraft.versions.V505; ++import ca.spottedleaf.dataconverter.minecraft.versions.V700; ++import ca.spottedleaf.dataconverter.minecraft.versions.V701; ++import ca.spottedleaf.dataconverter.minecraft.versions.V702; ++import ca.spottedleaf.dataconverter.minecraft.versions.V703; ++import ca.spottedleaf.dataconverter.minecraft.versions.V704; ++import ca.spottedleaf.dataconverter.minecraft.versions.V705; ++import ca.spottedleaf.dataconverter.minecraft.versions.V804; ++import ca.spottedleaf.dataconverter.minecraft.versions.V806; ++import ca.spottedleaf.dataconverter.minecraft.versions.V808; ++import ca.spottedleaf.dataconverter.minecraft.versions.V813; ++import ca.spottedleaf.dataconverter.minecraft.versions.V816; ++import ca.spottedleaf.dataconverter.minecraft.versions.V820; ++import ca.spottedleaf.dataconverter.minecraft.versions.V99; ++ ++public final class MCTypeRegistry { ++ ++ public static final MCDataType LEVEL = new MCDataType("Level"); ++ public static final MCDataType PLAYER = new MCDataType("Player"); ++ public static final MCDataType CHUNK = new MCDataType("Chunk"); ++ public static final MCDataType HOTBAR = new MCDataType("CreativeHotbar"); ++ public static final MCDataType OPTIONS = new MCDataType("Options"); ++ public static final MCDataType STRUCTURE = new MCDataType("Structure"); ++ public static final MCDataType STATS = new MCDataType("Stats"); ++ public static final MCDataType SAVED_DATA = new MCDataType("SavedData"); ++ public static final MCDataType ADVANCEMENTS = new MCDataType("Advancements"); ++ public static final MCDataType POI_CHUNK = new MCDataType("PoiChunk"); ++ public static final MCDataType ENTITY_CHUNK = new MCDataType("EntityChunk"); ++ public static final IDDataType TILE_ENTITY = new IDDataType("TileEntity"); ++ public static final IDDataType ITEM_STACK = new IDDataType("ItemStack"); ++ public static final MCDataType BLOCK_STATE = new MCDataType("BlockState"); ++ public static final MCValueType ENTITY_NAME = new MCValueType("EntityName"); ++ public static final IDDataType ENTITY = new IDDataType("Entity"); ++ public static final MCValueType BLOCK_NAME = new MCValueType("BlockName"); ++ public static final MCValueType ITEM_NAME = new MCValueType("ItemName"); ++ public static final MCDataType UNTAGGED_SPAWNER = new MCDataType("Spawner"); ++ public static final MCDataType STRUCTURE_FEATURE = new MCDataType("StructureFeature"); ++ public static final MCDataType OBJECTIVE = new MCDataType("Objective"); ++ public static final MCDataType TEAM = new MCDataType("Team"); ++ public static final MCValueType RECIPE = new MCValueType("RecipeName"); ++ public static final MCValueType BIOME = new MCValueType("Biome"); ++ public static final MCDataType WORLD_GEN_SETTINGS = new MCDataType("WorldGenSettings"); ++ ++ static { ++ // General notes: ++ // - Structure converters run before everything. ++ // - ID specific converters run after structure converters. ++ // - Structure walkers run after id specific converters. ++ // - ID specific walkers run after structure walkers. ++ ++ V99.register(); // all legacy data before converters existed ++ V100.register(); // first version with version id ++ V101.register(); ++ V102.register(); ++ V105.register(); ++ V106.register(); ++ V107.register(); ++ V108.register(); ++ V109.register(); ++ V110.register(); ++ V111.register(); ++ V113.register(); ++ V135.register(); ++ V143.register(); ++ V147.register(); ++ V165.register(); ++ V501.register(); ++ V502.register(); ++ V505.register(); ++ V700.register(); ++ V701.register(); ++ V702.register(); ++ V703.register(); ++ V704.register(); ++ V705.register(); ++ V804.register(); ++ V806.register(); ++ V808.register(); ++ V813.register(); ++ V816.register(); ++ V820.register(); ++ V1022.register(); ++ V1125.register(); ++ // END OF LEGACY DATA CONVERTERS ++ ++ // V1.13 ++ V1344.register(); ++ V1446.register(); ++ // START THE FLATTENING ++ V1450.register(); ++ V1451.register(); ++ // END THE FLATTENING ++ ++ V1456.register(); ++ V1458.register(); ++ V1460.register(); ++ V1466.register(); ++ V1470.register(); ++ V1474.register(); ++ V1475.register(); ++ V1480.register(); ++ // V1481 is adding simple block entity ++ V1483.register(); ++ V1484.register(); ++ V1486.register(); ++ V1487.register(); ++ V1488.register(); ++ V1490.register(); ++ V1492.register(); ++ V1494.register(); ++ V1496.register(); ++ V1500.register(); ++ V1501.register(); ++ V1502.register(); ++ V1506.register(); ++ V1510.register(); ++ V1514.register(); ++ V1515.register(); ++ V1624.register(); ++ // V1.14 ++ V1800.register(); ++ V1801.register(); ++ V1802.register(); ++ V1803.register(); ++ V1904.register(); ++ V1905.register(); ++ V1906.register(); ++ // V1909 is just adding a simple block entity (jigsaw) ++ V1911.register(); ++ V1917.register(); ++ V1918.register(); ++ V1920.register(); ++ V1925.register(); ++ V1928.register(); ++ V1929.register(); ++ V1931.register(); ++ V1936.register(); ++ V1946.register(); ++ V1948.register(); ++ V1953.register(); ++ V1955.register(); ++ V1961.register(); ++ V1963.register(); ++ // V1.15 ++ V2100.register(); ++ V2202.register(); ++ V2209.register(); ++ V2211.register(); ++ V2218.register(); ++ // V1.16 ++ V2501.register(); ++ V2502.register(); ++ V2503.register(); ++ V2505.register(); ++ V2508.register(); ++ V2509.register(); ++ V2511.register(); ++ V2514.register(); ++ V2516.register(); ++ V2518.register(); ++ V2519.register(); ++ V2522.register(); ++ V2523.register(); ++ V2527.register(); ++ V2528.register(); ++ V2529.register(); ++ V2531.register(); ++ V2533.register(); ++ V2535.register(); ++ V2550.register(); ++ V2551.register(); ++ V2552.register(); ++ V2553.register(); ++ V2558.register(); ++ V2568.register(); ++ // V1.17 ++ // WARN: Mojang registers V2671 under 2571, but that version predates 1.16.5? So it looks like a typo... ++ // I changed it to 2671, just so that it's after 1.16.5, but even then this looks misplaced... Thankfully this is ++ // the first datafixer, and all it does is add a walker, so I think even if the version here is just wrong it will ++ // work. ++ V2671.register(); ++ V2679.register(); ++ V2680.register(); ++ // V2684 is registering a simple tile entity (skulk sensor) ++ V2686.register(); ++ V2688.register(); ++ V2690.register(); ++ V2691.register(); ++ V2696.register(); ++ V2700.register(); ++ V2701.register(); ++ V2702.register(); ++ // In reference to V2671, why the fuck is goat being registered again? For this obvious reason, V2704 is absent. ++ V2707.register(); ++ V2710.register(); ++ V2717.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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/datatypes/MCValueType.java +@@ -0,0 +1,83 @@ ++package ca.spottedleaf.dataconverter.minecraft.datatypes; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.converters.datatypes.DataHook; ++import ca.spottedleaf.dataconverter.converters.datatypes.DataType; ++import ca.spottedleaf.dataconverter.minecraft.MCVersionRegistry; ++import ca.spottedleaf.dataconverter.util.Long2ObjectArraySortedMap; ++import java.util.ArrayList; ++import java.util.List; ++ ++public class MCValueType extends DataType { ++ ++ public final String name; ++ ++ protected final ArrayList> converters = new ArrayList<>(); ++ protected final Long2ObjectArraySortedMap>> structureHooks = new Long2ObjectArraySortedMap<>(); ++ ++ public MCValueType(final String name) { ++ this.name = name; ++ } ++ ++ public void addStructureHook(final int minVersion, final DataHook hook) { ++ this.addStructureHook(minVersion, 0, hook); ++ } ++ ++ public void addStructureHook(final int minVersion, final int versionStep, final DataHook hook) { ++ this.structureHooks.computeIfAbsent(DataConverter.encodeVersions(minVersion, versionStep), (final long keyInMap) -> { ++ return new ArrayList<>(); ++ }).add(hook); ++ } ++ ++ public void addConverter(final DataConverter converter) { ++ MCVersionRegistry.checkVersion(converter.getEncodedVersion()); ++ this.converters.add(converter); ++ this.converters.sort(DataConverter.LOWEST_VERSION_COMPARATOR); ++ } ++ ++ @Override ++ public Object convert(final Object data, final long fromVersion, final long toVersion) { ++ Object ret = null; ++ final List> converters = this.converters; ++ ++ for (int i = 0, len = converters.size(); i < len; ++i) { ++ final DataConverter converter = converters.get(i); ++ final long converterVersion = converter.getEncodedVersion(); ++ ++ if (converterVersion <= fromVersion) { ++ continue; ++ } ++ ++ if (converterVersion > toVersion) { ++ break; ++ } ++ ++ final List> hooks = this.structureHooks.getFloor(converterVersion); ++ ++ if (hooks != null) { ++ for (int k = 0, klen = hooks.size(); k < klen; ++k) { ++ final Object replace = hooks.get(k).preHook(ret == null ? data : ret, fromVersion, toVersion); ++ if (replace != null) { ++ ret = replace; ++ } ++ } ++ } ++ ++ final Object converted = converter.convert(ret == null ? data : ret, fromVersion, toVersion); ++ if (converted != null) { ++ ret = converted; ++ } ++ ++ 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); ++ if (replace != null) { ++ ret = replace; ++ } ++ } ++ } ++ } ++ ++ return ret; ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/hooks/DataHookEnforceNamespacedID.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/hooks/DataHookEnforceNamespacedID.java +new file mode 100644 +index 0000000000000000000000000000000000000000..26a03007a4386ff037a1ae50045d0c44dd438235 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/hooks/DataHookEnforceNamespacedID.java +@@ -0,0 +1,35 @@ ++package ca.spottedleaf.dataconverter.minecraft.hooks; ++ ++import ca.spottedleaf.dataconverter.converters.datatypes.DataHook; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.util.NamespaceUtil; ++ ++public class DataHookEnforceNamespacedID implements DataHook, MapType> { ++ ++ private final String path; ++ ++ public DataHookEnforceNamespacedID() { ++ this("id"); ++ } ++ ++ public DataHookEnforceNamespacedID(final String path) { ++ this.path = path; ++ } ++ ++ @Override ++ public MapType preHook(final MapType data, final long fromVersion, final long toVersion) { ++ final String id = data.getString(this.path); ++ if (id != null) { ++ final String replace = NamespaceUtil.correctNamespaceOrNull(id); ++ if (replace != null) { ++ data.setString(this.path, replace); ++ } ++ } ++ return null; ++ } ++ ++ @Override ++ public MapType postHook(final MapType data, final long fromVersion, final long toVersion) { ++ return null; ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/hooks/DataHookValueTypeEnforceNamespaced.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/hooks/DataHookValueTypeEnforceNamespaced.java +new file mode 100644 +index 0000000000000000000000000000000000000000..7f88487e7db589070512fafef1eb243ae29a379a +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/hooks/DataHookValueTypeEnforceNamespaced.java +@@ -0,0 +1,20 @@ ++package ca.spottedleaf.dataconverter.minecraft.hooks; ++ ++import ca.spottedleaf.dataconverter.converters.datatypes.DataHook; ++import ca.spottedleaf.dataconverter.util.NamespaceUtil; ++ ++public class DataHookValueTypeEnforceNamespaced implements DataHook { ++ ++ @Override ++ public Object preHook(final Object data, final long fromVersion, final long toVersion) { ++ if (data instanceof String) { ++ return NamespaceUtil.correctNamespaceOrNull((String)data); ++ } ++ return null; ++ } ++ ++ @Override ++ public Object postHook(final Object data, final long fromVersion, final long toVersion) { ++ return null; ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V100.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V100.java +new file mode 100644 +index 0000000000000000000000000000000000000000..7e8f42eb57c12c885a1c17eafab1c9d9be4d8963 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V100.java +@@ -0,0 +1,159 @@ ++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.block_name.DataWalkerBlockNames; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItems; ++import ca.spottedleaf.dataconverter.minecraft.walkers.generic.WalkerUtils; ++import ca.spottedleaf.dataconverter.types.ObjectType; ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.Types; ++ ++public final class V100 { ++ ++ protected static final int VERSION = MCVersions.V15W32A; ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final ListType equipment = data.getList("Equipment", ObjectType.MAP); ++ data.remove("Equipment"); ++ ++ if (equipment != null) { ++ if (equipment.size() > 0 && data.getListUnchecked("HandItems") == null) { ++ final ListType handItems = Types.NBT.createEmptyList(); ++ data.setList("HandItems", handItems); ++ handItems.addMap(equipment.getMap(0)); ++ handItems.addMap(Types.NBT.createEmptyMap()); ++ } ++ ++ if (equipment.size() > 1 && data.getListUnchecked("ArmorItems") == null) { ++ final ListType armorItems = Types.NBT.createEmptyList(); ++ data.setList("ArmorItems", armorItems); ++ for (int i = 1; i < Math.min(equipment.size(), 5); ++i) { ++ armorItems.addMap(equipment.getMap(i)); ++ } ++ } ++ } ++ ++ final ListType dropChances = data.getList("DropChances", ObjectType.FLOAT); ++ data.remove("DropChances"); ++ ++ if (dropChances != null) { ++ if (data.getListUnchecked("HandDropChances") == null) { ++ final ListType handDropChances = Types.NBT.createEmptyList(); ++ data.setList("HandDropChances", handDropChances); ++ if (0 < dropChances.size()) { ++ handDropChances.addFloat(dropChances.getFloat(0)); ++ } else { ++ handDropChances.addFloat(0.0F); ++ } ++ handDropChances.addFloat(0.0F); ++ } ++ ++ if (data.getListUnchecked("ArmorDropChances") == null) { ++ final ListType armorDropChances = Types.NBT.createEmptyList(); ++ data.setList("ArmorDropChances", armorDropChances); ++ for (int i = 1; i < 5; ++i) { ++ if (i < dropChances.size()) { ++ armorDropChances.addFloat(dropChances.getFloat(i)); ++ } else { ++ armorDropChances.addFloat(0.0F); ++ } ++ } ++ } ++ } ++ ++ return null; ++ } ++ }); ++ ++ registerMob("ArmorStand"); ++ registerMob("Creeper"); ++ registerMob("Skeleton"); ++ registerMob("Spider"); ++ registerMob("Giant"); ++ registerMob("Zombie"); ++ registerMob("Slime"); ++ registerMob("Ghast"); ++ registerMob("PigZombie"); ++ registerMob("Enderman"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "Enderman", new DataWalkerBlockNames("carried")); ++ registerMob("CaveSpider"); ++ registerMob("Silverfish"); ++ registerMob("Blaze"); ++ registerMob("LavaSlime"); ++ registerMob("EnderDragon"); ++ registerMob("WitherBoss"); ++ registerMob("Bat"); ++ registerMob("Witch"); ++ registerMob("Endermite"); ++ registerMob("Guardian"); ++ registerMob("Pig"); ++ registerMob("Sheep"); ++ registerMob("Cow"); ++ registerMob("Chicken"); ++ registerMob("Squid"); ++ registerMob("Wolf"); ++ registerMob("MushroomCow"); ++ registerMob("SnowMan"); ++ registerMob("Ozelot"); ++ registerMob("VillagerGolem"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "EntityHorse", new DataWalkerItemLists("Items", "ArmorItems", "HandItems")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "EntityHorse", new DataWalkerItems("ArmorItem", "SaddleItem")); ++ registerMob("Rabbit"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "Villager", (final MapType data, final long fromVersion, final long toVersion) -> { ++ WalkerUtils.convertList(MCTypeRegistry.ITEM_STACK, data, "Inventory", fromVersion, toVersion); ++ ++ final MapType offers = data.getMap("Offers"); ++ if (offers != null) { ++ final ListType recipes = offers.getList("Recipes", ObjectType.MAP); ++ if (recipes != null) { ++ for (int i = 0, len = recipes.size(); i < len; ++i) { ++ final MapType recipe = recipes.getMap(i); ++ WalkerUtils.convert(MCTypeRegistry.ITEM_STACK, recipe, "buy", fromVersion, toVersion); ++ WalkerUtils.convert(MCTypeRegistry.ITEM_STACK, recipe, "buyB", fromVersion, toVersion); ++ WalkerUtils.convert(MCTypeRegistry.ITEM_STACK, recipe, "sell", fromVersion, toVersion); ++ } ++ } ++ } ++ ++ WalkerUtils.convertList(MCTypeRegistry.ITEM_STACK, data, "ArmorItems", fromVersion, toVersion); ++ WalkerUtils.convertList(MCTypeRegistry.ITEM_STACK, data, "HandItems", fromVersion, toVersion); ++ ++ return null; ++ }); ++ registerMob("Shulker"); ++ ++ MCTypeRegistry.STRUCTURE.addStructureWalker(VERSION, (final MapType data, final long fromVersion, final long toVersion) -> { ++ final ListType entities = data.getList("entities", ObjectType.MAP); ++ if (entities != null) { ++ for (int i = 0, len = entities.size(); i < len; ++i) { ++ WalkerUtils.convert(MCTypeRegistry.ENTITY, entities.getMap(i), "nbt", fromVersion, toVersion); ++ } ++ } ++ ++ final ListType blocks = data.getList("blocks", ObjectType.MAP); ++ if (blocks != null) { ++ for (int i = 0, len = blocks.size(); i < len; ++i) { ++ WalkerUtils.convert(MCTypeRegistry.TILE_ENTITY, blocks.getMap(i), "nbt", fromVersion, toVersion); ++ } ++ } ++ ++ WalkerUtils.convertList(MCTypeRegistry.BLOCK_STATE, data, "palette", fromVersion, toVersion); ++ ++ return null; ++ }); ++ } ++ ++ private V100() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V101.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V101.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f91caab4432c87238d6ac2453068bb2ef05f7c35 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V101.java +@@ -0,0 +1,73 @@ ++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; ++import com.google.gson.JsonParseException; ++import net.minecraft.network.chat.Component; ++import net.minecraft.network.chat.TextComponent; ++import net.minecraft.util.GsonHelper; ++import net.minecraft.util.datafix.fixes.BlockEntitySignTextStrictJsonFix; ++ ++public final class V101 { ++ ++ protected static final int VERSION = MCVersions.V15W32A + 1; ++ ++ protected static void updateLine(final MapType data, final String path) { ++ final String textString = data.getString(path); ++ if (textString == null || textString.isEmpty() || "null".equals(textString)) { ++ data.setString(path, Component.Serializer.toJson(TextComponent.EMPTY)); ++ return; ++ } ++ ++ Component component = null; ++ ++ if (textString.charAt(0) == '"' && textString.charAt(textString.length() - 1) == '"' ++ || textString.charAt(0) == '{' && textString.charAt(textString.length() - 1) == '}') { ++ try { ++ component = GsonHelper.fromJson(BlockEntitySignTextStrictJsonFix.GSON, textString, Component.class, true); ++ if (component == null) { ++ component = TextComponent.EMPTY; ++ } ++ } catch (final JsonParseException ignored) {} ++ ++ if (component == null) { ++ try { ++ component = Component.Serializer.fromJson(textString); ++ } catch (final JsonParseException ignored) {} ++ } ++ ++ if (component == null) { ++ try { ++ component = Component.Serializer.fromJsonLenient(textString); ++ } catch (final JsonParseException ignored) {} ++ } ++ ++ if (component == null) { ++ component = new TextComponent(textString); ++ } ++ } else { ++ component = new TextComponent(textString); ++ } ++ ++ data.setString(path, Component.Serializer.toJson(component)); ++ } ++ ++ public static void register() { ++ MCTypeRegistry.TILE_ENTITY.addConverterForId("Sign", new DataConverter<>(VERSION) { ++ ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ updateLine(data, "Text1"); ++ updateLine(data, "Text2"); ++ updateLine(data, "Text3"); ++ updateLine(data, "Text4"); ++ return null; ++ } ++ }); ++ } ++ ++ private V101() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V102.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V102.java +new file mode 100644 +index 0000000000000000000000000000000000000000..660134f3fdc8db11033b310dbf52aed328bf4d99 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V102.java +@@ -0,0 +1,87 @@ ++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.HelperItemNameV102; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.types.ObjectType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.Types; ++import org.apache.logging.log4j.LogManager; ++import org.apache.logging.log4j.Logger; ++ ++public final class V102 { ++ ++ private static final Logger LOGGER = LogManager.getLogger(); ++ ++ protected static final int VERSION = MCVersions.V15W32A + 2; ++ ++ public static void register() { ++ // V102 -> V15W32A + 2 ++ // V102 schema only modifies ITEM_STACK to have only a string ID, but our ITEM_NAME is generic (int or String) so we don't ++ // actually need to update the walker ++ ++ MCTypeRegistry.ITEM_NAME.addConverter(new DataConverter<>(VERSION) { ++ @Override ++ public Object convert(final Object data, final long sourceVersion, final long toVersion) { ++ if (!(data instanceof Number)) { ++ return null; ++ } ++ final int id = ((Number)data).intValue(); ++ final String remap = HelperItemNameV102.getNameFromId(id); ++ if (remap == null) { ++ LOGGER.warn("Unknown legacy integer id (V102) " + id); ++ } ++ return remap == null ? HelperItemNameV102.getNameFromId(0) : remap; ++ } ++ }); ++ ++ MCTypeRegistry.ITEM_STACK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (!data.hasKey("id", ObjectType.NUMBER)) { ++ return null; ++ } ++ ++ final int id = data.getInt("id"); ++ ++ String remap = HelperItemNameV102.getNameFromId(id); ++ if (remap == null) { ++ LOGGER.warn("Unknown legacy integer id (V102) " + id); ++ remap = HelperItemNameV102.getNameFromId(0); ++ } ++ ++ data.setString("id", remap); ++ ++ return null; ++ } ++ }); ++ MCTypeRegistry.ITEM_STACK.addConverterForId("minecraft:potion", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final short damage = data.getShort("Damage"); ++ if (damage != 0) { ++ data.setShort("Damage", (short)0); ++ } ++ MapType tag = data.getMap("tag"); ++ if (tag == null) { ++ tag = Types.NBT.createEmptyMap(); ++ data.setMap("tag", tag); ++ } ++ ++ if (!tag.hasKey("Potion", ObjectType.STRING)) { ++ final String converted = HelperItemNameV102.getPotionNameFromId(damage); ++ tag.setString("Potion", converted == null ? "minecraft:water" : converted); ++ if ((damage & 16384) == 16384) { ++ data.setString("id", "minecraft:splash_potion"); ++ } ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++ private V102() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1022.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1022.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e251ead28d7d90937ae5871ffac489c1161e6e87 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1022.java +@@ -0,0 +1,45 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++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.MapType; ++ ++public final class V1022 { ++ ++ protected static final int VERSION = MCVersions.V17W06A; ++ ++ public static void register() { ++ MCTypeRegistry.PLAYER.addStructureWalker(VERSION, (final MapType data, final long fromVersion, final long toVersion) -> { ++ final MapType rootVehicle = data.getMap("RootVehicle"); ++ if (rootVehicle != null) { ++ WalkerUtils.convert(MCTypeRegistry.ENTITY, rootVehicle, "Entity", fromVersion, toVersion); ++ } ++ ++ WalkerUtils.convertList(MCTypeRegistry.ITEM_STACK, data, "Inventory", fromVersion, toVersion); ++ WalkerUtils.convertList(MCTypeRegistry.ITEM_STACK, data, "EnderItems", fromVersion, toVersion); ++ ++ WalkerUtils.convert(MCTypeRegistry.ENTITY, data, "ShoulderEntityLeft", fromVersion, toVersion); ++ WalkerUtils.convert(MCTypeRegistry.ENTITY, data, "ShoulderEntityRight", fromVersion, toVersion); ++ ++ final MapType recipeBook = data.getMap("recipeBook"); ++ if (recipeBook != null) { ++ WalkerUtils.convertList(MCTypeRegistry.RECIPE, recipeBook, "recipes", fromVersion, toVersion); ++ WalkerUtils.convertList(MCTypeRegistry.RECIPE, recipeBook, "toBeDisplayed", fromVersion, toVersion); ++ } ++ ++ return null; ++ }); ++ ++ MCTypeRegistry.HOTBAR.addStructureWalker(VERSION, (final MapType data, final long fromVersion, final long toVersion) -> { ++ for (final String key : data.keys()) { ++ WalkerUtils.convertList(MCTypeRegistry.ITEM_STACK, data, key, fromVersion, toVersion); ++ } ++ ++ return null; ++ }); ++ } ++ ++ private V1022() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V105.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V105.java +new file mode 100644 +index 0000000000000000000000000000000000000000..544f6a54041147a8c9ee3ff52c31c480a3696924 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V105.java +@@ -0,0 +1,49 @@ ++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.HelperSpawnEggNameV105; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.types.ObjectType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.Types; ++ ++public final class V105 { ++ ++ protected static final int VERSION = MCVersions.V15W32C + 1; ++ ++ public static void register() { ++ MCTypeRegistry.ITEM_STACK.addConverterForId("minecraft:spawn_egg", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ MapType tag = data.getMap("tag"); ++ if (tag == null) { ++ tag = Types.NBT.createEmptyMap(); ++ } ++ ++ final short damage = data.getShort("Damage"); ++ if (damage != 0) { ++ data.setShort("Damage", (short)0); ++ } ++ ++ MapType entityTag = tag.getMap("EntityTag"); ++ if (entityTag == null) { ++ entityTag = Types.NBT.createEmptyMap(); ++ } ++ ++ if (!entityTag.hasKey("id", ObjectType.STRING)) { ++ final String converted = HelperSpawnEggNameV105.getSpawnNameFromId(damage); ++ if (converted != null) { ++ entityTag.setString("id", converted); ++ tag.setMap("EntityTag", entityTag); ++ data.setMap("tag", tag); ++ } ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++ private V105() {} ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V106.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V106.java +new file mode 100644 +index 0000000000000000000000000000000000000000..951838b0f4f2b4ed82d707706ef15d779f3f41eb +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V106.java +@@ -0,0 +1,84 @@ ++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.ObjectType; ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.Types; ++ ++public final class V106 { ++ ++ protected static final int VERSION = MCVersions.V15W32C + 2; ++ ++ public static void register() { ++ // V106 -> V15W32C + 2 ++ ++ MCTypeRegistry.UNTAGGED_SPAWNER.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ // While all converters for spawners check the id for this version, we don't because spawners exist in minecarts. ooops! Loading a chunk ++ // with a minecart spawner from 1.7.10 in 1.16.5 vanilla will fail to convert! Clearly there was a mistake in how they ++ // used and applied spawner converters. In anycase, do not check the id - we are not guaranteed to be a tile ++ // entity. We can be a regular old minecart spawner. And we know we are a spawner because this is only called from data walkers. ++ ++ final String entityId = data.getString("EntityId"); ++ if (entityId != null) { ++ data.remove("EntityId"); ++ MapType spawnData = data.getMap("SpawnData"); ++ if (spawnData == null) { ++ spawnData = Types.NBT.createEmptyMap(); ++ data.setMap("SpawnData", spawnData); ++ } ++ spawnData.setString("id", entityId.isEmpty() ? "Pig" : entityId); ++ } ++ ++ final ListType spawnPotentials = data.getList("SpawnPotentials", ObjectType.MAP); ++ if (spawnPotentials != null) { ++ for (int i = 0, len = spawnPotentials.size(); i < len; ++i) { ++ // convert to standard entity format (it's not a coincidence a walker for spawners is only added ++ // in this version) ++ final MapType spawn = spawnPotentials.getMap(i); ++ final String spawnType = spawn.getString("Type"); ++ if (spawnType == null) { ++ continue; ++ } ++ spawn.remove("Type"); ++ ++ MapType properties = spawn.getMap("Properties"); ++ if (properties == null) { ++ properties = Types.NBT.createEmptyMap(); ++ } else { ++ spawn.remove("Properties"); ++ } ++ ++ properties.setString("id", spawnType); ++ ++ spawn.setMap("Entity", properties); ++ } ++ } ++ ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.UNTAGGED_SPAWNER.addStructureWalker(VERSION, (final MapType data, final long fromVersion, final long toVersion) -> { ++ final ListType spawnPotentials = data.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, "Entity", fromVersion, toVersion); ++ } ++ } ++ ++ WalkerUtils.convert(MCTypeRegistry.ENTITY, data, "SpawnData", fromVersion, toVersion); ++ ++ return null; ++ }); ++ } ++ ++ private V106() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V107.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V107.java +new file mode 100644 +index 0000000000000000000000000000000000000000..aa8c8d22ee2a77604d923b62f5a93ede9b3f333f +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V107.java +@@ -0,0 +1,44 @@ ++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 V107 { ++ ++ protected static final int VERSION = MCVersions.V15W32C + 3; ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("Minecart", new DataConverter<>(VERSION) { ++ protected final String[] MINECART_IDS = new String[] { ++ "MinecartRideable", // 0 ++ "MinecartChest", // 1 ++ "MinecartFurnace", // 2 ++ "MinecartTNT", // 3 ++ "MinecartSpawner", // 4 ++ "MinecartHopper", // 5 ++ "MinecartCommandBlock" // 6 ++ }; ++ // Vanilla does not use all of the IDs here. The legacy (pre DFU) code does, so I'm going to use them. ++ // No harm in catching more cases here. ++ ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ String newId = "MinecartRideable"; // dfl ++ final int type = data.getInt("Type"); ++ data.remove("Type"); ++ ++ if (type >= 0 && type < MINECART_IDS.length) { ++ newId = MINECART_IDS[type]; ++ } ++ data.setString("id", newId); ++ ++ return null; ++ } ++ }); ++ } ++ ++ private V107() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V108.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V108.java +new file mode 100644 +index 0000000000000000000000000000000000000000..2300f4db851510cfa3b8dd704b555e12bc4ea725 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V108.java +@@ -0,0 +1,47 @@ ++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; ++import org.apache.logging.log4j.LogManager; ++import org.apache.logging.log4j.Logger; ++import java.util.UUID; ++ ++public final class V108 { ++ ++ private static final Logger LOGGER = LogManager.getLogger(); ++ ++ protected static final int VERSION = MCVersions.V15W32C + 4; ++ ++ public static void register() { ++ // Convert String UUID into UUIDMost and UUIDLeast ++ MCTypeRegistry.ENTITY.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final String uuidString = data.getString("UUID"); ++ ++ if (uuidString == null) { ++ return null; ++ } ++ data.remove("UUID"); ++ ++ final UUID uuid; ++ try { ++ uuid = UUID.fromString(uuidString); ++ } catch (final Exception ex) { ++ LOGGER.warn("Failed to parse UUID for legacy entity (V108): " + uuidString, ex); ++ return null; ++ } ++ ++ data.setLong("UUIDMost", uuid.getMostSignificantBits()); ++ data.setLong("UUIDLeast", uuid.getLeastSignificantBits()); ++ ++ return null; ++ } ++ }); ++ } ++ ++ private V108() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V109.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V109.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5e0e52fa2d8988ca973f8a97b2374a8c3d4ef80c +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V109.java +@@ -0,0 +1,83 @@ ++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; ++import com.google.common.collect.Sets; ++import java.util.Set; ++ ++public final class V109 { ++ ++ protected static final int VERSION = MCVersions.V15W32C + 5; ++ ++ // DFU declares this exact field but leaves it unused. Not sure why, legacy conversion system checked if the ID matched. ++ // I'm going to leave it here unused as well, just in case it's needed in the future. ++ protected static final Set ENTITIES = Sets.newHashSet( ++ "ArmorStand", ++ "Bat", ++ "Blaze", ++ "CaveSpider", ++ "Chicken", ++ "Cow", ++ "Creeper", ++ "EnderDragon", ++ "Enderman", ++ "Endermite", ++ "EntityHorse", ++ "Ghast", ++ "Giant", ++ "Guardian", ++ "LavaSlime", ++ "MushroomCow", ++ "Ozelot", ++ "Pig", ++ "PigZombie", ++ "Rabbit", ++ "Sheep", ++ "Shulker", ++ "Silverfish", ++ "Skeleton", ++ "Slime", ++ "SnowMan", ++ "Spider", ++ "Squid", ++ "Villager", ++ "VillagerGolem", ++ "Witch", ++ "WitherBoss", ++ "Wolf", ++ "Zombie" ++ ); ++ ++ public static void register() { ++ // Converts health to be in float, and cleans up whatever the hell was going on with HealF and Health... ++ MCTypeRegistry.ENTITY.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final Number healF = data.getNumber("HealF"); ++ final Number heal = data.getNumber("Health"); ++ ++ final float newHealth; ++ ++ if (healF != null) { ++ data.remove("HealF"); ++ newHealth = healF.floatValue(); ++ } else { ++ if (heal == null) { ++ return null; ++ } ++ ++ newHealth = heal.floatValue(); ++ } ++ ++ data.setFloat("Health", newHealth); ++ ++ return null; ++ } ++ }); ++ } ++ ++ private V109() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V110.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V110.java +new file mode 100644 +index 0000000000000000000000000000000000000000..9771810a1f1cbf760fd9a8a5fd575f6052f40ea9 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V110.java +@@ -0,0 +1,39 @@ ++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.ObjectType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.Types; ++ ++public final class V110 { ++ ++ protected static final int VERSION = MCVersions.V15W32C + 6; ++ ++ public static void register() { ++ // Moves the Saddle boolean to be an actual saddle item. Note: The data walker for the SaddleItem exists ++ // in V99, it doesn't need to be added here. ++ MCTypeRegistry.ENTITY.addConverterForId("EntityHorse", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (!data.getBoolean("Saddle") || data.hasKey("SaddleItem", ObjectType.MAP)) { ++ return null; ++ } ++ ++ final MapType saddleItem = Types.NBT.createEmptyMap(); ++ data.remove("Saddle"); ++ data.setMap("SaddleItem", saddleItem); ++ ++ saddleItem.setString("id", "minecraft:saddle"); ++ saddleItem.setByte("Count", (byte)1); ++ saddleItem.setShort("Damage", (short)0); ++ ++ return null; ++ } ++ }); ++ } ++ ++ private V110() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V111.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V111.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5bae7effda7761a3f2a0a2ce550d867cb2c18b99 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V111.java +@@ -0,0 +1,67 @@ ++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 V111 { ++ ++ protected static final int VERSION = MCVersions.V15W33B; ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("Painting", new EntityRotationFix("Painting")); ++ MCTypeRegistry.ENTITY.addConverterForId("ItemFrame", new EntityRotationFix("ItemFrame")); ++ } ++ ++ private V111() {} ++ ++ protected static final class EntityRotationFix extends DataConverter, MapType> { ++ ++ private static final int[][] DIRECTIONS = new int[][] { ++ {0, 0, 1}, ++ {-1, 0, 0}, ++ {0, 0, -1}, ++ {1, 0, 0} ++ }; ++ ++ protected final String id; ++ ++ public EntityRotationFix(final String id) { ++ super(VERSION); ++ this.id = id; ++ } ++ ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (data.getNumber("Facing") != null) { ++ return null; ++ } ++ ++ final Number direction = data.getNumber("Direction"); ++ final int facing; ++ if (direction != null) { ++ data.remove("Direction"); ++ facing = direction.intValue() % DIRECTIONS.length; ++ final int[] offsets = DIRECTIONS[facing]; ++ data.setInt("TileX", data.getInt("TileX") + offsets[0]); ++ data.setInt("TileY", data.getInt("TileY") + offsets[1]); ++ data.setInt("TileZ", data.getInt("TileZ") + offsets[2]); ++ if ("ItemFrame".equals(data.getString("id"))) { ++ final Number rotation = data.getNumber("ItemRotation"); ++ if (rotation != null) { ++ data.setByte("ItemRotation", (byte)(rotation.byteValue() * 2)); ++ } ++ } ++ } else { ++ facing = data.getByte("Dir") % DIRECTIONS.length; ++ data.remove("Dir"); ++ } ++ ++ data.setByte("Facing", (byte)facing); ++ ++ return null; ++ } ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1125.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1125.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a6d3c28e8c97b53f388c03ccad3449390937d3b2 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1125.java +@@ -0,0 +1,102 @@ ++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.hooks.DataHookValueTypeEnforceNamespaced; ++import ca.spottedleaf.dataconverter.minecraft.walkers.generic.WalkerUtils; ++import ca.spottedleaf.dataconverter.types.ObjectType; ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.Types; ++ ++public final class V1125 { ++ ++ protected static final int VERSION = MCVersions.V17W15A; ++ protected static final int BED_BLOCK_ID = 416; ++ ++ public static void register() { ++ MCTypeRegistry.CHUNK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType level = data.getMap("Level"); ++ if (level == null) { ++ return null; ++ } ++ ++ final int chunkX = level.getInt("xPos"); ++ final int chunkZ = level.getInt("zPos"); ++ ++ final ListType sections = level.getList("Sections", ObjectType.MAP); ++ if (sections == null) { ++ return null; ++ } ++ ++ ListType tileEntities = level.getList("TileEntities", ObjectType.MAP); ++ if (tileEntities == null) { ++ tileEntities = Types.NBT.createEmptyList(); ++ level.setList("TileEntities", tileEntities); ++ } ++ ++ for (int i = 0, len = sections.size(); i < len; ++i) { ++ final MapType section = sections.getMap(i); ++ ++ final byte sectionY = section.getByte("Y"); ++ final byte[] blocks = section.getBytes("Blocks"); ++ ++ if (blocks == null) { ++ continue; ++ } ++ ++ for (int blockIndex = 0; blockIndex < blocks.length; ++blockIndex) { ++ if (BED_BLOCK_ID != ((blocks[blockIndex] & 255) << 4)) { ++ continue; ++ } ++ ++ final int localX = blockIndex & 15; ++ final int localZ = (blockIndex >> 4) & 15; ++ final int localY = (blockIndex >> 8) & 15; ++ ++ final MapType newTile = Types.NBT.createEmptyMap(); ++ newTile.setString("id", "minecraft:bed"); ++ newTile.setInt("x", localX + (chunkX << 4)); ++ newTile.setInt("y", localY + (sectionY << 4)); ++ newTile.setInt("z", localZ + (chunkZ << 4)); ++ newTile.setShort("color", (short)14); // Red ++ ++ tileEntities.addMap(newTile); ++ } ++ } ++ ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.ITEM_STACK.addConverterForId("minecraft:bed", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (data.getShort("Damage") == 0) { ++ data.setShort("Damage", (short)14); // Red ++ } ++ ++ return null; ++ } ++ }); ++ ++ ++ MCTypeRegistry.ADVANCEMENTS.addStructureWalker(VERSION, (final MapType data, final long fromVersion, final long toVersion) -> { ++ WalkerUtils.convertKeys(MCTypeRegistry.BIOME, data.getMap("minecraft:adventure/adventuring_time"), "criteria", fromVersion, toVersion); ++ WalkerUtils.convertKeys(MCTypeRegistry.ENTITY_NAME, data.getMap("minecraft:adventure/kill_a_mob"), "criteria", fromVersion, toVersion); ++ WalkerUtils.convertKeys(MCTypeRegistry.ENTITY_NAME, data.getMap("minecraft:adventure/kill_all_mobs"), "criteria", fromVersion, toVersion); ++ WalkerUtils.convertKeys(MCTypeRegistry.ENTITY_NAME, data.getMap("minecraft:adventure/bred_all_animals"), "criteria", fromVersion, toVersion); ++ ++ return null; ++ }); ++ ++ // Enforce namespacing for ids ++ MCTypeRegistry.BIOME.addStructureHook(VERSION, new DataHookValueTypeEnforceNamespaced()); ++ } ++ ++ private V1125() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V113.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V113.java +new file mode 100644 +index 0000000000000000000000000000000000000000..8b93bba2b084e20d346461f53d2f7662c3d6238b +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V113.java +@@ -0,0 +1,41 @@ ++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.ObjectType; ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.MapType; ++ ++public final class V113 { ++ ++ protected static final int VERSION = MCVersions.V15W33C + 1; ++ ++ protected static void checkList(final MapType data, final String id, final int requiredLength) { ++ final ListType list = data.getList(id, ObjectType.FLOAT); ++ if (list != null && list.size() == requiredLength) { ++ for (int i = 0; i < requiredLength; ++i) { ++ if (list.getFloat(i) != 0.0F) { ++ return; ++ } ++ } ++ } ++ ++ data.remove(id); ++ } ++ ++ public static void register() { ++ // Removes "HandDropChances" and "ArmorDropChances" if they're empty. ++ MCTypeRegistry.ENTITY.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ checkList(data, "HandDropChances", 2); ++ checkList(data, "ArmorDropChances", 4); ++ return null; ++ } ++ }); ++ } ++ ++ private V113() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1344.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1344.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ac390a6111ba1a4aae3d5726747f60f4929fa254 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1344.java +@@ -0,0 +1,177 @@ ++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; ++import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; ++ ++public final class V1344 { ++ ++ protected static final int VERSION = MCVersions.V1_12_2 + 1; ++ ++ private static final Int2ObjectOpenHashMap BUTTON_ID_TO_NAME = new Int2ObjectOpenHashMap<>(); ++ static { ++ BUTTON_ID_TO_NAME.put(0, "key.unknown"); ++ BUTTON_ID_TO_NAME.put(11, "key.0"); ++ BUTTON_ID_TO_NAME.put(2, "key.1"); ++ BUTTON_ID_TO_NAME.put(3, "key.2"); ++ BUTTON_ID_TO_NAME.put(4, "key.3"); ++ BUTTON_ID_TO_NAME.put(5, "key.4"); ++ BUTTON_ID_TO_NAME.put(6, "key.5"); ++ BUTTON_ID_TO_NAME.put(7, "key.6"); ++ BUTTON_ID_TO_NAME.put(8, "key.7"); ++ BUTTON_ID_TO_NAME.put(9, "key.8"); ++ BUTTON_ID_TO_NAME.put(10, "key.9"); ++ BUTTON_ID_TO_NAME.put(30, "key.a"); ++ BUTTON_ID_TO_NAME.put(40, "key.apostrophe"); ++ BUTTON_ID_TO_NAME.put(48, "key.b"); ++ BUTTON_ID_TO_NAME.put(43, "key.backslash"); ++ BUTTON_ID_TO_NAME.put(14, "key.backspace"); ++ BUTTON_ID_TO_NAME.put(46, "key.c"); ++ BUTTON_ID_TO_NAME.put(58, "key.caps.lock"); ++ BUTTON_ID_TO_NAME.put(51, "key.comma"); ++ BUTTON_ID_TO_NAME.put(32, "key.d"); ++ BUTTON_ID_TO_NAME.put(211, "key.delete"); ++ BUTTON_ID_TO_NAME.put(208, "key.down"); ++ BUTTON_ID_TO_NAME.put(18, "key.e"); ++ BUTTON_ID_TO_NAME.put(207, "key.end"); ++ BUTTON_ID_TO_NAME.put(28, "key.enter"); ++ BUTTON_ID_TO_NAME.put(13, "key.equal"); ++ BUTTON_ID_TO_NAME.put(1, "key.escape"); ++ BUTTON_ID_TO_NAME.put(33, "key.f"); ++ BUTTON_ID_TO_NAME.put(59, "key.f1"); ++ BUTTON_ID_TO_NAME.put(68, "key.f10"); ++ BUTTON_ID_TO_NAME.put(87, "key.f11"); ++ BUTTON_ID_TO_NAME.put(88, "key.f12"); ++ BUTTON_ID_TO_NAME.put(100, "key.f13"); ++ BUTTON_ID_TO_NAME.put(101, "key.f14"); ++ BUTTON_ID_TO_NAME.put(102, "key.f15"); ++ BUTTON_ID_TO_NAME.put(103, "key.f16"); ++ BUTTON_ID_TO_NAME.put(104, "key.f17"); ++ BUTTON_ID_TO_NAME.put(105, "key.f18"); ++ BUTTON_ID_TO_NAME.put(113, "key.f19"); ++ BUTTON_ID_TO_NAME.put(60, "key.f2"); ++ BUTTON_ID_TO_NAME.put(61, "key.f3"); ++ BUTTON_ID_TO_NAME.put(62, "key.f4"); ++ BUTTON_ID_TO_NAME.put(63, "key.f5"); ++ BUTTON_ID_TO_NAME.put(64, "key.f6"); ++ BUTTON_ID_TO_NAME.put(65, "key.f7"); ++ BUTTON_ID_TO_NAME.put(66, "key.f8"); ++ BUTTON_ID_TO_NAME.put(67, "key.f9"); ++ BUTTON_ID_TO_NAME.put(34, "key.g"); ++ BUTTON_ID_TO_NAME.put(41, "key.grave.accent"); ++ BUTTON_ID_TO_NAME.put(35, "key.h"); ++ BUTTON_ID_TO_NAME.put(199, "key.home"); ++ BUTTON_ID_TO_NAME.put(23, "key.i"); ++ BUTTON_ID_TO_NAME.put(210, "key.insert"); ++ BUTTON_ID_TO_NAME.put(36, "key.j"); ++ BUTTON_ID_TO_NAME.put(37, "key.k"); ++ BUTTON_ID_TO_NAME.put(82, "key.keypad.0"); ++ BUTTON_ID_TO_NAME.put(79, "key.keypad.1"); ++ BUTTON_ID_TO_NAME.put(80, "key.keypad.2"); ++ BUTTON_ID_TO_NAME.put(81, "key.keypad.3"); ++ BUTTON_ID_TO_NAME.put(75, "key.keypad.4"); ++ BUTTON_ID_TO_NAME.put(76, "key.keypad.5"); ++ BUTTON_ID_TO_NAME.put(77, "key.keypad.6"); ++ BUTTON_ID_TO_NAME.put(71, "key.keypad.7"); ++ BUTTON_ID_TO_NAME.put(72, "key.keypad.8"); ++ BUTTON_ID_TO_NAME.put(73, "key.keypad.9"); ++ BUTTON_ID_TO_NAME.put(78, "key.keypad.add"); ++ BUTTON_ID_TO_NAME.put(83, "key.keypad.decimal"); ++ BUTTON_ID_TO_NAME.put(181, "key.keypad.divide"); ++ BUTTON_ID_TO_NAME.put(156, "key.keypad.enter"); ++ BUTTON_ID_TO_NAME.put(141, "key.keypad.equal"); ++ BUTTON_ID_TO_NAME.put(55, "key.keypad.multiply"); ++ BUTTON_ID_TO_NAME.put(74, "key.keypad.subtract"); ++ BUTTON_ID_TO_NAME.put(38, "key.l"); ++ BUTTON_ID_TO_NAME.put(203, "key.left"); ++ BUTTON_ID_TO_NAME.put(56, "key.left.alt"); ++ BUTTON_ID_TO_NAME.put(26, "key.left.bracket"); ++ BUTTON_ID_TO_NAME.put(29, "key.left.control"); ++ BUTTON_ID_TO_NAME.put(42, "key.left.shift"); ++ BUTTON_ID_TO_NAME.put(219, "key.left.win"); ++ BUTTON_ID_TO_NAME.put(50, "key.m"); ++ BUTTON_ID_TO_NAME.put(12, "key.minus"); ++ BUTTON_ID_TO_NAME.put(49, "key.n"); ++ BUTTON_ID_TO_NAME.put(69, "key.num.lock"); ++ BUTTON_ID_TO_NAME.put(24, "key.o"); ++ BUTTON_ID_TO_NAME.put(25, "key.p"); ++ BUTTON_ID_TO_NAME.put(209, "key.page.down"); ++ BUTTON_ID_TO_NAME.put(201, "key.page.up"); ++ BUTTON_ID_TO_NAME.put(197, "key.pause"); ++ BUTTON_ID_TO_NAME.put(52, "key.period"); ++ BUTTON_ID_TO_NAME.put(183, "key.print.screen"); ++ BUTTON_ID_TO_NAME.put(16, "key.q"); ++ BUTTON_ID_TO_NAME.put(19, "key.r"); ++ BUTTON_ID_TO_NAME.put(205, "key.right"); ++ BUTTON_ID_TO_NAME.put(184, "key.right.alt"); ++ BUTTON_ID_TO_NAME.put(27, "key.right.bracket"); ++ BUTTON_ID_TO_NAME.put(157, "key.right.control"); ++ BUTTON_ID_TO_NAME.put(54, "key.right.shift"); ++ BUTTON_ID_TO_NAME.put(220, "key.right.win"); ++ BUTTON_ID_TO_NAME.put(31, "key.s"); ++ BUTTON_ID_TO_NAME.put(70, "key.scroll.lock"); ++ BUTTON_ID_TO_NAME.put(39, "key.semicolon"); ++ BUTTON_ID_TO_NAME.put(53, "key.slash"); ++ BUTTON_ID_TO_NAME.put(57, "key.space"); ++ BUTTON_ID_TO_NAME.put(20, "key.t"); ++ BUTTON_ID_TO_NAME.put(15, "key.tab"); ++ BUTTON_ID_TO_NAME.put(22, "key.u"); ++ BUTTON_ID_TO_NAME.put(200, "key.up"); ++ BUTTON_ID_TO_NAME.put(47, "key.v"); ++ BUTTON_ID_TO_NAME.put(17, "key.w"); ++ BUTTON_ID_TO_NAME.put(45, "key.x"); ++ BUTTON_ID_TO_NAME.put(21, "key.y"); ++ BUTTON_ID_TO_NAME.put(44, "key.z"); ++ } ++ ++ public static void register() { ++ MCTypeRegistry.OPTIONS.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ for (final String key : data.keys()) { ++ if (!key.startsWith("key_")) { ++ continue; ++ } ++ final String value = data.getString(key); ++ final int code; ++ try { ++ code = Integer.parseInt(value); ++ } catch (final NumberFormatException ex) { ++ continue; ++ } ++ ++ final String newEntry; ++ ++ if (code < 0) { ++ final int mouseCode = code + 100; ++ switch (mouseCode) { ++ case 0: ++ newEntry = "key.mouse.left"; ++ break; ++ case 1: ++ newEntry = "key.mouse.right"; ++ break; ++ case 2: ++ newEntry = "key.mouse.middle"; ++ break; ++ default: ++ newEntry = "key.mouse." + (mouseCode + 1); ++ break; ++ } ++ } else { ++ newEntry = BUTTON_ID_TO_NAME.getOrDefault(code, "key.unknown"); ++ } ++ ++ // No CMEs occur for existing entries in maps. ++ data.setString(key, newEntry); ++ } ++ return null; ++ } ++ }); ++ } ++ ++ private V1344() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V135.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V135.java +new file mode 100644 +index 0000000000000000000000000000000000000000..764a56fda5ee909ac47a0c1b3b581c8c26deb591 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V135.java +@@ -0,0 +1,61 @@ ++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.itemstack.DataWalkerItemLists; ++import ca.spottedleaf.dataconverter.minecraft.walkers.generic.WalkerUtils; ++import ca.spottedleaf.dataconverter.types.ObjectType; ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.Types; ++ ++public final class V135 { ++ ++ protected static final int VERSION = MCVersions.V15W40B + 1; ++ ++ public static void register() { ++ // In this update they changed the "Riding" value to be "Passengers", which is now a list. So it added ++ // support for multiple entities riding. Of course, Riding and Passenger are opposites - so it also will ++ // switch the data layout to be from highest rider to lowest rider, in terms of depth. ++ MCTypeRegistry.ENTITY.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(MapType data, final long sourceVersion, final long toVersion) { ++ MapType ret = null; ++ while (data.hasKey("Riding", ObjectType.MAP)) { ++ final MapType riding = data.getMap("Riding"); ++ data.remove("Riding"); ++ ++ final ListType passengers = Types.NBT.createEmptyList(); ++ riding.setList("Passengers", passengers); ++ passengers.addMap(data); ++ ++ ret = data = riding; ++ } ++ ++ return ret; ++ } ++ }); ++ ++ ++ MCTypeRegistry.PLAYER.addStructureWalker(VERSION, new DataWalkerItemLists("Inventory", "EnderItems")); ++ MCTypeRegistry.PLAYER.addStructureWalker(VERSION, (final MapType data, final long fromVersion, final long toVersion) -> { ++ final MapType rootVehicle = data.getMap("RootVehicle"); ++ if (rootVehicle != null) { ++ WalkerUtils.convert(MCTypeRegistry.ENTITY, rootVehicle, "Entity", fromVersion, toVersion); ++ } ++ ++ return null; ++ }); ++ ++ MCTypeRegistry.ENTITY.addStructureWalker(VERSION, (final MapType data, final long fromVersion, final long toVersion) -> { ++ WalkerUtils.convertList(MCTypeRegistry.ENTITY, data, "Passengers", fromVersion, toVersion); ++ ++ return null; ++ }); ++ ++ } ++ ++ private V135() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V143.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V143.java +new file mode 100644 +index 0000000000000000000000000000000000000000..451e837349e10f1e76ac7d9f5d49cbe0ff630f4d +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V143.java +@@ -0,0 +1,18 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.entity.ConverterAbstractEntityRename; ++ ++public final class V143 { ++ ++ protected static final int VERSION = MCVersions.V15W44B; ++ ++ public static void register() { ++ ConverterAbstractEntityRename.register(VERSION, (final String input) -> { ++ return "TippedArrow".equals(input) ? "Arrow" : null; ++ }); ++ } ++ ++ private V143() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1446.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1446.java +new file mode 100644 +index 0000000000000000000000000000000000000000..fc0ece569baed94bbf3cbbaa21a397fdc37e51e8 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1446.java +@@ -0,0 +1,36 @@ ++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 V1446 { ++ ++ protected static final int VERSION = MCVersions.V17W43B + 1; ++ ++ public static void register() { ++ MCTypeRegistry.OPTIONS.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ for (final String key : data.keys()) { ++ if (!key.startsWith("key_")) { ++ continue; ++ } ++ ++ final String value = data.getString(key); ++ ++ if (value.startsWith("key.mouse") || value.startsWith("scancode.")) { ++ continue; ++ } ++ ++ data.setString(key, "key.keyboard." + value.substring("key.".length())); ++ } ++ return null; ++ } ++ }); ++ } ++ ++ private V1446() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1450.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1450.java +new file mode 100644 +index 0000000000000000000000000000000000000000..711222cd33ee557b7f3d1f6ae73ad45d1caf6768 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1450.java +@@ -0,0 +1,25 @@ ++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.HelperBlockFlatteningV1450; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.types.MapType; ++ ++public final class V1450 { ++ ++ protected static final int VERSION = MCVersions.V17W46A + 1; ++ ++ public static void register() { ++ MCTypeRegistry.BLOCK_STATE.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType ret = HelperBlockFlatteningV1450.flattenNBT(data); ++ return ret == data ? null : ret.copy(); // copy to avoid problems with later state datafixers ++ } ++ }); ++ } ++ ++ private V1450() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1451.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1451.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a20d258814b0d2d0fa01d45be43a66987de19598 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1451.java +@@ -0,0 +1,512 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.converters.datatypes.DataHook; ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.chunk.ConverterFlattenChunk; ++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.HelperBlockFlatteningV1450; ++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.HelperItemNameV102; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemstack.ConverterFlattenItemStack; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemstack.ConverterFlattenSpawnEgg; ++import ca.spottedleaf.dataconverter.minecraft.converters.stats.ConverterFlattenStats; ++import ca.spottedleaf.dataconverter.minecraft.converters.entity.ConverterFlattenEntity; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.generic.DataWalkerTypePaths; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++import ca.spottedleaf.dataconverter.minecraft.walkers.generic.WalkerUtils; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItems; ++import ca.spottedleaf.dataconverter.minecraft.walkers.tile_entity.DataWalkerTileEntities; ++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 com.google.common.base.Splitter; ++import net.minecraft.resources.ResourceLocation; ++import net.minecraft.util.datafix.fixes.BlockStateData; ++import net.minecraft.util.datafix.fixes.EntityBlockStateFix; ++import org.apache.commons.lang3.math.NumberUtils; ++import java.util.Iterator; ++import java.util.List; ++import java.util.stream.Collectors; ++import java.util.stream.StreamSupport; ++ ++public final class V1451 { ++ ++ protected static final int VERSION = MCVersions.V17W47A; ++ ++ public static void register() { ++ // V0 ++ MCTypeRegistry.TILE_ENTITY.addWalker(VERSION, 0, "minecraft:trapped_chest", new DataWalkerItemLists("Items")); ++ ++ // V1 ++ MCTypeRegistry.CHUNK.addStructureConverter(new ConverterFlattenChunk()); ++ ++ MCTypeRegistry.CHUNK.addStructureWalker(VERSION, 1, (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.BLOCK_STATE, section, "Palette", fromVersion, toVersion); ++ } ++ } ++ ++ return null; ++ }); ++ ++ // V2 ++ MCTypeRegistry.TILE_ENTITY.addConverterForId("minecraft:piston", new DataConverter<>(VERSION, 2) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final int blockId = data.getInt("blockId"); ++ final int blockData = data.getInt("blockData") & 15; ++ ++ data.remove("blockId"); ++ data.remove("blockData"); ++ ++ data.setMap("blockState", HelperBlockFlatteningV1450.getNBTForId((blockId << 4) | blockData).copy()); // copy to avoid problems with later state datafixers ++ ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.TILE_ENTITY.addWalker(VERSION, 2, "minecraft:piston", new DataWalkerTypePaths<>(MCTypeRegistry.BLOCK_STATE, "blockState")); ++ ++ // V3 ++ ConverterFlattenEntity.register(); ++ MCTypeRegistry.ITEM_STACK.addConverterForId("minecraft:filled_map", new DataConverter<>(VERSION, 3) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ MapType tag = data.getMap("tag"); ++ if (tag == null) { ++ tag = Types.NBT.createEmptyMap(); ++ data.setMap("tag", tag); ++ } ++ ++ if (!tag.hasKey("map", ObjectType.NUMBER)) { // This if is from CB. as usual, no documentation from CB. I'm guessing it just wants to avoid possibly overwriting it. seems fine. ++ tag.setInt("map", data.getInt("Damage")); ++ } ++ ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.ENTITY.addWalker(VERSION, 3, "minecraft:potion", new DataWalkerItems("Potion")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, 3, "minecraft:arrow", new DataWalkerTypePaths<>(MCTypeRegistry.BLOCK_STATE, "inBlockState")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, 3, "minecraft:enderman", new DataWalkerItemLists("ArmorItems", "HandItems")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, 3, "minecraft:enderman", new DataWalkerTypePaths<>(MCTypeRegistry.BLOCK_STATE, "carriedBlockState")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, 3, "minecraft:falling_block", new DataWalkerTypePaths<>(MCTypeRegistry.BLOCK_STATE, "BlockState")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, 3, "minecraft:falling_block", new DataWalkerTileEntities("TileEntityData")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, 3, "minecraft:spectral_arrow", new DataWalkerTypePaths<>(MCTypeRegistry.BLOCK_STATE, "inBlockState")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, 3, "minecraft:chest_minecart", new DataWalkerTypePaths<>(MCTypeRegistry.BLOCK_STATE, "DisplayState")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, 3, "minecraft:chest_minecart", new DataWalkerItemLists("Items")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, 3, "minecraft:commandblock_minecart", new DataWalkerTypePaths<>(MCTypeRegistry.BLOCK_STATE, "DisplayState")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, 3, "minecraft:furnace_minecart", new DataWalkerTypePaths<>(MCTypeRegistry.BLOCK_STATE, "DisplayState")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, 3, "minecraft:hopper_minecart", new DataWalkerTypePaths<>(MCTypeRegistry.BLOCK_STATE, "DisplayState")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, 3, "minecraft:hopper_minecart", new DataWalkerItemLists("Items")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, 3, "minecraft:minecart", new DataWalkerTypePaths<>(MCTypeRegistry.BLOCK_STATE, "DisplayState")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, 3, "minecraft:spawner_minecart", new DataWalkerTypePaths<>(MCTypeRegistry.BLOCK_STATE, "DisplayState")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, 3, "minecraft:spawner_minecart", (final MapType data, final long fromVersion, final long toVersion) -> { ++ MCTypeRegistry.UNTAGGED_SPAWNER.convert(data, fromVersion, toVersion); ++ return null; ++ }); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, 3, "minecraft:tnt_minecart", new DataWalkerTypePaths<>(MCTypeRegistry.BLOCK_STATE, "DisplayState")); ++ ++ // V4 ++ MCTypeRegistry.BLOCK_NAME.addConverter(new DataConverter<>(VERSION, 4) { ++ @Override ++ public Object convert(final Object data, final long sourceVersion, final long toVersion) { ++ if (data instanceof Number) { ++ return HelperBlockFlatteningV1450.getNameForId(((Number)data).intValue()); ++ } else if (data instanceof String) { ++ return HelperBlockFlatteningV1450.getNewBlockName((String)data); // structure hook ensured data is namespaced ++ } ++ return null; ++ } ++ }); ++ MCTypeRegistry.ITEM_STACK.addStructureConverter(new ConverterFlattenItemStack()); ++ ++ // V5 ++ MCTypeRegistry.ITEM_STACK.addConverterForId("minecraft:spawn_egg", new ConverterFlattenSpawnEgg()); ++ /* This datafixer has been disabled because the collar colour handler did not change from 1.12 -> 1.13 at all. ++ // So clearly somebody fucked up. This fixes wolf colours incorrectly converting between versions ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:wolf", new DataConverter<>(VERSION, 5) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final Number colour = data.getNumber("CollarColor"); ++ ++ if (colour != null) { ++ data.setByte("CollarColor", (byte)(15 - colour.intValue())); ++ } ++ ++ return null; ++ } ++ }); ++ */ ++ MCTypeRegistry.TILE_ENTITY.addConverterForId("minecraft:banner", new DataConverter<>(VERSION, 5) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final Number base = data.getNumber("Base"); ++ if (base != null) { ++ data.setInt("Base", 15 - base.intValue()); ++ } ++ ++ final ListType patterns = data.getList("Patterns", ObjectType.MAP); ++ if (patterns != null) { ++ for (int i = 0, len = patterns.size(); i < len; ++i) { ++ final MapType pattern = patterns.getMap(i); ++ final Number colour = pattern.getNumber("Color"); ++ if (colour != null) { ++ pattern.setInt("Color", 15 - colour.intValue()); ++ } ++ } ++ } ++ ++ return null; ++ } ++ }); ++ MCTypeRegistry.LEVEL.addStructureConverter(new DataConverter<>(VERSION, 5) { ++ private final Splitter SPLITTER = Splitter.on(';').limit(5); ++ private final Splitter LAYER_SPLITTER = Splitter.on(','); ++ private final Splitter OLD_AMOUNT_SPLITTER = Splitter.on('x').limit(2); ++ private final Splitter AMOUNT_SPLITTER = Splitter.on('*').limit(2); ++ private final Splitter BLOCK_SPLITTER = Splitter.on(':').limit(3); ++ ++ // idk man i just copy and pasted this one ++ private String fixGeneratorSettings(final String generatorSettings) { ++ if (generatorSettings.isEmpty()) { ++ return "minecraft:bedrock,2*minecraft:dirt,minecraft:grass_block;1;village"; ++ } else { ++ Iterator iterator = SPLITTER.split(generatorSettings).iterator(); ++ String string2 = (String)iterator.next(); ++ int j; ++ String string4; ++ if (iterator.hasNext()) { ++ j = NumberUtils.toInt(string2, 0); ++ string4 = (String)iterator.next(); ++ } else { ++ j = 0; ++ string4 = string2; ++ } ++ ++ if (j >= 0 && j <= 3) { ++ StringBuilder stringBuilder = new StringBuilder(); ++ Splitter splitter = j < 3 ? OLD_AMOUNT_SPLITTER : AMOUNT_SPLITTER; ++ stringBuilder.append((String) StreamSupport.stream(LAYER_SPLITTER.split(string4).spliterator(), false).map((stringx) -> { ++ List list = splitter.splitToList(stringx); ++ int k; ++ String string3; ++ if (list.size() == 2) { ++ k = NumberUtils.toInt((String)list.get(0)); ++ string3 = (String)list.get(1); ++ } else { ++ k = 1; ++ string3 = (String)list.get(0); ++ } ++ ++ List list2 = BLOCK_SPLITTER.splitToList(string3); ++ int l = ((String)list2.get(0)).equals("minecraft") ? 1 : 0; ++ String string5 = (String)list2.get(l); ++ int m = j == 3 ? EntityBlockStateFix.getBlockId("minecraft:" + string5) : NumberUtils.toInt(string5, 0); ++ int n = l + 1; ++ int o = list2.size() > n ? NumberUtils.toInt((String)list2.get(n), 0) : 0; ++ return (k == 1 ? "" : k + "*") + BlockStateData.getTag(m << 4 | o).get("Name").asString(""); ++ }).collect(Collectors.joining(","))); ++ ++ while(iterator.hasNext()) { ++ stringBuilder.append(';').append((String)iterator.next()); ++ } ++ ++ return stringBuilder.toString(); ++ } else { ++ return "minecraft:bedrock,2*minecraft:dirt,minecraft:grass_block;1;village"; ++ } ++ } ++ } ++ ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (!"flat".equalsIgnoreCase(data.getString("generatorName"))) { ++ return null; ++ } ++ ++ final String generatorOptions = data.getString("generatorOptions"); ++ if (generatorOptions == null) { ++ return null; ++ } ++ ++ data.setString("generatorOptions", this.fixGeneratorSettings(generatorOptions)); ++ ++ return null; ++ } ++ }); ++ ++ // V6 ++ MCTypeRegistry.STATS.addStructureConverter(new ConverterFlattenStats()); ++ MCTypeRegistry.TILE_ENTITY.addConverterForId("minecraft:jukebox", new DataConverter<>(VERSION, 6) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final int record = data.getInt("Record"); ++ if (record <= 0) { ++ return null; ++ } ++ ++ data.remove("Record"); ++ ++ final String newItemId = ConverterFlattenItemStack.flattenItem(HelperItemNameV102.getNameFromId(record), 0); ++ if (newItemId == null) { ++ return null; ++ } ++ ++ final MapType recordItem = Types.NBT.createEmptyMap(); ++ data.setMap("RecordItem", recordItem); ++ ++ recordItem.setString("id", newItemId); ++ recordItem.setByte("Count", (byte)1); ++ ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.STATS.addStructureWalker(VERSION, 6, (final MapType data, final long fromVersion, final long toVersion) -> { ++ final MapType stats = data.getMap("stats"); ++ if (stats == null) { ++ return null; ++ } ++ ++ WalkerUtils.convertKeys(MCTypeRegistry.BLOCK_NAME, stats, "minecraft:mined", fromVersion, toVersion); ++ ++ WalkerUtils.convertKeys(MCTypeRegistry.ITEM_NAME, stats, "minecraft:crafted", fromVersion, toVersion); ++ WalkerUtils.convertKeys(MCTypeRegistry.ITEM_NAME, stats, "minecraft:used", fromVersion, toVersion); ++ WalkerUtils.convertKeys(MCTypeRegistry.ITEM_NAME, stats, "minecraft:broken", fromVersion, toVersion); ++ WalkerUtils.convertKeys(MCTypeRegistry.ITEM_NAME, stats, "minecraft:picked_up", fromVersion, toVersion); ++ WalkerUtils.convertKeys(MCTypeRegistry.ITEM_NAME, stats, "minecraft:dropped", fromVersion, toVersion); ++ ++ WalkerUtils.convertKeys(MCTypeRegistry.ENTITY_NAME, stats, "minecraft:killed", fromVersion, toVersion); ++ WalkerUtils.convertKeys(MCTypeRegistry.ENTITY_NAME, stats, "minecraft:killed_by", fromVersion, toVersion); ++ ++ return null; ++ }); ++ ++ MCTypeRegistry.OBJECTIVE.addStructureHook(VERSION, 6, new DataHook<>() { ++ private static String packWithDot(final String string) { ++ final ResourceLocation resourceLocation = ResourceLocation.tryParse(string); ++ return resourceLocation != null ? resourceLocation.getNamespace() + "." + resourceLocation.getPath() : string; ++ } ++ ++ @Override ++ public MapType preHook(final MapType data, final long fromVersion, final long toVersion) { ++ // unpack ++ final String criteriaName = data.getString("CriteriaName"); ++ String type; ++ String id; ++ ++ if (criteriaName != null) { ++ final int index = criteriaName.indexOf(':'); ++ if (index < 0) { ++ type = "_special"; ++ id = criteriaName; ++ } else { ++ try { ++ type = ResourceLocation.of(criteriaName.substring(0, index), '.').toString(); ++ id = ResourceLocation.of(criteriaName.substring(index + 1), '.').toString(); ++ } catch (final Exception ex) { ++ type = "_special"; ++ id = criteriaName; ++ } ++ } ++ } else { ++ type = null; ++ id = null; ++ } ++ ++ if (type != null && id != null) { ++ final MapType criteriaType = Types.NBT.createEmptyMap(); ++ data.setMap("CriteriaType", criteriaType); ++ ++ criteriaType.setString("type", type); ++ criteriaType.setString("id", id); ++ } ++ ++ return null; ++ } ++ ++ @Override ++ public MapType postHook(final MapType data, final long fromVersion, final long toVersion) { ++ // repack ++ final MapType criteriaType = data.getMap("CriteriaType"); ++ ++ final String newName; ++ if (criteriaType == null) { ++ newName = null; ++ } else { ++ final String type = criteriaType.getString("type"); ++ final String id = criteriaType.getString("id"); ++ if (type != null && id != null) { ++ if ("_special".equals(type)) { ++ newName = id; ++ } else { ++ newName = packWithDot(type) + ":" + packWithDot(id); ++ } ++ } else { ++ newName = null; ++ } ++ } ++ ++ if (newName != null) { ++ data.remove("CriteriaType"); ++ data.setString("CriteriaName", newName); ++ } ++ ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.OBJECTIVE.addStructureWalker(VERSION, 6, (final MapType data, final long fromVersion, final long toVersion) -> { ++ final MapType criteriaType = data.getMap("CriteriaType"); ++ if (criteriaType == null) { ++ return null; ++ } ++ ++ final String type = criteriaType.getString("type"); ++ ++ if (type == null) { ++ return null; ++ } ++ ++ switch (type) { ++ case "minecraft:mined": { ++ WalkerUtils.convert(MCTypeRegistry.ITEM_NAME, criteriaType, "id", fromVersion, toVersion); ++ break; ++ } ++ ++ case "minecraft:crafted": ++ case "minecraft:used": ++ case "minecraft:broken": ++ case "minecraft:picked_up": ++ case "minecraft:dropped": { ++ WalkerUtils.convert(MCTypeRegistry.BLOCK_NAME, criteriaType, "id", fromVersion, toVersion); ++ break; ++ } ++ ++ case "minecraft:killed": ++ case "minecraft:killed_by": { ++ WalkerUtils.convert(MCTypeRegistry.ENTITY_NAME, criteriaType, "id", fromVersion, toVersion); ++ break; ++ } ++ } ++ ++ return null; ++ }); ++ ++ ++ // V7 ++ MCTypeRegistry.STRUCTURE_FEATURE.addStructureConverter(new DataConverter<>(VERSION, 7) { ++ private static void convertToBlockState(final MapType data, final String path) { ++ final Number number = data.getNumber(path); ++ if (number == null) { ++ return; ++ } ++ ++ data.setMap(path, HelperBlockFlatteningV1450.getNBTForId(number.intValue() << 4).copy()); // copy to avoid problems with later state datafixers ++ } ++ ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final ListType children = data.getList("Children", ObjectType.MAP); ++ if (children == null) { ++ return null; ++ } ++ ++ for (int i = 0, len = children.size(); i < len; ++i) { ++ final MapType child = children.getMap(i); ++ ++ final String id = child.getString("id"); ++ ++ switch (id) { ++ case "ViF": ++ convertToBlockState(child, "CA"); ++ convertToBlockState(child, "CB"); ++ break; ++ case "ViDF": ++ convertToBlockState(child, "CA"); ++ convertToBlockState(child, "CB"); ++ convertToBlockState(child, "CC"); ++ convertToBlockState(child, "CD"); ++ break; ++ } ++ } ++ ++ return null; ++ } ++ }); ++ ++ // convert villagers to trade with pumpkins and not the carved pumpkin ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:villager", new DataConverter<>(VERSION, 7) { ++ private static void convertPumpkin(final MapType data, final String path) { ++ final MapType item = data.getMap(path); ++ if (item == null) { ++ return; ++ } ++ ++ final String id = item.getString("id"); ++ ++ if (id.equals("minecraft:carved_pumpkin")) { ++ item.setString("id", "minecraft:pumpkin"); ++ } ++ } ++ ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType offers = data.getMap("Offers"); ++ if (offers != null) { ++ final ListType recipes = offers.getList("Recipes", ObjectType.MAP); ++ if (recipes != null) { ++ for (int i = 0, len = recipes.size(); i < len; ++i) { ++ final MapType recipe = recipes.getMap(i); ++ ++ convertPumpkin(recipe, "buy"); ++ convertPumpkin(recipe, "buyB"); ++ convertPumpkin(recipe, "sell"); ++ } ++ } ++ } ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.STRUCTURE_FEATURE.addStructureWalker(VERSION, 7, (final MapType data, final long fromVersion, final long toVersion) -> { ++ final ListType list = data.getList("Children", ObjectType.MAP); ++ if (list == null) { ++ return null; ++ } ++ ++ for (int i = 0, len = list.size(); i < len; ++i) { ++ final MapType child = list.getMap(i); ++ WalkerUtils.convert(MCTypeRegistry.BLOCK_STATE, child, "CA", fromVersion, toVersion); ++ WalkerUtils.convert(MCTypeRegistry.BLOCK_STATE, child, "CB", fromVersion, toVersion); ++ WalkerUtils.convert(MCTypeRegistry.BLOCK_STATE, child, "CC", fromVersion, toVersion); ++ WalkerUtils.convert(MCTypeRegistry.BLOCK_STATE, child, "CD", fromVersion, toVersion); ++ } ++ ++ return null; ++ }); ++ } ++ ++ private V1451() {} ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1456.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1456.java +new file mode 100644 +index 0000000000000000000000000000000000000000..8ca5b9d7292ba9c81f7f0fdfb6ca8fd17f796990 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1456.java +@@ -0,0 +1,38 @@ ++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 V1456 { ++ ++ protected static final int VERSION = MCVersions.V17W49B + 1; ++ ++ protected static byte direction2dTo3d(final byte old) { ++ switch (old) { ++ case 0: ++ return 3; ++ case 1: ++ return 4; ++ case 2: ++ default: ++ return 2; ++ case 3: ++ return 5; ++ } ++ } ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:item_frame", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ data.setByte("Facing", direction2dTo3d(data.getByte("Facing"))); ++ return null; ++ } ++ }); ++ } ++ ++ private V1456() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1458.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1458.java +new file mode 100644 +index 0000000000000000000000000000000000000000..bcc8b8103b175654070228471bfa07309b5636f7 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1458.java +@@ -0,0 +1,90 @@ ++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; ++import net.minecraft.network.chat.Component; ++import net.minecraft.network.chat.TextComponent; ++import net.minecraft.network.chat.TranslatableComponent; ++ ++public final class V1458 { ++ ++ protected static final int VERSION = MCVersions.V17W50A + 1; ++ ++ public static MapType updateCustomName(final MapType data) { ++ final String customName = data.getString("CustomName", ""); ++ ++ if (customName.isEmpty()) { ++ data.remove("CustomName"); ++ } else { ++ data.setString("CustomName", Component.Serializer.toJson(new TextComponent(customName))); ++ } ++ ++ return null; ++ } ++ ++ public static void register() { ++ // From CB ++ MCTypeRegistry.PLAYER.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ return updateCustomName(data); ++ } ++ }); ++ ++ MCTypeRegistry.ENTITY.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if ("minecraft:commandblock_minecart".equals(data.getString("id"))) { ++ return null; ++ } ++ ++ return updateCustomName(data); ++ } ++ }); ++ ++ MCTypeRegistry.ITEM_STACK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType tag = data.getMap("tag"); ++ if (tag == null) { ++ return null; ++ } ++ ++ final MapType display = tag.getMap("display"); ++ if (display == null) { ++ return null; ++ } ++ ++ final String name = display.getString("Name"); ++ if (name != null) { ++ display.setString("Name", Component.Serializer.toJson(new TextComponent(name))); ++ } else { ++ final String localisedName = display.getString("LocName"); ++ if (localisedName != null) { ++ display.setString("Name", Component.Serializer.toJson(new TranslatableComponent(localisedName))); ++ display.remove("LocName"); ++ } ++ } ++ ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.TILE_ENTITY.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if ("minecraft:command_block".equals(data.getString("id"))) { ++ return null; ++ } ++ ++ return updateCustomName(data); ++ } ++ }); ++ ++ } ++ ++ private V1458() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1460.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1460.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f68b561b2bb750d5f632f17e538337fa38108472 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1460.java +@@ -0,0 +1,53 @@ ++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.block_name.DataWalkerBlockNames; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++import ca.spottedleaf.dataconverter.types.MapType; ++import net.minecraft.resources.ResourceLocation; ++import java.util.HashMap; ++import java.util.Locale; ++import java.util.Map; ++ ++public final class V1460 { ++ ++ private static final Map MOTIVE_REMAP = new HashMap<>(); ++ ++ static { ++ MOTIVE_REMAP.put("donkeykong", "donkey_kong"); ++ MOTIVE_REMAP.put("burningskull", "burning_skull"); ++ MOTIVE_REMAP.put("skullandroses", "skull_and_roses"); ++ }; ++ ++ protected static final int VERSION = MCVersions.V18W01A + 1; ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ private static void registerThrowableProjectile(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerBlockNames("inTile")); ++ } ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:painting", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ String motive = data.getString("Motive"); ++ if (motive != null) { ++ motive = motive.toLowerCase(Locale.ROOT); ++ data.setString("Motive", new ResourceLocation(MOTIVE_REMAP.getOrDefault(motive, motive)).toString()); ++ } ++ return null; ++ } ++ }); ++ ++ // No idea why so many type redefines exist here in Vanilla. nothing about the data structure changed, it's literally a copy of ++ // the existing types. ++ } ++ ++ private V1460() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1466.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1466.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c4fa8e36fb68a610106cee8bae1af243e51fae2e +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1466.java +@@ -0,0 +1,143 @@ ++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 V1466 { ++ ++ protected static final int VERSION = MCVersions.V18W06A; ++ ++ protected static short packOffsetCoordinates(final int x, final int y, final int z) { ++ return (short)((x & 15) | ((y & 15) << 4) | ((z & 15) << 8)); ++ } ++ ++ public static void register() { ++ // There is a rather critical change I've made to this converter: changing the chunk status determination. ++ // In Vanilla, this is determined by whether the terrain has been populated and whether the chunk is lit. ++ // For reference, here is the full status progression (at the time of 18w06a): ++ // empty -> base -> carved -> decorated -> lighted -> mobs_spawned -> finalized -> fullchunk -> postprocessed ++ // So one of those must be picked. ++ // If the chunk is lit and terrain is populated, the Vanilla converter will set the status to "mobs_spawned." ++ // If it is anything else, it will be "empty" ++ // I've changed it to the following: if terrain is populated, it is set to at least decorated. If it is populated ++ // and lit, it is set to "mobs_spawned" ++ // But what if it is not populated? If it is not populated, ignore the lit field - obviously that's just broken. ++ // It can't be lit and not populated. ++ // Let's take a look at chunk generation logic for a chunk that is not populated, or even near a populated chunk. ++ // It actually will generate a chunk up to the "carved" stage. It generates the base terrain, (i.e using noise ++ // to figure out where stone is, dirt, grass) and it will generate caves. Nothing else though. No populators. ++ // So "carved" is the correct stage to use, not empty. Setting it to empty would clobber chunk data, when we don't ++ // need to. If it is populated, at least set it to decorated. If it is lit and populated, set it to mobs_spawned. Else, ++ // it is carved. ++ // This change also fixes the random light check "bug" (really this is Mojang's fault for fucking up the status conversion here) ++ // caused by spigot, which would not set the lit value for some chunks. Now those chunks will not be regenerated. ++ ++ MCTypeRegistry.CHUNK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType level = data.getMap("Level"); ++ if (level == null) { ++ return null; ++ } ++ ++ final boolean terrainPopulated = level.getBoolean("TerrainPopulated"); ++ final boolean lightPopulated = level.getBoolean("LightPopulated") || level.getNumber("LightPopulated") == null; ++ final String newStatus = !terrainPopulated ? "carved" : (lightPopulated ? "mobs_spawned" : "decorated"); ++ ++ level.setString("Status", newStatus); ++ level.setBoolean("hasLegacyStructureData", true); ++ ++ // convert biome byte[] into int[] ++ final byte[] biomes = level.getBytes("Biomes"); ++ if (biomes != null) { ++ final int[] newBiomes = new int[256]; ++ for (int i = 0, len = Math.min(newBiomes.length, biomes.length); i < len; ++i) { ++ newBiomes[i] = biomes[i] & 255; ++ } ++ level.setInts("Biomes", newBiomes); ++ } ++ ++ // ProtoChunks have their own dedicated tick list, so we must convert the TileTicks to that. ++ final ListType ticks = level.getList("TileTicks", ObjectType.MAP); ++ if (ticks != null) { ++ final ListType sections = Types.NBT.createEmptyList(); ++ final ListType[] sectionAccess = new ListType[16]; ++ for (int i = 0; i < sectionAccess.length; ++i) { ++ sections.addList(sectionAccess[i] = Types.NBT.createEmptyList()); ++ } ++ level.setList("ToBeTicked", sections); ++ ++ for (int i = 0, len = ticks.size(); i < len; ++i) { ++ final MapType tick = ticks.getMap(i); ++ ++ final int x = tick.getInt("x"); ++ final int y = tick.getInt("y"); ++ final int z = tick.getInt("z"); ++ final short coordinate = packOffsetCoordinates(x, y, z); ++ ++ sectionAccess[y >> 4].addShort(coordinate); ++ } ++ } ++ ++ 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.BLOCK_STATE, section, "Palette", fromVersion, toVersion); ++ } ++ } ++ ++ WalkerUtils.convertValues(MCTypeRegistry.STRUCTURE_FEATURE, level.getMap("Structures"), "Starts", fromVersion, toVersion); ++ ++ return null; ++ }); ++ MCTypeRegistry.STRUCTURE_FEATURE.addStructureWalker(VERSION, (final MapType data, final long fromVersion, final long toVersion) -> { ++ final ListType list = data.getList("Children", ObjectType.MAP); ++ if (list != null) { ++ for (int i = 0, len = list.size(); i < len; ++i) { ++ final MapType child = list.getMap(i); ++ ++ WalkerUtils.convert(MCTypeRegistry.BLOCK_STATE, child, "CA", fromVersion, toVersion); ++ WalkerUtils.convert(MCTypeRegistry.BLOCK_STATE, child, "CB", fromVersion, toVersion); ++ WalkerUtils.convert(MCTypeRegistry.BLOCK_STATE, child, "CC", fromVersion, toVersion); ++ WalkerUtils.convert(MCTypeRegistry.BLOCK_STATE, child, "CD", fromVersion, toVersion); ++ } ++ } ++ ++ WalkerUtils.convert(MCTypeRegistry.BIOME, data, "biome", fromVersion, toVersion); ++ ++ return null; ++ }); ++ } ++ ++ private V1466() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V147.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V147.java +new file mode 100644 +index 0000000000000000000000000000000000000000..68dd3ce7709a998bc50a5080fe9c805b71a88365 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V147.java +@@ -0,0 +1,27 @@ ++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 V147 { ++ ++ protected static final int VERSION = MCVersions.V15W46A + 1; ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("ArmorStand", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (data.getBoolean("Silent") && !data.getBoolean("Marker")) { ++ data.remove("Silent"); ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++ private V147() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1470.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1470.java +new file mode 100644 +index 0000000000000000000000000000000000000000..669509286b18a173826938bae347c1aefffeed51 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1470.java +@@ -0,0 +1,31 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.generic.DataWalkerTypePaths; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++ ++public final class V1470 { ++ ++ protected static final int VERSION = MCVersions.V18W08A; ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ registerMob("minecraft:turtle"); ++ registerMob("minecraft:cod_mob"); ++ registerMob("minecraft:tropical_fish"); ++ registerMob("minecraft:salmon_mob"); ++ registerMob("minecraft:puffer_fish"); ++ registerMob("minecraft:phantom"); ++ registerMob("minecraft:dolphin"); ++ registerMob("minecraft:drowned"); ++ ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:trident", new DataWalkerTypePaths<>(MCTypeRegistry.BLOCK_STATE, "inBlockState")); ++ } ++ ++ private V1470() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1474.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1474.java +new file mode 100644 +index 0000000000000000000000000000000000000000..4cf1085b4392c9b348ebe65590cdbf287a908a38 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1474.java +@@ -0,0 +1,36 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.types.MapType; ++ ++public final class V1474 { ++ ++ protected static final int VERSION = MCVersions.V18W10B; ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:shulker", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (data.getInt("Color") == 10) { ++ data.setByte("Color", (byte)16); ++ } ++ return null; ++ } ++ }); ++ // data hooks ensure the inputs are namespaced ++ ConverterAbstractBlockRename.register(VERSION, (final String old) -> { ++ return "minecraft:purple_shulker_box".equals(old) ? "minecraft:shulker_box" : null; ++ }); ++ ConverterAbstractItemRename.register(VERSION, (final String old) -> { ++ return "minecraft:purple_shulker_box".equals(old) ? "minecraft:shulker_box" : null; ++ }); ++ ++ } ++ ++ private V1474() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1475.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1475.java +new file mode 100644 +index 0000000000000000000000000000000000000000..40b64efb8717b2de0ff13af87bcc99119c9f7c9d +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1475.java +@@ -0,0 +1,21 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import com.google.common.collect.ImmutableMap; ++ ++public final class V1475 { ++ ++ protected static final int VERSION = MCVersions.V18W10B + 1; ++ ++ public static void register() { ++ ConverterAbstractBlockRename.register(VERSION, ++ ImmutableMap.of( ++ "minecraft:flowing_water", "minecraft:water", ++ "minecraft:flowing_lava", "minecraft:lava" ++ )::get); ++ } ++ ++ private V1475() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1480.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1480.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e5373d4e6ca027749f634e9a508bd81b9b41ed3e +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1480.java +@@ -0,0 +1,42 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import com.google.common.collect.ImmutableMap; ++import java.util.Map; ++ ++public final class V1480 { ++ ++ protected static final int VERSION = MCVersions.V18W14A + 1; ++ ++ public static final Map RENAMED_IDS = ImmutableMap.builder() ++ .put("minecraft:blue_coral", "minecraft:tube_coral_block") ++ .put("minecraft:pink_coral", "minecraft:brain_coral_block") ++ .put("minecraft:purple_coral", "minecraft:bubble_coral_block") ++ .put("minecraft:red_coral", "minecraft:fire_coral_block") ++ .put("minecraft:yellow_coral", "minecraft:horn_coral_block") ++ .put("minecraft:blue_coral_plant", "minecraft:tube_coral") ++ .put("minecraft:pink_coral_plant", "minecraft:brain_coral") ++ .put("minecraft:purple_coral_plant", "minecraft:bubble_coral") ++ .put("minecraft:red_coral_plant", "minecraft:fire_coral") ++ .put("minecraft:yellow_coral_plant", "minecraft:horn_coral") ++ .put("minecraft:blue_coral_fan", "minecraft:tube_coral_fan") ++ .put("minecraft:pink_coral_fan", "minecraft:brain_coral_fan") ++ .put("minecraft:purple_coral_fan", "minecraft:bubble_coral_fan") ++ .put("minecraft:red_coral_fan", "minecraft:fire_coral_fan") ++ .put("minecraft:yellow_coral_fan", "minecraft:horn_coral_fan") ++ .put("minecraft:blue_dead_coral", "minecraft:dead_tube_coral") ++ .put("minecraft:pink_dead_coral", "minecraft:dead_brain_coral") ++ .put("minecraft:purple_dead_coral", "minecraft:dead_bubble_coral") ++ .put("minecraft:red_dead_coral", "minecraft:dead_fire_coral") ++ .put("minecraft:yellow_dead_coral", "minecraft:dead_horn_coral") ++ .build(); ++ ++ public static void register() { ++ ConverterAbstractBlockRename.register(VERSION, RENAMED_IDS::get); ++ ConverterAbstractItemRename.register(VERSION, RENAMED_IDS::get); ++ } ++ ++ private V1480() {} ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1483.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1483.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e6fb5d6870514a509f7f1aa5343ed7e762af8a72 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1483.java +@@ -0,0 +1,31 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.entity.ConverterAbstractEntityRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++import com.google.common.collect.ImmutableMap; ++ ++public final class V1483 { ++ ++ protected static final int VERSION = MCVersions.V18W16A; ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ ConverterAbstractEntityRename.register(VERSION, ImmutableMap.of( ++ "minecraft:puffer_fish", "minecraft:pufferfish" ++ )::get); ++ ConverterAbstractItemRename.register(VERSION, ImmutableMap.of( ++ "minecraft:puffer_fish_spawn_egg", "minecraft:pufferfish_spawn_egg" ++ )::get); ++ ++ MCTypeRegistry.ENTITY.copyWalkers(VERSION, "minecraft:puffer_fish", "minecraft:pufferfish"); ++ } ++ ++ private V1483() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1484.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1484.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f5b9c166304930e095bfc00e8f6b93edb706df48 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1484.java +@@ -0,0 +1,73 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.types.MapType; ++import com.google.common.collect.ImmutableMap; ++import java.util.Map; ++ ++public final class V1484 { ++ ++ protected static final int VERSION = MCVersions.V18W19A; ++ ++ public static void register() { ++ final Map renamed = ImmutableMap.of( ++ "minecraft:sea_grass", "minecraft:seagrass", ++ "minecraft:tall_sea_grass", "minecraft:tall_seagrass" ++ ); ++ ++ ConverterAbstractItemRename.register(VERSION, renamed::get); ++ ConverterAbstractBlockRename.register(VERSION, renamed::get); ++ ++ MCTypeRegistry.CHUNK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType level = data.getMap("Level"); ++ ++ if (level == null) { ++ return null; ++ } ++ ++ final MapType heightmaps = level.getMap("Heightmaps"); ++ ++ if (heightmaps == null) { ++ return null; ++ } ++ ++ final Object liquid = heightmaps.getGeneric("LIQUID"); ++ if (liquid != null) { ++ heightmaps.remove("LIQUID"); ++ heightmaps.setGeneric("WORLD_SURFACE_WG", liquid); ++ } ++ ++ final Object solid = heightmaps.getGeneric("SOLID"); ++ if (solid != null) { ++ heightmaps.remove("SOLID"); ++ heightmaps.setGeneric("OCEAN_FLOOR_WG", solid); ++ heightmaps.setGeneric("OCEAN_FLOOR", solid); ++ } ++ ++ final Object light = heightmaps.getGeneric("LIGHT"); ++ if (light != null) { ++ heightmaps.remove("LIGHT"); ++ heightmaps.setGeneric("LIGHT_BLOCKING", light); ++ } ++ ++ final Object rain = heightmaps.getGeneric("RAIN"); ++ if (rain != null) { ++ heightmaps.remove("RAIN"); ++ heightmaps.setGeneric("MOTION_BLOCKING", rain); ++ heightmaps.setGeneric("MOTION_BLOCKING_NO_LEAVES", rain); ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++ private V1484() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1486.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1486.java +new file mode 100644 +index 0000000000000000000000000000000000000000..cf7b0a77b30312a32d5fdb10be3fb45d10ba7870 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1486.java +@@ -0,0 +1,39 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.entity.ConverterAbstractEntityRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++import com.google.common.collect.ImmutableMap; ++import java.util.Map; ++ ++public final class V1486 { ++ ++ protected static final int VERSION = MCVersions.V18W19B + 1; ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static final Map RENAMED_ENTITY_IDS = ImmutableMap.builder() ++ .put("minecraft:salmon_mob", "minecraft:salmon") ++ .put("minecraft:cod_mob", "minecraft:cod") ++ .build(); ++ public static final Map RENAMED_ITEM_IDS = ImmutableMap.builder() ++ .put("minecraft:salmon_mob_spawn_egg", "minecraft:salmon_spawn_egg") ++ .put("minecraft:cod_mob_spawn_egg", "minecraft:cod_spawn_egg") ++ .build(); ++ ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.copyWalkers(VERSION, "minecraft:cod_mob", "minecraft:cod"); ++ MCTypeRegistry.ENTITY.copyWalkers(VERSION, "minecraft:salmon_mob", "minecraft:salmon"); ++ ++ ConverterAbstractEntityRename.register(VERSION, RENAMED_ENTITY_IDS::get); ++ ConverterAbstractItemRename.register(VERSION, RENAMED_ITEM_IDS::get); ++ } ++ ++ private V1486() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1487.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1487.java +new file mode 100644 +index 0000000000000000000000000000000000000000..dba4a64f61b27f1eb820e0e0a3fddb81517acc16 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1487.java +@@ -0,0 +1,25 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import com.google.common.collect.ImmutableMap; ++import java.util.Map; ++ ++public final class V1487 { ++ ++ protected static final int VERSION = MCVersions.V18W19B + 2; ++ ++ public static void register() { ++ final Map remap = ImmutableMap.of( ++ "minecraft:prismarine_bricks_slab", "minecraft:prismarine_brick_slab", ++ "minecraft:prismarine_bricks_stairs", "minecraft:prismarine_brick_stairs" ++ ); ++ ++ ConverterAbstractItemRename.register(VERSION, remap::get); ++ ConverterAbstractBlockRename.register(VERSION, remap::get); ++ } ++ ++ private V1487() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1488.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1488.java +new file mode 100644 +index 0000000000000000000000000000000000000000..cf3579524a9ba96f2065d98bca928bf920da081c +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1488.java +@@ -0,0 +1,88 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++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 com.google.common.collect.ImmutableMap; ++ ++public final class V1488 { ++ ++ protected static final int VERSION = MCVersions.V18W19B + 3; ++ ++ protected static boolean isIglooPiece(final MapType piece) { ++ return "Iglu".equals(piece.getString("id")); ++ } ++ ++ public static void register() { ++ ConverterAbstractBlockRename.register(VERSION, ImmutableMap.of( ++ "minecraft:kelp_top", "minecraft:kelp", ++ "minecraft:kelp", "minecraft:kelp_plant" ++ )::get); ++ ConverterAbstractItemRename.register(VERSION, ImmutableMap.of( ++ "minecraft:kelp_top", "minecraft:kelp" ++ )::get); ++ ++ // Don't ask me why in V1458 they wrote the converter to NOT do command blocks and THEN in THIS version ++ // to ONLY do command blocks. I don't know. ++ ++ MCTypeRegistry.TILE_ENTITY.addConverterForId("minecraft:command_block", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ return V1458.updateCustomName(data); ++ } ++ }); ++ ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:commandblock_minecart", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ return V1458.updateCustomName(data); ++ } ++ }); ++ ++ MCTypeRegistry.STRUCTURE_FEATURE.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final ListType children = data.getList("Children", ObjectType.MAP); ++ boolean isIgloo; ++ if (children != null) { ++ isIgloo = true; ++ for (int i = 0, len = children.size(); i < len; ++i) { ++ if (!isIglooPiece(children.getMap(i))) { ++ isIgloo = false; ++ break; ++ } ++ } ++ } else { ++ isIgloo = false; ++ } ++ ++ if (isIgloo) { ++ data.remove("Children"); ++ data.setString("id", "Igloo"); ++ return null; ++ } ++ ++ if (children != null) { ++ for (int i = 0; i < children.size();) { ++ final MapType child = children.getMap(i); ++ if (isIglooPiece(child)) { ++ children.remove(i); ++ continue; ++ } ++ ++i; ++ } ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++ private V1488() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1490.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1490.java +new file mode 100644 +index 0000000000000000000000000000000000000000..cb3afa0634cb47cbd5b324a66d140375b1c23e07 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1490.java +@@ -0,0 +1,25 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import com.google.common.collect.ImmutableMap; ++ ++public final class V1490 { ++ ++ protected static final int VERSION = MCVersions.V18W20A + 1; ++ ++ public static void register() { ++ ConverterAbstractBlockRename.register(VERSION, ImmutableMap.of( ++ "minecraft:melon_block", "minecraft:melon" ++ )::get); ++ ConverterAbstractItemRename.register(VERSION, ImmutableMap.of( ++ "minecraft:melon_block", "minecraft:melon", ++ "minecraft:melon", "minecraft:melon_slice", ++ "minecraft:speckled_melon", "minecraft:glistering_melon_slice" ++ )::get); ++ } ++ ++ private V1490() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1492.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1492.java +new file mode 100644 +index 0000000000000000000000000000000000000000..b8732c2035ee0659173a8299cc2b0a5f86ace7b0 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1492.java +@@ -0,0 +1,152 @@ ++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 com.google.common.collect.ImmutableMap; ++import com.mojang.datafixers.util.Pair; ++ ++public final class V1492 { ++ ++ private static final ImmutableMap>> RENAMES = ImmutableMap.>>builder() ++ .put("EndCity", Pair.of( ++ "ECP", ++ ImmutableMap.builder() ++ .put("second_floor", "second_floor_1") ++ .put("third_floor", "third_floor_1") ++ .put("third_floor_c", "third_floor_2") ++ .build() ++ ) ++ ) ++ ++ .put("Mansion", Pair.of( ++ "WMP", ++ ImmutableMap.builder() ++ .put("carpet_south", "carpet_south_1") ++ .put("carpet_west", "carpet_west_1") ++ .put("indoors_door", "indoors_door_1") ++ .put("indoors_wall", "indoors_wall_1") ++ .build() ++ ) ++ ) ++ ++ .put("Igloo", Pair.of( ++ "Iglu", ++ ImmutableMap.builder() ++ .put("minecraft:igloo/igloo_bottom", "minecraft:igloo/bottom") ++ .put("minecraft:igloo/igloo_middle", "minecraft:igloo/middle") ++ .put("minecraft:igloo/igloo_top", "minecraft:igloo/top") ++ .build() ++ ) ++ ) ++ .put("Ocean_Ruin", Pair.of( ++ "ORP", ++ ImmutableMap.builder() ++ .put("minecraft:ruin/big_ruin1_brick", "minecraft:underwater_ruin/big_brick_1") ++ .put("minecraft:ruin/big_ruin2_brick", "minecraft:underwater_ruin/big_brick_2") ++ .put("minecraft:ruin/big_ruin3_brick", "minecraft:underwater_ruin/big_brick_3") ++ .put("minecraft:ruin/big_ruin8_brick", "minecraft:underwater_ruin/big_brick_8") ++ .put("minecraft:ruin/big_ruin1_cracked", "minecraft:underwater_ruin/big_cracked_1") ++ .put("minecraft:ruin/big_ruin2_cracked", "minecraft:underwater_ruin/big_cracked_2") ++ .put("minecraft:ruin/big_ruin3_cracked", "minecraft:underwater_ruin/big_cracked_3") ++ .put("minecraft:ruin/big_ruin8_cracked", "minecraft:underwater_ruin/big_cracked_8") ++ .put("minecraft:ruin/big_ruin1_mossy", "minecraft:underwater_ruin/big_mossy_1") ++ .put("minecraft:ruin/big_ruin2_mossy", "minecraft:underwater_ruin/big_mossy_2") ++ .put("minecraft:ruin/big_ruin3_mossy", "minecraft:underwater_ruin/big_mossy_3") ++ .put("minecraft:ruin/big_ruin8_mossy", "minecraft:underwater_ruin/big_mossy_8") ++ .put("minecraft:ruin/big_ruin_warm4", "minecraft:underwater_ruin/big_warm_4") ++ .put("minecraft:ruin/big_ruin_warm5", "minecraft:underwater_ruin/big_warm_5") ++ .put("minecraft:ruin/big_ruin_warm6", "minecraft:underwater_ruin/big_warm_6") ++ .put("minecraft:ruin/big_ruin_warm7", "minecraft:underwater_ruin/big_warm_7") ++ .put("minecraft:ruin/ruin1_brick", "minecraft:underwater_ruin/brick_1") ++ .put("minecraft:ruin/ruin2_brick", "minecraft:underwater_ruin/brick_2") ++ .put("minecraft:ruin/ruin3_brick", "minecraft:underwater_ruin/brick_3") ++ .put("minecraft:ruin/ruin4_brick", "minecraft:underwater_ruin/brick_4") ++ .put("minecraft:ruin/ruin5_brick", "minecraft:underwater_ruin/brick_5") ++ .put("minecraft:ruin/ruin6_brick", "minecraft:underwater_ruin/brick_6") ++ .put("minecraft:ruin/ruin7_brick", "minecraft:underwater_ruin/brick_7") ++ .put("minecraft:ruin/ruin8_brick", "minecraft:underwater_ruin/brick_8") ++ .put("minecraft:ruin/ruin1_cracked", "minecraft:underwater_ruin/cracked_1") ++ .put("minecraft:ruin/ruin2_cracked", "minecraft:underwater_ruin/cracked_2") ++ .put("minecraft:ruin/ruin3_cracked", "minecraft:underwater_ruin/cracked_3") ++ .put("minecraft:ruin/ruin4_cracked", "minecraft:underwater_ruin/cracked_4") ++ .put("minecraft:ruin/ruin5_cracked", "minecraft:underwater_ruin/cracked_5") ++ .put("minecraft:ruin/ruin6_cracked", "minecraft:underwater_ruin/cracked_6") ++ .put("minecraft:ruin/ruin7_cracked", "minecraft:underwater_ruin/cracked_7") ++ .put("minecraft:ruin/ruin8_cracked", "minecraft:underwater_ruin/cracked_8") ++ .put("minecraft:ruin/ruin1_mossy", "minecraft:underwater_ruin/mossy_1") ++ .put("minecraft:ruin/ruin2_mossy", "minecraft:underwater_ruin/mossy_2") ++ .put("minecraft:ruin/ruin3_mossy", "minecraft:underwater_ruin/mossy_3") ++ .put("minecraft:ruin/ruin4_mossy", "minecraft:underwater_ruin/mossy_4") ++ .put("minecraft:ruin/ruin5_mossy", "minecraft:underwater_ruin/mossy_5") ++ .put("minecraft:ruin/ruin6_mossy", "minecraft:underwater_ruin/mossy_6") ++ .put("minecraft:ruin/ruin7_mossy", "minecraft:underwater_ruin/mossy_7") ++ .put("minecraft:ruin/ruin8_mossy", "minecraft:underwater_ruin/mossy_8") ++ .put("minecraft:ruin/ruin_warm1", "minecraft:underwater_ruin/warm_1") ++ .put("minecraft:ruin/ruin_warm2", "minecraft:underwater_ruin/warm_2") ++ .put("minecraft:ruin/ruin_warm3", "minecraft:underwater_ruin/warm_3") ++ .put("minecraft:ruin/ruin_warm4", "minecraft:underwater_ruin/warm_4") ++ .put("minecraft:ruin/ruin_warm5", "minecraft:underwater_ruin/warm_5") ++ .put("minecraft:ruin/ruin_warm6", "minecraft:underwater_ruin/warm_6") ++ .put("minecraft:ruin/ruin_warm7", "minecraft:underwater_ruin/warm_7") ++ .put("minecraft:ruin/ruin_warm8", "minecraft:underwater_ruin/warm_8") ++ .put("minecraft:ruin/big_brick_1", "minecraft:underwater_ruin/big_brick_1") ++ .put("minecraft:ruin/big_brick_2", "minecraft:underwater_ruin/big_brick_2") ++ .put("minecraft:ruin/big_brick_3", "minecraft:underwater_ruin/big_brick_3") ++ .put("minecraft:ruin/big_brick_8", "minecraft:underwater_ruin/big_brick_8") ++ .put("minecraft:ruin/big_mossy_1", "minecraft:underwater_ruin/big_mossy_1") ++ .put("minecraft:ruin/big_mossy_2", "minecraft:underwater_ruin/big_mossy_2") ++ .put("minecraft:ruin/big_mossy_3", "minecraft:underwater_ruin/big_mossy_3") ++ .put("minecraft:ruin/big_mossy_8", "minecraft:underwater_ruin/big_mossy_8") ++ .put("minecraft:ruin/big_cracked_1", "minecraft:underwater_ruin/big_cracked_1") ++ .put("minecraft:ruin/big_cracked_2", "minecraft:underwater_ruin/big_cracked_2") ++ .put("minecraft:ruin/big_cracked_3", "minecraft:underwater_ruin/big_cracked_3") ++ .put("minecraft:ruin/big_cracked_8", "minecraft:underwater_ruin/big_cracked_8") ++ .put("minecraft:ruin/big_warm_4", "minecraft:underwater_ruin/big_warm_4") ++ .put("minecraft:ruin/big_warm_5", "minecraft:underwater_ruin/big_warm_5") ++ .put("minecraft:ruin/big_warm_6", "minecraft:underwater_ruin/big_warm_6") ++ .put("minecraft:ruin/big_warm_7", "minecraft:underwater_ruin/big_warm_7") ++ .build() ++ ) ++ ) ++ ++ .build(); ++ ++ protected static final int VERSION = MCVersions.V18W20B + 1; ++ ++ public static void register() { ++ MCTypeRegistry.STRUCTURE_FEATURE.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final ListType children = data.getList("Children", ObjectType.MAP); ++ if (children == null) { ++ return null; ++ } ++ ++ final String id = data.getString("id"); ++ ++ final Pair> renames = RENAMES.get(id); ++ if (renames == null) { ++ return null; ++ } ++ ++ for (int i = 0, len = children.size(); i < len; ++i) { ++ final MapType child = children.getMap(i); ++ ++ if (renames.getFirst().equals(child.getString("id"))) { ++ final String template = child.getString("Template", ""); ++ child.setString("Template", renames.getSecond().getOrDefault(template, template)); ++ } ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++ private V1492() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1494.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1494.java +new file mode 100644 +index 0000000000000000000000000000000000000000..50411042a83d58c4c36768a8f5196b4b41b4d095 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1494.java +@@ -0,0 +1,89 @@ ++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 it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; ++ ++public final class V1494 { ++ ++ protected static final int VERSION = MCVersions.V18W20C + 1; ++ ++ private static final Int2ObjectOpenHashMap ENCH_ID_TO_NAME = new Int2ObjectOpenHashMap<>(); ++ static { ++ ENCH_ID_TO_NAME.put(0, "minecraft:protection"); ++ ENCH_ID_TO_NAME.put(1, "minecraft:fire_protection"); ++ ENCH_ID_TO_NAME.put(2, "minecraft:feather_falling"); ++ ENCH_ID_TO_NAME.put(3, "minecraft:blast_protection"); ++ ENCH_ID_TO_NAME.put(4, "minecraft:projectile_protection"); ++ ENCH_ID_TO_NAME.put(5, "minecraft:respiration"); ++ ENCH_ID_TO_NAME.put(6, "minecraft:aqua_affinity"); ++ ENCH_ID_TO_NAME.put(7, "minecraft:thorns"); ++ ENCH_ID_TO_NAME.put(8, "minecraft:depth_strider"); ++ ENCH_ID_TO_NAME.put(9, "minecraft:frost_walker"); ++ ENCH_ID_TO_NAME.put(10, "minecraft:binding_curse"); ++ ENCH_ID_TO_NAME.put(16, "minecraft:sharpness"); ++ ENCH_ID_TO_NAME.put(17, "minecraft:smite"); ++ ENCH_ID_TO_NAME.put(18, "minecraft:bane_of_arthropods"); ++ ENCH_ID_TO_NAME.put(19, "minecraft:knockback"); ++ ENCH_ID_TO_NAME.put(20, "minecraft:fire_aspect"); ++ ENCH_ID_TO_NAME.put(21, "minecraft:looting"); ++ ENCH_ID_TO_NAME.put(22, "minecraft:sweeping"); ++ ENCH_ID_TO_NAME.put(32, "minecraft:efficiency"); ++ ENCH_ID_TO_NAME.put(33, "minecraft:silk_touch"); ++ ENCH_ID_TO_NAME.put(34, "minecraft:unbreaking"); ++ ENCH_ID_TO_NAME.put(35, "minecraft:fortune"); ++ ENCH_ID_TO_NAME.put(48, "minecraft:power"); ++ ENCH_ID_TO_NAME.put(49, "minecraft:punch"); ++ ENCH_ID_TO_NAME.put(50, "minecraft:flame"); ++ ENCH_ID_TO_NAME.put(51, "minecraft:infinity"); ++ ENCH_ID_TO_NAME.put(61, "minecraft:luck_of_the_sea"); ++ ENCH_ID_TO_NAME.put(62, "minecraft:lure"); ++ ENCH_ID_TO_NAME.put(65, "minecraft:loyalty"); ++ ENCH_ID_TO_NAME.put(66, "minecraft:impaling"); ++ ENCH_ID_TO_NAME.put(67, "minecraft:riptide"); ++ ENCH_ID_TO_NAME.put(68, "minecraft:channeling"); ++ ENCH_ID_TO_NAME.put(70, "minecraft:mending"); ++ ENCH_ID_TO_NAME.put(71, "minecraft:vanishing_curse"); ++ } ++ ++ public static void register() { ++ MCTypeRegistry.ITEM_STACK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType tag = data.getMap("tag"); ++ if (tag == null) { ++ return null; ++ } ++ ++ final ListType enchants = tag.getList("ench", ObjectType.MAP); ++ if (enchants != null) { ++ tag.remove("ench"); ++ tag.setList("Enchantments", enchants); ++ ++ for (int i = 0, len = enchants.size(); i < len; ++i) { ++ final MapType enchant = enchants.getMap(i); ++ enchant.setString("id", ENCH_ID_TO_NAME.getOrDefault(enchant.getInt("id"), "null")); ++ } ++ } ++ ++ final ListType storedEnchants = tag.getList("StoredEnchantments", ObjectType.MAP); ++ if (storedEnchants != null) { ++ for (int i = 0, len = storedEnchants.size(); i < len; ++i) { ++ final MapType enchant = storedEnchants.getMap(i); ++ enchant.setString("id", ENCH_ID_TO_NAME.getOrDefault(enchant.getInt("id"), "null")); ++ } ++ } ++ ++ ++ return null; ++ } ++ }); ++ } ++ ++ private V1494() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1496.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1496.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c56d50c552d4609474f5b3b6b0b8be8b575764ea +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1496.java +@@ -0,0 +1,370 @@ ++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 com.mojang.datafixers.DataFixUtils; ++import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; ++import it.unimi.dsi.fastutil.ints.IntIterator; ++import it.unimi.dsi.fastutil.ints.IntOpenHashSet; ++import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; ++import net.minecraft.util.datafix.PackedBitStorage; ++import java.util.Arrays; ++import java.util.HashSet; ++import java.util.Set; ++ ++public final class V1496 { ++ ++ private V1496() {} ++ ++ protected static final int VERSION = MCVersions.V18W21B; ++ ++ private static final int[][] DIRECTIONS = new int[][] { ++ new int[] {-1, 0, 0}, ++ new int[] {1, 0, 0}, ++ new int[] {0, -1, 0}, ++ new int[] {0, 1, 0}, ++ new int[] {0, 0, -1}, ++ new int[] {0, 0, 1} ++ }; ++ ++ private static final Object2IntOpenHashMap LEAVES_TO_ID = new Object2IntOpenHashMap<>(); ++ static { ++ LEAVES_TO_ID.put("minecraft:acacia_leaves", 0); ++ LEAVES_TO_ID.put("minecraft:birch_leaves", 1); ++ LEAVES_TO_ID.put("minecraft:dark_oak_leaves", 2); ++ LEAVES_TO_ID.put("minecraft:jungle_leaves", 3); ++ LEAVES_TO_ID.put("minecraft:oak_leaves", 4); ++ LEAVES_TO_ID.put("minecraft:spruce_leaves", 5); ++ } ++ ++ private static final Set LOGS = new HashSet<>( ++ Arrays.asList( ++ "minecraft:acacia_bark", ++ "minecraft:birch_bark", ++ "minecraft:dark_oak_bark", ++ "minecraft:jungle_bark", ++ "minecraft:oak_bark", ++ "minecraft:spruce_bark", ++ "minecraft:acacia_log", ++ "minecraft:birch_log", ++ "minecraft:dark_oak_log", ++ "minecraft:jungle_log", ++ "minecraft:oak_log", ++ "minecraft:spruce_log", ++ "minecraft:stripped_acacia_log", ++ "minecraft:stripped_birch_log", ++ "minecraft:stripped_dark_oak_log", ++ "minecraft:stripped_jungle_log", ++ "minecraft:stripped_oak_log", ++ "minecraft:stripped_spruce_log" ++ ) ++ ); ++ ++ public static void register() { ++ MCTypeRegistry.CHUNK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType level = data.getMap("Level"); ++ if (level == null) { ++ return null; ++ } ++ ++ final ListType sectionsNBT = level.getList("Sections", ObjectType.MAP); ++ if (sectionsNBT == null) { ++ return null; ++ } ++ ++ int newSides = 0; ++ ++ final LeavesSection[] sections = new LeavesSection[16]; ++ boolean skippable = true; ++ for (int i = 0, len = sectionsNBT.size(); i < len; ++i) { ++ final LeavesSection section = new LeavesSection(sectionsNBT.getMap(i)); ++ sections[section.sectionY] = section; ++ ++ skippable &= section.isSkippable(); ++ } ++ ++ if (skippable) { ++ return null; ++ } ++ ++ final IntOpenHashSet[] positionsByDistance = new IntOpenHashSet[7]; ++ for (int i = 0; i < positionsByDistance.length; ++i) { ++ positionsByDistance[i] = new IntOpenHashSet(); ++ } ++ ++ for (final LeavesSection section : sections) { ++ if (section == null || section.isSkippable()) { ++ continue; ++ } ++ ++ for (int index = 0; index < 4096; ++index) { ++ final int block = section.getBlock(index); ++ if (section.isLog(block)) { ++ positionsByDistance[0].add(section.getSectionY() << 12 | index); ++ } else if (section.isLeaf(block)) { ++ int x = getX(index); ++ int z = getZ(index); ++ newSides |= getSideMask(x == 0, x == 15, z == 0, z == 15); ++ } ++ } ++ } ++ ++ // this is basically supposed to recalculate the distances, because a higher cap was added ++ for (int distance = 1; distance < 7; ++distance) { ++ final IntOpenHashSet positionsLess = positionsByDistance[distance - 1]; ++ final IntOpenHashSet positionsEqual = positionsByDistance[distance]; ++ ++ for (final IntIterator iterator = positionsLess.iterator(); iterator.hasNext();) { ++ final int position = iterator.nextInt(); ++ final int fromX = getX(position); ++ final int fromY = getY(position); ++ final int fromZ = getZ(position); ++ ++ for (final int[] direction : DIRECTIONS) { ++ final int toX = fromX + direction[0]; ++ final int toY = fromY + direction[1]; ++ final int toZ = fromZ + direction[2]; ++ ++ if (!(toX >= 0 && toX <= 15 && toZ >= 0 && toZ <= 15 && toY >= 0 && toY <= 255)) { ++ continue; ++ } ++ ++ final LeavesSection toSection = sections[toY >> 4]; ++ if (toSection == null || toSection.isSkippable()) { ++ continue; ++ } ++ ++ final int sectionLocalIndex = getIndex(toX, toY & 15, toZ); ++ final int toBlock = toSection.getBlock(sectionLocalIndex); ++ ++ if (toSection.isLeaf(toBlock)) { ++ final int newDistance = toSection.getDistance(toBlock); ++ if (newDistance > distance) { ++ toSection.setDistance(sectionLocalIndex, toBlock, distance); ++ positionsEqual.add(getIndex(toX, toY, toZ)); ++ } ++ } ++ } ++ } ++ } ++ ++ // done updating blocks, now just update the blockstates and palette ++ for (int i = 0, len = sectionsNBT.size(); i < len; ++i) { ++ final MapType sectionNBT = sectionsNBT.getMap(i); ++ final int y = sectionNBT.getInt("Y"); ++ final LeavesSection section = sections[y]; ++ ++ section.writeInto(sectionNBT); ++ } ++ ++ // if sides changed during process, update it now ++ if (newSides != 0) { ++ MapType upgradeData = level.getMap("UpgradeData"); ++ if (upgradeData == null) { ++ level.setMap("UpgradeData", upgradeData = Types.NBT.createEmptyMap()); ++ } ++ ++ upgradeData.setByte("Sides", (byte)(upgradeData.getByte("Sides") | newSides)); ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++ public static int getIndex(final int x, final int y, final int z) { ++ return y << 8 | z << 4 | x; ++ } ++ ++ public static int getX(final int index) { ++ return index & 15; ++ } ++ ++ public static int getY(final int index) { ++ return index >> 8 & 255; ++ } ++ ++ public static int getZ(final int index) { ++ return index >> 4 & 15; ++ } ++ ++ public static int getSideMask(final boolean noLeft, final boolean noRight, final boolean noBack, final boolean noForward) { ++ final int ret; ++ ++ if (noBack) { ++ if (noRight) { ++ ret = 2; ++ } else if (noLeft) { ++ ret = 128; ++ } else { ++ ret = 1; ++ } ++ } else if (noForward) { ++ if (noLeft) { ++ ret = 32; ++ } else if (noRight) { ++ ret = 8; ++ } else { ++ ret = 16; ++ } ++ } else if (noRight) { ++ ret = 4; ++ } else if (noLeft) { ++ ret = 64; ++ } else { ++ ret = 0; ++ } ++ ++ return ret; ++ } ++ ++ public abstract static class Section { ++ protected final ListType palette; ++ protected final int sectionY; ++ protected PackedBitStorage storage; ++ ++ public Section(final MapType section) { ++ this.palette = section.getList("Palette", ObjectType.MAP); ++ this.sectionY = section.getInt("Y"); ++ this.readStorage(section); ++ } ++ ++ protected void readStorage(final MapType section) { ++ if (this.initSkippable()) { ++ this.storage = null; ++ } else { ++ final long[] states = section.getLongs("BlockStates"); ++ final int bits = Math.max(4, DataFixUtils.ceillog2(this.palette.size())); ++ this.storage = new PackedBitStorage(bits, 4096, states); ++ } ++ } ++ ++ public void writeInto(final MapType section) { ++ if (this.isSkippable()) { ++ return; ++ } ++ ++ section.setList("Palette", this.palette); ++ section.setLongs("BlockStates", this.storage.getRaw()); ++ } ++ ++ public boolean isSkippable() { ++ return this.storage == null; ++ } ++ ++ public int getBlock(final int index) { ++ return this.storage.get(index); ++ } ++ ++ protected int getStateId(final String name, final boolean persistent, final int distance) { ++ return LEAVES_TO_ID.getInt(name) << 5 | (persistent ? 16 : 0) | distance; ++ } ++ ++ protected int getSectionY() { ++ return this.sectionY; ++ } ++ ++ protected abstract boolean initSkippable(); ++ } ++ ++ public static final class LeavesSection extends Section { ++ private IntOpenHashSet leaveIds; ++ private IntOpenHashSet logIds; ++ private Int2IntOpenHashMap stateToIdMap; ++ ++ public LeavesSection(final MapType section) { ++ super(section); ++ } ++ ++ @Override ++ protected boolean initSkippable() { ++ this.leaveIds = new IntOpenHashSet(); ++ this.logIds = new IntOpenHashSet(); ++ this.stateToIdMap = new Int2IntOpenHashMap(); ++ this.stateToIdMap.defaultReturnValue(-1); ++ ++ for(int i = 0; i < this.palette.size(); ++i) { ++ final MapType blockState = this.palette.getMap(i); ++ final String name = blockState.getString("Name", ""); ++ if (LEAVES_TO_ID.containsKey(name)) { ++ final MapType properties = blockState.getMap("Properties"); ++ final boolean notDecayable = properties != null && "false".equals(properties.getString("decayable")); ++ ++ this.leaveIds.add(i); ++ this.stateToIdMap.put(this.getStateId(name, notDecayable, 7), i); ++ this.palette.setMap(i, this.makeNewLeafTag(name, notDecayable, 7)); ++ } ++ ++ if (LOGS.contains(name)) { ++ this.logIds.add(i); ++ } ++ } ++ ++ return this.leaveIds.isEmpty() && this.logIds.isEmpty(); ++ } ++ ++ private MapType makeNewLeafTag(final String name, final boolean notDecayable, final int distance) { ++ final MapType properties = Types.NBT.createEmptyMap(); ++ final MapType ret = Types.NBT.createEmptyMap(); ++ ++ ret.setString("Name", name); ++ ret.setMap("Properties", properties); ++ ++ properties.setString("persistent", Boolean.toString(notDecayable)); ++ properties.setString("distance", Integer.toString(distance)); ++ ++ return ret; ++ } ++ ++ public boolean isLog(final int id) { ++ return this.logIds.contains(id); ++ } ++ ++ public boolean isLeaf(final int id) { ++ return this.leaveIds.contains(id); ++ } ++ ++ // only call for logs or leaves, will throw otherwise! ++ private int getDistance(final int id) { ++ if (this.isLog(id)) { ++ return 0; ++ } ++ ++ return Integer.parseInt(this.palette.getMap(id).getMap("Properties").getString("distance")); ++ } ++ ++ private void setDistance(final int index, final int id, final int distance) { ++ final MapType state = this.palette.getMap(id); ++ final String name = state.getString("Name"); ++ final boolean persistent = "true".equals(state.getMap("Properties").getString("persistent")); ++ final int newState = this.getStateId(name, persistent, distance); ++ int newStateId; ++ if ((newStateId = this.stateToIdMap.get(newState)) == -1) { ++ newStateId = this.palette.size(); ++ this.leaveIds.add(newStateId); ++ this.stateToIdMap.put(newState, newStateId); ++ this.palette.addMap(this.makeNewLeafTag(name, persistent, distance)); ++ } ++ ++ if (1 << this.storage.getBits() <= newStateId) { ++ // need to widen storage ++ final PackedBitStorage newStorage = new PackedBitStorage(this.storage.getBits() + 1, 4096); ++ ++ for(int i = 0; i < 4096; ++i) { ++ newStorage.set(i, this.storage.get(i)); ++ } ++ ++ this.storage = newStorage; ++ } ++ ++ this.storage.set(index, newStateId); ++ } ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1500.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1500.java +new file mode 100644 +index 0000000000000000000000000000000000000000..9208152e2a158470f37b0eb022478e8e5287c12b +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1500.java +@@ -0,0 +1,24 @@ ++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 V1500 { ++ ++ protected static final int VERSION = MCVersions.V18W22C + 1; ++ ++ private V1500() {} ++ ++ public static void register() { ++ MCTypeRegistry.TILE_ENTITY.addConverterForId("DUMMY", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ data.setBoolean("keepPacked", true); ++ return null; ++ } ++ }); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1501.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1501.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c497faa60c37f30ceb0d7c394446d6074599c1b7 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1501.java +@@ -0,0 +1,75 @@ ++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; ++import java.util.Map; ++ ++public final class V1501 { ++ ++ protected static final int VERSION = MCVersions.V1_13_PRE1; ++ ++ private static final Map RENAMES = ImmutableMap.builder() ++ .put("minecraft:recipes/brewing/speckled_melon", "minecraft:recipes/brewing/glistering_melon_slice") ++ .put("minecraft:recipes/building_blocks/black_stained_hardened_clay", "minecraft:recipes/building_blocks/black_terracotta") ++ .put("minecraft:recipes/building_blocks/blue_stained_hardened_clay", "minecraft:recipes/building_blocks/blue_terracotta") ++ .put("minecraft:recipes/building_blocks/brown_stained_hardened_clay", "minecraft:recipes/building_blocks/brown_terracotta") ++ .put("minecraft:recipes/building_blocks/cyan_stained_hardened_clay", "minecraft:recipes/building_blocks/cyan_terracotta") ++ .put("minecraft:recipes/building_blocks/gray_stained_hardened_clay", "minecraft:recipes/building_blocks/gray_terracotta") ++ .put("minecraft:recipes/building_blocks/green_stained_hardened_clay", "minecraft:recipes/building_blocks/green_terracotta") ++ .put("minecraft:recipes/building_blocks/light_blue_stained_hardened_clay", "minecraft:recipes/building_blocks/light_blue_terracotta") ++ .put("minecraft:recipes/building_blocks/light_gray_stained_hardened_clay", "minecraft:recipes/building_blocks/light_gray_terracotta") ++ .put("minecraft:recipes/building_blocks/lime_stained_hardened_clay", "minecraft:recipes/building_blocks/lime_terracotta") ++ .put("minecraft:recipes/building_blocks/magenta_stained_hardened_clay", "minecraft:recipes/building_blocks/magenta_terracotta") ++ .put("minecraft:recipes/building_blocks/orange_stained_hardened_clay", "minecraft:recipes/building_blocks/orange_terracotta") ++ .put("minecraft:recipes/building_blocks/pink_stained_hardened_clay", "minecraft:recipes/building_blocks/pink_terracotta") ++ .put("minecraft:recipes/building_blocks/purple_stained_hardened_clay", "minecraft:recipes/building_blocks/purple_terracotta") ++ .put("minecraft:recipes/building_blocks/red_stained_hardened_clay", "minecraft:recipes/building_blocks/red_terracotta") ++ .put("minecraft:recipes/building_blocks/white_stained_hardened_clay", "minecraft:recipes/building_blocks/white_terracotta") ++ .put("minecraft:recipes/building_blocks/yellow_stained_hardened_clay", "minecraft:recipes/building_blocks/yellow_terracotta") ++ .put("minecraft:recipes/building_blocks/acacia_wooden_slab", "minecraft:recipes/building_blocks/acacia_slab") ++ .put("minecraft:recipes/building_blocks/birch_wooden_slab", "minecraft:recipes/building_blocks/birch_slab") ++ .put("minecraft:recipes/building_blocks/dark_oak_wooden_slab", "minecraft:recipes/building_blocks/dark_oak_slab") ++ .put("minecraft:recipes/building_blocks/jungle_wooden_slab", "minecraft:recipes/building_blocks/jungle_slab") ++ .put("minecraft:recipes/building_blocks/oak_wooden_slab", "minecraft:recipes/building_blocks/oak_slab") ++ .put("minecraft:recipes/building_blocks/spruce_wooden_slab", "minecraft:recipes/building_blocks/spruce_slab") ++ .put("minecraft:recipes/building_blocks/brick_block", "minecraft:recipes/building_blocks/bricks") ++ .put("minecraft:recipes/building_blocks/chiseled_stonebrick", "minecraft:recipes/building_blocks/chiseled_stone_bricks") ++ .put("minecraft:recipes/building_blocks/end_bricks", "minecraft:recipes/building_blocks/end_stone_bricks") ++ .put("minecraft:recipes/building_blocks/lit_pumpkin", "minecraft:recipes/building_blocks/jack_o_lantern") ++ .put("minecraft:recipes/building_blocks/magma", "minecraft:recipes/building_blocks/magma_block") ++ .put("minecraft:recipes/building_blocks/melon_block", "minecraft:recipes/building_blocks/melon") ++ .put("minecraft:recipes/building_blocks/mossy_stonebrick", "minecraft:recipes/building_blocks/mossy_stone_bricks") ++ .put("minecraft:recipes/building_blocks/nether_brick", "minecraft:recipes/building_blocks/nether_bricks") ++ .put("minecraft:recipes/building_blocks/pillar_quartz_block", "minecraft:recipes/building_blocks/quartz_pillar") ++ .put("minecraft:recipes/building_blocks/red_nether_brick", "minecraft:recipes/building_blocks/red_nether_bricks") ++ .put("minecraft:recipes/building_blocks/snow", "minecraft:recipes/building_blocks/snow_block") ++ .put("minecraft:recipes/building_blocks/smooth_red_sandstone", "minecraft:recipes/building_blocks/cut_red_sandstone") ++ .put("minecraft:recipes/building_blocks/smooth_sandstone", "minecraft:recipes/building_blocks/cut_sandstone") ++ .put("minecraft:recipes/building_blocks/stonebrick", "minecraft:recipes/building_blocks/stone_bricks") ++ .put("minecraft:recipes/building_blocks/stone_stairs", "minecraft:recipes/building_blocks/cobblestone_stairs") ++ .put("minecraft:recipes/building_blocks/string_to_wool", "minecraft:recipes/building_blocks/white_wool_from_string") ++ .put("minecraft:recipes/decorations/fence", "minecraft:recipes/decorations/oak_fence") ++ .put("minecraft:recipes/decorations/purple_shulker_box", "minecraft:recipes/decorations/shulker_box") ++ .put("minecraft:recipes/decorations/slime", "minecraft:recipes/decorations/slime_block") ++ .put("minecraft:recipes/decorations/snow_layer", "minecraft:recipes/decorations/snow") ++ .put("minecraft:recipes/misc/bone_meal_from_block", "minecraft:recipes/misc/bone_meal_from_bone_block") ++ .put("minecraft:recipes/misc/bone_meal_from_bone", "minecraft:recipes/misc/bone_meal") ++ .put("minecraft:recipes/misc/gold_ingot_from_block", "minecraft:recipes/misc/gold_ingot_from_gold_block") ++ .put("minecraft:recipes/misc/iron_ingot_from_block", "minecraft:recipes/misc/iron_ingot_from_iron_block") ++ .put("minecraft:recipes/redstone/fence_gate", "minecraft:recipes/redstone/oak_fence_gate") ++ .put("minecraft:recipes/redstone/noteblock", "minecraft:recipes/redstone/note_block") ++ .put("minecraft:recipes/redstone/trapdoor", "minecraft:recipes/redstone/oak_trapdoor") ++ .put("minecraft:recipes/redstone/wooden_button", "minecraft:recipes/redstone/oak_button") ++ .put("minecraft:recipes/redstone/wooden_door", "minecraft:recipes/redstone/oak_door") ++ .put("minecraft:recipes/redstone/wooden_pressure_plate", "minecraft:recipes/redstone/oak_pressure_plate") ++ .put("minecraft:recipes/transportation/boat", "minecraft:recipes/transportation/oak_boat") ++ .put("minecraft:recipes/transportation/golden_rail", "minecraft:recipes/transportation/powered_rail") ++ .build(); ++ ++ private V1501() {} ++ ++ public static void register() { ++ ConverterAbstractAdvancementsRename.register(VERSION, RENAMES::get); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1502.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1502.java +new file mode 100644 +index 0000000000000000000000000000000000000000..7db79b279a047ec5907a3fb2c6e1a75be14a2f68 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1502.java +@@ -0,0 +1,74 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.recipe.ConverterAbstractRecipeRename; ++import com.google.common.collect.ImmutableMap; ++import java.util.Map; ++ ++public final class V1502 { ++ ++ protected static final int VERSION = MCVersions.V1_13_PRE2; ++ ++ private static final Map RECIPES_UPDATES = ImmutableMap.builder() ++ .put("minecraft:acacia_wooden_slab", "minecraft:acacia_slab") ++ .put("minecraft:birch_wooden_slab", "minecraft:birch_slab") ++ .put("minecraft:black_stained_hardened_clay", "minecraft:black_terracotta") ++ .put("minecraft:blue_stained_hardened_clay", "minecraft:blue_terracotta") ++ .put("minecraft:boat", "minecraft:oak_boat") ++ .put("minecraft:bone_meal_from_block", "minecraft:bone_meal_from_bone_block") ++ .put("minecraft:bone_meal_from_bone", "minecraft:bone_meal") ++ .put("minecraft:brick_block", "minecraft:bricks") ++ .put("minecraft:brown_stained_hardened_clay", "minecraft:brown_terracotta") ++ .put("minecraft:chiseled_stonebrick", "minecraft:chiseled_stone_bricks") ++ .put("minecraft:cyan_stained_hardened_clay", "minecraft:cyan_terracotta") ++ .put("minecraft:dark_oak_wooden_slab", "minecraft:dark_oak_slab") ++ .put("minecraft:end_bricks", "minecraft:end_stone_bricks") ++ .put("minecraft:fence_gate", "minecraft:oak_fence_gate") ++ .put("minecraft:fence", "minecraft:oak_fence") ++ .put("minecraft:golden_rail", "minecraft:powered_rail") ++ .put("minecraft:gold_ingot_from_block", "minecraft:gold_ingot_from_gold_block") ++ .put("minecraft:gray_stained_hardened_clay", "minecraft:gray_terracotta") ++ .put("minecraft:green_stained_hardened_clay", "minecraft:green_terracotta") ++ .put("minecraft:iron_ingot_from_block", "minecraft:iron_ingot_from_iron_block") ++ .put("minecraft:jungle_wooden_slab", "minecraft:jungle_slab") ++ .put("minecraft:light_blue_stained_hardened_clay", "minecraft:light_blue_terracotta") ++ .put("minecraft:light_gray_stained_hardened_clay", "minecraft:light_gray_terracotta") ++ .put("minecraft:lime_stained_hardened_clay", "minecraft:lime_terracotta") ++ .put("minecraft:lit_pumpkin", "minecraft:jack_o_lantern") ++ .put("minecraft:magenta_stained_hardened_clay", "minecraft:magenta_terracotta") ++ .put("minecraft:magma", "minecraft:magma_block") ++ .put("minecraft:melon_block", "minecraft:melon") ++ .put("minecraft:mossy_stonebrick", "minecraft:mossy_stone_bricks") ++ .put("minecraft:noteblock", "minecraft:note_block") ++ .put("minecraft:oak_wooden_slab", "minecraft:oak_slab") ++ .put("minecraft:orange_stained_hardened_clay", "minecraft:orange_terracotta") ++ .put("minecraft:pillar_quartz_block", "minecraft:quartz_pillar") ++ .put("minecraft:pink_stained_hardened_clay", "minecraft:pink_terracotta") ++ .put("minecraft:purple_shulker_box", "minecraft:shulker_box") ++ .put("minecraft:purple_stained_hardened_clay", "minecraft:purple_terracotta") ++ .put("minecraft:red_nether_brick", "minecraft:red_nether_bricks") ++ .put("minecraft:red_stained_hardened_clay", "minecraft:red_terracotta") ++ .put("minecraft:slime", "minecraft:slime_block") ++ .put("minecraft:smooth_red_sandstone", "minecraft:cut_red_sandstone") ++ .put("minecraft:smooth_sandstone", "minecraft:cut_sandstone") ++ .put("minecraft:snow_layer", "minecraft:snow") ++ .put("minecraft:snow", "minecraft:snow_block") ++ .put("minecraft:speckled_melon", "minecraft:glistering_melon_slice") ++ .put("minecraft:spruce_wooden_slab", "minecraft:spruce_slab") ++ .put("minecraft:stonebrick", "minecraft:stone_bricks") ++ .put("minecraft:stone_stairs", "minecraft:cobblestone_stairs") ++ .put("minecraft:string_to_wool", "minecraft:white_wool_from_string") ++ .put("minecraft:trapdoor", "minecraft:oak_trapdoor") ++ .put("minecraft:white_stained_hardened_clay", "minecraft:white_terracotta") ++ .put("minecraft:wooden_button", "minecraft:oak_button") ++ .put("minecraft:wooden_door", "minecraft:oak_door") ++ .put("minecraft:wooden_pressure_plate", "minecraft:oak_pressure_plate") ++ .put("minecraft:yellow_stained_hardened_clay", "minecraft:yellow_terracotta") ++ .build(); ++ ++ private V1502() {} ++ ++ public static void register() { ++ ConverterAbstractRecipeRename.register(VERSION, RECIPES_UPDATES::get); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1506.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1506.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d67a44b7154825efd3e3fd50e0e708ef839866f7 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1506.java +@@ -0,0 +1,219 @@ ++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; ++import ca.spottedleaf.dataconverter.types.json.JsonMapType; ++import ca.spottedleaf.dataconverter.types.json.JsonTypeUtil; ++import ca.spottedleaf.dataconverter.types.nbt.NBTMapType; ++import com.google.common.base.Splitter; ++import com.google.common.collect.ImmutableMap; ++import com.google.common.collect.Lists; ++import com.google.common.collect.Maps; ++import com.mojang.datafixers.util.Pair; ++import com.mojang.serialization.Dynamic; ++import com.mojang.serialization.DynamicOps; ++import net.minecraft.nbt.CompoundTag; ++import net.minecraft.nbt.NbtOps; ++import net.minecraft.nbt.Tag; ++import net.minecraft.util.GsonHelper; ++import java.util.ArrayList; ++import java.util.Collections; ++import java.util.HashMap; ++import java.util.Iterator; ++import java.util.List; ++import java.util.Locale; ++import java.util.Map; ++import java.util.stream.Collectors; ++ ++public final class V1506 { ++ ++ protected static final int VERSION = MCVersions.V1_13_PRE4 + 2; ++ ++ static final Map MAP = new HashMap<>(); ++ static { ++ MAP.put("0", "minecraft:ocean"); ++ MAP.put("1", "minecraft:plains"); ++ MAP.put("2", "minecraft:desert"); ++ MAP.put("3", "minecraft:mountains"); ++ MAP.put("4", "minecraft:forest"); ++ MAP.put("5", "minecraft:taiga"); ++ MAP.put("6", "minecraft:swamp"); ++ MAP.put("7", "minecraft:river"); ++ MAP.put("8", "minecraft:nether"); ++ MAP.put("9", "minecraft:the_end"); ++ MAP.put("10", "minecraft:frozen_ocean"); ++ MAP.put("11", "minecraft:frozen_river"); ++ MAP.put("12", "minecraft:snowy_tundra"); ++ MAP.put("13", "minecraft:snowy_mountains"); ++ MAP.put("14", "minecraft:mushroom_fields"); ++ MAP.put("15", "minecraft:mushroom_field_shore"); ++ MAP.put("16", "minecraft:beach"); ++ MAP.put("17", "minecraft:desert_hills"); ++ MAP.put("18", "minecraft:wooded_hills"); ++ MAP.put("19", "minecraft:taiga_hills"); ++ MAP.put("20", "minecraft:mountain_edge"); ++ MAP.put("21", "minecraft:jungle"); ++ MAP.put("22", "minecraft:jungle_hills"); ++ MAP.put("23", "minecraft:jungle_edge"); ++ MAP.put("24", "minecraft:deep_ocean"); ++ MAP.put("25", "minecraft:stone_shore"); ++ MAP.put("26", "minecraft:snowy_beach"); ++ MAP.put("27", "minecraft:birch_forest"); ++ MAP.put("28", "minecraft:birch_forest_hills"); ++ MAP.put("29", "minecraft:dark_forest"); ++ MAP.put("30", "minecraft:snowy_taiga"); ++ MAP.put("31", "minecraft:snowy_taiga_hills"); ++ MAP.put("32", "minecraft:giant_tree_taiga"); ++ MAP.put("33", "minecraft:giant_tree_taiga_hills"); ++ MAP.put("34", "minecraft:wooded_mountains"); ++ MAP.put("35", "minecraft:savanna"); ++ MAP.put("36", "minecraft:savanna_plateau"); ++ MAP.put("37", "minecraft:badlands"); ++ MAP.put("38", "minecraft:wooded_badlands_plateau"); ++ MAP.put("39", "minecraft:badlands_plateau"); ++ MAP.put("40", "minecraft:small_end_islands"); ++ MAP.put("41", "minecraft:end_midlands"); ++ MAP.put("42", "minecraft:end_highlands"); ++ MAP.put("43", "minecraft:end_barrens"); ++ MAP.put("44", "minecraft:warm_ocean"); ++ MAP.put("45", "minecraft:lukewarm_ocean"); ++ MAP.put("46", "minecraft:cold_ocean"); ++ MAP.put("47", "minecraft:deep_warm_ocean"); ++ MAP.put("48", "minecraft:deep_lukewarm_ocean"); ++ MAP.put("49", "minecraft:deep_cold_ocean"); ++ MAP.put("50", "minecraft:deep_frozen_ocean"); ++ MAP.put("127", "minecraft:the_void"); ++ MAP.put("129", "minecraft:sunflower_plains"); ++ MAP.put("130", "minecraft:desert_lakes"); ++ MAP.put("131", "minecraft:gravelly_mountains"); ++ MAP.put("132", "minecraft:flower_forest"); ++ MAP.put("133", "minecraft:taiga_mountains"); ++ MAP.put("134", "minecraft:swamp_hills"); ++ MAP.put("140", "minecraft:ice_spikes"); ++ MAP.put("149", "minecraft:modified_jungle"); ++ MAP.put("151", "minecraft:modified_jungle_edge"); ++ MAP.put("155", "minecraft:tall_birch_forest"); ++ MAP.put("156", "minecraft:tall_birch_hills"); ++ MAP.put("157", "minecraft:dark_forest_hills"); ++ MAP.put("158", "minecraft:snowy_taiga_mountains"); ++ MAP.put("160", "minecraft:giant_spruce_taiga"); ++ MAP.put("161", "minecraft:giant_spruce_taiga_hills"); ++ MAP.put("162", "minecraft:modified_gravelly_mountains"); ++ MAP.put("163", "minecraft:shattered_savanna"); ++ MAP.put("164", "minecraft:shattered_savanna_plateau"); ++ MAP.put("165", "minecraft:eroded_badlands"); ++ MAP.put("166", "minecraft:modified_wooded_badlands_plateau"); ++ MAP.put("167", "minecraft:modified_badlands_plateau"); ++ } ++ ++ private V1506() {} ++ ++ public static void register() { ++ MCTypeRegistry.LEVEL.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final String generatorOptions = data.getString("generatorOptions"); ++ final String generatorName = data.getString("generatorName"); ++ if ("flat".equalsIgnoreCase(generatorName)) { ++ data.setMap("generatorOptions", V1506.convert(generatorOptions == null ? "" : generatorOptions)); ++ } else if ("buffet".equalsIgnoreCase(generatorName) && generatorOptions != null) { ++ data.setMap("generatorOptions", JsonTypeUtil.convertJsonToNBT(new JsonMapType(GsonHelper.parse(generatorName, true), false))); ++ } ++ return null; ++ } ++ }); ++ } ++ ++ private static MapType convert(final String param0) { ++ final Dynamic dynamic = convert(param0, NbtOps.INSTANCE); ++ ++ return new NBTMapType((CompoundTag)dynamic.getValue()); ++ } ++ ++ // Yeah I ain't touching that. This is basically magic value hell. ++ private static Dynamic convert(final String generatorSettings, final DynamicOps ops) { ++ final Iterator splitSettings = Splitter.on(';').split(generatorSettings).iterator(); ++ String biome = "minecraft:plains"; ++ final Map> structures = Maps.newHashMap(); ++ final List> layers; ++ if (!generatorSettings.isEmpty() && splitSettings.hasNext()) { ++ layers = getLayersInfoFromString(splitSettings.next()); ++ if (!layers.isEmpty()) { ++ // biome is next ++ if (splitSettings.hasNext()) { ++ biome = MAP.getOrDefault(splitSettings.next(), "minecraft:plains"); ++ } ++ ++ // structures is next ++ if (splitSettings.hasNext()) { ++ final String[] structuresSplit = splitSettings.next().toLowerCase(Locale.ROOT).split(","); ++ ++ for (final String structureString : structuresSplit) { ++ final String[] structureInfo = structureString.split("\\(", 2); ++ if (!structureInfo[0].isEmpty()) { ++ structures.put(structureInfo[0], Maps.newHashMap()); ++ if (structureInfo.length > 1 && structureInfo[1].endsWith(")") && structureInfo[1].length() > 1) { ++ // I can't even guess the mappings for these. Not worth my time, it will work regardless of the mappings ++ final String[] var7 = structureInfo[1].substring(0, structureInfo[1].length() - 1).split(" "); ++ ++ for (final String var8 : var7) { ++ String[] var9 = var8.split("=", 2); ++ if (var9.length == 2) { ++ structures.get(structureInfo[0]).put(var9[0], var9[1]); ++ } ++ } ++ } ++ } ++ } ++ } else { ++ structures.put("village", Maps.newHashMap()); ++ } ++ } ++ } else { ++ layers = Lists.newArrayList(); ++ layers.add(Pair.of(1, "minecraft:bedrock")); ++ layers.add(Pair.of(2, "minecraft:dirt")); ++ layers.add(Pair.of(1, "minecraft:grass_block")); ++ structures.put("village", Maps.newHashMap()); ++ } ++ ++ final T layerTag = ops.createList(layers.stream().map((param1x) -> ops.createMap(ImmutableMap.of(ops.createString("height"), ops.createInt(param1x.getFirst()), ops.createString("block"), ops.createString(param1x.getSecond()))))); ++ final T structuresTag = ops.createMap(structures.entrySet().stream().map((param1x) -> Pair.of(ops.createString(param1x.getKey().toLowerCase(Locale.ROOT)), ops.createMap(param1x.getValue().entrySet().stream().map((param1xx) -> Pair.of(ops.createString(param1xx.getKey()), ops.createString(param1xx.getValue()))).collect(Collectors.toMap(Pair::getFirst, Pair::getSecond))))).collect(Collectors.toMap(Pair::getFirst, Pair::getSecond))); ++ return new Dynamic<>(ops, ops.createMap(ImmutableMap.of(ops.createString("layers"), layerTag, ops.createString("biome"), ops.createString(biome), ops.createString("structures"), structuresTag))); ++ } ++ ++ private static Pair getLayerInfoFromString(final String layerString) { ++ final String[] split = layerString.split("\\*", 2); ++ int layerCount; ++ if (split.length == 2) { ++ try { ++ layerCount = Integer.parseInt(split[0]); ++ } catch (final NumberFormatException ex) { ++ return null; ++ } ++ } else { ++ layerCount = 1; ++ } ++ ++ final String blockName = split[split.length - 1]; ++ return Pair.of(layerCount, blockName); ++ } ++ ++ private static List> getLayersInfoFromString(final String layersString) { ++ final List> ret = new ArrayList<>(); ++ final String[] layers = layersString.split(","); ++ ++ for (final String layerString : layers) { ++ final Pair layer = getLayerInfoFromString(layerString); ++ if (layer == null) { ++ return Collections.emptyList(); ++ } ++ ++ ret.add(layer); ++ } ++ ++ return ret; ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1510.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1510.java +new file mode 100644 +index 0000000000000000000000000000000000000000..7dbc6ac66a29d3b5e4df5d0105c8fc03a6aea0e0 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1510.java +@@ -0,0 +1,103 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.entity.ConverterAbstractEntityRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.recipe.ConverterAbstractRecipeRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.stats.ConverterAbstractStatsRename; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.types.MapType; ++import com.google.common.collect.ImmutableMap; ++ ++import java.util.Map; ++ ++public final class V1510 { ++ ++ public static final Map RENAMED_ENTITY_IDS = ImmutableMap.builder() ++ .put("minecraft:commandblock_minecart", "minecraft:command_block_minecart") ++ .put("minecraft:ender_crystal", "minecraft:end_crystal") ++ .put("minecraft:snowman", "minecraft:snow_golem") ++ .put("minecraft:evocation_illager", "minecraft:evoker") ++ .put("minecraft:evocation_fangs", "minecraft:evoker_fangs") ++ .put("minecraft:illusion_illager", "minecraft:illusioner") ++ .put("minecraft:vindication_illager", "minecraft:vindicator") ++ .put("minecraft:villager_golem", "minecraft:iron_golem") ++ .put("minecraft:xp_orb", "minecraft:experience_orb") ++ .put("minecraft:xp_bottle", "minecraft:experience_bottle") ++ .put("minecraft:eye_of_ender_signal", "minecraft:eye_of_ender") ++ .put("minecraft:fireworks_rocket", "minecraft:firework_rocket") ++ .build(); ++ ++ public static final Map RENAMED_BLOCKS = ImmutableMap.builder() ++ .put("minecraft:portal", "minecraft:nether_portal") ++ .put("minecraft:oak_bark", "minecraft:oak_wood") ++ .put("minecraft:spruce_bark", "minecraft:spruce_wood") ++ .put("minecraft:birch_bark", "minecraft:birch_wood") ++ .put("minecraft:jungle_bark", "minecraft:jungle_wood") ++ .put("minecraft:acacia_bark", "minecraft:acacia_wood") ++ .put("minecraft:dark_oak_bark", "minecraft:dark_oak_wood") ++ .put("minecraft:stripped_oak_bark", "minecraft:stripped_oak_wood") ++ .put("minecraft:stripped_spruce_bark", "minecraft:stripped_spruce_wood") ++ .put("minecraft:stripped_birch_bark", "minecraft:stripped_birch_wood") ++ .put("minecraft:stripped_jungle_bark", "minecraft:stripped_jungle_wood") ++ .put("minecraft:stripped_acacia_bark", "minecraft:stripped_acacia_wood") ++ .put("minecraft:stripped_dark_oak_bark", "minecraft:stripped_dark_oak_wood") ++ .put("minecraft:mob_spawner", "minecraft:spawner") ++ .build(); ++ ++ public static final Map RENAMED_ITEMS = ImmutableMap.builder() ++ .putAll(RENAMED_BLOCKS) ++ .put("minecraft:clownfish", "minecraft:tropical_fish") ++ .put("minecraft:chorus_fruit_popped", "minecraft:popped_chorus_fruit") ++ .put("minecraft:evocation_illager_spawn_egg", "minecraft:evoker_spawn_egg") ++ .put("minecraft:vindication_illager_spawn_egg", "minecraft:vindicator_spawn_egg") ++ .build(); ++ ++ private static final Map RECIPES_UPDATES = ImmutableMap.builder() ++ .put("minecraft:acacia_bark", "minecraft:acacia_wood") ++ .put("minecraft:birch_bark", "minecraft:birch_wood") ++ .put("minecraft:dark_oak_bark", "minecraft:dark_oak_wood") ++ .put("minecraft:jungle_bark", "minecraft:jungle_wood") ++ .put("minecraft:oak_bark", "minecraft:oak_wood") ++ .put("minecraft:spruce_bark", "minecraft:spruce_wood") ++ .build(); ++ ++ protected static final int VERSION = MCVersions.V1_13_PRE4 + 6; ++ ++ private V1510() {} ++ ++ public static void register() { ++ ConverterAbstractBlockRename.register(VERSION, RENAMED_BLOCKS::get); ++ ConverterAbstractItemRename.register(VERSION, RENAMED_ITEMS::get); ++ ConverterAbstractRecipeRename.register(VERSION, RECIPES_UPDATES::get); ++ ++ ConverterAbstractEntityRename.register(VERSION, (String input) -> { ++ if (input.startsWith("minecraft:bred_")) { ++ input = "minecraft:".concat(input.substring("minecraft:bred_".length())); ++ } ++ ++ return RENAMED_ENTITY_IDS.get(input); ++ }); ++ ++ ConverterAbstractStatsRename.register(VERSION, ImmutableMap.of( ++ "minecraft:swim_one_cm", "minecraft:walk_on_water_one_cm", ++ "minecraft:dive_one_cm", "minecraft:walk_under_water_one_cm" ++ )::get); ++ ++ ++ MCTypeRegistry.ENTITY.copyWalkers(VERSION, "minecraft:commandblock_minecart", "minecraft:command_block_minecart"); ++ MCTypeRegistry.ENTITY.copyWalkers(VERSION, "minecraft:ender_crystal", "minecraft:end_crystal"); ++ MCTypeRegistry.ENTITY.copyWalkers(VERSION, "minecraft:snowman", "minecraft:snow_golem"); ++ MCTypeRegistry.ENTITY.copyWalkers(VERSION, "minecraft:evocation_illager", "minecraft:evoker"); ++ MCTypeRegistry.ENTITY.copyWalkers(VERSION, "minecraft:evocation_fangs", "minecraft:evoker_fangs"); ++ MCTypeRegistry.ENTITY.copyWalkers(VERSION, "minecraft:illusion_illager", "minecraft:illusioner"); ++ MCTypeRegistry.ENTITY.copyWalkers(VERSION, "minecraft:vindication_illager", "minecraft:vindicator"); ++ MCTypeRegistry.ENTITY.copyWalkers(VERSION, "minecraft:villager_golem", "minecraft:iron_golem"); ++ MCTypeRegistry.ENTITY.copyWalkers(VERSION, "minecraft:xp_orb", "minecraft:experience_orb"); ++ MCTypeRegistry.ENTITY.copyWalkers(VERSION, "minecraft:xp_bottle", "minecraft:experience_bottle"); ++ MCTypeRegistry.ENTITY.copyWalkers(VERSION, "minecraft:eye_of_ender_signal", "minecraft:eye_of_ender"); ++ MCTypeRegistry.ENTITY.copyWalkers(VERSION, "minecraft:fireworks_rocket", "minecraft:firework_rocket"); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1514.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1514.java +new file mode 100644 +index 0000000000000000000000000000000000000000..62feb7e81963f7aeea6332fbae3d71c2d9bf2b95 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1514.java +@@ -0,0 +1,70 @@ ++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; ++import net.minecraft.network.chat.Component; ++import net.minecraft.network.chat.TextComponent; ++import net.minecraft.world.scores.criteria.ObjectiveCriteria; ++ ++public final class V1514 { ++ ++ protected static final int VERSION = MCVersions.V1_13_PRE7 + 1; ++ ++ private V1514() {} ++ ++ public static void register() { ++ MCTypeRegistry.OBJECTIVE.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final String displayName = data.getString("DisplayName"); ++ if (displayName == null) { ++ return null; ++ } ++ ++ final String update = Component.Serializer.toJson(new TextComponent(displayName)); ++ ++ data.setString("DisplayName", update); ++ ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.TEAM.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final String displayName = data.getString("DisplayName"); ++ if (displayName == null) { ++ return null; ++ } ++ ++ final String update = Component.Serializer.toJson(new TextComponent(displayName)); ++ ++ data.setString("DisplayName", update); ++ ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.OBJECTIVE.addStructureConverter(new DataConverter<>(VERSION) { ++ private static ObjectiveCriteria.RenderType getRenderType(String string) { ++ return string.equals("health") ? ObjectiveCriteria.RenderType.HEARTS : ObjectiveCriteria.RenderType.INTEGER; ++ } ++ ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final String renderType = data.getString("RenderType"); ++ if (renderType != null) { ++ return null; ++ } ++ ++ final String criteriaName = data.getString("CriteriaName", ""); ++ ++ data.setString("RenderType", getRenderType(criteriaName).getId()); ++ ++ return null; ++ } ++ }); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1515.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1515.java +new file mode 100644 +index 0000000000000000000000000000000000000000..8f7a2ff2d5a154f667da35215f0e1d0756dbe2a0 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1515.java +@@ -0,0 +1,25 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import com.google.common.collect.ImmutableMap; ++import java.util.Map; ++ ++public final class V1515 { ++ ++ protected static final int VERSION = MCVersions.V1_13_PRE7 + 2; ++ ++ public static final Map RENAMED_BLOCK_IDS = ImmutableMap.builder() ++ .put("minecraft:tube_coral_fan", "minecraft:tube_coral_wall_fan") ++ .put("minecraft:brain_coral_fan", "minecraft:brain_coral_wall_fan") ++ .put("minecraft:bubble_coral_fan", "minecraft:bubble_coral_wall_fan") ++ .put("minecraft:fire_coral_fan", "minecraft:fire_coral_wall_fan") ++ .put("minecraft:horn_coral_fan", "minecraft:horn_coral_wall_fan") ++ .build(); ++ ++ private V1515() {} ++ ++ public static void register() { ++ ConverterAbstractBlockRename.register(VERSION, RENAMED_BLOCK_IDS::get); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1624.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1624.java +new file mode 100644 +index 0000000000000000000000000000000000000000..fc236f356067295a74e6d74e5e10ac099fc8ffa4 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1624.java +@@ -0,0 +1,110 @@ ++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 it.unimi.dsi.fastutil.ints.IntOpenHashSet; ++import org.apache.logging.log4j.LogManager; ++import org.apache.logging.log4j.Logger; ++ ++public final class V1624 { ++ ++ private static final Logger LOGGER = LogManager.getLogger(); ++ ++ protected static final int VERSION = MCVersions.V18W32A + 1; ++ ++ private V1624() {} ++ ++ public static void register() { ++ MCTypeRegistry.CHUNK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType level = data.getMap("Level"); ++ ++ if (level == null) { ++ return null; ++ } ++ ++ final ListType sections = level.getList("Sections", ObjectType.MAP); ++ if (sections == null) { ++ return null; ++ } ++ ++ final IntOpenHashSet positionsToLook = new IntOpenHashSet(); ++ ++ for (int i = 0, len = sections.size(); i < len; ++i) { ++ final TrappedChestSection section = new TrappedChestSection(sections.getMap(i)); ++ if (section.isSkippable()) { ++ continue; ++ } ++ ++ for (int index = 0; index < 4096; ++index) { ++ if (section.isTrappedChest(section.getBlock(index))) { ++ positionsToLook.add(section.getSectionY() << 12 | index); ++ } ++ } ++ } ++ ++ final int chunkX = level.getInt("xPos"); ++ final int chunkZ = level.getInt("zPos"); ++ ++ final ListType tileEntities = level.getList("TileEntities", ObjectType.MAP); ++ ++ if (tileEntities != null) { ++ for (int i = 0, len = tileEntities.size(); i < len; ++i) { ++ final MapType tile = tileEntities.getMap(i); ++ ++ final int x = tile.getInt("x"); ++ final int y = tile.getInt("y"); ++ final int z = tile.getInt("z"); ++ ++ final int index = V1496.getIndex(x - (chunkX << 4), y, z - (chunkZ << 4)); ++ if (!positionsToLook.contains(index)) { ++ continue; ++ } ++ ++ final String id = tile.getString("id"); ++ if (!"minecraft:chest".equals(id)) { ++ LOGGER.warn("Block Entity ({},{},{}) was expected to be a chest (V1624)", x, y, z); ++ } ++ ++ tile.setString("id", "minecraft:trapped_chest"); ++ } ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++ public static final class TrappedChestSection extends V1496.Section { ++ ++ private IntOpenHashSet chestIds; ++ ++ public TrappedChestSection(final MapType section) { ++ super(section); ++ } ++ ++ @Override ++ protected boolean initSkippable() { ++ this.chestIds = new IntOpenHashSet(); ++ ++ for (int i = 0; i < this.palette.size(); ++i) { ++ final MapType blockState = this.palette.getMap(i); ++ final String name = blockState.getString("Name"); ++ if ("minecraft:trapped_chest".equals(name)) { ++ this.chestIds.add(i); ++ } ++ } ++ ++ return this.chestIds.isEmpty(); ++ } ++ ++ public boolean isTrappedChest(final int id) { ++ return this.chestIds.contains(id); ++ } ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V165.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V165.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e94facc0dde06d0abbf415cce3cc0f31207900df +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V165.java +@@ -0,0 +1,79 @@ ++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.ObjectType; ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import com.google.gson.JsonParseException; ++import net.minecraft.network.chat.Component; ++import net.minecraft.network.chat.TextComponent; ++import net.minecraft.util.GsonHelper; ++import net.minecraft.util.datafix.fixes.BlockEntitySignTextStrictJsonFix; ++import org.apache.commons.lang3.StringUtils; ++ ++public final class V165 { ++ ++ protected static final int VERSION = MCVersions.V1_9_PRE2; ++ ++ public static void register() { ++ MCTypeRegistry.ITEM_STACK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType tag = data.getMap("tag"); ++ if (tag == null) { ++ return null; ++ } ++ ++ final ListType pages = tag.getList("pages", ObjectType.STRING); ++ if (pages == null) { ++ return null; ++ } ++ ++ for (int i = 0, len = pages.size(); i < len; ++i) { ++ final String page = pages.getString(i); ++ Component component = null; ++ ++ if (!"null".equals(page) && !StringUtils.isEmpty(page)) { ++ if (page.charAt(0) == '"' && page.charAt(page.length() - 1) == '"' || page.charAt(0) == '{' && page.charAt(page.length() - 1) == '}') { ++ try { ++ component = GsonHelper.fromJson(BlockEntitySignTextStrictJsonFix.GSON, page, Component.class, true); ++ if (component == null) { ++ component = TextComponent.EMPTY; ++ } ++ } catch (final JsonParseException ignored) {} ++ ++ if (component == null) { ++ try { ++ component = Component.Serializer.fromJson(page); ++ } catch (final JsonParseException ignored) {} ++ } ++ ++ if (component == null) { ++ try { ++ component = Component.Serializer.fromJsonLenient(page); ++ } catch (JsonParseException ignored) {} ++ } ++ ++ if (component == null) { ++ component = new TextComponent(page); ++ } ++ } else { ++ component = new TextComponent(page); ++ } ++ } else { ++ component = TextComponent.EMPTY; ++ } ++ ++ pages.setString(i, Component.Serializer.toJson(component)); ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++ private V165() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1800.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1800.java +new file mode 100644 +index 0000000000000000000000000000000000000000..1362a7917243715305650c197a8e7e5689bcfe4a +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1800.java +@@ -0,0 +1,33 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++import com.google.common.collect.ImmutableMap; ++import java.util.Map; ++ ++public final class V1800 { ++ ++ protected static final int VERSION = MCVersions.V1_13_2 + 169; ++ ++ public static final Map RENAMED_ITEM_IDS = ImmutableMap.builder() ++ .put("minecraft:cactus_green", "minecraft:green_dye") ++ .put("minecraft:rose_red", "minecraft:red_dye") ++ .put("minecraft:dandelion_yellow", "minecraft:yellow_dye") ++ .build(); ++ ++ private V1800() {} ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ ConverterAbstractItemRename.register(VERSION, RENAMED_ITEM_IDS::get); ++ ++ registerMob("minecraft:panda"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:pillager", new DataWalkerItemLists("Inventory", "ArmorItems", "HandItems")); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1801.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1801.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a4e2fa4ed6ede8d1783b5591ef1b5ebf3cd28bf0 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1801.java +@@ -0,0 +1,21 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++ ++public final class V1801 { ++ ++ protected static final int VERSION = MCVersions.V1_13_2 + 170; ++ ++ private V1801() {} ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ registerMob("minecraft:illager_beast"); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1802.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1802.java +new file mode 100644 +index 0000000000000000000000000000000000000000..cd5110ef3c18662871020456b60edfb3aeb67166 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1802.java +@@ -0,0 +1,25 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import com.google.common.collect.ImmutableMap; ++ ++public final class V1802 { ++ ++ protected static final int VERSION = MCVersions.V1_13_2 + 171; ++ ++ private V1802() {} ++ ++ public static void register() { ++ ConverterAbstractBlockRename.register(VERSION, ImmutableMap.of( ++ "minecraft:stone_slab", "minecraft:smooth_stone_slab", ++ "minecraft:sign", "minecraft:oak_sign", "minecraft:wall_sign", "minecraft:oak_wall_sign" ++ )::get); ++ ConverterAbstractItemRename.register(VERSION, ImmutableMap.of( ++ "minecraft:stone_slab", "minecraft:smooth_stone_slab", ++ "minecraft:sign", "minecraft:oak_sign" ++ )::get); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1803.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1803.java +new file mode 100644 +index 0000000000000000000000000000000000000000..87d156b6b6066b4c312608d82e5d7d6b0c01abc7 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1803.java +@@ -0,0 +1,48 @@ ++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 net.minecraft.network.chat.Component; ++import net.minecraft.network.chat.TextComponent; ++ ++public final class V1803 { ++ ++ protected static final int VERSION = MCVersions.V1_13_2 + 172; ++ ++ private V1803() {} ++ ++ public static void register() { ++ MCTypeRegistry.ITEM_STACK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType tag = data.getMap("tag"); ++ ++ if (tag == null) { ++ return null; ++ } ++ ++ final MapType display = tag.getMap("display"); ++ ++ if (display == null) { ++ return null; ++ } ++ ++ final ListType lore = display.getList("Lore", ObjectType.STRING); ++ if (lore == null) { ++ return null; ++ } ++ ++ for (int i = 0, len = lore.size(); i < len; ++i) { ++ lore.setString(i, Component.Serializer.toJson(new TextComponent(lore.getString(i)))); ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1904.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1904.java +new file mode 100644 +index 0000000000000000000000000000000000000000..09955e0c2245d8d42ce6ae664ae81e97db8a85f2 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1904.java +@@ -0,0 +1,44 @@ ++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.itemstack.DataWalkerItemLists; ++import ca.spottedleaf.dataconverter.types.MapType; ++ ++public final class V1904 { ++ ++ protected static final int VERSION = MCVersions.V18W43C + 1; ++ ++ private V1904() {} ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:ocelot", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final int catType = data.getInt("CatType"); ++ ++ if (catType == 0) { ++ final String owner = data.getString("Owner"); ++ final String ownerUUID = data.getString("OwnerUUID"); ++ if ((owner != null && owner.length() > 0) || (ownerUUID != null && ownerUUID.length() > 0)) { ++ data.setBoolean("Trusting", true); ++ } ++ } else if (catType > 0 && catType < 4) { ++ data.setString("id", "minecraft:cat"); ++ data.setString("OwnerUUID", data.getString("OwnerUUID", "")); ++ } ++ ++ return null; ++ } ++ }); ++ ++ registerMob("minecraft:cat"); ++ } ++ ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1905.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1905.java +new file mode 100644 +index 0000000000000000000000000000000000000000..2eeec0d9cbd35ff20ba239ea7fd9c2f52f7e4f9e +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1905.java +@@ -0,0 +1,35 @@ ++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 V1905 { ++ ++ protected static final int VERSION = MCVersions.V18W43C + 2; ++ ++ private V1905() {} ++ ++ public static void register() { ++ MCTypeRegistry.CHUNK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType level = data.getMap("Level"); ++ ++ if (level == null) { ++ return null; ++ } ++ ++ final String status = level.getString("Status"); ++ ++ if ("postprocessed".equals(status)) { ++ level.setString("Status", "fullchunk"); ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1906.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1906.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6358c2e0861a3743a3ea6d46a644870892256a79 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1906.java +@@ -0,0 +1,21 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItems; ++ ++public final class V1906 { ++ ++ protected static final int VERSION = MCVersions.V18W43C + 3; ++ ++ private V1906() {} ++ ++ public static void register() { ++ MCTypeRegistry.TILE_ENTITY.addWalker(VERSION, "minecraft:barrel", new DataWalkerItemLists("Items")); ++ MCTypeRegistry.TILE_ENTITY.addWalker(VERSION, "minecraft:smoker", new DataWalkerItemLists("Items")); ++ MCTypeRegistry.TILE_ENTITY.addWalker(VERSION, "minecraft:blast_furnace", new DataWalkerItemLists("Items")); ++ MCTypeRegistry.TILE_ENTITY.addWalker(VERSION, "minecraft:lectern", new DataWalkerItems("Book")); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1911.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1911.java +new file mode 100644 +index 0000000000000000000000000000000000000000..b9cc2e4a2ae42e12ccf4e0b634fd74d3aad317ab +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1911.java +@@ -0,0 +1,48 @@ ++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; ++import com.google.common.collect.ImmutableMap; ++import java.util.Map; ++ ++public final class V1911 { ++ ++ protected static final int VERSION = MCVersions.V18W46A + 1; ++ ++ private static final Map CHUNK_STATUS_REMAP = ImmutableMap.builder() ++ .put("structure_references", "empty") ++ .put("biomes", "empty") ++ .put("base", "surface") ++ .put("carved", "carvers") ++ .put("liquid_carved", "liquid_carvers") ++ .put("decorated", "features") ++ .put("lighted", "light") ++ .put("mobs_spawned", "spawn") ++ .put("finalized", "heightmaps") ++ .put("fullchunk", "full") ++ .build(); ++ ++ ++ private V1911() {} ++ ++ public static void register() { ++ MCTypeRegistry.CHUNK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType level = data.getMap("Level"); ++ ++ if (level == null) { ++ return null; ++ } ++ ++ final String status = level.getString("Status", "empty"); ++ level.setString("Status", CHUNK_STATUS_REMAP.getOrDefault(status, "empty")); ++ ++ return null; ++ } ++ }); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1917.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1917.java +new file mode 100644 +index 0000000000000000000000000000000000000000..71538d858a681c91f7193003e0808cdb4fd1f847 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1917.java +@@ -0,0 +1,26 @@ ++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 V1917 { ++ ++ protected static final int VERSION = MCVersions.V18W49A + 1; ++ ++ private V1917() {} ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:cat", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (data.getInt("CatType") == 9) { ++ data.setInt("CatType", 10); ++ } ++ return null; ++ } ++ }); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1918.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1918.java +new file mode 100644 +index 0000000000000000000000000000000000000000..28fc06da723792e9abc4999376c0941f9a835aff +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1918.java +@@ -0,0 +1,65 @@ ++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; ++import ca.spottedleaf.dataconverter.types.Types; ++ ++public final class V1918 { ++ ++ protected static final int VERSION = MCVersions.V18W49A + 2; ++ ++ private V1918() {} ++ ++ private static String getProfessionString(final int professionId, final int careerId) { ++ if (professionId == 0) { ++ if (careerId == 2) { ++ return "minecraft:fisherman"; ++ } else if (careerId == 3) { ++ return "minecraft:shepherd"; ++ } else { ++ return careerId == 4 ? "minecraft:fletcher" : "minecraft:farmer"; ++ } ++ } else if (professionId == 1) { ++ return careerId == 2 ? "minecraft:cartographer" : "minecraft:librarian"; ++ } else if (professionId == 2) { ++ return "minecraft:cleric"; ++ } else if (professionId == 3) { ++ if (careerId == 2) { ++ return "minecraft:weaponsmith"; ++ } else { ++ return careerId == 3 ? "minecraft:toolsmith" : "minecraft:armorer"; ++ } ++ } else if (professionId == 4) { ++ return careerId == 2 ? "minecraft:leatherworker" : "minecraft:butcher"; ++ } else { ++ return professionId == 5 ? "minecraft:nitwit" : "minecraft:none"; ++ } ++ } ++ ++ public static void register() { ++ final DataConverter, MapType> converter = new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final int profession = data.getInt("Profession"); ++ final int career = data.getInt("Career"); ++ final int careerLevel = data.getInt("CareerLevel", 1); ++ data.remove("Profession"); ++ data.remove("Career"); ++ data.remove("CareerLevel"); ++ ++ final MapType villagerData = Types.NBT.createEmptyMap(); ++ data.setMap("VillagerData", villagerData); ++ villagerData.setString("type", "minecraft:plains"); ++ villagerData.setString("profession", getProfessionString(profession, career)); ++ villagerData.setInt("level", careerLevel); ++ ++ return null; ++ } ++ }; ++ ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:villager", converter); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:zombie_villager", converter); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1920.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1920.java +new file mode 100644 +index 0000000000000000000000000000000000000000..264264b4f8b9f3b365a5c3e971f817b42359556d +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1920.java +@@ -0,0 +1,75 @@ ++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.itemstack.DataWalkerItemLists; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.util.NamespaceUtil; ++ ++public final class V1920 { ++ ++ protected static final int VERSION = MCVersions.V18W50A + 1; ++ ++ private V1920() {} ++ ++ public static void register() { ++ MCTypeRegistry.CHUNK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType level = data.getMap("Level"); ++ if (level == null) { ++ return null; ++ } ++ ++ final MapType structures = level.getMap("Structures"); ++ if (structures == null) { ++ return null; ++ } ++ ++ final MapType starts = structures.getMap("Starts"); ++ if (starts != null) { ++ final MapType village = starts.getMap("New_Village"); ++ if (village != null) { ++ starts.remove("New_Village"); ++ starts.setMap("Village", village); ++ } else { ++ starts.remove("Village"); ++ } ++ } ++ ++ final MapType references = structures.getMap("References"); ++ if (references != null) { ++ final MapType newVillage = references.getMap("New_Village"); ++ // I believe Mojang had a typo here, removing Village from references only made sense ++ // if the new village didn't exist. DFU removes it whether or not it exists, but still relocates ++ // New_Village to Village first. It doesn't make sense to me to relocate it just to remove it, so it ++ // must be a typo. ++ if (newVillage == null) { ++ references.remove("Village"); ++ } else { ++ references.remove("New_Village"); ++ references.setMap("Village", newVillage); ++ } ++ } ++ ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.STRUCTURE_FEATURE.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final String id = data.getString("id"); ++ ++ if ("minecraft:new_village".equals(NamespaceUtil.correctNamespace(id))) { ++ data.setString("id", "minecraft:village"); ++ } ++ ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:campfire", new DataWalkerItemLists("Items")); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1925.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1925.java +new file mode 100644 +index 0000000000000000000000000000000000000000..19dc3d9b18d95d5f0e898d4c52c77a527066adf1 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1925.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; ++import ca.spottedleaf.dataconverter.types.Types; ++ ++public final class V1925 { ++ ++ protected static final int VERSION = MCVersions.V19W03C + 1; ++ ++ public static void register() { ++ MCTypeRegistry.SAVED_DATA.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType root, final long sourceVersion, final long toVersion) { ++ final MapType data = root.getMap("data"); ++ if (data == null) { ++ final MapType ret = Types.NBT.createEmptyMap(); ++ ret.setMap("data", root); ++ ++ return ret; ++ } ++ return null; ++ } ++ }); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1928.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1928.java +new file mode 100644 +index 0000000000000000000000000000000000000000..86caefba615a917d3530112edcc083caaaea4bb9 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1928.java +@@ -0,0 +1,30 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.entity.ConverterAbstractEntityRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++import com.google.common.collect.ImmutableMap; ++ ++public final class V1928 { ++ ++ protected static final int VERSION = MCVersions.V19W04B + 1; ++ ++ private V1928() {} ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ ConverterAbstractEntityRename.register(VERSION, ImmutableMap.of( ++ "minecraft:illager_beast", "minecraft:ravager" ++ )::get); ++ ConverterAbstractItemRename.register(VERSION, ImmutableMap.of( ++ "minecraft:illager_beast_spawn_egg", "minecraft:ravager_spawn_egg" ++ )::get); ++ ++ registerMob("minecraft:ravager"); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1929.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1929.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d58c32aa89e416e40cfef7c5840b772dd4991173 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1929.java +@@ -0,0 +1,49 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++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; ++ ++public final class V1929 { ++ ++ protected static final int VERSION = MCVersions.V19W04B + 2; ++ ++ private V1929() {} ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:wandering_trader", (final MapType data, final long fromVersion, final long toVersion) -> { ++ WalkerUtils.convertList(MCTypeRegistry.ITEM_STACK, data, "Inventory", fromVersion, toVersion); ++ ++ final MapType offers = data.getMap("Offers"); ++ if (offers != null) { ++ final ListType recipes = offers.getList("Recipes", ObjectType.MAP); ++ if (recipes != null) { ++ for (int i = 0, len = recipes.size(); i < len; ++i) { ++ final MapType recipe = recipes.getMap(i); ++ WalkerUtils.convert(MCTypeRegistry.ITEM_STACK, recipe, "buy", fromVersion, toVersion); ++ WalkerUtils.convert(MCTypeRegistry.ITEM_STACK, recipe, "buyB", fromVersion, toVersion); ++ WalkerUtils.convert(MCTypeRegistry.ITEM_STACK, recipe, "sell", fromVersion, toVersion); ++ } ++ } ++ } ++ ++ WalkerUtils.convertList(MCTypeRegistry.ITEM_STACK, data, "ArmorItems", fromVersion, toVersion); ++ WalkerUtils.convertList(MCTypeRegistry.ITEM_STACK, data, "HandItems", fromVersion, toVersion); ++ ++ return null; ++ }); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:trader_llama", (final MapType data, final long fromVersion, final long toVersion) -> { ++ WalkerUtils.convert(MCTypeRegistry.ITEM_STACK, data, "SaddleItem", fromVersion, toVersion); ++ WalkerUtils.convert(MCTypeRegistry.ITEM_STACK, data, "DecorItem", fromVersion, toVersion); ++ ++ WalkerUtils.convertList(MCTypeRegistry.ITEM_STACK, data, "Items", fromVersion, toVersion); ++ WalkerUtils.convertList(MCTypeRegistry.ITEM_STACK, data, "ArmorItems", fromVersion, toVersion); ++ WalkerUtils.convertList(MCTypeRegistry.ITEM_STACK, data, "HandItems", fromVersion, toVersion); ++ ++ return null; ++ }); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1931.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1931.java +new file mode 100644 +index 0000000000000000000000000000000000000000..da845df91d42f1059490d749b235758850ce7254 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1931.java +@@ -0,0 +1,21 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++ ++public final class V1931 { ++ ++ protected static final int VERSION = MCVersions.V19W06A; ++ ++ private V1931() {} ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ registerMob("minecraft:fox"); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1936.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1936.java +new file mode 100644 +index 0000000000000000000000000000000000000000..4e7b22874f17f531b583146db3aa4e57bdd5f27c +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1936.java +@@ -0,0 +1,37 @@ ++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 V1936 { ++ ++ protected static final int VERSION = MCVersions.V19W09A + 1; ++ ++ private V1936() {} ++ ++ public static void register() { ++ MCTypeRegistry.OPTIONS.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final String chatOpacity = data.getString("chatOpacity"); ++ if (chatOpacity != null) { ++ // Vanilla uses createDouble here, but options is always string -> string. I presume they made ++ // a mistake with this converter. ++ data.setString("textBackgroundOpacity", Double.toString(calculateBackground(chatOpacity))); ++ } ++ return null; ++ } ++ }); ++ } ++ ++ private static double calculateBackground(final String opacity) { ++ try { ++ final double d = 0.9D * Double.parseDouble(opacity) + 0.1D; ++ return d / 2.0D; ++ } catch (final NumberFormatException ex) { ++ return 0.5D; ++ } ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1946.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1946.java +new file mode 100644 +index 0000000000000000000000000000000000000000..00e4bd45b04feee990d9d3414c34c0966e65e4ea +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1946.java +@@ -0,0 +1,42 @@ ++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; ++import ca.spottedleaf.dataconverter.types.Types; ++ ++public final class V1946 { ++ ++ protected static final int VERSION = MCVersions.V19W14B + 1; ++ ++ private V1946() {} ++ ++ public static void register() { ++ MCTypeRegistry.POI_CHUNK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType sections = Types.NBT.createEmptyMap(); ++ data.setMap("Sections", sections); ++ ++ for (int y = 0; y < 16; ++y) { ++ final String key = Integer.toString(y); ++ final Object records = data.getGeneric(key); ++ ++ if (records == null) { ++ continue; ++ } ++ ++ data.remove(key); ++ ++ final MapType section = Types.NBT.createEmptyMap(); ++ section.setGeneric("Records", records); ++ sections.setMap(key, section); // integer keys convert to string in DFU (at least for NBT ops) ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1948.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1948.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6b4af1fe7c53e6122d7db952770d14a753f8cab3 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1948.java +@@ -0,0 +1,39 @@ ++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 V1948 { ++ ++ protected static final int VERSION = MCVersions.V1_14_PRE2; ++ ++ private V1948() {} ++ ++ public static void register() { ++ MCTypeRegistry.ITEM_STACK.addConverterForId("minecraft:white_banner", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType tag = data.getMap("tag"); ++ if (tag == null) { ++ return null; ++ } ++ ++ final MapType display = tag.getMap("display"); ++ if (display == null) { ++ return null; ++ } ++ ++ final String name = display.getString("Name"); ++ if (name == null) { ++ return null; ++ } ++ ++ display.setString("Name", name.replace("\"translate\":\"block.minecraft.illager_banner\"", "\"translate\":\"block.minecraft.ominous_banner\"")); ++ ++ return null; ++ } ++ }); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1953.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1953.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a73471e8f3df3b22349b2f842c3e98c2ff8bb5e1 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1953.java +@@ -0,0 +1,27 @@ ++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 V1953 { ++ ++ protected static final int VERSION = MCVersions.V1_14 + 1; ++ ++ private V1953() {} ++ ++ public static void register() { ++ MCTypeRegistry.TILE_ENTITY.addConverterForId("minecraft:banner", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final String name = data.getString("CustomName"); ++ if (name != null) { ++ data.setString("CustomName", name.replace("\"translate\":\"block.minecraft.illager_banner\"", "\"translate\":\"block.minecraft.ominous_banner\"")); ++ } ++ return null; ++ } ++ }); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1955.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1955.java +new file mode 100644 +index 0000000000000000000000000000000000000000..33bfe82709b507c4fd57199f5d8a44d131718d7f +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1955.java +@@ -0,0 +1,94 @@ ++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 net.minecraft.util.Mth; ++ ++public final class V1955 { ++ ++ protected static final int VERSION = MCVersions.V1_14_1_PRE1; ++ ++ private static final int[] LEVEL_XP_THRESHOLDS = new int[] { ++ 0, ++ 10, ++ 50, ++ 100, ++ 150 ++ }; ++ ++ private V1955() {} ++ ++ static int getMinXpPerLevel(final int level) { ++ return LEVEL_XP_THRESHOLDS[Mth.clamp(level - 1, 0, LEVEL_XP_THRESHOLDS.length - 1)]; ++ } ++ ++ static void addLevel(final MapType data, final int level) { ++ MapType villagerData = data.getMap("VillagerData"); ++ if (villagerData == null) { ++ villagerData = Types.NBT.createEmptyMap(); ++ data.setMap("VillagerData", villagerData); ++ } ++ villagerData.setInt("level", level); ++ } ++ ++ static void addXpFromLevel(final MapType data, final int level) { ++ data.setInt("Xp", getMinXpPerLevel(level)); ++ } ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:villager", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType villagerData = data.getMap("VillagerData"); ++ int level = villagerData == null ? 0 : villagerData.getInt("level"); ++ if (level == 0 || level == 1) { ++ // count recipes ++ final MapType offers = data.getMap("Offers"); ++ final ListType recipes = offers == null ? null : offers.getList("Recipes", ObjectType.MAP); ++ final int recipeCount; ++ if (recipes != null) { ++ recipeCount = recipes.size(); ++ } else { ++ recipeCount = 0; ++ } ++ ++ level = Mth.clamp(recipeCount / 2, 1, 5); ++ if (level > 1) { ++ addLevel(data, level); ++ } ++ } ++ ++ if (!data.hasKey("Xp", ObjectType.NUMBER)) { ++ addXpFromLevel(data, level); ++ } ++ ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:zombie_villager", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final Number xp = data.getNumber("Xp"); ++ if (xp == null) { ++ final int level; ++ final MapType villagerData = data.getMap("VillagerData"); ++ if (villagerData == null) { ++ level = 1; ++ } else { ++ level = villagerData.getInt("level", 1); ++ } ++ ++ data.setInt("Xp", getMinXpPerLevel(level)); ++ } ++ return null; ++ } ++ }); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1961.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1961.java +new file mode 100644 +index 0000000000000000000000000000000000000000..8bc6a8734034942a81b282b8766b21fddbe2b304 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1961.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 V1961 { ++ ++ protected static final int VERSION = MCVersions.V1_14_2_PRE3 + 1; ++ ++ private V1961() {} ++ ++ public static void register() { ++ MCTypeRegistry.CHUNK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType level = data.getMap("Level"); ++ if (level == null) { ++ return null; ++ } ++ ++ level.remove("isLightOn"); ++ ++ return null; ++ } ++ }); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1963.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1963.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5e4e7299cec1d3809d1b55ae460e64ec6d2bb477 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V1963.java +@@ -0,0 +1,40 @@ ++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; ++ ++public final class V1963 { ++ ++ protected static final int VERSION = MCVersions.V1_14_2; ++ ++ private V1963() {} ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:villager", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final ListType gossips = data.getList("Gossips", ObjectType.MAP); ++ if (gossips == null) { ++ return null; ++ } ++ ++ for (int i = 0; i < gossips.size();) { ++ final MapType gossip = gossips.getMap(i); ++ if ("golem".equals(gossip.getString("Type"))) { ++ gossips.remove(i); ++ continue; ++ } ++ ++ ++i; ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2100.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2100.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d905043c4f3071e8dc340ac2afe2b81aac345e1e +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2100.java +@@ -0,0 +1,46 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.advancements.ConverterAbstractAdvancementsRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.recipe.ConverterAbstractRecipeRename; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.generic.WalkerUtils; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.ObjectType; ++import com.google.common.collect.ImmutableMap; ++import java.util.Map; ++ ++public final class V2100 { ++ ++ protected static final int VERSION = MCVersions.V1_14_4 + 124; ++ protected static final Map RECIPE_RENAMES = ImmutableMap.of( ++ "minecraft:sugar", "sugar_from_sugar_cane" ++ ); ++ ++ private V2100() {} ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ ConverterAbstractRecipeRename.register(VERSION, RECIPE_RENAMES::get); ++ ConverterAbstractAdvancementsRename.register(VERSION, ImmutableMap.of( ++ "minecraft:recipes/misc/sugar", "minecraft:recipes/misc/sugar_from_sugar_cane" ++ )::get); ++ ++ registerMob("minecraft:bee"); ++ registerMob("minecraft:bee_stinger"); ++ MCTypeRegistry.TILE_ENTITY.addWalker(VERSION, "minecraft:beehive", (data, fromVersion, toVersion) -> { ++ final ListType bees = data.getList("Bees", ObjectType.MAP); ++ if (bees != null) { ++ for (int i = 0, len = bees.size(); i < len; ++i) { ++ WalkerUtils.convert(MCTypeRegistry.ENTITY, bees.getMap(i), "EntityData", fromVersion, toVersion); ++ } ++ } ++ ++ return null; ++ }); ++ } ++} +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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2202.java +@@ -0,0 +1,50 @@ ++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 V2202 { ++ ++ protected static final int VERSION = MCVersions.V19W35A + 1; ++ ++ private V2202() {} ++ ++ public static void register() { ++ MCTypeRegistry.CHUNK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType level = data.getMap("Level"); ++ if (level == null) { ++ return null; ++ } ++ ++ final int[] oldBiomes = level.getInts("Biomes"); ++ ++ if (oldBiomes == null) { ++ return null; ++ } ++ ++ final int[] newBiomes = new int[1024]; ++ level.setInts("Biomes", newBiomes); ++ ++ int n; ++ for(n = 0; n < 4; ++n) { ++ for(int j = 0; j < 4; ++j) { ++ 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; ++ } ++ } ++ ++ for(n = 1; n < 64; ++n) { ++ System.arraycopy(newBiomes, 0, newBiomes, n * 16, 16); ++ } ++ ++ return null; ++ } ++ }); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2209.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2209.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6054cd31cb2e140f05b92736405a7d073be5cf5c +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2209.java +@@ -0,0 +1,26 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.poi.ConverterAbstractPOIRename; ++import com.google.common.collect.ImmutableMap; ++import java.util.Map; ++ ++public final class V2209 { ++ ++ protected static final int VERSION = MCVersions.V19W40A + 1; ++ ++ private V2209() {} ++ ++ public static void register() { ++ final Map renamedIds = ImmutableMap.of( ++ "minecraft:bee_hive", "minecraft:beehive" ++ ); ++ ++ ConverterAbstractBlockRename.register(VERSION, renamedIds::get); ++ ConverterAbstractItemRename.register(VERSION, renamedIds::get); ++ ConverterAbstractPOIRename.register(VERSION, renamedIds::get); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2211.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2211.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6cff6d723616e0a38811872f7b5d28799240ddfe +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2211.java +@@ -0,0 +1,32 @@ ++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; ++import ca.spottedleaf.dataconverter.types.ObjectType; ++ ++public final class V2211 { ++ ++ protected static final int VERSION = MCVersions.V19W41A + 1; ++ ++ private V2211() {} ++ ++ public static void register() { ++ MCTypeRegistry.STRUCTURE_FEATURE.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (!data.hasKey("references", ObjectType.NUMBER)) { ++ return null; ++ } ++ ++ final int references = data.getInt("references"); ++ if (references <= 0) { ++ data.setInt("references", 1); ++ } ++ return null; ++ } ++ }); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2218.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2218.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e06a98a01086c9d6eb9fc80a151f0403247b0033 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2218.java +@@ -0,0 +1,33 @@ ++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 V2218 { ++ ++ protected static final int VERSION = MCVersions.V1_15_PRE1; ++ ++ private V2218() {} ++ ++ public static void register() { ++ MCTypeRegistry.POI_CHUNK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType sections = data.getMap("Sections"); ++ if (sections == null) { ++ return null; ++ } ++ ++ for (final String key : sections.keys()) { ++ final MapType section = sections.getMap(key); ++ ++ section.remove("Valid"); ++ } ++ ++ return null; ++ } ++ }); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2501.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2501.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c8901f95e1076ae8be220c03efd83ce9bd18d2a8 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2501.java +@@ -0,0 +1,65 @@ ++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.MapType; ++import ca.spottedleaf.dataconverter.types.Types; ++ ++public final class V2501 { ++ ++ protected static final int VERSION = MCVersions.V1_15_2 + 271; ++ ++ private V2501() {} ++ ++ private static void registerFurnace(final String id) { ++ MCTypeRegistry.TILE_ENTITY.addWalker(VERSION, id, (data, fromVersion, toVersion) -> { ++ WalkerUtils.convertList(MCTypeRegistry.ITEM_STACK, data, "Items", fromVersion, toVersion); ++ ++ WalkerUtils.convertKeys(MCTypeRegistry.RECIPE, data, "RecipesUsed", fromVersion, toVersion); ++ ++ return null; ++ }); ++ } ++ ++ public static void register() { ++ final DataConverter, MapType> converter = new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final int recipesUsedSize = data.getInt("RecipesUsedSize"); ++ data.remove("RecipesUsedSize"); ++ ++ if (recipesUsedSize <= 0) { ++ return null; ++ } ++ ++ final MapType newRecipes = Types.NBT.createEmptyMap(); ++ data.setMap("RecipesUsed", newRecipes); ++ ++ for (int i = 0; i < recipesUsedSize; ++i) { ++ final String recipeKey = data.getString("RecipeLocation" + i); ++ data.remove("RecipeLocation" + i); ++ final int recipeAmount = data.getInt("RecipeAmount" + i); ++ data.remove("RecipeAmount" + i); ++ ++ if (i <= 0 || recipeKey == null) { ++ continue; ++ } ++ ++ newRecipes.setInt(recipeKey, recipeAmount); ++ } ++ ++ return null; ++ } ++ }; ++ ++ MCTypeRegistry.TILE_ENTITY.addConverterForId("minecraft:furnace", converter); ++ MCTypeRegistry.TILE_ENTITY.addConverterForId("minecraft:blast_furnace", converter); ++ MCTypeRegistry.TILE_ENTITY.addConverterForId("minecraft:smoker", converter); ++ ++ registerFurnace("minecraft:furnace"); ++ registerFurnace("minecraft:smoker"); ++ registerFurnace("minecraft:blast_furnace"); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2502.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2502.java +new file mode 100644 +index 0000000000000000000000000000000000000000..7075f7beb8aacfdadd68f4353d2cf32edaa1905d +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2502.java +@@ -0,0 +1,20 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++ ++public final class V2502 { ++ ++ protected static final int VERSION = MCVersions.V1_15_2 + 272; ++ ++ private V2502() {} ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ registerMob("minecraft:hoglin"); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2503.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2503.java +new file mode 100644 +index 0000000000000000000000000000000000000000..cd1bca807236b917244bbacd8df6f25fd3c42407 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2503.java +@@ -0,0 +1,67 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.advancements.ConverterAbstractAdvancementsRename; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.types.MapType; ++import com.google.common.collect.ImmutableMap; ++import com.google.common.collect.ImmutableSet; ++import java.util.Set; ++ ++public final class V2503 { ++ ++ protected static final int VERSION = MCVersions.V1_15_2 + 273; ++ ++ private static final Set WALL_BLOCKS = ImmutableSet.of( ++ "minecraft:andesite_wall", ++ "minecraft:brick_wall", ++ "minecraft:cobblestone_wall", ++ "minecraft:diorite_wall", ++ "minecraft:end_stone_brick_wall", ++ "minecraft:granite_wall", ++ "minecraft:mossy_cobblestone_wall", ++ "minecraft:mossy_stone_brick_wall", ++ "minecraft:nether_brick_wall", ++ "minecraft:prismarine_wall", ++ "minecraft:red_nether_brick_wall", ++ "minecraft:red_sandstone_wall", ++ "minecraft:sandstone_wall", ++ "minecraft:stone_brick_wall" ++ ); ++ ++ private V2503() {} ++ ++ private static void changeWallProperty(final MapType properties, final String path) { ++ final String property = properties.getString(path); ++ if (property != null) { ++ properties.setString(path, "true".equals(property) ? "low" : "none"); ++ } ++ } ++ ++ public static void register() { ++ MCTypeRegistry.BLOCK_STATE.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (!WALL_BLOCKS.contains(data.getString("Name"))) { ++ return null; ++ } ++ ++ final MapType properties = data.getMap("Properties"); ++ if (properties == null) { ++ return null; ++ } ++ ++ changeWallProperty(properties, "east"); ++ changeWallProperty(properties, "west"); ++ changeWallProperty(properties, "north"); ++ changeWallProperty(properties, "south"); ++ ++ return null; ++ } ++ }); ++ ConverterAbstractAdvancementsRename.register(VERSION, ImmutableMap.of( ++ "minecraft:recipes/misc/composter", "minecraft:recipes/decorations/composter" ++ )::get); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2505.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2505.java +new file mode 100644 +index 0000000000000000000000000000000000000000..350f5cca06d2a864b5a3cc028753fd6489a28ad5 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2505.java +@@ -0,0 +1,49 @@ ++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.itemstack.DataWalkerItemLists; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.Types; ++ ++public final class V2505 { ++ ++ protected static final int VERSION = MCVersions.V20W06A + 1; ++ ++ private V2505() {} ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:villager", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType brain = data.getMap("Brain"); ++ if (brain == null) { ++ return null; ++ } ++ ++ final MapType memories = brain.getMap("memories"); ++ if (memories == null) { ++ return null; ++ } ++ ++ for (final String key : memories.keys()) { ++ final Object value = memories.getGeneric(key); ++ ++ final MapType wrapped = Types.NBT.createEmptyMap(); ++ wrapped.setGeneric("value", value); ++ ++ memories.setMap(key, wrapped); ++ } ++ ++ return null; ++ } ++ }); ++ ++ registerMob("minecraft:piglin"); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2508.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2508.java +new file mode 100644 +index 0000000000000000000000000000000000000000..8cfd380e870ceea4892b8cd7bf855a6e5dc8cc6f +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2508.java +@@ -0,0 +1,24 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import com.google.common.collect.ImmutableMap; ++import java.util.Map; ++ ++public final class V2508 { ++ ++ protected static final int VERSION = MCVersions.V20W08A + 1; ++ ++ private V2508() {} ++ ++ public static void register() { ++ final Map remap = ImmutableMap.of( ++ "minecraft:warped_fungi", "minecraft:warped_fungus", ++ "minecraft:crimson_fungi", "minecraft:crimson_fungus" ++ ); ++ ++ ConverterAbstractBlockRename.register(VERSION, remap::get); ++ ConverterAbstractItemRename.register(VERSION, remap::get); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2509.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2509.java +new file mode 100644 +index 0000000000000000000000000000000000000000..1fa93dec8a81719a19c0f7db589778935ce44d56 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2509.java +@@ -0,0 +1,30 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.entity.ConverterAbstractEntityRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++import com.google.common.collect.ImmutableMap; ++ ++public final class V2509 { ++ ++ protected static final int VERSION = MCVersions.V20W08A + 2; ++ ++ private V2509() {} ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ ConverterAbstractItemRename.register(VERSION, ImmutableMap.of( ++ "minecraft:zombie_pigman_spawn_egg", "minecraft:zombified_piglin_spawn_egg" ++ )::get); ++ ConverterAbstractEntityRename.register(VERSION, ImmutableMap.of( ++ "minecraft:zombie_pigman", "minecraft:zombified_piglin" ++ )::get); ++ ++ registerMob("minecraft:zombified_piglin"); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2511.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2511.java +new file mode 100644 +index 0000000000000000000000000000000000000000..183ab7ed77e30bf87e71e5f682a59fc3a64a7672 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2511.java +@@ -0,0 +1,97 @@ ++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.itemstack.DataWalkerItems; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.Types; ++ ++public final class V2511 { ++ ++ protected static final int VERSION = MCVersions.V20W09A + 1; ++ ++ private V2511() {} ++ ++ private static int[] createUUIDArray(final long most, final long least) { ++ return new int[] { ++ (int)(most >>> 32), ++ (int)most, ++ (int)(least >>> 32), ++ (int)least ++ }; ++ } ++ ++ private static void setUUID(final MapType data, final long most, final long least) { ++ if (most != 0L && least != 0L) { ++ data.setInts("OwnerUUID", createUUIDArray(most, least)); ++ } ++ } ++ ++ public static void register() { ++ final DataConverter, MapType> throwableConverter = new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType owner = data.getMap("owner"); ++ data.remove("owner"); ++ if (owner == null) { ++ return null; ++ } ++ ++ setUUID(data, owner.getLong("M"), owner.getLong("L")); ++ ++ return null; ++ } ++ }; ++ final DataConverter, MapType> potionConverter = new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType potion = data.getMap("Potion"); ++ data.remove("Potion"); ++ ++ data.setMap("Item", potion == null ? Types.NBT.createEmptyMap() : potion); ++ ++ return null; ++ } ++ }; ++ final DataConverter, MapType> llamaSpitConverter = new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType owner = data.getMap("Owner"); ++ data.remove("Owner"); ++ if (owner == null) { ++ return null; ++ } ++ ++ setUUID(data, owner.getLong("OwnerUUIDMost"), owner.getLong("OwnerUUIDLeast")); ++ ++ return null; ++ } ++ }; ++ final DataConverter, MapType> arrowConverter = new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ setUUID(data, data.getLong("OwnerUUIDMost"), data.getLong("OwnerUUIDLeast")); ++ ++ data.remove("OwnerUUIDMost"); ++ data.remove("OwnerUUIDLeast"); ++ ++ return null; ++ } ++ }; ++ ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:egg", throwableConverter); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:ender_pearl", throwableConverter); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:experience_bottle", throwableConverter); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:snowball", throwableConverter); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:potion", throwableConverter); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:potion", potionConverter); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:llama_spit", llamaSpitConverter); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:arrow", arrowConverter); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:spectral_arrow", arrowConverter); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:trident", arrowConverter); ++ ++ // Vanilla migrates the potion item but does not change the schema. ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:potion", new DataWalkerItems("Item")); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2514.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2514.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d494fac0900f61e60c01617e631a0431bbda9438 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2514.java +@@ -0,0 +1,590 @@ ++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 com.google.common.collect.Sets; ++import java.util.Set; ++import java.util.UUID; ++ ++public final class V2514 { ++ ++ protected static final int VERSION = MCVersions.V20W11A + 1; ++ ++ private static final Set ABSTRACT_HORSES = Sets.newHashSet(); ++ private static final Set TAMEABLE_ANIMALS = Sets.newHashSet(); ++ private static final Set ANIMALS = Sets.newHashSet(); ++ private static final Set MOBS = Sets.newHashSet(); ++ private static final Set LIVING_ENTITIES = Sets.newHashSet(); ++ private static final Set PROJECTILES = Sets.newHashSet(); ++ static { ++ ABSTRACT_HORSES.add("minecraft:donkey"); ++ ABSTRACT_HORSES.add("minecraft:horse"); ++ ABSTRACT_HORSES.add("minecraft:llama"); ++ ABSTRACT_HORSES.add("minecraft:mule"); ++ ABSTRACT_HORSES.add("minecraft:skeleton_horse"); ++ ABSTRACT_HORSES.add("minecraft:trader_llama"); ++ ABSTRACT_HORSES.add("minecraft:zombie_horse"); ++ ++ TAMEABLE_ANIMALS.add("minecraft:cat"); ++ TAMEABLE_ANIMALS.add("minecraft:parrot"); ++ TAMEABLE_ANIMALS.add("minecraft:wolf"); ++ ++ ANIMALS.add("minecraft:bee"); ++ ANIMALS.add("minecraft:chicken"); ++ ANIMALS.add("minecraft:cow"); ++ ANIMALS.add("minecraft:fox"); ++ ANIMALS.add("minecraft:mooshroom"); ++ ANIMALS.add("minecraft:ocelot"); ++ ANIMALS.add("minecraft:panda"); ++ ANIMALS.add("minecraft:pig"); ++ ANIMALS.add("minecraft:polar_bear"); ++ ANIMALS.add("minecraft:rabbit"); ++ ANIMALS.add("minecraft:sheep"); ++ ANIMALS.add("minecraft:turtle"); ++ ANIMALS.add("minecraft:hoglin"); ++ ++ MOBS.add("minecraft:bat"); ++ MOBS.add("minecraft:blaze"); ++ MOBS.add("minecraft:cave_spider"); ++ MOBS.add("minecraft:cod"); ++ MOBS.add("minecraft:creeper"); ++ MOBS.add("minecraft:dolphin"); ++ MOBS.add("minecraft:drowned"); ++ MOBS.add("minecraft:elder_guardian"); ++ MOBS.add("minecraft:ender_dragon"); ++ MOBS.add("minecraft:enderman"); ++ MOBS.add("minecraft:endermite"); ++ MOBS.add("minecraft:evoker"); ++ MOBS.add("minecraft:ghast"); ++ MOBS.add("minecraft:giant"); ++ MOBS.add("minecraft:guardian"); ++ MOBS.add("minecraft:husk"); ++ MOBS.add("minecraft:illusioner"); ++ MOBS.add("minecraft:magma_cube"); ++ MOBS.add("minecraft:pufferfish"); ++ MOBS.add("minecraft:zombified_piglin"); ++ MOBS.add("minecraft:salmon"); ++ MOBS.add("minecraft:shulker"); ++ MOBS.add("minecraft:silverfish"); ++ MOBS.add("minecraft:skeleton"); ++ MOBS.add("minecraft:slime"); ++ MOBS.add("minecraft:snow_golem"); ++ MOBS.add("minecraft:spider"); ++ MOBS.add("minecraft:squid"); ++ MOBS.add("minecraft:stray"); ++ MOBS.add("minecraft:tropical_fish"); ++ MOBS.add("minecraft:vex"); ++ MOBS.add("minecraft:villager"); ++ MOBS.add("minecraft:iron_golem"); ++ MOBS.add("minecraft:vindicator"); ++ MOBS.add("minecraft:pillager"); ++ MOBS.add("minecraft:wandering_trader"); ++ MOBS.add("minecraft:witch"); ++ MOBS.add("minecraft:wither"); ++ MOBS.add("minecraft:wither_skeleton"); ++ MOBS.add("minecraft:zombie"); ++ MOBS.add("minecraft:zombie_villager"); ++ MOBS.add("minecraft:phantom"); ++ MOBS.add("minecraft:ravager"); ++ MOBS.add("minecraft:piglin"); ++ ++ LIVING_ENTITIES.add("minecraft:armor_stand"); ++ ++ PROJECTILES.add("minecraft:arrow"); ++ PROJECTILES.add("minecraft:dragon_fireball"); ++ PROJECTILES.add("minecraft:firework_rocket"); ++ PROJECTILES.add("minecraft:fireball"); ++ PROJECTILES.add("minecraft:llama_spit"); ++ PROJECTILES.add("minecraft:small_fireball"); ++ PROJECTILES.add("minecraft:snowball"); ++ PROJECTILES.add("minecraft:spectral_arrow"); ++ PROJECTILES.add("minecraft:egg"); ++ PROJECTILES.add("minecraft:ender_pearl"); ++ PROJECTILES.add("minecraft:experience_bottle"); ++ PROJECTILES.add("minecraft:potion"); ++ PROJECTILES.add("minecraft:trident"); ++ PROJECTILES.add("minecraft:wither_skull"); ++ } ++ ++ static int[] createUUIDArray(final long most, final long least) { ++ return new int[] { ++ (int)(most >>> 32), ++ (int)most, ++ (int)(least >>> 32), ++ (int)least ++ }; ++ } ++ ++ static int[] createUUIDFromString(final MapType data, final String path) { ++ if (data == null) { ++ return null; ++ } ++ ++ final String uuidString = data.getString(path); ++ if (uuidString == null) { ++ return null; ++ } ++ ++ try { ++ final UUID uuid = UUID.fromString(uuidString); ++ return createUUIDArray(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits()); ++ } catch (final IllegalArgumentException ignore) { ++ return null; ++ } ++ } ++ ++ static int[] createUUIDFromLongs(final MapType data, final String most, final String least) { ++ if (data == null) { ++ return null; ++ } ++ ++ final long mostBits = data.getLong(most); ++ final long leastBits = data.getLong(least); ++ ++ return (mostBits != 0 || leastBits != 0) ? createUUIDArray(mostBits, leastBits) : null; ++ } ++ ++ static void replaceUUIDString(final MapType data, final String oldPath, final String newPath) { ++ final int[] newUUID = createUUIDFromString(data, oldPath); ++ if (newUUID != null) { ++ data.remove(oldPath); ++ data.setInts(newPath, newUUID); ++ } ++ } ++ ++ static void replaceUUIDMLTag(final MapType data, final String oldPath, final String newPath) { ++ final int[] uuid = createUUIDFromLongs(data.getMap(oldPath), "M", "L"); ++ if (uuid != null) { ++ data.remove(oldPath); ++ data.setInts(newPath, uuid); ++ } ++ } ++ ++ static void replaceUUIDLeastMost(final MapType data, final String prefix, final String newPath) { ++ final String mostPath = prefix.concat("Most"); ++ final String leastPath = prefix.concat("Least"); ++ ++ final int[] uuid = createUUIDFromLongs(data, mostPath, leastPath); ++ if (uuid != null) { ++ data.remove(mostPath); ++ data.remove(leastPath); ++ data.setInts(newPath, uuid); ++ } ++ } ++ ++ private V2514() {} ++ ++ private static void updatePiglin(final MapType data) { ++ final MapType brain = data.getMap("Brain"); ++ if (brain == null) { ++ return; ++ } ++ ++ final MapType memories = brain.getMap("memories"); ++ if (memories == null) { ++ return; ++ } ++ ++ final MapType angryAt = memories.getMap("minecraft:angry_at"); ++ ++ replaceUUIDString(angryAt, "value", "value"); ++ } ++ ++ private static void updateEvokerFangs(final MapType data) { ++ replaceUUIDLeastMost(data, "OwnerUUID", "Owner"); ++ } ++ ++ private static void updateZombieVillager(final MapType data) { ++ replaceUUIDLeastMost(data, "ConversionPlayer", "ConversionPlayer"); ++ } ++ ++ private static void updateAreaEffectCloud(final MapType data) { ++ replaceUUIDLeastMost(data, "OwnerUUID", "Owner"); ++ } ++ ++ private static void updateShulkerBullet(final MapType data) { ++ replaceUUIDMLTag(data, "Owner", "Owner"); ++ replaceUUIDMLTag(data, "Target", "Target"); ++ } ++ ++ private static void updateItem(final MapType data) { ++ replaceUUIDMLTag(data, "Owner", "Owner"); ++ replaceUUIDMLTag(data, "Thrower", "Thrower"); ++ } ++ ++ private static void updateFox(final MapType data) { ++ final ListType trustedUUIDS = data.getList("TrustedUUIDs", ObjectType.MAP); ++ if (trustedUUIDS == null) { ++ return; ++ } ++ ++ final ListType newUUIDs = Types.NBT.createEmptyList(); ++ data.remove("TrustedUUIDs"); ++ data.setList("Trusted", newUUIDs); ++ ++ for (int i = 0, len = trustedUUIDS.size(); i < len; ++i) { ++ final MapType uuid = trustedUUIDS.getMap(i); ++ final int[] newUUID = createUUIDFromLongs(uuid, "M", "L"); ++ if (newUUID != null) { ++ newUUIDs.addIntArray(newUUID); ++ } ++ } ++ } ++ ++ private static void updateHurtBy(final MapType data) { ++ replaceUUIDString(data, "HurtBy", "HurtBy"); ++ } ++ ++ private static void updateAnimalOwner(final MapType data) { ++ updateAnimal(data); ++ ++ replaceUUIDString(data, "OwnerUUID", "Owner"); ++ } ++ ++ private static void updateAnimal(final MapType data) { ++ updateMob(data); ++ ++ replaceUUIDLeastMost(data, "LoveCause", "LoveCause"); ++ } ++ ++ private static void updateMob(final MapType data) { ++ updateLivingEntity(data); ++ ++ final MapType leash = data.getMap("Leash"); ++ if (leash == null) { ++ return; ++ } ++ ++ replaceUUIDLeastMost(leash, "UUID", "UUID"); ++ } ++ ++ private static void updateLivingEntity(final MapType data) { ++ final ListType attributes = data.getList("Attributes", ObjectType.MAP); ++ if (attributes == null) { ++ return; ++ } ++ ++ for (int i = 0, len = attributes.size(); i < len; ++i) { ++ final MapType attribute = attributes.getMap(i); ++ ++ final ListType modifiers = attribute.getList("Modifiers", ObjectType.MAP); ++ if (modifiers == null) { ++ continue; ++ } ++ ++ for (int k = 0; k < modifiers.size(); ++k) { ++ replaceUUIDLeastMost(modifiers.getMap(k), "UUID", "UUID"); ++ } ++ } ++ } ++ ++ private static void updateProjectile(final MapType data) { ++ final Object ownerUUID = data.getGeneric("OwnerUUID"); ++ if (ownerUUID != null) { ++ data.remove("OwnerUUID"); ++ data.setGeneric("Owner", ownerUUID); ++ } ++ } ++ ++ private static void updateEntityUUID(final MapType data) { ++ replaceUUIDLeastMost(data, "UUID", "UUID"); ++ } ++ ++ public static void register() { ++ // Entity UUID fixes ++ ++ MCTypeRegistry.ENTITY.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ updateEntityUUID(data); ++ return null; ++ } ++ }); ++ ++ final DataConverter, MapType> animalOwnerConverter = new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ updateAnimalOwner(data); ++ return null; ++ } ++ }; ++ final DataConverter, MapType> animalConverter = new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ updateAnimal(data); ++ return null; ++ } ++ }; ++ final DataConverter, MapType> mobConverter = new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ updateMob(data); ++ return null; ++ } ++ }; ++ final DataConverter, MapType> livingEntityConverter = new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ updateLivingEntity(data); ++ return null; ++ } ++ }; ++ final DataConverter, MapType> projectileConverter = new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ updateProjectile(data); ++ return null; ++ } ++ }; ++ for (final String id : ABSTRACT_HORSES) { ++ MCTypeRegistry.ENTITY.addConverterForId(id, animalOwnerConverter); ++ } ++ for (final String id : TAMEABLE_ANIMALS) { ++ MCTypeRegistry.ENTITY.addConverterForId(id, animalOwnerConverter); ++ } ++ for (final String id : ANIMALS) { ++ MCTypeRegistry.ENTITY.addConverterForId(id, animalConverter); ++ } ++ for (final String id : MOBS) { ++ MCTypeRegistry.ENTITY.addConverterForId(id, mobConverter); ++ } ++ for (final String id : LIVING_ENTITIES) { ++ MCTypeRegistry.ENTITY.addConverterForId(id, livingEntityConverter); ++ } ++ for (final String id : PROJECTILES) { ++ MCTypeRegistry.ENTITY.addConverterForId(id, projectileConverter); ++ } ++ ++ ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:bee", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ updateHurtBy(data); ++ return null; ++ } ++ }); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:zombified_piglin", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ updateHurtBy(data); ++ return null; ++ } ++ }); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:fox", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ updateFox(data); ++ return null; ++ } ++ }); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:item", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ updateItem(data); ++ return null; ++ } ++ }); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:shulker_bullet", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ updateShulkerBullet(data); ++ return null; ++ } ++ }); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:area_effect_cloud", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ updateAreaEffectCloud(data); ++ return null; ++ } ++ }); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:zombie_villager", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ updateZombieVillager(data); ++ return null; ++ } ++ }); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:evoker_fangs", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ updateEvokerFangs(data); ++ return null; ++ } ++ }); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:piglin", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ updatePiglin(data); ++ return null; ++ } ++ }); ++ ++ ++ // Update TE ++ MCTypeRegistry.TILE_ENTITY.addConverterForId("minecraft:conduit", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ replaceUUIDMLTag(data, "target_uuid", "Target"); ++ return null; ++ } ++ }); ++ MCTypeRegistry.TILE_ENTITY.addConverterForId("minecraft:skull", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType owner = data.getMap("Owner"); ++ if (owner == null) { ++ return null; ++ } ++ ++ data.remove("Owner"); ++ ++ replaceUUIDString(owner, "Id", "Id"); ++ ++ data.setMap("SkullOwner", owner); ++ ++ return null; ++ } ++ }); ++ ++ // Player UUID ++ MCTypeRegistry.PLAYER.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ updateLivingEntity(data); ++ updateEntityUUID(data); ++ ++ final MapType rootVehicle = data.getMap("RootVehicle"); ++ if (rootVehicle == null) { ++ return null; ++ } ++ ++ replaceUUIDLeastMost(rootVehicle, "Attach", "Attach"); ++ ++ return null; ++ } ++ }); ++ ++ // Level.dat ++ MCTypeRegistry.LEVEL.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ replaceUUIDString(data, "WanderingTraderId", "WanderingTraderId"); ++ ++ final MapType dimensionData = data.getMap("DimensionData"); ++ if (dimensionData != null) { ++ for (final String key : dimensionData.keys()) { ++ final MapType dimension = dimensionData.getMap(key); ++ ++ final MapType dragonFight = dimension.getMap("DragonFight"); ++ if (dragonFight == null) { ++ continue; ++ } ++ ++ replaceUUIDLeastMost(dragonFight, "DragonUUID", "Dragon"); ++ } ++ } ++ ++ final MapType customBossEvents = data.getMap("CustomBossEvents"); ++ if (customBossEvents != null) { ++ for (final String key : customBossEvents.keys()) { ++ final MapType customBossEvent = customBossEvents.getMap(key); ++ ++ final ListType players = customBossEvent.getList("Players", ObjectType.MAP); ++ if (players == null) { ++ continue; ++ } ++ ++ final ListType newPlayers = Types.NBT.createEmptyList(); ++ customBossEvent.setList("Players", newPlayers); ++ ++ for (int i = 0, len = players.size(); i < len; ++i) { ++ final int[] newUUID = createUUIDFromLongs(players.getMap(i), "M", "L"); ++ if (newUUID != null) { ++ newPlayers.addIntArray(newUUID); ++ } ++ } ++ } ++ } ++ ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.SAVED_DATA.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType root, final long sourceVersion, final long toVersion) { ++ final MapType data = root.getMap("data"); ++ if (data == null) { ++ return null; ++ } ++ ++ final ListType raids = data.getList("Raids", ObjectType.MAP); ++ if (raids == null) { ++ return null; ++ } ++ ++ for (int i = 0, len = raids.size(); i < len; ++i) { ++ final MapType raid = raids.getMap(i); ++ ++ final ListType heros = raid.getList("HeroesOfTheVillage", ObjectType.MAP); ++ ++ if (heros == null) { ++ continue; ++ } ++ ++ final ListType newHeros = Types.NBT.createEmptyList(); ++ raid.setList("HeroesOfTheVillage", newHeros); ++ ++ for (int k = 0, klen = heros.size(); k < klen; ++k) { ++ final MapType uuidOld = heros.getMap(i); ++ final int[] uuidNew = createUUIDFromLongs(uuidOld, "UUIDMost", "UUIDLeast"); ++ if (uuidNew != null) { ++ newHeros.addIntArray(uuidNew); ++ } ++ } ++ } ++ ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.ITEM_STACK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType tag = data.getMap("tag"); ++ if (tag == null) { ++ return null; ++ } ++ ++ updateAttributeModifiers(tag); ++ ++ if ("minecraft:player_head".equals(data.getString("id"))) { ++ updateSkullOwner(tag); ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++ private static void updateAttributeModifiers(final MapType tag) { ++ final ListType attributes = tag.getList("AttributeModifiers", ObjectType.MAP); ++ if (attributes == null) { ++ return; ++ } ++ ++ for (int i = 0, len = attributes.size(); i < len; ++i) { ++ replaceUUIDLeastMost(attributes.getMap(i), "UUID", "UUID"); ++ } ++ } ++ ++ private static void updateSkullOwner(final MapType tag) { ++ replaceUUIDString(tag.getMap("SkullOwner"), "Id", "Id"); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2516.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2516.java +new file mode 100644 +index 0000000000000000000000000000000000000000..40bf0a1788520bbf1d66da53b6532bdd8af246f4 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2516.java +@@ -0,0 +1,37 @@ ++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; ++ ++public final class V2516 { ++ ++ protected static final int VERSION = MCVersions.V20W12A + 1; ++ ++ private V2516() {} ++ ++ public static void register() { ++ final DataConverter, MapType> gossipUUIDConverter = new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final ListType gossips = data.getList("Gossips", ObjectType.MAP); ++ ++ if (gossips == null) { ++ return null; ++ } ++ ++ for (int i = 0, len = gossips.size(); i < len; ++i) { ++ V2514.replaceUUIDLeastMost(gossips.getMap(i), "Target", "Target"); ++ } ++ ++ return null; ++ } ++ }; ++ ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:villager", gossipUUIDConverter); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:zombie_villager", gossipUUIDConverter); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2518.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2518.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e7a55eeb02fb99289e4c8bfe2d28fc4a0c716719 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2518.java +@@ -0,0 +1,63 @@ ++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; ++import com.google.common.collect.ImmutableMap; ++import java.util.Map; ++ ++public final class V2518 { ++ ++ protected static final int VERSION = MCVersions.V20W12A + 3; ++ ++ private static final Map FACING_RENAMES = ImmutableMap.builder() ++ .put("down", "down_south") ++ .put("up", "up_north") ++ .put("north", "north_up") ++ .put("south", "south_up") ++ .put("west", "west_up") ++ .put("east", "east_up") ++ .build(); ++ ++ ++ private V2518() {} ++ ++ public static void register() { ++ MCTypeRegistry.TILE_ENTITY.addConverterForId("minecraft:jigsaw", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final String type = data.getString("attachement_type", "minecraft:empty"); ++ final String pool = data.getString("target_pool", "minecraft:empty"); ++ data.remove("attachement_type"); ++ data.remove("target_pool"); ++ ++ data.setString("name", type); ++ data.setString("target", type); ++ data.setString("pool", pool); ++ ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.BLOCK_STATE.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (!"minecraft:jigsaw".equals(data.getString("Name"))) { ++ return null; ++ } ++ ++ final MapType properties = data.getMap("Properties"); ++ if (properties == null) { ++ return null; ++ } ++ ++ final String facing = properties.getString("facing", "north"); ++ properties.remove("facing"); ++ properties.setString("orientation", FACING_RENAMES.getOrDefault(facing, facing)); ++ ++ return null; ++ } ++ }); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2519.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2519.java +new file mode 100644 +index 0000000000000000000000000000000000000000..47a8bb340e72e48fd237d4001b55c81b1182a72f +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2519.java +@@ -0,0 +1,20 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++ ++public final class V2519 { ++ ++ protected static final int VERSION = MCVersions.V20W12A + 4; ++ ++ private V2519() {} ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ registerMob("minecraft:strider"); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2522.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2522.java +new file mode 100644 +index 0000000000000000000000000000000000000000..3a91600427cb013cdfc03b084017b6f32d9e05fa +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2522.java +@@ -0,0 +1,20 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++ ++public final class V2522 { ++ ++ protected static final int VERSION = MCVersions.V20W13B + 1; ++ ++ private V2522() {} ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ registerMob("minecraft:zoglin"); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2523.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2523.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5d3726bb7670bc89feb8ebeed5c097a77e909f5a +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2523.java +@@ -0,0 +1,92 @@ ++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 com.google.common.collect.ImmutableMap; ++import java.util.Map; ++ ++public final class V2523 { ++ ++ protected static final int VERSION = MCVersions.V20W13B + 2; ++ ++ private static final Map RENAMES = ImmutableMap.builder() ++ .put("generic.maxHealth", "generic.max_health") ++ .put("Max Health", "generic.max_health") ++ .put("zombie.spawnReinforcements", "zombie.spawn_reinforcements") ++ .put("Spawn Reinforcements Chance", "zombie.spawn_reinforcements") ++ .put("horse.jumpStrength", "horse.jump_strength") ++ .put("Jump Strength", "horse.jump_strength") ++ .put("generic.followRange", "generic.follow_range") ++ .put("Follow Range", "generic.follow_range") ++ .put("generic.knockbackResistance", "generic.knockback_resistance") ++ .put("Knockback Resistance", "generic.knockback_resistance") ++ .put("generic.movementSpeed", "generic.movement_speed") ++ .put("Movement Speed", "generic.movement_speed") ++ .put("generic.flyingSpeed", "generic.flying_speed") ++ .put("Flying Speed", "generic.flying_speed") ++ .put("generic.attackDamage", "generic.attack_damage") ++ .put("generic.attackKnockback", "generic.attack_knockback") ++ .put("generic.attackSpeed", "generic.attack_speed") ++ .put("generic.armorToughness", "generic.armor_toughness") ++ .build(); ++ ++ private V2523() {} ++ ++ private static void updateName(final MapType data, final String path) { ++ if (data == null) { ++ return; ++ } ++ ++ final String name = data.getString(path); ++ if (name != null) { ++ final String renamed = RENAMES.get(name); ++ if (renamed != null) { ++ data.setString(path, renamed); ++ } ++ } ++ } ++ ++ public static void register() { ++ final DataConverter, MapType> entityConverter = new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final ListType attributes = data.getList("Attributes", ObjectType.MAP); ++ ++ if (attributes == null) { ++ return null; ++ } ++ ++ for (int i = 0, len = attributes.size(); i < len; ++i) { ++ updateName(attributes.getMap(i), "Name"); ++ } ++ ++ return null; ++ } ++ }; ++ ++ MCTypeRegistry.ENTITY.addStructureConverter(entityConverter); ++ MCTypeRegistry.PLAYER.addStructureConverter(entityConverter); ++ ++ MCTypeRegistry.ITEM_STACK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final ListType attributes = data.getList("AttributeModifiers", ObjectType.MAP); ++ ++ if (attributes == null) { ++ return null; ++ } ++ ++ for (int i = 0, len = attributes.size(); i < len; ++i) { ++ updateName(attributes.getMap(i), "AttributeName"); ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2527.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2527.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5e951f91d03f95ed671bb7403592960690c65879 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2527.java +@@ -0,0 +1,123 @@ ++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 com.mojang.datafixers.DataFixUtils; ++import net.minecraft.util.Mth; ++ ++public final class V2527 { ++ ++ protected static final int VERSION = MCVersions.V20W16A + 1; ++ ++ private V2527() {} ++ ++ public static void register() { ++ MCTypeRegistry.CHUNK.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType level = data.getMap("Level"); ++ ++ if (level == null) { ++ return null; ++ } ++ ++ 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); ++ ++ final ListType palette = section.getList("Palette", ObjectType.MAP); ++ ++ if (palette == null) { ++ continue; ++ } ++ ++ final int bits = Math.max(4, DataFixUtils.ceillog2(palette.size())); ++ ++ if (Mth.isPowerOfTwo(bits)) { ++ // fits perfectly ++ continue; ++ } ++ ++ final long[] states = section.getLongs("BlockStates"); ++ if (states == null) { ++ // wat ++ continue; ++ } ++ ++ section.setLongs("BlockStates", addPadding(4096, bits, states)); ++ } ++ } ++ ++ final MapType heightMaps = level.getMap("Heightmaps"); ++ if (heightMaps != null) { ++ for (final String key : heightMaps.keys()) { ++ final long[] old = heightMaps.getLongs(key); ++ heightMaps.setLongs(key, addPadding(256, 9, old)); ++ } ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++ public static long[] addPadding(final int indices, final int bits, final long[] old) { ++ int k = old.length; ++ if (k == 0) { ++ return old; ++ } else { ++ long l = (1L << bits) - 1L; ++ int m = 64 / bits; ++ int n = (indices + m - 1) / m; ++ long[] padded = new long[n]; ++ int o = 0; ++ int p = 0; ++ long q = 0L; ++ int r = 0; ++ long s = old[0]; ++ long t = k > 1 ? old[1] : 0L; ++ ++ for(int u = 0; u < indices; ++u) { ++ int v = u * bits; ++ int w = v >> 6; ++ int x = (u + 1) * bits - 1 >> 6; ++ int y = v ^ w << 6; ++ if (w != r) { ++ s = t; ++ t = w + 1 < k ? old[w + 1] : 0L; ++ r = w; ++ } ++ ++ long ab; ++ int ac; ++ if (w == x) { ++ ab = s >>> y & l; ++ } else { ++ ac = 64 - y; ++ ab = (s >>> y | t << ac) & l; ++ } ++ ++ ac = p + bits; ++ if (ac >= 64) { ++ padded[o++] = q; ++ q = ab; ++ p = bits; ++ } else { ++ q |= ab << p; ++ p = ac; ++ } ++ } ++ ++ if (q != 0L) { ++ padded[o] = q; ++ } ++ ++ return padded; ++ } ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2528.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2528.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f7f2e3f75a9f8a5533fe010bc23e8384878d7ce8 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2528.java +@@ -0,0 +1,25 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import com.google.common.collect.ImmutableMap; ++ ++public final class V2528 { ++ ++ protected static final int VERSION = MCVersions.V20W16A + 2; ++ ++ private V2528() {} ++ ++ public static void register() { ++ ConverterAbstractItemRename.register(VERSION, ImmutableMap.of( ++ "minecraft:soul_fire_torch", "minecraft:soul_torch", ++ "minecraft:soul_fire_lantern", "minecraft:soul_lantern" ++ )::get); ++ ConverterAbstractBlockRename.register(VERSION, ImmutableMap.of( ++ "minecraft:soul_fire_torch", "minecraft:soul_torch", ++ "minecraft:soul_fire_wall_torch", "minecraft:soul_wall_torch", ++ "minecraft:soul_fire_lantern", "minecraft:soul_lantern" ++ )::get); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2529.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2529.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f239dbf4beb87efd1fff4e5d8d6f041fa8687f01 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2529.java +@@ -0,0 +1,25 @@ ++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 V2529 { ++ ++ protected static final int VERSION = MCVersions.V20W17A; ++ ++ private V2529() {} ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:strider", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (data.getBoolean("NoGravity")) { ++ data.setBoolean("NoGravity", false); ++ } ++ return null; ++ } ++ }); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2531.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2531.java +new file mode 100644 +index 0000000000000000000000000000000000000000..7783d75578d29e09029b26c8c8c0b053c5526eb9 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2531.java +@@ -0,0 +1,63 @@ ++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 V2531 { ++ ++ protected static final int VERSION = MCVersions.V20W17A + 2; ++ ++ private V2531() {} ++ ++ private static boolean isConnected(final String facing) { ++ return !"none".equals(facing); ++ } ++ ++ public static void register() { ++ MCTypeRegistry.BLOCK_STATE.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (!"minecraft:redstone_wire".equals(data.getString("Name"))) { ++ return null; ++ } ++ ++ final MapType properties = data.getMap("Properties"); ++ ++ if (properties == null) { ++ return null; ++ } ++ ++ ++ final String east = properties.getString("east", "none"); ++ final String west = properties.getString("west", "none"); ++ final String north = properties.getString("north", "none"); ++ final String south = properties.getString("south", "none"); ++ ++ final boolean connectedX = isConnected(east) || isConnected(west); ++ final boolean connectedZ = isConnected(north) || isConnected(south); ++ ++ final String newEast = !isConnected(east) && !connectedZ ? "side" : east; ++ final String newWest = !isConnected(west) && !connectedZ ? "side" : west; ++ final String newNorth = !isConnected(north) && !connectedX ? "side" : north; ++ final String newSouth = !isConnected(south) && !connectedX ? "side" : south; ++ ++ if (properties.hasKey("east")) { ++ properties.setString("east", newEast); ++ } ++ if (properties.hasKey("west")) { ++ properties.setString("west", newWest); ++ } ++ if (properties.hasKey("north")) { ++ properties.setString("north", newNorth); ++ } ++ if (properties.hasKey("south")) { ++ properties.setString("south", newSouth); ++ } ++ ++ return null; ++ } ++ }); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2533.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2533.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ece1cd5afab80a8271b2ebac95dcc0a6239cd42c +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2533.java +@@ -0,0 +1,43 @@ ++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; ++ ++public final class V2533 { ++ ++ protected static final int VERSION = MCVersions.V20W18A + 1; ++ ++ private V2533() {} ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:villager", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final ListType attributes = data.getList("Attributes", ObjectType.MAP); ++ ++ if (attributes == null) { ++ return null; ++ } ++ ++ for (int i = 0, len = attributes.size(); i < len; ++i) { ++ final MapType attribute = attributes.getMap(i); ++ ++ if (!"generic.follow_range".equals(attribute.getString("Name"))) { ++ continue; ++ } ++ ++ if (attribute.getDouble("Base") == 16.0) { ++ attribute.setDouble("Base", 48.0); ++ } ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2535.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2535.java +new file mode 100644 +index 0000000000000000000000000000000000000000..9648299bb96c20c783bb7c7010173a0f007584e0 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2535.java +@@ -0,0 +1,34 @@ ++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; ++ ++public final class V2535 { ++ ++ protected static final int VERSION = MCVersions.V20W19A + 1; ++ ++ private V2535() {} ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:shulker", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ // Mojang uses doubles for whatever reason... rotation is in FLOAT. by using double here ++ // the entity load will just ignore rotation and set it to 0... ++ final ListType rotation = data.getList("Rotation", ObjectType.FLOAT); ++ ++ if (rotation == null || rotation.size() == 0) { ++ return null; ++ } ++ ++ rotation.setFloat(0, rotation.getFloat(0) - 180.0F); ++ ++ return null; ++ } ++ }); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2550.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2550.java +new file mode 100644 +index 0000000000000000000000000000000000000000..19d900f5198982eb22a9fbc7639d333be9800fe2 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2550.java +@@ -0,0 +1,341 @@ ++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 com.google.common.collect.ImmutableMap; ++import org.apache.commons.lang3.math.NumberUtils; ++import java.util.HashMap; ++import java.util.Locale; ++import java.util.Map; ++ ++public final class V2550 { ++ ++ protected static final int VERSION = MCVersions.V20W20B + 13; ++ ++ private static final ImmutableMap DEFAULTS = ImmutableMap.builder() ++ .put("minecraft:village", new StructureFeatureConfiguration(32, 8, 10387312)) ++ .put("minecraft:desert_pyramid", new StructureFeatureConfiguration(32, 8, 14357617)) ++ .put("minecraft:igloo", new StructureFeatureConfiguration(32, 8, 14357618)) ++ .put("minecraft:jungle_pyramid", new StructureFeatureConfiguration(32, 8, 14357619)) ++ .put("minecraft:swamp_hut", new StructureFeatureConfiguration(32, 8, 14357620)) ++ .put("minecraft:pillager_outpost", new StructureFeatureConfiguration(32, 8, 165745296)) ++ .put("minecraft:monument", new StructureFeatureConfiguration(32, 5, 10387313)) ++ .put("minecraft:endcity", new StructureFeatureConfiguration(20, 11, 10387313)) ++ .put("minecraft:mansion", new StructureFeatureConfiguration(80, 20, 10387319)) ++ .build(); ++ ++ record StructureFeatureConfiguration(int spacing, int separation, int salt) { ++ ++ public MapType serialize() { ++ final MapType ret = Types.NBT.createEmptyMap(); ++ ++ ret.setInt("spacing", this.spacing); ++ ret.setInt("separation", this.separation); ++ ret.setInt("salt", this.salt); ++ ++ return ret; ++ } ++ } ++ ++ 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 long seed = data.getLong("RandomSeed"); ++ String generatorName = data.getString("generatorName"); ++ if (generatorName != null) { ++ generatorName = generatorName.toLowerCase(Locale.ROOT); ++ } ++ String legacyCustomOptions = data.getString("legacy_custom_options"); ++ if (legacyCustomOptions == null) { ++ legacyCustomOptions = "customized".equals(generatorName) ? data.getString("generatorOptions") : null; ++ } ++ ++ final MapType generator; ++ boolean caves = false; ++ ++ if ("customized".equals(generatorName) || generatorName == null) { ++ generator = defaultOverworld(seed); ++ } else { ++ switch (generatorName) { ++ case "flat": { ++ final MapType generatorOptions = data.getMap("generatorOptions"); ++ ++ final MapType structures = fixFlatStructures(generatorOptions); ++ final MapType settings = Types.NBT.createEmptyMap(); ++ generator = Types.NBT.createEmptyMap(); ++ generator.setString("type", "minecraft:flat"); ++ generator.setMap("settings", settings); ++ ++ settings.setMap("structures", structures); ++ ++ ListType layers = generatorOptions.getList("layers", ObjectType.MAP); ++ if (layers == null) { ++ layers = Types.NBT.createEmptyList(); ++ ++ final int[] heights = new int[] { 1, 2, 1 }; ++ final String[] blocks = new String[] { "minecraft:bedrock", "minecraft:dirt", "minecraft:grass_block" }; ++ for (int i = 0; i < 3; ++i) { ++ final MapType layer = Types.NBT.createEmptyMap(); ++ layer.setInt("height", heights[i]); ++ layer.setString("block", blocks[i]); ++ } ++ } ++ ++ settings.setList("layers", layers); ++ settings.setString("biome", generatorOptions.getString("biome", "minecraft:plains")); ++ ++ break; ++ } ++ ++ case "debug_all_block_states": { ++ generator = Types.NBT.createEmptyMap(); ++ generator.setString("type", "minecraft:debug"); ++ break; ++ } ++ ++ case "buffet": { ++ final MapType generatorOptions = data.getMap("generatorOptions"); ++ final MapType chunkGenerator = generatorOptions == null ? null : generatorOptions.getMap("chunk_generator"); ++ final String chunkGeneratorType = chunkGenerator == null ? null : chunkGenerator.getString("type"); ++ ++ final String newType; ++ if ("minecraft:caves".equals(chunkGeneratorType)) { ++ newType = "minecraft:caves"; ++ caves = true; ++ } else if ("minecraft:floating_islands".equals(chunkGeneratorType)) { ++ newType = "minecraft:floating_islands"; ++ } else { ++ newType = "minecraft:overworld"; ++ } ++ ++ MapType biomeSource = generatorOptions == null ? null : generatorOptions.getMap("biome_source"); ++ if (biomeSource == null) { ++ biomeSource = Types.NBT.createEmptyMap(); ++ biomeSource.setString("type", "minecraft:fixed"); ++ } ++ ++ if ("minecraft:fixed".equals(biomeSource.getString("type"))) { ++ final MapType options = biomeSource.getMap("options"); ++ final ListType biomes = options == null ? null : options.getList("biomes", ObjectType.STRING); ++ final String biome = biomes == null || biomes.size() == 0 ? "minecraft:ocean" : biomes.getString(0); ++ biomeSource.remove("options"); ++ biomeSource.setString("biome", biome); ++ } ++ ++ generator = noise(seed, newType, biomeSource); ++ break; ++ } ++ ++ default: { ++ boolean defaultGen = generatorName.equals("default"); ++ boolean default11Gen = generatorName.equals("default_1_1") || defaultGen && data.getInt("generatorVersion") == 0; ++ boolean amplified = generatorName.equals("amplified"); ++ boolean largeBiomes = generatorName.equals("largebiomes"); ++ ++ generator = noise(seed, amplified ? "minecraft:amplified" : "minecraft:overworld", ++ vanillaBiomeSource(seed, default11Gen, largeBiomes)); ++ break; ++ } ++ } ++ } ++ ++ final boolean mapFeatures = data.getBoolean("MapFeatures", true); ++ final boolean bonusChest = data.getBoolean("BonusChest", false); ++ ++ final MapType ret = Types.NBT.createEmptyMap(); ++ ++ ret.setLong("seed", seed); ++ ret.setBoolean("generate_features", mapFeatures); ++ ret.setBoolean("bonus_chest", bonusChest); ++ ret.setMap("dimensions", vanillaLevels(seed, generator, caves)); ++ if (legacyCustomOptions != null) { ++ ret.setString("legacy_custom_options", legacyCustomOptions); ++ } ++ ++ return ret; ++ } ++ }); ++ } ++ ++ public static MapType noise(final long seed, final String worldType, final MapType biomeSource) { ++ final MapType ret = Types.NBT.createEmptyMap(); ++ ++ ret.setString("type", "minecraft:noise"); ++ ret.setMap("biome_source", biomeSource); ++ ret.setLong("seed", seed); ++ ret.setString("settings", worldType); ++ ++ return ret; ++ } ++ ++ public static MapType vanillaBiomeSource(final long seed, final boolean default11Gen, final boolean largeBiomes) { ++ final MapType ret = Types.NBT.createEmptyMap(); ++ ++ ret.setString("type", "minecraft:vanilla_layered"); ++ ret.setLong("seed", seed); ++ ret.setBoolean("large_biomes", largeBiomes); ++ if (default11Gen) { ++ ret.setBoolean("legacy_biome_init_layer", default11Gen); ++ } ++ ++ return ret; ++ } ++ ++ public static MapType fixFlatStructures(final MapType generatorOptions) { ++ int distance = 32; ++ int spread = 3; ++ int count = 128; ++ boolean stronghold = false; ++ final Map newStructures = new HashMap<>(); ++ ++ if (generatorOptions == null) { ++ stronghold = true; ++ newStructures.put("minecraft:village", DEFAULTS.get("minecraft:village")); ++ } ++ ++ final MapType oldStructures = generatorOptions == null ? null : generatorOptions.getMap("structures"); ++ if (oldStructures != null) { ++ for (final String structureName : oldStructures.keys()) { ++ final MapType structureValues = oldStructures.getMap(structureName); ++ if (structureValues == null) { ++ continue; ++ } ++ ++ for (final String structureValueKey : structureValues.keys()) { ++ final String structureValue = structureValues.getString(structureValueKey); ++ ++ if ("stronghold".equals(structureName)) { ++ stronghold = true; ++ switch (structureValueKey) { ++ case "distance": ++ distance = getInt(structureValue, distance, 1); ++ break; ++ case "spread": ++ spread = getInt(structureValue, spread, 1); ++ break; ++ case "count": ++ count = getInt(structureValue, count, 1); ++ break; ++ } ++ } else { ++ switch (structureValueKey) { ++ case "distance": ++ switch (structureName) { ++ case "village": ++ setSpacing(newStructures, "minecraft:village", structureValue, 9); ++ break; ++ case "biome_1": ++ setSpacing(newStructures, "minecraft:desert_pyramid", structureValue, 9); ++ setSpacing(newStructures, "minecraft:igloo", structureValue, 9); ++ setSpacing(newStructures, "minecraft:jungle_pyramid", structureValue, 9); ++ setSpacing(newStructures, "minecraft:swamp_hut", structureValue, 9); ++ setSpacing(newStructures, "minecraft:pillager_outpost", structureValue, 9); ++ break; ++ case "endcity": ++ setSpacing(newStructures, "minecraft:endcity", structureValue, 1); ++ break; ++ case "mansion": ++ setSpacing(newStructures, "minecraft:mansion", structureValue, 1); ++ break; ++ default: ++ break; ++ } ++ case "separation": ++ if ("oceanmonument".equals(structureName)) { ++ final StructureFeatureConfiguration structure = newStructures.getOrDefault("minecraft:monument", DEFAULTS.get("minecraft:monument")); ++ final int newSpacing = getInt(structureValue, structure.separation, 1); ++ newStructures.put("minecraft:monument", new StructureFeatureConfiguration(newSpacing, structure.separation, structure.salt)); ++ } ++ ++ break; ++ case "spacing": ++ if ("oceanmonument".equals(structureName)) { ++ setSpacing(newStructures, "minecraft:monument", structureValue, 1); ++ } ++ ++ break; ++ } ++ } ++ } ++ } ++ } ++ ++ final MapType ret = Types.NBT.createEmptyMap(); ++ final MapType structuresSerialized = Types.NBT.createEmptyMap(); ++ ret.setMap("structures", structuresSerialized); ++ for (final String key : newStructures.keySet()) { ++ structuresSerialized.setMap(key, newStructures.get(key).serialize()); ++ } ++ ++ if (stronghold) { ++ final MapType strongholdData = Types.NBT.createEmptyMap(); ++ ret.setMap("stronghold", strongholdData); ++ ++ strongholdData.setInt("distance", distance); ++ strongholdData.setInt("spread", spread); ++ strongholdData.setInt("count", count); ++ } ++ ++ return ret; ++ } ++ ++ public static MapType vanillaLevels(final long seed, final MapType generator, final boolean caves) { ++ final MapType ret = Types.NBT.createEmptyMap(); ++ ++ final MapType overworld = Types.NBT.createEmptyMap(); ++ final MapType nether = Types.NBT.createEmptyMap(); ++ final MapType end = Types.NBT.createEmptyMap(); ++ ++ ret.setMap("minecraft:overworld", overworld); ++ ret.setMap("minecraft:the_nether", nether); ++ ret.setMap("minecraft:the_end", end); ++ ++ // overworld ++ overworld.setString("type", caves ? "minecraft:overworld_caves" : "minecraft:overworld"); ++ overworld.setMap("generator", generator); ++ ++ // nether ++ nether.setString("type", "minecraft:the_nether"); ++ final MapType netherBiomeSource = Types.NBT.createEmptyMap(); ++ netherBiomeSource.setString("type", "minecraft:multi_noise"); ++ netherBiomeSource.setLong("seed", seed); ++ netherBiomeSource.setString("preset", "minecraft:nether"); ++ ++ nether.setMap("generator", noise(seed, "minecraft:nether", netherBiomeSource)); ++ ++ // end ++ end.setString("type", "minecraft:the_end"); ++ final MapType endBiomeSource = Types.NBT.createEmptyMap(); ++ endBiomeSource.setString("type", "minecraft:the_end"); ++ endBiomeSource.setLong("seed", seed); ++ end.setMap("generator", noise(seed,"minecraft:end", endBiomeSource)); ++ ++ return ret; ++ } ++ ++ public static MapType defaultOverworld(final long seed) { ++ return noise(seed, "minecraft:overworld", vanillaBiomeSource(seed, false, false)); ++ } ++ ++ private static int getInt(final String value, final int dfl) { ++ return NumberUtils.toInt(value, dfl); ++ } ++ ++ private static int getInt(final String value, final int dfl, final int minVal) { ++ return Math.max(minVal, getInt(value, dfl)); ++ } ++ ++ private static void setSpacing(final Map structures, final String structureName, ++ final String value, final int minVal) { ++ final StructureFeatureConfiguration structure = structures.getOrDefault(structureName, DEFAULTS.get(structureName)); ++ final int newSpacing = getInt(value, structure.spacing, minVal); ++ ++ structures.put(structureName, new StructureFeatureConfiguration(newSpacing, structure.separation, structure.salt)); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2551.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2551.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ac0c4475556fe5202a6aa5724cb47b35c0cc9c00 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2551.java +@@ -0,0 +1,101 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++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; ++ ++public final class V2551 { ++ ++ protected static final int VERSION = MCVersions.V20W20B + 14; ++ ++ public static void register() { ++ 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": { ++ // Vanilla's schema is 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; ++ }); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2552.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2552.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c06aa2099494f82bad2c1f212b2db07405f8f6ff +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2552.java +@@ -0,0 +1,20 @@ ++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 V2552 { ++ ++ protected static final int VERSION = MCVersions.V20W20B + 15; ++ ++ private V2552() {} ++ ++ public static void register() { ++ ConverterAbstractStringValueTypeRename.register(VERSION, MCTypeRegistry.BIOME, ImmutableMap.of( ++ "minecraft:nether", "minecraft:nether_wastes" ++ )::get); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2553.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2553.java +new file mode 100644 +index 0000000000000000000000000000000000000000..20c57f66e92922a8843887fb032f01c873c45679 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2553.java +@@ -0,0 +1,75 @@ ++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; ++import java.util.Map; ++ ++public final class V2553 { ++ ++ protected static final int VERSION = MCVersions.V20W20B + 16; ++ ++ public static final Map BIOME_RENAMES = ImmutableMap.builder() ++ .put("minecraft:extreme_hills", "minecraft:mountains") ++ .put("minecraft:swampland", "minecraft:swamp") ++ .put("minecraft:hell", "minecraft:nether_wastes") ++ .put("minecraft:sky", "minecraft:the_end") ++ .put("minecraft:ice_flats", "minecraft:snowy_tundra") ++ .put("minecraft:ice_mountains", "minecraft:snowy_mountains") ++ .put("minecraft:mushroom_island", "minecraft:mushroom_fields") ++ .put("minecraft:mushroom_island_shore", "minecraft:mushroom_field_shore") ++ .put("minecraft:beaches", "minecraft:beach") ++ .put("minecraft:forest_hills", "minecraft:wooded_hills") ++ .put("minecraft:smaller_extreme_hills", "minecraft:mountain_edge") ++ .put("minecraft:stone_beach", "minecraft:stone_shore") ++ .put("minecraft:cold_beach", "minecraft:snowy_beach") ++ .put("minecraft:roofed_forest", "minecraft:dark_forest") ++ .put("minecraft:taiga_cold", "minecraft:snowy_taiga") ++ .put("minecraft:taiga_cold_hills", "minecraft:snowy_taiga_hills") ++ .put("minecraft:redwood_taiga", "minecraft:giant_tree_taiga") ++ .put("minecraft:redwood_taiga_hills", "minecraft:giant_tree_taiga_hills") ++ .put("minecraft:extreme_hills_with_trees", "minecraft:wooded_mountains") ++ .put("minecraft:savanna_rock", "minecraft:savanna_plateau") ++ .put("minecraft:mesa", "minecraft:badlands") ++ .put("minecraft:mesa_rock", "minecraft:wooded_badlands_plateau") ++ .put("minecraft:mesa_clear_rock", "minecraft:badlands_plateau") ++ .put("minecraft:sky_island_low", "minecraft:small_end_islands") ++ .put("minecraft:sky_island_medium", "minecraft:end_midlands") ++ .put("minecraft:sky_island_high", "minecraft:end_highlands") ++ .put("minecraft:sky_island_barren", "minecraft:end_barrens") ++ .put("minecraft:void", "minecraft:the_void") ++ .put("minecraft:mutated_plains", "minecraft:sunflower_plains") ++ .put("minecraft:mutated_desert", "minecraft:desert_lakes") ++ .put("minecraft:mutated_extreme_hills", "minecraft:gravelly_mountains") ++ .put("minecraft:mutated_forest", "minecraft:flower_forest") ++ .put("minecraft:mutated_taiga", "minecraft:taiga_mountains") ++ .put("minecraft:mutated_swampland", "minecraft:swamp_hills") ++ .put("minecraft:mutated_ice_flats", "minecraft:ice_spikes") ++ .put("minecraft:mutated_jungle", "minecraft:modified_jungle") ++ .put("minecraft:mutated_jungle_edge", "minecraft:modified_jungle_edge") ++ .put("minecraft:mutated_birch_forest", "minecraft:tall_birch_forest") ++ .put("minecraft:mutated_birch_forest_hills", "minecraft:tall_birch_hills") ++ .put("minecraft:mutated_roofed_forest", "minecraft:dark_forest_hills") ++ .put("minecraft:mutated_taiga_cold", "minecraft:snowy_taiga_mountains") ++ .put("minecraft:mutated_redwood_taiga", "minecraft:giant_spruce_taiga") ++ .put("minecraft:mutated_redwood_taiga_hills", "minecraft:giant_spruce_taiga_hills") ++ .put("minecraft:mutated_extreme_hills_with_trees", "minecraft:modified_gravelly_mountains") ++ .put("minecraft:mutated_savanna", "minecraft:shattered_savanna") ++ .put("minecraft:mutated_savanna_rock", "minecraft:shattered_savanna_plateau") ++ .put("minecraft:mutated_mesa", "minecraft:eroded_badlands") ++ .put("minecraft:mutated_mesa_rock", "minecraft:modified_wooded_badlands_plateau") ++ .put("minecraft:mutated_mesa_clear_rock", "minecraft:modified_badlands_plateau") ++ .put("minecraft:warm_deep_ocean", "minecraft:deep_warm_ocean") ++ .put("minecraft:lukewarm_deep_ocean", "minecraft:deep_lukewarm_ocean") ++ .put("minecraft:cold_deep_ocean", "minecraft:deep_cold_ocean") ++ .put("minecraft:frozen_deep_ocean", "minecraft:deep_frozen_ocean") ++ .build(); ++ ++ ++ private V2553() {} ++ ++ public static void register() { ++ ConverterAbstractStringValueTypeRename.register(VERSION, MCTypeRegistry.BIOME, BIOME_RENAMES::get); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2558.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2558.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0e228fd642cbca13e8682950b5f0ec4e3e8a4da7 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2558.java +@@ -0,0 +1,45 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.options.ConverterAbstractOptionsRename; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.Types; ++import com.google.common.collect.ImmutableMap; ++ ++public final class V2558 { ++ ++ protected static final int VERSION = MCVersions.V1_16_PRE2 + 1; ++ ++ private V2558() {} ++ ++ public static void register() { ++ ConverterAbstractOptionsRename.register(VERSION, ImmutableMap.of( ++ "key_key.swapHands", "key_key.swapOffhand" ++ )::get); ++ ++ MCTypeRegistry.WORLD_GEN_SETTINGS.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ MapType dimensions = data.getMap("dimensions"); ++ if (dimensions == null) { ++ dimensions = Types.NBT.createEmptyMap(); ++ data.setMap("dimensions", dimensions); ++ } ++ ++ if (dimensions.isEmpty()) { ++ data.setMap("dimensions", recreateSettings(data)); ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++ private static MapType recreateSettings(final MapType data) { ++ final long seed = data.getLong("seed"); ++ ++ return V2550.vanillaLevels(seed, V2550.defaultOverworld(seed), false); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2568.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2568.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5835159d7016fb4f05d137acba709fa7d8e8e752 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2568.java +@@ -0,0 +1,20 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++ ++public final class V2568 { ++ ++ protected static final int VERSION = MCVersions.V1_16_1 + 1; ++ ++ private V2568() {} ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ registerMob("minecraft:piglin_brute"); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2671.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2671.java +new file mode 100644 +index 0000000000000000000000000000000000000000..374d24db80f3ed8cd241e18d776c39a8da72d8fd +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2671.java +@@ -0,0 +1,18 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++ ++public final class V2671 { ++ ++ protected static final int VERSION = MCVersions.V1_16_5 + 85; ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ registerMob("minecraft:goat"); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2679.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2679.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6c788f51be0439797bf9fc8711d4cf8e382f5c11 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2679.java +@@ -0,0 +1,36 @@ ++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 V2679 { ++ ++ protected static final int VERSION = MCVersions.V1_16_5 + 93; ++ ++ public static void register() { ++ MCTypeRegistry.BLOCK_STATE.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (!"minecraft:cauldron".equals(data.getString("Name"))) { ++ return null; ++ } ++ ++ final MapType properties = data.getMap("Properties"); ++ ++ if (properties == null) { ++ return null; ++ } ++ ++ if (properties.getString("level", "0").equals("0")) { ++ data.remove("Properties"); ++ return null; ++ } else { ++ data.setString("Name", "minecraft:water_cauldron"); ++ return null; ++ } ++ } ++ }); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2680.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2680.java +new file mode 100644 +index 0000000000000000000000000000000000000000..232a28c07c6341a996253282d9872d76b3fce0e3 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2680.java +@@ -0,0 +1,21 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import com.google.common.collect.ImmutableMap; ++ ++public final class V2680 { ++ ++ protected static final int VERSION = MCVersions.V1_16_5 + 94; ++ ++ public static void register() { ++ ConverterAbstractItemRename.register(VERSION, ImmutableMap.of( ++ "minecraft:grass_path", "minecraft:dirt_path" ++ )::get); ++ ConverterAbstractBlockRename.registerAndFixJigsaw(VERSION, ImmutableMap.of( ++ "minecraft:grass_path", "minecraft:dirt_path" ++ )::get); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2686.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2686.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f6a6f33d4f701f4188828994c8e56dea21950366 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2686.java +@@ -0,0 +1,18 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++ ++public final class V2686 { ++ ++ protected static final int VERSION = MCVersions.V20W49A + 1; ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ registerMob("minecraft:axolotl"); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2688.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2688.java +new file mode 100644 +index 0000000000000000000000000000000000000000..2c6450ae2786d05a9eed8c2e8ae03acf5ff3dab4 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2688.java +@@ -0,0 +1,18 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++ ++public final class V2688 { ++ ++ protected static final int VERSION = MCVersions.V20W51A + 1; ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ registerMob("minecraft:glow_squid"); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2690.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2690.java +new file mode 100644 +index 0000000000000000000000000000000000000000..1638f04efd4063c23807b29bc226ad33eed27e7b +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2690.java +@@ -0,0 +1,39 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import com.google.common.collect.ImmutableMap; ++ ++public final class V2690 { ++ ++ protected static final int VERSION = MCVersions.V21W05A; ++ ++ protected static final ImmutableMap RENAMES = ImmutableMap.builder() ++ .put("minecraft:weathered_copper_block", "minecraft:oxidized_copper_block") ++ .put("minecraft:semi_weathered_copper_block", "minecraft:weathered_copper_block") ++ .put("minecraft:lightly_weathered_copper_block", "minecraft:exposed_copper_block") ++ .put("minecraft:weathered_cut_copper", "minecraft:oxidized_cut_copper") ++ .put("minecraft:semi_weathered_cut_copper", "minecraft:weathered_cut_copper") ++ .put("minecraft:lightly_weathered_cut_copper", "minecraft:exposed_cut_copper") ++ .put("minecraft:weathered_cut_copper_stairs", "minecraft:oxidized_cut_copper_stairs") ++ .put("minecraft:semi_weathered_cut_copper_stairs", "minecraft:weathered_cut_copper_stairs") ++ .put("minecraft:lightly_weathered_cut_copper_stairs", "minecraft:exposed_cut_copper_stairs") ++ .put("minecraft:weathered_cut_copper_slab", "minecraft:oxidized_cut_copper_slab") ++ .put("minecraft:semi_weathered_cut_copper_slab", "minecraft:weathered_cut_copper_slab") ++ .put("minecraft:lightly_weathered_cut_copper_slab", "minecraft:exposed_cut_copper_slab") ++ .put("minecraft:waxed_semi_weathered_copper", "minecraft:waxed_weathered_copper") ++ .put("minecraft:waxed_lightly_weathered_copper", "minecraft:waxed_exposed_copper") ++ .put("minecraft:waxed_semi_weathered_cut_copper", "minecraft:waxed_weathered_cut_copper") ++ .put("minecraft:waxed_lightly_weathered_cut_copper", "minecraft:waxed_exposed_cut_copper") ++ .put("minecraft:waxed_semi_weathered_cut_copper_stairs", "minecraft:waxed_weathered_cut_copper_stairs") ++ .put("minecraft:waxed_lightly_weathered_cut_copper_stairs", "minecraft:waxed_exposed_cut_copper_stairs") ++ .put("minecraft:waxed_semi_weathered_cut_copper_slab", "minecraft:waxed_weathered_cut_copper_slab") ++ .put("minecraft:waxed_lightly_weathered_cut_copper_slab", "minecraft:waxed_exposed_cut_copper_slab") ++ .build(); ++ ++ public static void register() { ++ ConverterAbstractItemRename.register(VERSION, RENAMES::get); ++ ConverterAbstractBlockRename.registerAndFixJigsaw(VERSION, RENAMES::get); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2691.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2691.java +new file mode 100644 +index 0000000000000000000000000000000000000000..3841780d52c2e242609fc076efa5902c063b7b48 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2691.java +@@ -0,0 +1,23 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import com.google.common.collect.ImmutableMap; ++ ++public final class V2691 { ++ ++ protected static final int VERSION = MCVersions.V21W05A + 1; ++ ++ protected static final ImmutableMap RENAMES = ImmutableMap.builder() ++ .put("minecraft:waxed_copper", "minecraft:waxed_copper_block") ++ .put("minecraft:oxidized_copper_block", "minecraft:oxidized_copper") ++ .put("minecraft:weathered_copper_block", "minecraft:weathered_copper") ++ .put("minecraft:exposed_copper_block", "minecraft:exposed_copper") ++ .build(); ++ ++ public static void register() { ++ ConverterAbstractItemRename.register(VERSION, RENAMES::get); ++ ConverterAbstractBlockRename.registerAndFixJigsaw(VERSION, RENAMES::get); ++ } ++} +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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2696.java +@@ -0,0 +1,36 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import com.google.common.collect.ImmutableMap; ++ ++public final class V2696 { ++ ++ protected static final int VERSION = MCVersions.V21W07A + 1; ++ ++ protected static final ImmutableMap RENAMES = ImmutableMap.builder() ++ .put("minecraft:grimstone", "minecraft:deepslate") ++ .put("minecraft:grimstone_slab", "minecraft:cobbled_deepslate_slab") ++ .put("minecraft:grimstone_stairs", "minecraft:cobbled_deepslate_stairs") ++ .put("minecraft:grimstone_wall", "minecraft:cobbled_deepslate_wall") ++ .put("minecraft:polished_grimstone", "minecraft:polished_deepslate") ++ .put("minecraft:polished_grimstone_slab", "minecraft:polished_deepslate_slab") ++ .put("minecraft:polished_grimstone_stairs", "minecraft:polished_deepslate_stairs") ++ .put("minecraft:polished_grimstone_wall", "minecraft:polished_deepslate_wall") ++ .put("minecraft:grimstone_tiles", "minecraft:deepslate_tiles") ++ .put("minecraft:grimstone_tile_slab", "minecraft:deepslate_tile_slab") ++ .put("minecraft:grimstone_tile_stairs", "minecraft:deepslate_tile_stairs") ++ .put("minecraft:grimstone_tile_wall", "minecraft:deepslate_tile_wall") ++ .put("minecraft:grimstone_bricks", "minecraft:deepslate_bricks") ++ .put("minecraft:grimstone_brick_slab", "minecraft:deepslate_brick_slab") ++ .put("minecraft:grimstone_brick_stairs", "minecraft:deepslate_brick_stairs") ++ .put("minecraft:grimstone_brick_wall", "minecraft:deepslate_brick_wall") ++ .put("minecraft:chiseled_grimstone", "minecraft:chiseled_deepslate") ++ .build(); ++ ++ public static void register() { ++ ConverterAbstractItemRename.register(VERSION, RENAMES::get); ++ ConverterAbstractBlockRename.registerAndFixJigsaw(VERSION, RENAMES::get); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2700.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2700.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c37142033061a3e4865686ee64d8f15f040d7d41 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2700.java +@@ -0,0 +1,17 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import com.google.common.collect.ImmutableMap; ++ ++public final class V2700 { ++ ++ protected static final int VERSION = MCVersions.V21W10A + 1; ++ ++ public static void register() { ++ ConverterAbstractBlockRename.registerAndFixJigsaw(VERSION, ImmutableMap.of( ++ "minecraft:cave_vines_head", "minecraft:cave_vines", ++ "minecraft:cave_vines_body", "minecraft:cave_vines_plant" ++ )::get); ++ } ++} +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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2701.java +@@ -0,0 +1,209 @@ ++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 com.google.common.collect.Sets; ++import java.util.Set; ++import java.util.regex.Matcher; ++import java.util.regex.Pattern; ++ ++public final class V2701 { ++ ++ protected static final int VERSION = MCVersions.V21W10A + 2; ++ ++ private static final Pattern INDEX_PATTERN = Pattern.compile("\\[(\\d+)\\]"); ++ ++ private static final Set PIECE_TYPE = Sets.newHashSet( ++ "minecraft:jigsaw", ++ "minecraft:nvi", ++ "minecraft:pcp", ++ "minecraft:bastionremnant", ++ "minecraft:runtime" ++ ); ++ private static final Set FEATURES = Sets.newHashSet( ++ "minecraft:tree", ++ "minecraft:flower", ++ "minecraft:block_pile", ++ "minecraft:random_patch" ++ ); ++ ++ public static void register() { ++ MCTypeRegistry.STRUCTURE_FEATURE.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final ListType children = data.getList("Children", ObjectType.MAP); ++ ++ if (children == null) { ++ return null; ++ } ++ ++ for (int i = 0, len = children.size(); i < len; ++i) { ++ final MapType child = children.getMap(i); ++ ++ if (!PIECE_TYPE.contains(child.getString("id"))) { ++ continue; ++ } ++ ++ final String poolElement = child.getString("pool_element"); ++ if (!"minecraft:feature_pool_element".equals(poolElement)) { ++ continue; ++ } ++ ++ final MapType feature = child.getMap("feature"); ++ if (feature == null) { ++ continue; ++ } ++ ++ final String featureName = feature.getString("name"); ++ ++ if (featureName == null) { ++ continue; ++ } ++ ++ final String replacement = convertToString(feature); ++ ++ if (replacement != null) { ++ child.setString("feature", replacement); ++ } ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++ private static String getNestedString(final MapType root, final String... paths) { ++ if (paths.length == 0) { ++ throw new IllegalArgumentException("Missing path"); ++ } ++ ++ Object current = root.getGeneric(paths[0]); ++ ++ for (int i = 1; i < paths.length; ++i) { ++ final String path = paths[i]; ++ ++ final Matcher indexMatcher = INDEX_PATTERN.matcher(path); ++ if (!indexMatcher.matches()) { ++ current = (current instanceof MapType) ? ((MapType)current).getGeneric(path) : null; ++ if (current == null) { ++ break; ++ } ++ continue; ++ } ++ ++ final int index = Integer.parseInt(indexMatcher.group(1)); ++ if (!(current instanceof ListType)) { ++ current = null; ++ break; ++ } else { ++ final ListType list = (ListType)current; ++ if (index >= 0 && index < list.size()) { ++ current = list.getGeneric(index); ++ } else { ++ current = null; ++ break; ++ } ++ } ++ } ++ ++ return current instanceof String ? (String)current : ""; ++ } ++ ++ protected static String convertToString(final MapType feature) { ++ return getReplacement( ++ getNestedString(feature, "type"), ++ getNestedString(feature, "name"), ++ getNestedString(feature, "config", "state_provider", "type"), ++ getNestedString(feature, "config", "state_provider", "state", "Name"), ++ getNestedString(feature, "config", "state_provider", "entries", "[0]", "data", "Name"), ++ getNestedString(feature, "config", "foliage_placer", "type"), ++ getNestedString(feature, "config", "leaves_provider", "state", "Name") ++ ); ++ } ++ ++ private static String getReplacement(final String type, final String name, final String stateType, final String stateName, ++ final String firstEntryName, final String foliageName, final String leavesName) { ++ final String actualType; ++ if (!type.isEmpty()) { ++ actualType = type; ++ } else { ++ if (name.isEmpty()) { ++ return null; ++ } ++ ++ if ("minecraft:normal_tree".equals(name)) { ++ actualType = "minecraft:tree"; ++ } else { ++ actualType = name; ++ } ++ } ++ ++ if (FEATURES.contains(actualType)) { ++ if ("minecraft:random_patch".equals(actualType)) { ++ if ("minecraft:simple_state_provider".equals(stateType)) { ++ if ("minecraft:sweet_berry_bush".equals(stateName)) { ++ return "minecraft:patch_berry_bush"; ++ } ++ ++ if ("minecraft:cactus".equals(stateName)) { ++ return "minecraft:patch_cactus"; ++ } ++ } else if ("minecraft:weighted_state_provider".equals(stateType) && ("minecraft:grass".equals(firstEntryName) || "minecraft:fern".equals(firstEntryName))) { ++ return "minecraft:patch_taiga_grass"; ++ } ++ } else if ("minecraft:block_pile".equals(actualType)) { ++ if (!"minecraft:simple_state_provider".equals(stateType) && !"minecraft:rotated_block_provider".equals(stateType)) { ++ if ("minecraft:weighted_state_provider".equals(stateType)) { ++ if ("minecraft:packed_ice".equals(firstEntryName) || "minecraft:blue_ice".equals(firstEntryName)) { ++ return "minecraft:pile_ice"; ++ } ++ ++ if ("minecraft:jack_o_lantern".equals(firstEntryName) || "minecraft:pumpkin".equals(firstEntryName)) { ++ return "minecraft:pile_pumpkin"; ++ } ++ } ++ } else { ++ if ("minecraft:hay_block".equals(stateName)) { ++ return "minecraft:pile_hay"; ++ } ++ ++ if ("minecraft:melon".equals(stateName)) { ++ return "minecraft:pile_melon"; ++ } ++ ++ if ("minecraft:snow".equals(stateName)) { ++ return "minecraft:pile_snow"; ++ } ++ } ++ } else { ++ if ("minecraft:flower".equals(actualType)) { ++ return "minecraft:flower_plain"; ++ } ++ ++ if ("minecraft:tree".equals(actualType)) { ++ if ("minecraft:acacia_foliage_placer".equals(foliageName)) { ++ return "minecraft:acacia"; ++ } ++ ++ if ("minecraft:blob_foliage_placer".equals(foliageName) && "minecraft:oak_leaves".equals(leavesName)) { ++ return "minecraft:oak"; ++ } ++ ++ if ("minecraft:pine_foliage_placer".equals(foliageName)) { ++ return "minecraft:pine"; ++ } ++ ++ if ("minecraft:spruce_foliage_placer".equals(foliageName)) { ++ return "minecraft:spruce"; ++ } ++ } ++ } ++ } ++ ++ return null; ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2702.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2702.java +new file mode 100644 +index 0000000000000000000000000000000000000000..53e45b14c05dab35cd5725998458d47e28718075 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2702.java +@@ -0,0 +1,33 @@ ++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 V2702 { ++ ++ protected static final int VERSION = MCVersions.V21W10A + 3; ++ ++ public static void register() { ++ final DataConverter, MapType> arrowConverter = new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (data.hasKey("pickup")) { ++ return null; ++ } ++ ++ final boolean player = data.getBoolean("player", true); ++ data.remove("player"); ++ ++ data.setByte("pickup", player ? (byte)1 : (byte)0); ++ ++ return null; ++ } ++ }; ++ ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:arrow", arrowConverter); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:spectral_arrow", arrowConverter); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:trident", arrowConverter); ++ } ++} +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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2707.java +@@ -0,0 +1,18 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++ ++public final class V2707 { ++ ++ protected static final int VERSION = MCVersions.V21W14A + 1; ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ registerMob("minecraft:marker"); // ????????????? ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2710.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2710.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0d31b10a4f93c0eb8bff66dd062ffb950b2182ec +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2710.java +@@ -0,0 +1,17 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.stats.ConverterAbstractStatsRename; ++import com.google.common.collect.ImmutableMap; ++ ++public final class V2710 { ++ ++ protected static final int VERSION = MCVersions.V21W15A + 1; ++ ++ public static void register() { ++ ConverterAbstractStatsRename.register(VERSION, ImmutableMap.of( ++ "minecraft:play_one_minute", "minecraft:play_time" ++ )::get); ++ } ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2717.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2717.java +new file mode 100644 +index 0000000000000000000000000000000000000000..8678ba95b5abe96b399a310623078f8827dfa0f0 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V2717.java +@@ -0,0 +1,20 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.blockname.ConverterAbstractBlockRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import com.google.common.collect.ImmutableMap; ++ ++public final class V2717 { ++ ++ protected static final int VERSION = MCVersions.V1_17_PRE1 + 1; ++ ++ public static void register() { ++ final ImmutableMap rename = ImmutableMap.of( ++ "minecraft:azalea_leaves_flowers", "minecraft:flowering_azalea_leaves" ++ ); ++ ConverterAbstractItemRename.register(VERSION, rename::get); ++ ConverterAbstractBlockRename.register(VERSION, rename::get); ++ } ++ ++} +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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V501.java +@@ -0,0 +1,21 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++ ++public final class V501 { ++ ++ protected static final int VERSION = MCVersions.V16W20A; ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ registerMob("PolarBear"); ++ } ++ ++ private V501() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V502.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V502.java +new file mode 100644 +index 0000000000000000000000000000000000000000..08fa912f9afac69dca8869d6d443226ea033c9db +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V502.java +@@ -0,0 +1,47 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.types.MapType; ++ ++import java.util.concurrent.ThreadLocalRandom; ++ ++public final class V502 { ++ ++ protected static final int VERSION = MCVersions.V16W20A + 1; ++ ++ public static void register() { ++ ConverterAbstractItemRename.register(VERSION, (final String name) -> { ++ return "minecraft:cooked_fished".equals(name) ? "minecraft:cooked_fish" : null; ++ }); ++ MCTypeRegistry.ENTITY.addConverterForId("Zombie", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (!data.getBoolean("IsVillager")) { ++ return null; ++ } ++ ++ data.remove("IsVillager"); ++ ++ if (data.hasKey("ZombieType")) { ++ return null; ++ } ++ ++ int type = data.getInt("VillagerProfession", -1); ++ // Vanilla doesn't remove the profession tag, so we don't! ++ if (type < 0 || type >= 6) { ++ type = ThreadLocalRandom.current().nextInt(6); ++ } ++ ++ data.setInt("ZombieType", type); ++ ++ return null; ++ } ++ }); ++ } ++ ++ private V502() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V505.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V505.java +new file mode 100644 +index 0000000000000000000000000000000000000000..30769f902c7d694bce41ab319d0b9a87c6103f11 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V505.java +@@ -0,0 +1,24 @@ ++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 V505 { ++ ++ protected static final int VERSION = MCVersions.V16W21B + 1; ++ ++ public static void register() { ++ MCTypeRegistry.OPTIONS.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ data.setString("useVbo", "true"); ++ return null; ++ } ++ }); ++ } ++ ++ private V505() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V700.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V700.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f46b1a0c4bc96d638853cc61e5703798dbf6b886 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V700.java +@@ -0,0 +1,33 @@ ++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.itemstack.DataWalkerItemLists; ++import ca.spottedleaf.dataconverter.types.MapType; ++ ++public final class V700 { ++ ++ protected static final int VERSION = MCVersions.V1_10_2 + 188; ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("Guardian", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (data.getBoolean("Elder")) { ++ data.setString("id", "ElderGuardian"); ++ } ++ data.remove("Elder"); ++ return null; ++ } ++ }); ++ ++ registerMob("ElderGuardian"); ++ } ++ ++ private V700() {} ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V701.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V701.java +new file mode 100644 +index 0000000000000000000000000000000000000000..42f173f426fb7d26e5ddb5a1c92c63b2e6a4930c +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V701.java +@@ -0,0 +1,43 @@ ++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.itemstack.DataWalkerItemLists; ++import ca.spottedleaf.dataconverter.types.MapType; ++ ++public final class V701 { ++ ++ protected static final int VERSION = MCVersions.V1_10_2 + 189; ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("Skeleton", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final int type = data.getInt("SkeletonType"); ++ data.remove("SkeletonType"); ++ ++ switch (type) { ++ case 1: ++ data.setString("id", "WitherSkeleton"); ++ break; ++ case 2: ++ data.setString("id", "Stray"); ++ break; ++ } ++ ++ return null; ++ } ++ }); ++ ++ registerMob("WitherSkeleton"); ++ registerMob("Stray"); ++ } ++ ++ private V701() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V702.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V702.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5cc91edde9c8160f75165bcef554023246e0a224 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V702.java +@@ -0,0 +1,53 @@ ++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.itemstack.DataWalkerItemLists; ++import ca.spottedleaf.dataconverter.types.MapType; ++ ++public final class V702 { ++ ++ protected static final int VERSION = MCVersions.V1_10_2 + 190; ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("Zombie", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final int zombieType = data.getInt("ZombieType"); ++ data.remove("ZombieType"); ++ ++ switch (zombieType) { ++ case 0: ++ default: ++ break; ++ ++ case 1: ++ case 2: ++ case 3: ++ case 4: ++ case 5: ++ data.setString("id", "ZombieVillager"); ++ data.setInt("Profession", zombieType - 1); ++ break; ++ ++ case 6: ++ data.setString("id", "Husk"); ++ break; ++ } ++ ++ return null; ++ } ++ }); ++ ++ registerMob("ZombieVillager"); ++ registerMob( "Husk"); ++ } ++ ++ private V702() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V703.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V703.java +new file mode 100644 +index 0000000000000000000000000000000000000000..88d9c0fcd88ccfd6d6b46ae050914079c816fa3f +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V703.java +@@ -0,0 +1,66 @@ ++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.itemstack.DataWalkerItemLists; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItems; ++import ca.spottedleaf.dataconverter.types.MapType; ++ ++public final class V703 { ++ ++ protected static final int VERSION = MCVersions.V1_10_2 + 191; ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("EntityHorse", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final int type = data.getInt("Type"); ++ data.remove("Type"); ++ ++ switch (type) { ++ case 0: ++ default: ++ data.setString("id", "Horse"); ++ break; ++ ++ case 1: ++ data.setString("id", "Donkey"); ++ break; ++ ++ case 2: ++ data.setString("id", "Mule"); ++ break; ++ ++ case 3: ++ data.setString("id", "ZombieHorse"); ++ break; ++ ++ case 4: ++ data.setString("id", "SkeletonHorse"); ++ break; ++ } ++ ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "Horse", new DataWalkerItems("ArmorItem", "SaddleItem")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "Horse", new DataWalkerItemLists("ArmorItems", "HandItems")); ++ ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "Donkey", new DataWalkerItems("SaddleItem")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "Donkey", new DataWalkerItemLists("Items", "ArmorItems", "HandItems")); ++ ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "Mule", new DataWalkerItems("SaddleItem")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "Mule", new DataWalkerItemLists("Items", "ArmorItems", "HandItems")); ++ ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "ZombieHorse", new DataWalkerItems("SaddleItem")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "ZombieHorse", new DataWalkerItemLists("ArmorItems", "HandItems")); ++ ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "SkeletonHorse", new DataWalkerItems("SaddleItem")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "SkeletonHorse", new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ private V703() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V704.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V704.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5f9cc332c7969f613a912ae66606cbc7352e5ed7 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V704.java +@@ -0,0 +1,335 @@ ++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.hooks.DataHookEnforceNamespacedID; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++import ca.spottedleaf.dataconverter.minecraft.walkers.item_name.DataWalkerItemNames; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItems; ++import ca.spottedleaf.dataconverter.minecraft.walkers.generic.WalkerUtils; ++import ca.spottedleaf.dataconverter.types.ObjectType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import net.minecraft.core.Registry; ++import net.minecraft.world.item.BlockItem; ++import net.minecraft.world.item.Item; ++import net.minecraft.world.level.block.EntityBlock; ++import org.apache.logging.log4j.LogManager; ++import org.apache.logging.log4j.Logger; ++import java.util.HashMap; ++import java.util.Map; ++ ++public final class V704 { ++ ++ private static final Logger LOGGER = LogManager.getLogger(); ++ ++ protected static final int VERSION = MCVersions.V1_10_2 + 192; ++ ++ public static final Map ITEM_ID_TO_TILE_ENTITY_ID = new HashMap() { ++ @Override ++ public String put(final String key, final String value) { ++ if (this.containsKey(key)) { ++ LOGGER.fatal("Duplicate item id to tile key: " + key); ++ throw new RuntimeException(); // only devs should see the consequence of this... at least start up the damn thing... ++ } ++ return super.put(key, value); ++ } ++ }; ++ static { ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:furnace", "minecraft:furnace"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:lit_furnace", "minecraft:furnace"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:chest", "minecraft:chest"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:trapped_chest", "minecraft:chest"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:ender_chest", "minecraft:ender_chest"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:jukebox", "minecraft:jukebox"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:dispenser", "minecraft:dispenser"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:dropper", "minecraft:dropper"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:sign", "minecraft:sign"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:mob_spawner", "minecraft:mob_spawner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:spawner", "minecraft:mob_spawner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:noteblock", "minecraft:noteblock"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:brewing_stand", "minecraft:brewing_stand"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:enhanting_table", "minecraft:enchanting_table"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:command_block", "minecraft:command_block"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:beacon", "minecraft:beacon"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:skull", "minecraft:skull"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:daylight_detector", "minecraft:daylight_detector"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:hopper", "minecraft:hopper"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:flower_pot", "minecraft:flower_pot"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:repeating_command_block", "minecraft:command_block"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:chain_command_block", "minecraft:command_block"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:shulker_box", "minecraft:shulker_box"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:white_shulker_box", "minecraft:shulker_box"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:orange_shulker_box", "minecraft:shulker_box"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:magenta_shulker_box", "minecraft:shulker_box"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:light_blue_shulker_box", "minecraft:shulker_box"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:yellow_shulker_box", "minecraft:shulker_box"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:lime_shulker_box", "minecraft:shulker_box"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:pink_shulker_box", "minecraft:shulker_box"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:gray_shulker_box", "minecraft:shulker_box"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:silver_shulker_box", "minecraft:shulker_box"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:cyan_shulker_box", "minecraft:shulker_box"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:purple_shulker_box", "minecraft:shulker_box"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:blue_shulker_box", "minecraft:shulker_box"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:brown_shulker_box", "minecraft:shulker_box"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:green_shulker_box", "minecraft:shulker_box"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:red_shulker_box", "minecraft:shulker_box"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:black_shulker_box", "minecraft:shulker_box"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:bed", "minecraft:bed"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:light_gray_shulker_box", "minecraft:shulker_box"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:white_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:orange_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:magenta_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:light_blue_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:yellow_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:lime_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:pink_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:gray_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:silver_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:cyan_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:purple_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:blue_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:brown_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:green_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:red_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:black_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:standing_sign", "minecraft:sign"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:wall_sign", "minecraft:sign"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:piston_head", "minecraft:piston"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:daylight_detector_inverted", "minecraft:daylight_detector"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:unpowered_comparator", "minecraft:comparator"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:powered_comparator", "minecraft:comparator"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:wall_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:standing_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:structure_block", "minecraft:structure_block"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:end_portal", "minecraft:end_portal"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:end_gateway", "minecraft:end_gateway"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:shield", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:white_bed", "minecraft:bed"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:orange_bed", "minecraft:bed"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:magenta_bed", "minecraft:bed"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:light_blue_bed", "minecraft:bed"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:yellow_bed", "minecraft:bed"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:lime_bed", "minecraft:bed"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:pink_bed", "minecraft:bed"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:gray_bed", "minecraft:bed"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:silver_bed", "minecraft:bed"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:cyan_bed", "minecraft:bed"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:purple_bed", "minecraft:bed"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:blue_bed", "minecraft:bed"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:brown_bed", "minecraft:bed"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:green_bed", "minecraft:bed"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:red_bed", "minecraft:bed"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:black_bed", "minecraft:bed"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:oak_sign", "minecraft:sign"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:spruce_sign", "minecraft:sign"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:birch_sign", "minecraft:sign"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:jungle_sign", "minecraft:sign"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:acacia_sign", "minecraft:sign"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:dark_oak_sign", "minecraft:sign"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:crimson_sign", "minecraft:sign"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:warped_sign", "minecraft:sign"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:skeleton_skull", "minecraft:skull"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:wither_skeleton_skull", "minecraft:skull"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:zombie_head", "minecraft:skull"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:player_head", "minecraft:skull"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:creeper_head", "minecraft:skull"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:dragon_head", "minecraft:skull"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:barrel", "minecraft:barrel"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:conduit", "minecraft:conduit"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:smoker", "minecraft:smoker"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:blast_furnace", "minecraft:blast_furnace"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:lectern", "minecraft:lectern"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:bell", "minecraft:bell"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:jigsaw", "minecraft:jigsaw"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:campfire", "minecraft:campfire"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:bee_nest", "minecraft:beehive"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:beehive", "minecraft:beehive"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:sculk_sensor", "minecraft:sculk_sensor"); ++ ++ // These are missing from Vanilla (TODO check on update) ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:enchanting_table", "minecraft:enchanting_table"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:comparator", "minecraft:comparator"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:light_gray_bed", "minecraft:bed"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:light_gray_banner", "minecraft:banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:soul_campfire", "minecraft:campfire"); ++ } ++ ++ // This class is responsible for also integrity checking the item id to tile id map here, we just use the item registry to figure it out ++ ++ static { ++ for (final Item item : Registry.ITEM) { ++ if (!(item instanceof BlockItem)) { ++ continue; ++ } ++ ++ if (!(((BlockItem)item).getBlock() instanceof EntityBlock)) { ++ continue; ++ } ++ ++ final String itemName = Registry.ITEM.getKey(item).toString(); ++ if (!ITEM_ID_TO_TILE_ENTITY_ID.containsKey(itemName)) { ++ LOGGER.error("Item id " + itemName + " does not contain tile mapping! (V704)"); ++ } ++ } ++ } ++ ++ protected static final Map TILE_ID_UPDATE = new HashMap<>(); ++ static { ++ TILE_ID_UPDATE.put("Airportal", "minecraft:end_portal"); ++ TILE_ID_UPDATE.put("Banner", "minecraft:banner"); ++ TILE_ID_UPDATE.put("Beacon", "minecraft:beacon"); ++ TILE_ID_UPDATE.put("Cauldron", "minecraft:brewing_stand"); ++ TILE_ID_UPDATE.put("Chest", "minecraft:chest"); ++ TILE_ID_UPDATE.put("Comparator", "minecraft:comparator"); ++ TILE_ID_UPDATE.put("Control", "minecraft:command_block"); ++ TILE_ID_UPDATE.put("DLDetector", "minecraft:daylight_detector"); ++ TILE_ID_UPDATE.put("Dropper", "minecraft:dropper"); ++ TILE_ID_UPDATE.put("EnchantTable", "minecraft:enchanting_table"); ++ TILE_ID_UPDATE.put("EndGateway", "minecraft:end_gateway"); ++ TILE_ID_UPDATE.put("EnderChest", "minecraft:ender_chest"); ++ TILE_ID_UPDATE.put("FlowerPot", "minecraft:flower_pot"); ++ TILE_ID_UPDATE.put("Furnace", "minecraft:furnace"); ++ TILE_ID_UPDATE.put("Hopper", "minecraft:hopper"); ++ TILE_ID_UPDATE.put("MobSpawner", "minecraft:mob_spawner"); ++ TILE_ID_UPDATE.put("Music", "minecraft:noteblock"); ++ TILE_ID_UPDATE.put("Piston", "minecraft:piston"); ++ TILE_ID_UPDATE.put("RecordPlayer", "minecraft:jukebox"); ++ TILE_ID_UPDATE.put("Sign", "minecraft:sign"); ++ TILE_ID_UPDATE.put("Skull", "minecraft:skull"); ++ TILE_ID_UPDATE.put("Structure", "minecraft:structure_block"); ++ TILE_ID_UPDATE.put("Trap", "minecraft:dispenser"); ++ } ++ ++ protected static void registerInventory(final String id) { ++ MCTypeRegistry.TILE_ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("Items")); ++ } ++ ++ public static void register() { ++ MCTypeRegistry.TILE_ENTITY.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final String id = data.getString("id"); ++ if (id == null) { ++ return null; ++ } ++ ++ data.setString("id", TILE_ID_UPDATE.getOrDefault(id, id)); ++ return null; ++ } ++ }); ++ ++ ++ ++ registerInventory( "minecraft:furnace"); ++ registerInventory( "minecraft:chest"); ++ MCTypeRegistry.TILE_ENTITY.addWalker(VERSION, "minecraft:jukebox", new DataWalkerItems("RecordItem")); ++ registerInventory("minecraft:dispenser"); ++ registerInventory("minecraft:dropper"); ++ MCTypeRegistry.TILE_ENTITY.addWalker(VERSION, "minecraft:mob_spawner", (final MapType data, final long fromVersion, final long toVersion) -> { ++ MCTypeRegistry.UNTAGGED_SPAWNER.convert(data, fromVersion, toVersion); ++ return null; ++ }); ++ registerInventory("minecraft:brewing_stand"); ++ registerInventory("minecraft:hopper"); ++ MCTypeRegistry.TILE_ENTITY.addWalker(VERSION, "minecraft:flower_pot", new DataWalkerItemNames("Item")); ++ ++ MCTypeRegistry.ITEM_STACK.addStructureWalker(VERSION, (final MapType data, final long fromVersion, final long toVersion) -> { ++ WalkerUtils.convert(MCTypeRegistry.ITEM_NAME, data, "id", fromVersion, toVersion); ++ ++ final MapType tag = data.getMap("tag"); ++ if (tag == null) { ++ return null; ++ } ++ ++ // only things here are in tag, if changed update if above ++ ++ WalkerUtils.convertList(MCTypeRegistry.ITEM_STACK, tag, "Items", fromVersion, toVersion); ++ ++ MapType entityTag = tag.getMap("EntityTag"); ++ if (entityTag != null) { ++ final String itemId = data.getString("id"); ++ final String entityId; ++ if ("minecraft:armor_stand".equals(itemId)) { ++ // The check for version id is changed here. For whatever reason, the legacy ++ // data converters used entity id "minecraft:armor_stand" when version was greater-than 514, ++ // but entity ids were not namespaced until V705! So somebody fucked up the legacy converters. ++ // DFU agrees with my analysis here, it will only set the entityId here to the namespaced variant ++ // with the V705 schema. ++ entityId = DataConverter.getVersion(fromVersion) < 705 ? "ArmorStand" : "minecraft:armor_stand"; ++ } else if (itemId != null && itemId.contains("_spawn_egg")) { ++ // V1451 changes spawn eggs to have the sub entity id be a part of the item id, but of course Mojang never ++ // bothered to write in logic to set the sub entity id, so we have to. ++ // format is ALWAYS :_spawn_egg post flattening ++ entityId = itemId.substring(0, itemId.indexOf("_spawn_egg")); ++ } else if ("minecraft:item_frame".equals(itemId)) { ++ // add missing item_frame entity id ++ // version check is same for armorstand, as both were namespaced at the same time ++ entityId = DataConverter.getVersion(fromVersion) < 705 ? "ItemFrame" : "minecraft:item_frame"; ++ } else { ++ entityId = entityTag.getString("id"); ++ } ++ ++ final boolean removeId; ++ if (entityId == null) { ++ if (!"minecraft:air".equals(itemId)) { ++ LOGGER.warn("Unable to resolve Entity for ItemStack (V704): " + itemId); ++ } ++ removeId = false; ++ } else { ++ removeId = !entityTag.hasKey("id", ObjectType.STRING); ++ if (removeId) { ++ entityTag.setString("id", entityId); ++ } ++ } ++ ++ final MapType replace = MCTypeRegistry.ENTITY.convert(entityTag, fromVersion, toVersion); ++ ++ if (replace != null) { ++ entityTag = replace; ++ tag.setMap("EntityTag", entityTag); ++ } ++ if (removeId) { ++ entityTag.remove("id"); ++ } ++ } ++ ++ MapType blockEntityTag = tag.getMap("BlockEntityTag"); ++ if (blockEntityTag != null) { ++ final String itemId = data.getString("id"); ++ final String entityId = ITEM_ID_TO_TILE_ENTITY_ID.get(itemId); ++ final boolean removeId; ++ if (entityId == null) { ++ if (!"minecraft:air".equals(itemId)) { ++ LOGGER.warn("Unable to resolve BlockEntity for ItemStack (V704): " + itemId); ++ } ++ removeId = false; ++ } else { ++ removeId = !blockEntityTag.hasKey("id", ObjectType.STRING); ++ if (removeId) { ++ blockEntityTag.setString("id", entityId); ++ } ++ } ++ final MapType replace = MCTypeRegistry.TILE_ENTITY.convert(blockEntityTag, fromVersion, toVersion); ++ if (replace != null) { ++ blockEntityTag = replace; ++ tag.setMap("BlockEntityTag", entityTag); ++ } ++ if (removeId) { ++ blockEntityTag.remove("id"); ++ } ++ } ++ ++ WalkerUtils.convertList(MCTypeRegistry.BLOCK_NAME, tag, "CanDestroy", fromVersion, toVersion); ++ WalkerUtils.convertList(MCTypeRegistry.BLOCK_NAME, tag, "CanPlaceOn", fromVersion, toVersion); ++ ++ return null; ++ }); ++ ++ // Enforce namespace for ids ++ MCTypeRegistry.TILE_ENTITY.addStructureHook(VERSION, new DataHookEnforceNamespacedID()); ++ } ++ ++ private V704() {} ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V705.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V705.java +new file mode 100644 +index 0000000000000000000000000000000000000000..95e3dd0b76bc9e9ef7388617bdb2dd340b8dfc0d +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V705.java +@@ -0,0 +1,231 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.entity.ConverterAbstractEntityRename; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.hooks.DataHookEnforceNamespacedID; ++import ca.spottedleaf.dataconverter.minecraft.hooks.DataHookValueTypeEnforceNamespaced; ++import ca.spottedleaf.dataconverter.minecraft.walkers.block_name.DataWalkerBlockNames; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItems; ++import ca.spottedleaf.dataconverter.minecraft.walkers.tile_entity.DataWalkerTileEntities; ++import ca.spottedleaf.dataconverter.minecraft.walkers.generic.WalkerUtils; ++import ca.spottedleaf.dataconverter.types.ObjectType; ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import org.apache.logging.log4j.LogManager; ++import org.apache.logging.log4j.Logger; ++import java.util.HashMap; ++import java.util.Map; ++ ++public final class V705 { ++ ++ private static final Logger LOGGER = LogManager.getLogger(); ++ ++ protected static final int VERSION = MCVersions.V1_10_2 + 193; ++ ++ protected static final Map ENTITY_ID_UPDATE = new HashMap<>(); ++ static { ++ ENTITY_ID_UPDATE.put("AreaEffectCloud", "minecraft:area_effect_cloud"); ++ ENTITY_ID_UPDATE.put("ArmorStand", "minecraft:armor_stand"); ++ ENTITY_ID_UPDATE.put("Arrow", "minecraft:arrow"); ++ ENTITY_ID_UPDATE.put("Bat", "minecraft:bat"); ++ ENTITY_ID_UPDATE.put("Blaze", "minecraft:blaze"); ++ ENTITY_ID_UPDATE.put("Boat", "minecraft:boat"); ++ ENTITY_ID_UPDATE.put("CaveSpider", "minecraft:cave_spider"); ++ ENTITY_ID_UPDATE.put("Chicken", "minecraft:chicken"); ++ ENTITY_ID_UPDATE.put("Cow", "minecraft:cow"); ++ ENTITY_ID_UPDATE.put("Creeper", "minecraft:creeper"); ++ ENTITY_ID_UPDATE.put("Donkey", "minecraft:donkey"); ++ ENTITY_ID_UPDATE.put("DragonFireball", "minecraft:dragon_fireball"); ++ ENTITY_ID_UPDATE.put("ElderGuardian", "minecraft:elder_guardian"); ++ ENTITY_ID_UPDATE.put("EnderCrystal", "minecraft:ender_crystal"); ++ ENTITY_ID_UPDATE.put("EnderDragon", "minecraft:ender_dragon"); ++ ENTITY_ID_UPDATE.put("Enderman", "minecraft:enderman"); ++ ENTITY_ID_UPDATE.put("Endermite", "minecraft:endermite"); ++ ENTITY_ID_UPDATE.put("EyeOfEnderSignal", "minecraft:eye_of_ender_signal"); ++ ENTITY_ID_UPDATE.put("FallingSand", "minecraft:falling_block"); ++ ENTITY_ID_UPDATE.put("Fireball", "minecraft:fireball"); ++ ENTITY_ID_UPDATE.put("FireworksRocketEntity", "minecraft:fireworks_rocket"); ++ ENTITY_ID_UPDATE.put("Ghast", "minecraft:ghast"); ++ ENTITY_ID_UPDATE.put("Giant", "minecraft:giant"); ++ ENTITY_ID_UPDATE.put("Guardian", "minecraft:guardian"); ++ ENTITY_ID_UPDATE.put("Horse", "minecraft:horse"); ++ ENTITY_ID_UPDATE.put("Husk", "minecraft:husk"); ++ ENTITY_ID_UPDATE.put("Item", "minecraft:item"); ++ ENTITY_ID_UPDATE.put("ItemFrame", "minecraft:item_frame"); ++ ENTITY_ID_UPDATE.put("LavaSlime", "minecraft:magma_cube"); ++ ENTITY_ID_UPDATE.put("LeashKnot", "minecraft:leash_knot"); ++ ENTITY_ID_UPDATE.put("MinecartChest", "minecraft:chest_minecart"); ++ ENTITY_ID_UPDATE.put("MinecartCommandBlock", "minecraft:commandblock_minecart"); ++ ENTITY_ID_UPDATE.put("MinecartFurnace", "minecraft:furnace_minecart"); ++ ENTITY_ID_UPDATE.put("MinecartHopper", "minecraft:hopper_minecart"); ++ ENTITY_ID_UPDATE.put("MinecartRideable", "minecraft:minecart"); ++ ENTITY_ID_UPDATE.put("MinecartSpawner", "minecraft:spawner_minecart"); ++ ENTITY_ID_UPDATE.put("MinecartTNT", "minecraft:tnt_minecart"); ++ ENTITY_ID_UPDATE.put("Mule", "minecraft:mule"); ++ ENTITY_ID_UPDATE.put("MushroomCow", "minecraft:mooshroom"); ++ ENTITY_ID_UPDATE.put("Ozelot", "minecraft:ocelot"); ++ ENTITY_ID_UPDATE.put("Painting", "minecraft:painting"); ++ ENTITY_ID_UPDATE.put("Pig", "minecraft:pig"); ++ ENTITY_ID_UPDATE.put("PigZombie", "minecraft:zombie_pigman"); ++ ENTITY_ID_UPDATE.put("PolarBear", "minecraft:polar_bear"); ++ ENTITY_ID_UPDATE.put("PrimedTnt", "minecraft:tnt"); ++ ENTITY_ID_UPDATE.put("Rabbit", "minecraft:rabbit"); ++ ENTITY_ID_UPDATE.put("Sheep", "minecraft:sheep"); ++ ENTITY_ID_UPDATE.put("Shulker", "minecraft:shulker"); ++ ENTITY_ID_UPDATE.put("ShulkerBullet", "minecraft:shulker_bullet"); ++ ENTITY_ID_UPDATE.put("Silverfish", "minecraft:silverfish"); ++ ENTITY_ID_UPDATE.put("Skeleton", "minecraft:skeleton"); ++ ENTITY_ID_UPDATE.put("SkeletonHorse", "minecraft:skeleton_horse"); ++ ENTITY_ID_UPDATE.put("Slime", "minecraft:slime"); ++ ENTITY_ID_UPDATE.put("SmallFireball", "minecraft:small_fireball"); ++ ENTITY_ID_UPDATE.put("SnowMan", "minecraft:snowman"); ++ ENTITY_ID_UPDATE.put("Snowball", "minecraft:snowball"); ++ ENTITY_ID_UPDATE.put("SpectralArrow", "minecraft:spectral_arrow"); ++ ENTITY_ID_UPDATE.put("Spider", "minecraft:spider"); ++ ENTITY_ID_UPDATE.put("Squid", "minecraft:squid"); ++ ENTITY_ID_UPDATE.put("Stray", "minecraft:stray"); ++ ENTITY_ID_UPDATE.put("ThrownEgg", "minecraft:egg"); ++ ENTITY_ID_UPDATE.put("ThrownEnderpearl", "minecraft:ender_pearl"); ++ ENTITY_ID_UPDATE.put("ThrownExpBottle", "minecraft:xp_bottle"); ++ ENTITY_ID_UPDATE.put("ThrownPotion", "minecraft:potion"); ++ ENTITY_ID_UPDATE.put("Villager", "minecraft:villager"); ++ ENTITY_ID_UPDATE.put("VillagerGolem", "minecraft:villager_golem"); ++ ENTITY_ID_UPDATE.put("Witch", "minecraft:witch"); ++ ENTITY_ID_UPDATE.put("WitherBoss", "minecraft:wither"); ++ ENTITY_ID_UPDATE.put("WitherSkeleton", "minecraft:wither_skeleton"); ++ ENTITY_ID_UPDATE.put("WitherSkull", "minecraft:wither_skull"); ++ ENTITY_ID_UPDATE.put("Wolf", "minecraft:wolf"); ++ ENTITY_ID_UPDATE.put("XPOrb", "minecraft:xp_orb"); ++ ENTITY_ID_UPDATE.put("Zombie", "minecraft:zombie"); ++ ENTITY_ID_UPDATE.put("ZombieHorse", "minecraft:zombie_horse"); ++ ENTITY_ID_UPDATE.put("ZombieVillager", "minecraft:zombie_villager"); ++ } ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("ArmorItems", "HandItems")); ++ } ++ ++ private static void registerThrowableProjectile(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerBlockNames("inTile")); ++ } ++ ++ public static void register() { ++ ConverterAbstractEntityRename.register(VERSION, ENTITY_ID_UPDATE::get); ++ ++ registerMob("minecraft:armor_stand"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:arrow", new DataWalkerBlockNames("inTile")); ++ registerMob("minecraft:bat"); ++ registerMob("minecraft:blaze"); ++ registerMob("minecraft:cave_spider"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:chest_minecart", new DataWalkerBlockNames("DisplayTile")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:chest_minecart", new DataWalkerItemLists("Items")); ++ registerMob("minecraft:chicken"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:commandblock_minecart", new DataWalkerBlockNames("DisplayTile")); ++ registerMob("minecraft:cow"); ++ registerMob("minecraft:creeper"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:donkey", new DataWalkerItemLists("Items", "ArmorItems", "HandItems")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:donkey", new DataWalkerItems("SaddleItem")); ++ registerThrowableProjectile("minecraft:egg"); ++ registerMob("minecraft:elder_guardian"); ++ registerMob("minecraft:ender_dragon"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:enderman", new DataWalkerItemLists("ArmorItems", "HandItems")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:enderman", new DataWalkerBlockNames("carried")); ++ registerMob("minecraft:endermite"); ++ registerThrowableProjectile("minecraft:ender_pearl"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:falling_block", new DataWalkerBlockNames("Block")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:falling_block", new DataWalkerTileEntities("TileEntityData")); ++ registerThrowableProjectile("minecraft:fireball"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:fireworks_rocket", new DataWalkerItems("FireworksItem")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:furnace_minecart", new DataWalkerBlockNames("DisplayTile")); ++ registerMob("minecraft:ghast"); ++ registerMob("minecraft:giant"); ++ registerMob("minecraft:guardian"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:hopper_minecart", new DataWalkerBlockNames("DisplayTile")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:hopper_minecart", new DataWalkerItemLists("Items")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:horse", new DataWalkerItems("ArmorItem", "SaddleItem")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:horse", new DataWalkerItemLists("ArmorItems", "HandItems")); ++ registerMob("minecraft:husk"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:item", new DataWalkerItems("Item")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:item_frame", new DataWalkerItems("Item")); ++ registerMob("minecraft:magma_cube"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:minecart", new DataWalkerBlockNames("DisplayTile")); ++ registerMob("minecraft:mooshroom"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:mule", new DataWalkerItemLists("Items", "ArmorItems", "HandItems")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:mule", new DataWalkerItems("SaddleItem")); ++ registerMob("minecraft:ocelot"); ++ registerMob("minecraft:pig"); ++ registerMob("minecraft:polar_bear"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:potion", new DataWalkerItems("Potion")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:potion", new DataWalkerBlockNames("inTile")); ++ registerMob("minecraft:rabbit"); ++ registerMob("minecraft:sheep"); ++ registerMob("minecraft:shulker"); ++ registerMob("minecraft:silverfish"); ++ registerMob("minecraft:skeleton"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:skeleton_horse", new DataWalkerItemLists("ArmorItems", "HandItems")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:skeleton_horse", new DataWalkerItems("SaddleItem")); ++ registerMob("minecraft:slime"); ++ registerThrowableProjectile("minecraft:small_fireball"); ++ registerThrowableProjectile("minecraft:snowball"); ++ registerMob("minecraft:snowman"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:spawner_minecart", new DataWalkerBlockNames("DisplayTile")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:spawner_minecart", (final MapType data, final long fromVersion, final long toVersion) -> { ++ MCTypeRegistry.UNTAGGED_SPAWNER.convert(data, fromVersion, toVersion); ++ return null; ++ }); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:spectral_arrow", new DataWalkerBlockNames("inTile")); ++ registerMob("minecraft:spider"); ++ registerMob("minecraft:squid"); ++ registerMob("minecraft:stray"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:tnt_minecart", new DataWalkerBlockNames("DisplayTile")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:villager", (final MapType data, final long fromVersion, final long toVersion) -> { ++ WalkerUtils.convertList(MCTypeRegistry.ITEM_STACK, data, "Inventory", fromVersion, toVersion); ++ ++ final MapType offers = data.getMap("Offers"); ++ if (offers != null) { ++ final ListType recipes = offers.getList("Recipes", ObjectType.MAP); ++ if (recipes != null) { ++ for (int i = 0, len = recipes.size(); i < len; ++i) { ++ final MapType recipe = recipes.getMap(i); ++ WalkerUtils.convert(MCTypeRegistry.ITEM_STACK, recipe, "buy", fromVersion, toVersion); ++ WalkerUtils.convert(MCTypeRegistry.ITEM_STACK, recipe, "buyB", fromVersion, toVersion); ++ WalkerUtils.convert(MCTypeRegistry.ITEM_STACK, recipe, "sell", fromVersion, toVersion); ++ } ++ } ++ } ++ ++ WalkerUtils.convertList(MCTypeRegistry.ITEM_STACK, data, "ArmorItems", fromVersion, toVersion); ++ WalkerUtils.convertList(MCTypeRegistry.ITEM_STACK, data, "HandItems", fromVersion, toVersion); ++ ++ return null; ++ }); ++ registerMob("minecraft:villager_golem"); ++ registerMob("minecraft:witch"); ++ registerMob("minecraft:wither"); ++ registerMob("minecraft:wither_skeleton"); ++ registerThrowableProjectile("minecraft:wither_skull"); ++ registerMob("minecraft:wolf"); ++ registerThrowableProjectile("minecraft:xp_bottle"); ++ registerMob("minecraft:zombie"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:zombie_horse", new DataWalkerItems("SaddleItem")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:zombie_horse", new DataWalkerItemLists("ArmorItems", "HandItems")); ++ registerMob("minecraft:zombie_pigman"); ++ registerMob("minecraft:zombie_villager"); ++ registerMob("minecraft:evocation_illager"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:llama", new DataWalkerItemLists("Items", "ArmorItems", "HandItems")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "minecraft:llama", new DataWalkerItems("SaddleItem", "DecorItem")); ++ registerMob("minecraft:vex"); ++ registerMob("minecraft:vindication_illager"); ++ // Don't need to re-register itemstack walker, the V704 will correctly choose the right id for armorstand based on ++ // the source version ++ ++ // Enforce namespace for ids ++ MCTypeRegistry.ENTITY.addStructureHook(VERSION, new DataHookEnforceNamespacedID()); ++ MCTypeRegistry.ENTITY_NAME.addStructureHook(VERSION, new DataHookValueTypeEnforceNamespaced()); ++ } ++ ++ private V705() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V804.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V804.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0070a3d02a87b0f08cd5e74d4f106f3e97f6b4f8 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V804.java +@@ -0,0 +1,60 @@ ++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.ObjectType; ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.MapType; ++ ++public final class V804 { ++ ++ protected static final int VERSION = MCVersions.V16W35A + 1; ++ ++ public static void register() { ++ MCTypeRegistry.ITEM_STACK.addConverterForId("minecraft:banner", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType tag = data.getMap("tag"); ++ if (tag == null) { ++ return null; ++ } ++ ++ final MapType blockEntity = tag.getMap("BlockEntityTag"); ++ if (blockEntity == null) { ++ return null; ++ } ++ ++ if (!blockEntity.hasKey("Base", ObjectType.NUMBER)) { ++ return null; ++ } ++ ++ data.setShort("Damage", (short)(blockEntity.getShort("Base") & 15)); ++ ++ final MapType display = tag.getMap("display"); ++ if (display != null) { ++ final ListType lore = display.getList("Lore", ObjectType.STRING); ++ if (lore != null) { ++ if (lore.size() == 1 && "(+NBT)".equals(lore.getString(0))) { ++ return null; ++ } ++ } ++ } ++ ++ blockEntity.remove("Base"); ++ if (blockEntity.isEmpty()) { ++ tag.remove("BlockEntityTag"); ++ } ++ ++ if (tag.isEmpty()) { ++ data.remove("tag"); ++ } ++ ++ return null; ++ } ++ }); ++ } ++ ++ private V804() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V806.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V806.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a2717b9d936872ec07141b0f3ae2a6eec81f2dbf +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V806.java +@@ -0,0 +1,40 @@ ++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.ObjectType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.Types; ++ ++public final class V806 { ++ ++ protected static final int VERSION = MCVersions.V16W36A + 1; ++ ++ public static void register() { ++ final DataConverter, MapType> potionWaterUpdater = new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ MapType tag = data.getMap("tag"); ++ if (tag == null) { ++ tag = Types.NBT.createEmptyMap(); ++ data.setMap("tag", tag); ++ } ++ ++ if (!tag.hasKey("Potion", ObjectType.STRING)) { ++ tag.setString("Potion", "minecraft:water"); ++ } ++ ++ return null; ++ } ++ }; ++ ++ MCTypeRegistry.ITEM_STACK.addConverterForId("minecraft:potion", potionWaterUpdater); ++ MCTypeRegistry.ITEM_STACK.addConverterForId("minecraft:splash_potion", potionWaterUpdater); ++ MCTypeRegistry.ITEM_STACK.addConverterForId("minecraft:lingering_potion", potionWaterUpdater); ++ MCTypeRegistry.ITEM_STACK.addConverterForId("minecraft:tipped_arrow", potionWaterUpdater); ++ } ++ ++ private V806() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V808.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V808.java +new file mode 100644 +index 0000000000000000000000000000000000000000..b058e5e9b34a9dd134ef93e7a397b5f1e4e11fbd +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V808.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.minecraft.walkers.itemstack.DataWalkerItemLists; ++import ca.spottedleaf.dataconverter.types.ObjectType; ++import ca.spottedleaf.dataconverter.types.MapType; ++ ++public final class V808 { ++ ++ protected static final int VERSION = MCVersions.V16W38A + 1; ++ ++ public static void register() { ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:shulker", new DataConverter<>(VERSION, 1) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ if (!data.hasKey("Color", ObjectType.NUMBER)) { ++ data.setByte("Color", (byte)10); ++ } ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.TILE_ENTITY.addWalker(VERSION, "minecraft:shulker_box", new DataWalkerItemLists("Items")); ++ } ++ ++ private V808() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V813.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V813.java +new file mode 100644 +index 0000000000000000000000000000000000000000..b6de7c32acd0adf78812edbbd184117661599c80 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V813.java +@@ -0,0 +1,64 @@ ++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 V813 { ++ ++ protected static final int VERSION = MCVersions.V16W40A; ++ ++ public static final String[] SHULKER_ID_BY_COLOUR = new String[] { ++ "minecraft:white_shulker_box", ++ "minecraft:orange_shulker_box", ++ "minecraft:magenta_shulker_box", ++ "minecraft:light_blue_shulker_box", ++ "minecraft:yellow_shulker_box", ++ "minecraft:lime_shulker_box", ++ "minecraft:pink_shulker_box", ++ "minecraft:gray_shulker_box", ++ "minecraft:silver_shulker_box", ++ "minecraft:cyan_shulker_box", ++ "minecraft:purple_shulker_box", ++ "minecraft:blue_shulker_box", ++ "minecraft:brown_shulker_box", ++ "minecraft:green_shulker_box", ++ "minecraft:red_shulker_box", ++ "minecraft:black_shulker_box" ++ }; ++ ++ public static void register() { ++ MCTypeRegistry.ITEM_STACK.addConverterForId("minecraft:shulker_box", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final MapType tag = data.getMap("tag"); ++ if (tag == null) { ++ return null; ++ } ++ ++ final MapType blockEntity = tag.getMap("BlockEntityTag"); ++ if (blockEntity == null) { ++ return null; ++ } ++ ++ final int color = blockEntity.getInt("Color"); ++ blockEntity.remove("Color"); ++ ++ data.setString("id", SHULKER_ID_BY_COLOUR[color % SHULKER_ID_BY_COLOUR.length]); ++ ++ return null; ++ } ++ }); ++ ++ MCTypeRegistry.TILE_ENTITY.addConverterForId("minecraft:shulker_box", new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ data.remove("Color"); ++ return null; ++ } ++ }); ++ } ++ ++ private V813() {} ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V816.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V816.java +new file mode 100644 +index 0000000000000000000000000000000000000000..4b427c128bd75d2dc8b36f0c377454385c029467 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V816.java +@@ -0,0 +1,28 @@ ++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; ++import java.util.Locale; ++ ++public final class V816 { ++ ++ protected static final int VERSION = MCVersions.V16W43A; ++ ++ public static void register() { ++ MCTypeRegistry.OPTIONS.addStructureConverter(new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ final String lang = data.getString("lang"); ++ if (lang != null) { ++ data.setString("lang", lang.toLowerCase(Locale.ROOT)); ++ } ++ return null; ++ } ++ }); ++ } ++ ++ private V816() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V820.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V820.java +new file mode 100644 +index 0000000000000000000000000000000000000000..9e2ef3cea4fd382a75a4d787fe2e2ff509eb49fc +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V820.java +@@ -0,0 +1,19 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.itemname.ConverterAbstractItemRename; ++import com.google.common.collect.ImmutableMap; ++ ++public final class V820 { ++ ++ protected static final int VERSION = MCVersions.V1_11 + 1; ++ ++ public static void register() { ++ ConverterAbstractItemRename.register(VERSION, ImmutableMap.of( ++ "minecraft:totem", "minecraft:totem_of_undying" ++ )::get); ++ } ++ ++ private V820() {} ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V99.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V99.java +new file mode 100644 +index 0000000000000000000000000000000000000000..44aff7897f03e0f758e69111b729df6b53ee2ff8 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/versions/V99.java +@@ -0,0 +1,348 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.helpers.HelperItemNameV102; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.hooks.DataHookEnforceNamespacedID; ++import ca.spottedleaf.dataconverter.minecraft.hooks.DataHookValueTypeEnforceNamespaced; ++import ca.spottedleaf.dataconverter.minecraft.walkers.block_name.DataWalkerBlockNames; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItemLists; ++import ca.spottedleaf.dataconverter.minecraft.walkers.item_name.DataWalkerItemNames; ++import ca.spottedleaf.dataconverter.minecraft.walkers.itemstack.DataWalkerItems; ++import ca.spottedleaf.dataconverter.minecraft.walkers.tile_entity.DataWalkerTileEntities; ++import ca.spottedleaf.dataconverter.minecraft.walkers.generic.WalkerUtils; ++import ca.spottedleaf.dataconverter.types.ObjectType; ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import org.apache.logging.log4j.LogManager; ++import org.apache.logging.log4j.Logger; ++import java.util.HashMap; ++import java.util.Map; ++ ++public final class V99 { ++ ++ // Structure for all data before data upgrading was added to minecraft (pre 15w32a) ++ ++ protected static final Logger LOGGER = LogManager.getLogger(); ++ ++ protected static final int VERSION = MCVersions.V15W32A - 1; ++ ++ protected static final Map ITEM_ID_TO_TILE_ENTITY_ID = new HashMap<>(); ++ ++ static { ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:furnace", "Furnace"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:lit_furnace", "Furnace"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:chest", "Chest"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:trapped_chest", "Chest"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:ender_chest", "EnderChest"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:jukebox", "RecordPlayer"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:dispenser", "Trap"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:dropper", "Dropper"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:sign", "Sign"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:mob_spawner", "MobSpawner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:noteblock", "Music"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:brewing_stand", "Cauldron"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:enhanting_table", "EnchantTable"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:command_block", "CommandBlock"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:beacon", "Beacon"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:skull", "Skull"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:daylight_detector", "DLDetector"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:hopper", "Hopper"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:banner", "Banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:flower_pot", "FlowerPot"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:repeating_command_block", "CommandBlock"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:chain_command_block", "CommandBlock"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:standing_sign", "Sign"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:wall_sign", "Sign"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:piston_head", "Piston"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:daylight_detector_inverted", "DLDetector"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:unpowered_comparator", "Comparator"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:powered_comparator", "Comparator"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:wall_banner", "Banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:standing_banner", "Banner"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:structure_block", "Structure"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:end_portal", "Airportal"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:end_gateway", "EndGateway"); ++ ITEM_ID_TO_TILE_ENTITY_ID.put("minecraft:shield", "Banner"); ++ } ++ ++ private static void registerMob(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("Equipment")); ++ } ++ ++ private static void registerProjectile(final String id) { ++ MCTypeRegistry.ENTITY.addWalker(VERSION, id, new DataWalkerBlockNames("inTile")); ++ } ++ ++ private static void registerInventory(final String id) { ++ MCTypeRegistry.TILE_ENTITY.addWalker(VERSION, id, new DataWalkerItemLists("Items")); ++ } ++ ++ public static void register() { ++ // entities ++ MCTypeRegistry.ENTITY.addStructureWalker(VERSION, (final MapType data, final long fromVersion, final long toVersion) -> { ++ WalkerUtils.convert(MCTypeRegistry.ENTITY, data, "Riding", fromVersion, toVersion); ++ ++ return null; ++ }); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "Item", new DataWalkerItems("Item")); ++ registerProjectile("ThrownEgg"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "Arrow", new DataWalkerBlockNames("inTile")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "TippedArrow", new DataWalkerBlockNames("inTile")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "SpectralArrow", new DataWalkerBlockNames("inTile")); ++ registerProjectile("Snowball"); ++ registerProjectile("Fireball"); ++ registerProjectile("SmallFireball"); ++ registerProjectile("ThrownEnderpearl"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "ThrownPotion", new DataWalkerBlockNames("inTile")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "ThrownPotion", new DataWalkerItems("Potion")); ++ registerProjectile("ThrownExpBottle"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "ItemFrame", new DataWalkerItems("Item")); ++ registerProjectile("WitherSkull"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "FallingSand", new DataWalkerBlockNames("Block")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "FallingSand", new DataWalkerTileEntities("TileEntityData")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "FireworksRocketEntity", new DataWalkerItems("FireworksItem")); ++ // Note: Minecart is the generic entity. It can be subtyped via an int to become one of the specific minecarts ++ // (i.e rideable, chest, furnace, tnt, etc) ++ // Because of this, we add all walkers to the generic type, even though they might not be needed. ++ // Vanilla does not make the generic minecart convert spawners, but we do. ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "Minecart", new DataWalkerBlockNames("DisplayTile")); // for all minecart types ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "Minecart", new DataWalkerItemLists("Items")); // for chest types ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "Minecart", (final MapType data, final long fromVersion, final long toVersion) -> { ++ MCTypeRegistry.UNTAGGED_SPAWNER.convert(data, fromVersion, toVersion); ++ return null; ++ }); // for spawner type ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "MinecartRideable", new DataWalkerBlockNames("DisplayTile")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "MinecartChest", new DataWalkerBlockNames("DisplayTile")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "MinecartChest", new DataWalkerItemLists("Items")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "MinecartFurnace", new DataWalkerBlockNames("DisplayTile")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "MinecartTNT", new DataWalkerBlockNames("DisplayTile")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "MinecartSpawner", new DataWalkerBlockNames("DisplayTile")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "MinecartSpawner", (final MapType data, final long fromVersion, final long toVersion) -> { ++ MCTypeRegistry.UNTAGGED_SPAWNER.convert(data, fromVersion, toVersion); ++ return null; ++ }); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "MinecartHopper", new DataWalkerBlockNames("DisplayTile")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "MinecartHopper", new DataWalkerItemLists("Items")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "MinecartCommandBlock", new DataWalkerBlockNames("DisplayTile")); ++ registerMob("ArmorStand"); ++ registerMob("Creeper"); ++ registerMob("Skeleton"); ++ registerMob("Spider"); ++ registerMob("Giant"); ++ registerMob("Zombie"); ++ registerMob("Slime"); ++ registerMob("Ghast"); ++ registerMob("PigZombie"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "Enderman", new DataWalkerBlockNames("carried")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "Enderman", new DataWalkerItemLists("Equipment")); ++ registerMob("CaveSpider"); ++ registerMob("Silverfish"); ++ registerMob("Blaze"); ++ registerMob("LavaSlime"); ++ registerMob("EnderDragon"); ++ registerMob("WitherBoss"); ++ registerMob("Bat"); ++ registerMob("Witch"); ++ registerMob("Endermite"); ++ registerMob("Guardian"); ++ registerMob("Pig"); ++ registerMob("Sheep"); ++ registerMob("Cow"); ++ registerMob("Chicken"); ++ registerMob("Squid"); ++ registerMob("Wolf"); ++ registerMob("MushroomCow"); ++ registerMob("SnowMan"); ++ registerMob("Ozelot"); ++ registerMob("VillagerGolem"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "EntityHorse", new DataWalkerItemLists("Items", "Equipment")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "EntityHorse", new DataWalkerItems("ArmorItem", "SaddleItem")); ++ registerMob("Rabbit"); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "Villager", new DataWalkerItemLists("Inventory", "Equipment")); ++ MCTypeRegistry.ENTITY.addWalker(VERSION, "Villager", (final MapType data, final long fromVersion, final long toVersion) -> { ++ final MapType offers = data.getMap("Offers"); ++ if (offers != null) { ++ final ListType recipes = offers.getList("Recipes", ObjectType.MAP); ++ if (recipes != null) { ++ for (int i = 0; i < recipes.size(); ++i) { ++ final MapType recipe = recipes.getMap(i); ++ ++ WalkerUtils.convert(MCTypeRegistry.ITEM_STACK, recipe, "buy", fromVersion, toVersion); ++ WalkerUtils.convert(MCTypeRegistry.ITEM_STACK, recipe, "buyB", fromVersion, toVersion); ++ WalkerUtils.convert(MCTypeRegistry.ITEM_STACK, recipe, "sell", fromVersion, toVersion); ++ } ++ } ++ } ++ ++ return null; ++ }); ++ registerMob("Shulker"); ++ ++ // tile entities ++ ++ // Inventory -> new DataWalkerItemLists("Items") ++ registerInventory("Furnace"); ++ registerInventory("Chest"); ++ MCTypeRegistry.TILE_ENTITY.addWalker(VERSION, "RecordPlayer", new DataWalkerItems("RecordItem")); ++ registerInventory("Trap"); ++ registerInventory("Dropper"); ++ MCTypeRegistry.TILE_ENTITY.addWalker(VERSION, "MobSpawner", (final MapType data, final long fromVersion, final long toVersion) -> { ++ MCTypeRegistry.UNTAGGED_SPAWNER.convert(data, fromVersion, toVersion); ++ return null; ++ }); ++ registerInventory("Cauldron"); ++ registerInventory("Hopper"); ++ // Note: Vanilla does not properly handle this case, it will not convert int ids! ++ MCTypeRegistry.TILE_ENTITY.addWalker(VERSION, "FlowerPot", new DataWalkerItemNames("Item")); ++ ++ // rest ++ ++ MCTypeRegistry.ITEM_STACK.addStructureWalker(VERSION, (final MapType data, final long fromVersion, final long toVersion) -> { ++ WalkerUtils.convert(MCTypeRegistry.ITEM_NAME, data, "id", fromVersion, toVersion); ++ ++ final MapType tag = data.getMap("tag"); ++ if (tag == null) { ++ return null; ++ } ++ ++ // only things here are in tag, if changed update if above ++ ++ WalkerUtils.convertList(MCTypeRegistry.ITEM_STACK, tag, "Items", fromVersion, toVersion); ++ ++ MapType entityTag = tag.getMap("EntityTag"); ++ if (entityTag != null) { ++ String itemId = getStringId(data.getString("id")); ++ final String entityId; ++ if ("minecraft:armor_stand".equals(itemId)) { ++ // The check for version id is removed here. For whatever reason, the legacy ++ // data converters used entity id "minecraft:armor_stand" when version was greater-than 514, ++ // but entity ids were not namespaced until V705! So somebody fucked up the legacy converters. ++ // DFU agrees with my analysis here, it will only set the entityId here to the namespaced variant ++ // with the V705 schema. ++ entityId = "ArmorStand"; ++ } else if ("minecraft:item_frame".equals(itemId)) { ++ // add missing item_frame entity id ++ entityId = "ItemFrame"; ++ } else { ++ entityId = entityTag.getString("id"); ++ } ++ ++ final boolean removeId; ++ if (entityId == null) { ++ if (!"minecraft:air".equals(itemId)) { ++ LOGGER.warn("Unable to resolve Entity for ItemStack (V99): " + data.getGeneric("id")); ++ } ++ removeId = false; ++ } else { ++ removeId = !entityTag.hasKey("id", ObjectType.STRING); ++ if (removeId) { ++ entityTag.setString("id", entityId); ++ } ++ } ++ ++ final MapType replace = MCTypeRegistry.ENTITY.convert(entityTag, fromVersion, toVersion); ++ ++ if (replace != null) { ++ entityTag = replace; ++ tag.setMap("EntityTag", entityTag); ++ } ++ if (removeId) { ++ entityTag.remove("id"); ++ } ++ } ++ ++ MapType blockEntityTag = tag.getMap("BlockEntityTag"); ++ if (blockEntityTag != null) { ++ final String itemId = getStringId(data.getString("id")); ++ final String entityId = ITEM_ID_TO_TILE_ENTITY_ID.get(itemId); ++ final boolean removeId; ++ if (entityId == null) { ++ if (!"minecraft:air".equals(itemId)) { ++ LOGGER.warn("Unable to resolve BlockEntity for ItemStack (V99): " + data.getGeneric("id")); ++ } ++ removeId = false; ++ } else { ++ removeId = !blockEntityTag.hasKey("id", ObjectType.STRING); ++ blockEntityTag.setString("id", entityId); ++ } ++ final MapType replace = MCTypeRegistry.TILE_ENTITY.convert(blockEntityTag, fromVersion, toVersion); ++ if (replace != null) { ++ blockEntityTag = replace; ++ tag.setMap("BlockEntityTag", blockEntityTag); ++ } ++ if (removeId) { ++ blockEntityTag.remove("id"); ++ } ++ } ++ ++ WalkerUtils.convertList(MCTypeRegistry.BLOCK_NAME, tag, "CanDestroy", fromVersion, toVersion); ++ WalkerUtils.convertList(MCTypeRegistry.BLOCK_NAME, tag, "CanPlaceOn", fromVersion, toVersion); ++ ++ return null; ++ }); ++ ++ MCTypeRegistry.PLAYER.addStructureWalker(VERSION, new DataWalkerItemLists("Inventory", "EnderItems")); ++ ++ 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); ++ } ++ } ++ ++ return null; ++ }); ++ ++ MCTypeRegistry.ENTITY_CHUNK.addStructureWalker(VERSION, (final MapType data, final long fromVersion, final long toVersion) -> { ++ WalkerUtils.convertList(MCTypeRegistry.ENTITY, data, "Entities", fromVersion, toVersion); ++ ++ return null; ++ }); ++ ++ MCTypeRegistry.SAVED_DATA.addStructureWalker(VERSION, (final MapType root, final long fromVersion, final long toVersion) -> { ++ final MapType data = root.getMap("data"); ++ if (data == null) { ++ return null; ++ } ++ ++ WalkerUtils.convertValues(MCTypeRegistry.STRUCTURE_FEATURE, data, "Features", fromVersion, toVersion); ++ WalkerUtils.convertList(MCTypeRegistry.OBJECTIVE, data, "Objectives", fromVersion, toVersion); ++ WalkerUtils.convertList(MCTypeRegistry.TEAM, data, "Teams", fromVersion, toVersion); ++ ++ return null; ++ }); ++ ++ ++ // Enforce namespacing for ids ++ MCTypeRegistry.BLOCK_NAME.addStructureHook(VERSION, new DataHookValueTypeEnforceNamespaced()); ++ MCTypeRegistry.ITEM_NAME.addStructureHook(VERSION, new DataHookValueTypeEnforceNamespaced()); ++ MCTypeRegistry.ITEM_STACK.addStructureHook(VERSION, new DataHookEnforceNamespacedID()); ++ ++ // Entity is absent; the String form is not yet namespaced, unlike the above. ++ } ++ ++ protected static String getStringId(final Object id) { ++ if (id instanceof String) { ++ return (String)id; ++ } else if (id instanceof Number) { ++ return HelperItemNameV102.getNameFromId(((Number)id).intValue()); ++ } else { ++ return null; ++ } ++ } ++ ++ private V99() { ++ throw new RuntimeException(); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/block_name/DataWalkerBlockNames.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/block_name/DataWalkerBlockNames.java +new file mode 100644 +index 0000000000000000000000000000000000000000..930e014858ef635ebe25f7f92dc81ba0eaac50a8 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/block_name/DataWalkerBlockNames.java +@@ -0,0 +1,11 @@ ++package ca.spottedleaf.dataconverter.minecraft.walkers.block_name; ++ ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.generic.DataWalkerTypePaths; ++ ++public final class DataWalkerBlockNames extends DataWalkerTypePaths { ++ ++ public DataWalkerBlockNames(final String... paths) { ++ super(MCTypeRegistry.BLOCK_NAME, paths); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/generic/DataWalkerListPaths.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/generic/DataWalkerListPaths.java +new file mode 100644 +index 0000000000000000000000000000000000000000..7c8f6a5034b48e1ec2c5925211f491115ca735aa +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/generic/DataWalkerListPaths.java +@@ -0,0 +1,38 @@ ++package ca.spottedleaf.dataconverter.minecraft.walkers.generic; ++ ++import ca.spottedleaf.dataconverter.converters.datatypes.DataType; ++import ca.spottedleaf.dataconverter.converters.datatypes.DataWalker; ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.MapType; ++ ++public class DataWalkerListPaths implements DataWalker { ++ ++ protected final DataType type; ++ protected final String[] paths; ++ ++ public DataWalkerListPaths(final DataType type, final String... paths) { ++ this.type = type; ++ this.paths = paths; ++ } ++ ++ @Override ++ public final MapType walk(final MapType data, final long fromVersion, final long toVersion) { ++ final DataType type = this.type; ++ for (final String path : this.paths) { ++ final ListType list = data.getListUnchecked(path); ++ if (list == null) { ++ continue; ++ } ++ ++ for (int i = 0, len = list.size(); i < len; ++i) { ++ final Object current = list.getGeneric(i); ++ final Object converted = type.convert((T)current, fromVersion, toVersion); ++ if (converted != null) { ++ list.setGeneric(i, converted); ++ } ++ } ++ } ++ ++ return null; ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/generic/DataWalkerTypePaths.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/generic/DataWalkerTypePaths.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e66b4e0f7cdb032b545ace7ba852ad7979f3c96a +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/generic/DataWalkerTypePaths.java +@@ -0,0 +1,34 @@ ++package ca.spottedleaf.dataconverter.minecraft.walkers.generic; ++ ++import ca.spottedleaf.dataconverter.converters.datatypes.DataType; ++import ca.spottedleaf.dataconverter.converters.datatypes.DataWalker; ++import ca.spottedleaf.dataconverter.types.MapType; ++ ++public class DataWalkerTypePaths implements DataWalker { ++ ++ protected final DataType type; ++ protected final String[] paths; ++ ++ public DataWalkerTypePaths(final DataType type, final String... paths) { ++ this.type = type; ++ this.paths = paths; ++ } ++ ++ @Override ++ public final MapType walk(final MapType data, final long fromVersion, final long toVersion) { ++ for (final String path : this.paths) { ++ final Object current = data.getGeneric(path); ++ if (current == null) { ++ continue; ++ } ++ ++ final Object converted = this.type.convert((T)current, fromVersion, toVersion); ++ ++ if (converted != null) { ++ data.setGeneric(path, converted); ++ } ++ } ++ ++ return null; ++ } ++} +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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/generic/WalkerUtils.java +@@ -0,0 +1,130 @@ ++package ca.spottedleaf.dataconverter.minecraft.walkers.generic; ++ ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCDataType; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCValueType; ++import ca.spottedleaf.dataconverter.types.ObjectType; ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import java.util.ArrayList; ++ ++public final class WalkerUtils { ++ ++ public static void convert(final MCDataType type, final MapType data, final String path, final long fromVersion, final long toVersion) { ++ if (data == null) { ++ return; ++ } ++ ++ final MapType map = data.getMap(path); ++ if (map != null) { ++ final MapType replace = type.convert(map, fromVersion, toVersion); ++ if (replace != null) { ++ data.setMap(path, replace); ++ } ++ } ++ } ++ ++ public static void convertList(final MCDataType type, final MapType data, final String path, final long fromVersion, final long toVersion) { ++ if (data == null) { ++ return; ++ } ++ ++ final ListType list = data.getList(path, ObjectType.MAP); ++ if (list != null) { ++ for (int i = 0, len = list.size(); i < len; ++i) { ++ final MapType replace = type.convert(list.getMap(i), fromVersion, toVersion); ++ if (replace != null) { ++ list.setMap(i, replace); ++ } ++ } ++ } ++ } ++ ++ public static void convert(final MCValueType type, final MapType data, final String path, final long fromVersion, final long toVersion) { ++ if (data == null) { ++ return; ++ } ++ ++ final Object value = data.getGeneric(path); ++ if (value != null) { ++ final Object converted = type.convert(value, fromVersion, toVersion); ++ if (converted != null) { ++ data.setGeneric(path, converted); ++ } ++ } ++ } ++ ++ public static void convert(final MCValueType type, final ListType data, final long fromVersion, final long toVersion) { ++ if (data == null) { ++ return; ++ } ++ ++ for (int i = 0, len = data.size(); i < len; ++i) { ++ final Object value = data.getGeneric(i); ++ final Object converted = type.convert(value, fromVersion, toVersion); ++ if (converted != null) { ++ data.setGeneric(i, converted); ++ } ++ } ++ } ++ ++ public static void convertList(final MCValueType type, final MapType data, final String path, final long fromVersion, final long toVersion) { ++ if (data == null) { ++ return; ++ } ++ ++ final ListType list = data.getListUnchecked(path); ++ if (list != null) { ++ convert(type, list, fromVersion, toVersion); ++ } ++ } ++ ++ public static void convertKeys(final MCValueType type, final MapType data, final String path, final long fromVersion, final long toVersion) { ++ if (data == null) { ++ return; ++ } ++ ++ final MapType map = data.getMap(path); ++ if (map != null) { ++ convertKeys(type, map, fromVersion, toVersion); ++ } ++ } ++ ++ public static void convertKeys(final MCValueType type, final MapType data, final long fromVersion, final long toVersion) { ++ if (data == null) { ++ 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); ++ } ++ } ++ } ++ ++ public static void convertValues(final MCDataType type, final MapType data, final String path, final long fromVersion, final long toVersion) { ++ if (data == null) { ++ return; ++ } ++ ++ convertValues(type, data.getMap(path), fromVersion, toVersion); ++ } ++ ++ public static void convertValues(final MCDataType type, final MapType data, final long fromVersion, final long toVersion) { ++ if (data == null) { ++ return; ++ } ++ ++ for (final String key : data.keys()) { ++ final MapType value = data.getMap(key); ++ if (value != null) { ++ final MapType replace = type.convert(value, fromVersion, toVersion); ++ if (replace != null) { ++ // no CME, key is in map already ++ data.setMap(key, replace); ++ } ++ } ++ } ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/item_name/DataWalkerItemNames.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/item_name/DataWalkerItemNames.java +new file mode 100644 +index 0000000000000000000000000000000000000000..14e291efd864d97dcf83db01c09b9daaae1949bd +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/item_name/DataWalkerItemNames.java +@@ -0,0 +1,11 @@ ++package ca.spottedleaf.dataconverter.minecraft.walkers.item_name; ++ ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.generic.DataWalkerTypePaths; ++ ++public final class DataWalkerItemNames extends DataWalkerTypePaths { ++ ++ public DataWalkerItemNames(final String... paths) { ++ super(MCTypeRegistry.ITEM_NAME, paths); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/itemstack/DataWalkerItemLists.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/itemstack/DataWalkerItemLists.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5b4402c3cc4e68e9c591e8bbb4a2542d8e2214d4 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/itemstack/DataWalkerItemLists.java +@@ -0,0 +1,12 @@ ++package ca.spottedleaf.dataconverter.minecraft.walkers.itemstack; ++ ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.generic.DataWalkerListPaths; ++import ca.spottedleaf.dataconverter.types.MapType; ++ ++public class DataWalkerItemLists extends DataWalkerListPaths, MapType> { ++ ++ public DataWalkerItemLists(final String... paths) { ++ super(MCTypeRegistry.ITEM_STACK, paths); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/itemstack/DataWalkerItems.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/itemstack/DataWalkerItems.java +new file mode 100644 +index 0000000000000000000000000000000000000000..04770e8378ac8784895cdfe400a47b0b601c2187 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/itemstack/DataWalkerItems.java +@@ -0,0 +1,12 @@ ++package ca.spottedleaf.dataconverter.minecraft.walkers.itemstack; ++ ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.generic.DataWalkerTypePaths; ++import ca.spottedleaf.dataconverter.types.MapType; ++ ++public class DataWalkerItems extends DataWalkerTypePaths, MapType> { ++ ++ public DataWalkerItems(final String... paths) { ++ super(MCTypeRegistry.ITEM_STACK, paths); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/tile_entity/DataWalkerTileEntities.java b/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/tile_entity/DataWalkerTileEntities.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d9cc21bf41cb4b377752b684f8e59818cd620103 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/minecraft/walkers/tile_entity/DataWalkerTileEntities.java +@@ -0,0 +1,12 @@ ++package ca.spottedleaf.dataconverter.minecraft.walkers.tile_entity; ++ ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.minecraft.walkers.generic.DataWalkerTypePaths; ++import ca.spottedleaf.dataconverter.types.MapType; ++ ++public final class DataWalkerTileEntities extends DataWalkerTypePaths, MapType> { ++ ++ public DataWalkerTileEntities(final String... paths) { ++ super(MCTypeRegistry.TILE_ENTITY, paths); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/types/ListType.java b/src/main/java/ca/spottedleaf/dataconverter/types/ListType.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a91834f7eb6d1aa49f42b0f25085ac84b93471bd +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/types/ListType.java +@@ -0,0 +1,270 @@ ++package ca.spottedleaf.dataconverter.types; ++ ++public interface ListType { ++ ++ @Override ++ public int hashCode(); ++ ++ @Override ++ public boolean equals(final Object other); ++ ++ // Provides a deep copy of this list ++ public ListType copy(); ++ ++ // returns NONE if no type has been assigned. if NONE, then this list is also empty. It is not true on the other hand that an empty list has no type. ++ public ObjectType getType(); ++ ++ public int size(); ++ ++ public void remove(final int index); ++ ++ public default Object getGeneric(final int index) { ++ switch (this.getType()) { ++ case NONE: ++ throw new IllegalStateException("List is empty and has no type"); ++ case BYTE: ++ return Byte.valueOf(this.getByte(index)); ++ case SHORT: ++ return Short.valueOf(this.getShort(index)); ++ case INT: ++ return Integer.valueOf(this.getInt(index)); ++ case LONG: ++ return Long.valueOf(this.getLong(index)); ++ case FLOAT: ++ return Float.valueOf(this.getFloat(index)); ++ case DOUBLE: ++ return Double.valueOf(this.getDouble(index)); ++ case NUMBER: ++ return this.getNumber(index); ++ case BYTE_ARRAY: ++ return this.getBytes(index); ++ case SHORT_ARRAY: ++ return this.getShorts(index); ++ case INT_ARRAY: ++ return this.getInts(index); ++ case LONG_ARRAY: ++ return this.getLongs(index); ++ case LIST: ++ return this.getList(index); ++ case MAP: ++ return this.getMap(index); ++ case STRING: ++ return this.getString(index); ++ default: ++ throw new UnsupportedOperationException(this.getType().name()); ++ } ++ } ++ ++ public default void setGeneric(final int index, final Object to) { ++ if (to instanceof Number) { ++ if (to instanceof Byte) { ++ this.setByte(index, ((Byte)to).byteValue()); ++ return; ++ } else if (to instanceof Short) { ++ this.setShort(index, ((Short)to).shortValue()); ++ return; ++ } else if (to instanceof Integer) { ++ this.setInt(index, ((Integer)to).intValue()); ++ return; ++ } else if (to instanceof Long) { ++ this.setLong(index, ((Long)to).longValue()); ++ return; ++ } else if (to instanceof Float) { ++ this.setFloat(index, ((Float)to).floatValue()); ++ return; ++ } else if (to instanceof Double) { ++ this.setDouble(index, ((Double)to).doubleValue()); ++ return; ++ } // else fall through to throw ++ } else if (to instanceof MapType) { ++ this.setMap(index, (MapType)to); ++ return; ++ } else if (to instanceof ListType) { ++ this.setList(index, (ListType)to); ++ return; ++ } else if (to instanceof String) { ++ this.setString(index, (String)to); ++ return; ++ } else if (to.getClass().isArray()) { ++ if (to instanceof byte[]) { ++ this.setBytes(index, (byte[])to); ++ return; ++ } else if (to instanceof short[]) { ++ this.setShorts(index, (short[])to); ++ return; ++ } else if (to instanceof int[]) { ++ this.setInts(index, (int[])to); ++ return; ++ } else if (to instanceof long[]) { ++ this.setLongs(index, (long[])to); ++ return; ++ } // else fall through to throw ++ } ++ ++ throw new IllegalArgumentException("Object " + to + " is not a valid type!"); ++ } ++ ++ // types here are strict. if the type on get does not match the underlying type, will throw. ++ ++ public Number getNumber(final int index); ++ ++ // if the value at index is a Number but not a byte, then returns the number casted to byte. If the value at the index is not a number, then throws ++ public byte getByte(final int index); ++ ++ public void setByte(final int index, final byte to); ++ ++ // if the value at index is a Number but not a short, then returns the number casted to short. If the value at the index is not a number, then throws ++ public short getShort(final int index); ++ ++ public void setShort(final int index, final short to); ++ ++ // if the value at index is a Number but not a int, then returns the number casted to int. If the value at the index is not a number, then throws ++ public int getInt(final int index); ++ ++ public void setInt(final int index, final int to); ++ ++ // if the value at index is a Number but not a long, then returns the number casted to long. If the value at the index is not a number, then throws ++ public long getLong(final int index); ++ ++ public void setLong(final int index, final long to); ++ ++ // if the value at index is a Number but not a float, then returns the number casted to float. If the value at the index is not a number, then throws ++ public float getFloat(final int index); ++ ++ public void setFloat(final int index, final float to); ++ ++ // if the value at index is a Number but not a double, then returns the number casted to double. If the value at the index is not a number, then throws ++ public double getDouble(final int index); ++ ++ public void setDouble(final int index, final double to); ++ ++ public byte[] getBytes(final int index); ++ ++ public void setBytes(final int index, final byte[] to); ++ ++ public short[] getShorts(final int index); ++ ++ public void setShorts(final int index, final short[] to); ++ ++ public int[] getInts(final int index); ++ ++ public void setInts(final int index, final int[] to); ++ ++ public long[] getLongs(final int index); ++ ++ public void setLongs(final int index, final long[] to); ++ ++ public ListType getList(final int index); ++ ++ public void setList(final int index, final ListType list); ++ ++ public MapType getMap(final int index); ++ ++ public void setMap(final int index, final MapType to); ++ ++ public String getString(final int index); ++ ++ public void setString(final int index, final String to); ++ ++ public default void addGeneric(final Object to) { ++ if (to instanceof Number) { ++ if (to instanceof Byte) { ++ this.addByte(((Byte)to).byteValue()); ++ return; ++ } else if (to instanceof Short) { ++ this.addShort(((Short)to).shortValue()); ++ return; ++ } else if (to instanceof Integer) { ++ this.addInt(((Integer)to).intValue()); ++ return; ++ } else if (to instanceof Long) { ++ this.addLong(((Long)to).longValue()); ++ return; ++ } else if (to instanceof Float) { ++ this.addFloat(((Float)to).floatValue()); ++ return; ++ } else if (to instanceof Double) { ++ this.addDouble(((Double)to).doubleValue()); ++ return; ++ } // else fall through to throw ++ } else if (to instanceof MapType) { ++ this.addMap((MapType)to); ++ return; ++ } else if (to instanceof ListType) { ++ this.addList((ListType)to); ++ return; ++ } else if (to instanceof String) { ++ this.addString((String)to); ++ return; ++ } else if (to.getClass().isArray()) { ++ if (to instanceof byte[]) { ++ this.addByteArray((byte[])to); ++ return; ++ } else if (to instanceof short[]) { ++ this.addShortArray((short[])to); ++ return; ++ } else if (to instanceof int[]) { ++ this.addIntArray((int[])to); ++ return; ++ } else if (to instanceof long[]) { ++ this.addLongArray((long[])to); ++ return; ++ } // else fall through to throw ++ } ++ ++ throw new IllegalArgumentException("Object " + to + " is not a valid type!"); ++ } ++ ++ public void addByte(final byte b); ++ ++ public void addByte(final int index, final byte b); ++ ++ public void addShort(final short s); ++ ++ public void addShort(final int index, final short s); ++ ++ public void addInt(final int i); ++ ++ public void addInt(final int index, final int i); ++ ++ public void addLong(final long l); ++ ++ public void addLong(final int index, final long l); ++ ++ public void addFloat(final float f); ++ ++ public void addFloat(final int index, final float f); ++ ++ public void addDouble(final double d); ++ ++ public void addDouble(final int index, final double d); ++ ++ public void addByteArray(final byte[] arr); ++ ++ public void addByteArray(final int index, final byte[] arr); ++ ++ public void addShortArray(final short[] arr); ++ ++ public void addShortArray(final int index, final short[] arr); ++ ++ public void addIntArray(final int[] arr); ++ ++ public void addIntArray(final int index, final int[] arr); ++ ++ public void addLongArray(final long[] arr); ++ ++ public void addLongArray(final int index, final long[] arr); ++ ++ public void addList(final ListType list); ++ ++ public void addList(final int index, final ListType list); ++ ++ public void addMap(final MapType map); ++ ++ public void addMap(final int index, final MapType map); ++ ++ public void addString(final String string); ++ ++ public void addString(final int index, final String string); ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/types/MapType.java b/src/main/java/ca/spottedleaf/dataconverter/types/MapType.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d3ed052fea257ef2f8be54bf5e15f89a454d355f +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/types/MapType.java +@@ -0,0 +1,199 @@ ++package ca.spottedleaf.dataconverter.types; ++ ++import java.util.Set; ++ ++public interface MapType { ++ ++ @Override ++ public int hashCode(); ++ ++ @Override ++ public boolean equals(final Object other); ++ ++ public int size(); ++ ++ public boolean isEmpty(); ++ ++ public void clear(); ++ ++ public Set keys(); ++ ++ // Provides a deep copy of this map ++ public MapType copy(); ++ ++ public boolean hasKey(final K key); ++ ++ public boolean hasKey(final K key, final ObjectType type); ++ ++ public void remove(final K key); ++ ++ public Object getGeneric(final K key); ++ ++ // types here are not strict. if the key maps to a different type, default is always returned ++ // if default is not a parameter, then default is always null ++ ++ public Number getNumber(final K key); ++ ++ public Number getNumber(final K key, final Number dfl); ++ ++ public boolean getBoolean(final K key); ++ ++ public boolean getBoolean(final K key, final boolean dfl); ++ ++ public void setBoolean(final K key, final boolean val); ++ ++ // if the mapped value is a Number but not a byte, then the number is casted to byte. If the mapped value does not exist or is not a number, returns 0 ++ public byte getByte(final K key); ++ ++ // if the mapped value is a Number but not a byte, then the number is casted to byte. If the mapped value does not exist or is not a number, returns dfl ++ public byte getByte(final K key, final byte dfl); ++ ++ public void setByte(final K key, final byte val); ++ ++ // if the mapped value is a Number but not a short, then the number is casted to short. If the mapped value does not exist or is not a number, returns 0 ++ public short getShort(final K key); ++ ++ // if the mapped value is a Number but not a short, then the number is casted to short. If the mapped value does not exist or is not a number, returns dfl ++ public short getShort(final K key, final short dfl); ++ ++ public void setShort(final K key, final short val); ++ ++ // if the mapped value is a Number but not a int, then the number is casted to int. If the mapped value does not exist or is not a number, returns 0 ++ public int getInt(final K key); ++ ++ // if the mapped value is a Number but not a int, then the number is casted to int. If the mapped value does not exist or is not a number, returns dfl ++ public int getInt(final K key, final int dfl); ++ ++ public void setInt(final K key, final int val); ++ ++ // if the mapped value is a Number but not a long, then the number is casted to long. If the mapped value does not exist or is not a number, returns 0 ++ public long getLong(final K key); ++ ++ // if the mapped value is a Number but not a long, then the number is casted to long. If the mapped value does not exist or is not a number, returns dfl ++ public long getLong(final K key, final long dfl); ++ ++ public void setLong(final K key, final long val); ++ ++ // if the mapped value is a Number but not a float, then the number is casted to float. If the mapped value does not exist or is not a number, returns 0 ++ public float getFloat(final K key); ++ ++ // if the mapped value is a Number but not a float, then the number is casted to float. If the mapped value does not exist or is not a number, returns dfl ++ public float getFloat(final K key, final float dfl); ++ ++ public void setFloat(final K key, final float val); ++ ++ // if the mapped value is a Number but not a double, then the number is casted to double. If the mapped value does not exist or is not a number, returns 0 ++ public double getDouble(final K key); ++ ++ // if the mapped value is a Number but not a double, then the number is casted to double. If the mapped value does not exist or is not a number, returns dfl ++ public double getDouble(final K key, final double dfl); ++ ++ public void setDouble(final K key, final double val); ++ ++ public byte[] getBytes(final K key); ++ ++ public byte[] getBytes(final K key, final byte[] dfl); ++ ++ public void setBytes(final K key, final byte[] val); ++ ++ public short[] getShorts(final K key); ++ ++ public short[] getShorts(final K key, final short[] dfl); ++ ++ public void setShorts(final K key, final short[] val); ++ ++ public int[] getInts(final K key); ++ ++ public int[] getInts(final K key, final int[] dfl); ++ ++ public void setInts(final K key, final int[] val); ++ ++ public long[] getLongs(final K key); ++ ++ public long[] getLongs(final K key, final long[] dfl); ++ ++ public void setLongs(final K key, final long[] val); ++ ++ public ListType getListUnchecked(final K key); ++ ++ public ListType getListUnchecked(final K key, final ListType dfl); ++ ++ public default ListType getList(final K key, final ObjectType type) { ++ return this.getList(key, type, null); ++ } ++ ++ public default ListType getList(final K key, final ObjectType type, final ListType dfl) { ++ final ListType ret = this.getListUnchecked(key, null); ++ final ObjectType retType; ++ if (ret != null && ((retType = ret.getType()) == type || retType == ObjectType.UNDEFINED)) { ++ return ret; ++ } else { ++ return dfl; ++ } ++ } ++ ++ public void setList(final K key, final ListType val); ++ ++ public MapType getMap(final K key); ++ ++ public MapType getMap(final K key, final MapType dfl); ++ ++ public void setMap(final K key, final MapType val); ++ ++ public String getString(final K key); ++ ++ public String getString(final K key, final String dfl); ++ ++ public void setString(final K key, final String val); ++ ++ public default void setGeneric(final K key, final Object value) { ++ if (value instanceof Boolean) { ++ this.setBoolean(key, ((Boolean)value).booleanValue()); ++ } else if (value instanceof Number) { ++ if (value instanceof Byte) { ++ this.setByte(key, ((Byte)value).byteValue()); ++ return; ++ } else if (value instanceof Short) { ++ this.setShort(key, ((Short)value).shortValue()); ++ return; ++ } else if (value instanceof Integer) { ++ this.setInt(key, ((Integer)value).intValue()); ++ return; ++ } else if (value instanceof Long) { ++ this.setLong(key, ((Long)value).longValue()); ++ return; ++ } else if (value instanceof Float) { ++ this.setFloat(key, ((Float)value).floatValue()); ++ return; ++ } else if (value instanceof Double) { ++ this.setDouble(key, ((Double)value).doubleValue()); ++ return; ++ } // else fall through to throw ++ } else if (value instanceof MapType) { ++ this.setMap(key, (MapType)value); ++ return; ++ } else if (value instanceof ListType) { ++ this.setList(key, (ListType)value); ++ return; ++ } else if (value instanceof String) { ++ this.setString(key, (String)value); ++ return; ++ } else if (value.getClass().isArray()) { ++ if (value instanceof byte[]) { ++ this.setBytes(key, (byte[])value); ++ return; ++ } else if (value instanceof short[]) { ++ this.setShorts(key, (short[])value); ++ return; ++ } else if (value instanceof int[]) { ++ this.setInts(key, (int[])value); ++ return; ++ } else if (value instanceof long[]) { ++ this.setLongs(key, (long[])value); ++ return; ++ } // else fall through to throw ++ } ++ ++ throw new IllegalArgumentException("Object " + value + " is not a valid type!"); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/types/ObjectType.java b/src/main/java/ca/spottedleaf/dataconverter/types/ObjectType.java +new file mode 100644 +index 0000000000000000000000000000000000000000..1aab91233ddb98c3af5d424bac120891f1ee16c7 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/types/ObjectType.java +@@ -0,0 +1,72 @@ ++package ca.spottedleaf.dataconverter.types; ++ ++public enum ObjectType { ++ NONE(null), ++ BYTE(Byte.class), ++ SHORT(Short.class), ++ INT(Integer.class), ++ LONG(Long.class), ++ FLOAT(Float.class), ++ DOUBLE(Double.class), ++ NUMBER(Number.class), ++ BYTE_ARRAY(byte[].class), ++ SHORT_ARRAY(short[].class), ++ INT_ARRAY(int[].class), ++ LONG_ARRAY(long[].class), ++ LIST(ListType.class), ++ MAP(MapType.class), ++ STRING(String.class), ++ UNDEFINED(null); ++ ++ private final Class clazz; ++ private final boolean isNumber; ++ ++ private ObjectType(final Class clazz) { ++ this.clazz = clazz; ++ this.isNumber = clazz != null && Number.class.isAssignableFrom(clazz); ++ } ++ ++ public boolean isNumber() { ++ return this.isNumber; ++ } ++ ++ public Class getObjectClass() { ++ return this.clazz; ++ } ++ ++ public static ObjectType getType(final Object object) { ++ if (object instanceof Number) { ++ if (object instanceof Byte) { ++ return BYTE; ++ } else if (object instanceof Short) { ++ return SHORT; ++ } else if (object instanceof Integer) { ++ return INT; ++ } else if (object instanceof Long) { ++ return LONG; ++ } else if (object instanceof Float) { ++ return FLOAT; ++ } else if (object instanceof Double) { ++ return DOUBLE; ++ } // else return null ++ } else if (object instanceof MapType) { ++ return MAP; ++ } else if (object instanceof ListType) { ++ return LIST; ++ } else if (object instanceof String) { ++ return STRING; ++ } else if (object.getClass().isArray()) { ++ if (object instanceof byte[]) { ++ return BYTE_ARRAY; ++ } else if (object instanceof short[]) { ++ return SHORT_ARRAY; ++ } else if (object instanceof int[]) { ++ return INT_ARRAY; ++ } else if (object instanceof long[]) { ++ return LONG_ARRAY; ++ } // else return null ++ } ++ ++ return null; ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/types/TypeUtil.java b/src/main/java/ca/spottedleaf/dataconverter/types/TypeUtil.java +new file mode 100644 +index 0000000000000000000000000000000000000000..156a2ea46f8f88a02e88b50d7bb7be82ecd41919 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/types/TypeUtil.java +@@ -0,0 +1,9 @@ ++package ca.spottedleaf.dataconverter.types; ++ ++public interface TypeUtil { ++ ++ public ListType createEmptyList(); ++ ++ public MapType createEmptyMap(); ++ ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/types/Types.java b/src/main/java/ca/spottedleaf/dataconverter/types/Types.java +new file mode 100644 +index 0000000000000000000000000000000000000000..2ab9e3b579f20c9a189518496c522155630a36c4 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/types/Types.java +@@ -0,0 +1,15 @@ ++package ca.spottedleaf.dataconverter.types; ++ ++import ca.spottedleaf.dataconverter.types.json.JsonTypeCompressedUtil; ++import ca.spottedleaf.dataconverter.types.json.JsonTypeUtil; ++import ca.spottedleaf.dataconverter.types.nbt.NBTTypeUtil; ++ ++public interface Types { ++ ++ public static final TypeUtil NBT = new NBTTypeUtil(); ++ ++ public static final TypeUtil JSON = new JsonTypeUtil(); ++ ++ // why does this exist ++ public static final TypeUtil JSON_COMPRESSED = new JsonTypeCompressedUtil(); ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/types/json/JsonListType.java b/src/main/java/ca/spottedleaf/dataconverter/types/json/JsonListType.java +new file mode 100644 +index 0000000000000000000000000000000000000000..4c56144f0cba37f3d7788e5c08e41f6874ae92fc +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/types/json/JsonListType.java +@@ -0,0 +1,408 @@ ++package ca.spottedleaf.dataconverter.types.json; ++ ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.ObjectType; ++import com.google.gson.JsonArray; ++import com.google.gson.JsonElement; ++import com.google.gson.JsonObject; ++import com.google.gson.JsonPrimitive; ++ ++public final class JsonListType implements ListType { ++ ++ protected final JsonArray array; ++ protected final boolean compressed; ++ ++ public JsonListType(final boolean compressed) { ++ this.array = new JsonArray(); ++ this.compressed = compressed; ++ } ++ ++ public JsonListType(final JsonArray array, final boolean compressed) { ++ this.array = array; ++ this.compressed = compressed; ++ } ++ ++ @Override ++ public boolean equals(final Object obj) { ++ if (this == obj) { ++ return true; ++ } ++ ++ if (obj == null || obj.getClass() != JsonListType.class) { ++ return false; ++ } ++ ++ return this.array.equals(((JsonListType)obj).array); ++ } ++ ++ @Override ++ public int hashCode() { ++ return this.array.hashCode(); ++ } ++ ++ @Override ++ public String toString() { ++ return "JsonListType{" + ++ "array=" + this.array + ++ ", compressed=" + this.compressed + ++ '}'; ++ } ++ ++ public JsonArray getJson() { ++ return this.array; ++ } ++ ++ @Override ++ public ListType copy() { ++ return new JsonListType(JsonTypeUtil.copyJson(this.array), this.compressed); ++ } ++ ++ @Override ++ public ObjectType getType() { ++ return ObjectType.UNDEFINED; ++ } ++ ++ @Override ++ public int size() { ++ return this.array.size(); ++ } ++ ++ @Override ++ public void remove(final int index) { ++ this.array.remove(index); ++ } ++ ++ @Override ++ public Number getNumber(final int index) { ++ final JsonElement element = this.array.get(index); ++ if (element instanceof JsonPrimitive) { ++ final JsonPrimitive primitive = (JsonPrimitive)element; ++ if (primitive.isNumber()) { ++ return primitive.getAsNumber(); ++ } else if (primitive.isBoolean()) { ++ return primitive.getAsBoolean() ? Byte.valueOf((byte)1) : Byte.valueOf((byte)0); ++ } else if (this.compressed && primitive.isString()) { ++ try { ++ return Integer.valueOf(Integer.parseInt(primitive.getAsString())); ++ } catch (final NumberFormatException ex) { ++ return null; ++ } ++ } ++ } ++ ++ return null; ++ } ++ ++ @Override ++ public byte getByte(final int index) { ++ final Number number = this.getNumber(index); ++ ++ return number == null ? 0 : number.byteValue(); ++ } ++ ++ @Override ++ public void setByte(final int index, final byte to) { ++ this.array.set(index, new JsonPrimitive(Byte.valueOf(to))); ++ } ++ ++ @Override ++ public short getShort(final int index) { ++ final Number number = this.getNumber(index); ++ ++ return number == null ? 0 : number.shortValue(); ++ } ++ ++ @Override ++ public void setShort(final int index, final short to) { ++ this.array.set(index, new JsonPrimitive(Short.valueOf(to))); ++ } ++ ++ @Override ++ public int getInt(final int index) { ++ final Number number = this.getNumber(index); ++ ++ return number == null ? 0 : number.intValue(); ++ } ++ ++ @Override ++ public void setInt(final int index, final int to) { ++ this.array.set(index, new JsonPrimitive(Integer.valueOf(to))); ++ } ++ ++ @Override ++ public long getLong(final int index) { ++ final Number number = this.getNumber(index); ++ ++ return number == null ? 0 : number.longValue(); ++ } ++ ++ @Override ++ public void setLong(final int index, final long to) { ++ this.array.set(index, new JsonPrimitive(Long.valueOf(to))); ++ } ++ ++ @Override ++ public float getFloat(final int index) { ++ final Number number = this.getNumber(index); ++ ++ return number == null ? 0 : number.floatValue(); ++ } ++ ++ @Override ++ public void setFloat(final int index, final float to) { ++ this.array.set(index, new JsonPrimitive(Float.valueOf(to))); ++ } ++ ++ @Override ++ public double getDouble(final int index) { ++ final Number number = this.getNumber(index); ++ ++ return number == null ? 0 : number.doubleValue(); ++ } ++ ++ @Override ++ public void setDouble(final int index, final double to) { ++ this.array.set(index, new JsonPrimitive(Double.valueOf(to))); ++ } ++ ++ @Override ++ public byte[] getBytes(final int index) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void setBytes(final int index, final byte[] to) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public short[] getShorts(final int index) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void setShorts(final int index, final short[] to) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public int[] getInts(final int index) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void setInts(final int index, final int[] to) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public long[] getLongs(final int index) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void setLongs(final int index, final long[] to) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public ListType getList(final int index) { ++ final JsonElement element = this.array.get(index); ++ if (element instanceof JsonArray) { ++ return new JsonListType((JsonArray)element, this.compressed); ++ } ++ return null; ++ } ++ ++ @Override ++ public void setList(final int index, final ListType list) { ++ this.array.set(index, ((JsonListType)list).array); ++ } ++ ++ @Override ++ public MapType getMap(final int index) { ++ final JsonElement element = this.array.get(index); ++ if (element instanceof JsonObject) { ++ return new JsonMapType((JsonObject)element, this.compressed); ++ } ++ return null; ++ } ++ ++ @Override ++ public void setMap(final int index, final MapType to) { ++ this.array.set(index, ((JsonMapType)to).map); ++ } ++ ++ @Override ++ public String getString(final int index) { ++ final JsonElement element = this.array.get(index); ++ if (element instanceof JsonPrimitive) { ++ final JsonPrimitive primitive = (JsonPrimitive)element; ++ if (primitive.isString() || (this.compressed && primitive.isNumber())) { ++ return primitive.getAsString(); ++ } ++ } ++ ++ return null; ++ } ++ ++ @Override ++ public void setString(final int index, final String to) { ++ this.array.set(index, new JsonPrimitive(to)); ++ } ++ ++ @Override ++ public void addByte(final byte b) { ++ this.array.add(Byte.valueOf(b)); ++ } ++ ++ @Override ++ public void addByte(final int index, final byte b) { ++ // doesn't implement any methods for adding at index... yee haw... ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void addShort(final short s) { ++ this.array.add(Short.valueOf(s)); ++ } ++ ++ @Override ++ public void addShort(final int index, final short s) { ++ // doesn't implement any methods for adding at index... yee haw... ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void addInt(final int i) { ++ this.array.add(Integer.valueOf(i)); ++ } ++ ++ @Override ++ public void addInt(final int index, final int i) { ++ // doesn't implement any methods for adding at index... yee haw... ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void addLong(final long l) { ++ this.array.add(Long.valueOf(l)); ++ } ++ ++ @Override ++ public void addLong(final int index, final long l) { ++ // doesn't implement any methods for adding at index... yee haw... ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void addFloat(final float f) { ++ this.array.add(Float.valueOf(f)); ++ } ++ ++ @Override ++ public void addFloat(final int index, final float f) { ++ // doesn't implement any methods for adding at index... yee haw... ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void addDouble(final double d) { ++ this.array.add(Double.valueOf(d)); ++ } ++ ++ @Override ++ public void addDouble(final int index, final double d) { ++ // doesn't implement any methods for adding at index... yee haw... ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void addByteArray(final byte[] arr) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void addByteArray(final int index, final byte[] arr) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void addShortArray(final short[] arr) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void addShortArray(final int index, final short[] arr) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void addIntArray(final int[] arr) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void addIntArray(final int index, final int[] arr) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void addLongArray(final long[] arr) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void addLongArray(final int index, final long[] arr) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void addList(final ListType list) { ++ this.array.add(((JsonListType)list).array); ++ } ++ ++ @Override ++ public void addList(final int index, final ListType list) { ++ // doesn't implement any methods for adding at index... yee haw... ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void addMap(final MapType map) { ++ this.array.add(((JsonMapType)map).map); ++ } ++ ++ @Override ++ public void addMap(final int index, final MapType map) { ++ // doesn't implement any methods for adding at index... yee haw... ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void addString(final String string) { ++ this.array.add(string); ++ } ++ ++ @Override ++ public void addString(final int index, final String string) { ++ // doesn't implement any methods for adding at index... yee haw... ++ throw new UnsupportedOperationException(); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/types/json/JsonMapType.java b/src/main/java/ca/spottedleaf/dataconverter/types/json/JsonMapType.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f495c4d65519b3bba6ac371415b7338a726195d8 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/types/json/JsonMapType.java +@@ -0,0 +1,443 @@ ++package ca.spottedleaf.dataconverter.types.json; ++ ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.ObjectType; ++import com.google.gson.JsonArray; ++import com.google.gson.JsonElement; ++import com.google.gson.JsonObject; ++import com.google.gson.JsonPrimitive; ++import java.util.LinkedHashSet; ++import java.util.Map; ++import java.util.Set; ++ ++public final class JsonMapType implements MapType { ++ ++ protected final JsonObject map; ++ protected final boolean compressed; ++ ++ public JsonMapType(final boolean compressed) { ++ this.map = new JsonObject(); ++ this.compressed = compressed; ++ } ++ ++ public JsonMapType(final JsonObject map, final boolean compressed) { ++ this.map = map; ++ this.compressed = compressed; ++ } ++ ++ @Override ++ public boolean equals(final Object obj) { ++ if (this == obj) { ++ return true; ++ } ++ ++ if (obj == null || obj.getClass() != JsonMapType.class) { ++ return false; ++ } ++ ++ return this.map.equals(((JsonMapType)obj).map); ++ } ++ ++ @Override ++ public int hashCode() { ++ return this.map.hashCode(); ++ } ++ ++ @Override ++ public String toString() { ++ return "JsonMapType{" + ++ "map=" + this.map + ++ ", compressed=" + this.compressed + ++ '}'; ++ } ++ ++ public JsonObject getJson() { ++ return this.map; ++ } ++ ++ @Override ++ public int size() { ++ return this.map.entrySet().size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return this.map.entrySet().isEmpty(); ++ } ++ ++ @Override ++ public void clear() { ++ this.map.entrySet().clear(); ++ } ++ ++ @Override ++ public Set keys() { ++ // ah shit. no keyset method ++ final Set keys = new LinkedHashSet<>(); ++ ++ for (final Map.Entry entry : this.map.entrySet()) { ++ keys.add(entry.getKey()); ++ } ++ ++ return keys; ++ } ++ ++ @Override ++ public MapType copy() { ++ return new JsonMapType(JsonTypeUtil.copyJson(this.map), this.compressed); ++ } ++ ++ @Override ++ public boolean hasKey(final String key) { ++ return this.map.has(key); ++ } ++ ++ @Override ++ public boolean hasKey(final String key, final ObjectType type) { ++ final JsonElement element = this.map.get(key); ++ if (element == null) { ++ return false; ++ } ++ ++ if (type == ObjectType.UNDEFINED) { ++ return true; ++ } ++ ++ if (element.isJsonArray()) { ++ return type == ObjectType.LIST; ++ } else if (element.isJsonObject()) { ++ return type == ObjectType.MAP; ++ } else if (element.isJsonNull()) { ++ return false; ++ } ++ ++ final JsonPrimitive primitive = (JsonPrimitive)element; ++ if (primitive.isString()) { ++ return type == ObjectType.STRING || (this.compressed && type == ObjectType.NUMBER); ++ } else if (primitive.isBoolean()) { ++ return type.isNumber(); ++ } else { ++ // is number ++ final Number number = primitive.getAsNumber(); ++ if (number instanceof Byte) { ++ return type == ObjectType.BYTE || (this.compressed && type == ObjectType.STRING); ++ } else if (number instanceof Short) { ++ return type == ObjectType.SHORT || (this.compressed && type == ObjectType.STRING); ++ } else if (number instanceof Integer) { ++ return type == ObjectType.INT || (this.compressed && type == ObjectType.STRING); ++ } else if (number instanceof Long) { ++ return type == ObjectType.LONG || (this.compressed && type == ObjectType.STRING); ++ } else if (number instanceof Float) { ++ return type == ObjectType.FLOAT || (this.compressed && type == ObjectType.STRING); ++ } else { ++ return type == ObjectType.DOUBLE || (this.compressed && type == ObjectType.STRING); ++ } ++ } ++ } ++ ++ @Override ++ public void remove(final String key) { ++ this.map.remove(key); ++ } ++ ++ @Override ++ public Object getGeneric(final String key) { ++ final JsonElement element = this.map.get(key); ++ if (element == null || element.isJsonNull()) { ++ return null; ++ } else if (element.isJsonObject()) { ++ return new JsonMapType((JsonObject)element, this.compressed); ++ } else if (element.isJsonArray()) { ++ return new JsonListType((JsonArray)element, this.compressed); ++ } else { ++ // primitive ++ final JsonPrimitive primitive = (JsonPrimitive)element; ++ if (primitive.isNumber()) { ++ return primitive.getAsNumber(); ++ } else if (primitive.isString()) { ++ return primitive.getAsString(); ++ } else if (primitive.isBoolean()) { ++ return Boolean.valueOf(primitive.getAsBoolean()); ++ } else { ++ throw new IllegalStateException("Unknown json object " + element); ++ } ++ } ++ } ++ ++ @Override ++ public Number getNumber(final String key) { ++ return this.getNumber(key, null); ++ } ++ ++ @Override ++ public Number getNumber(final String key, final Number dfl) { ++ final JsonElement element = this.map.get(key); ++ if (element instanceof JsonPrimitive) { ++ final JsonPrimitive primitive = (JsonPrimitive)element; ++ if (primitive.isNumber()) { ++ return primitive.getAsNumber(); ++ } else if (primitive.isBoolean()) { ++ return primitive.getAsBoolean() ? Byte.valueOf((byte)1) : Byte.valueOf((byte)0); ++ } else if (this.compressed && primitive.isString()) { ++ try { ++ return Integer.valueOf(Integer.parseInt(primitive.getAsString())); ++ } catch (final NumberFormatException ex) { ++ return null; ++ } ++ } ++ } ++ ++ return dfl; ++ } ++ ++ @Override ++ public boolean getBoolean(final String key) { ++ return this.getBoolean(key, false); ++ } ++ ++ @Override ++ public boolean getBoolean(final String key, final boolean dfl) { ++ final JsonElement element = this.map.get(key); ++ if (element instanceof JsonPrimitive) { ++ final JsonPrimitive primitive = (JsonPrimitive)element; ++ if (primitive.isNumber()) { ++ return primitive.getAsNumber().byteValue() != 0; ++ } else if (primitive.isBoolean()) { ++ return primitive.getAsBoolean(); ++ } ++ } ++ ++ return dfl; ++ } ++ ++ @Override ++ public void setBoolean(final String key, final boolean val) { ++ this.map.addProperty(key, Boolean.valueOf(val)); ++ } ++ ++ @Override ++ public byte getByte(final String key) { ++ return this.getByte(key, (byte)0); ++ } ++ ++ @Override ++ public byte getByte(final String key, final byte dfl) { ++ final Number ret = this.getNumber(key, null); ++ return ret == null ? dfl : ret.byteValue(); ++ } ++ ++ @Override ++ public void setByte(final String key, final byte val) { ++ this.map.addProperty(key, Byte.valueOf(val)); ++ } ++ ++ @Override ++ public short getShort(final String key) { ++ return this.getShort(key, (short)0); ++ } ++ ++ @Override ++ public short getShort(final String key, final short dfl) { ++ final Number ret = this.getNumber(key, null); ++ return ret == null ? dfl : ret.shortValue(); ++ } ++ ++ @Override ++ public void setShort(final String key, final short val) { ++ this.map.addProperty(key, Short.valueOf(val)); ++ } ++ ++ @Override ++ public int getInt(final String key) { ++ return this.getInt(key, 0); ++ } ++ ++ @Override ++ public int getInt(final String key, final int dfl) { ++ final Number ret = this.getNumber(key, null); ++ return ret == null ? dfl : ret.intValue(); ++ } ++ ++ @Override ++ public void setInt(final String key, final int val) { ++ this.map.addProperty(key, Integer.valueOf(val)); ++ } ++ ++ @Override ++ public long getLong(final String key) { ++ return this.getLong(key, 0L); ++ } ++ ++ @Override ++ public long getLong(final String key, final long dfl) { ++ final Number ret = this.getNumber(key, null); ++ return ret == null ? dfl : ret.longValue(); ++ } ++ ++ @Override ++ public void setLong(final String key, final long val) { ++ this.map.addProperty(key, Long.valueOf(val)); ++ } ++ ++ @Override ++ public float getFloat(final String key) { ++ return this.getFloat(key, 0.0F); ++ } ++ ++ @Override ++ public float getFloat(final String key, final float dfl) { ++ final Number ret = this.getNumber(key, null); ++ return ret == null ? dfl : ret.floatValue(); ++ } ++ ++ @Override ++ public void setFloat(final String key, final float val) { ++ this.map.addProperty(key, Float.valueOf(val)); ++ } ++ ++ @Override ++ public double getDouble(final String key) { ++ return this.getDouble(key, 0.0D); ++ } ++ ++ @Override ++ public double getDouble(final String key, final double dfl) { ++ final Number ret = this.getNumber(key, null); ++ return ret == null ? dfl : ret.doubleValue(); ++ } ++ ++ @Override ++ public void setDouble(final String key, final double val) { ++ this.map.addProperty(key, Double.valueOf(val)); ++ } ++ ++ @Override ++ public byte[] getBytes(final String key) { ++ return this.getBytes(key, null); ++ } ++ ++ @Override ++ public byte[] getBytes(final String key, final byte[] dfl) { ++ return dfl; ++ } ++ ++ @Override ++ public void setBytes(final String key, final byte[] val) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public short[] getShorts(final String key) { ++ return this.getShorts(key, null); ++ } ++ ++ @Override ++ public short[] getShorts(final String key, final short[] dfl) { ++ return dfl; ++ } ++ ++ @Override ++ public void setShorts(final String key, final short[] val) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public int[] getInts(final String key) { ++ return this.getInts(key, null); ++ } ++ ++ @Override ++ public int[] getInts(final String key, final int[] dfl) { ++ return dfl; ++ } ++ ++ @Override ++ public void setInts(final String key, final int[] val) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public long[] getLongs(final String key) { ++ return this.getLongs(key, null); ++ } ++ ++ @Override ++ public long[] getLongs(final String key, final long[] dfl) { ++ return dfl; ++ } ++ ++ @Override ++ public void setLongs(final String key, final long[] val) { ++ // JSON does not support raw primitive arrays ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public ListType getListUnchecked(final String key) { ++ return this.getListUnchecked(key, null); ++ } ++ ++ @Override ++ public ListType getListUnchecked(final String key, final ListType dfl) { ++ final JsonElement element = this.map.get(key); ++ if (element instanceof JsonArray) { ++ return new JsonListType((JsonArray)element, this.compressed); ++ } ++ ++ return dfl; ++ } ++ ++ @Override ++ public void setList(final String key, final ListType val) { ++ this.map.add(key, ((JsonListType)val).getJson()); ++ } ++ ++ @Override ++ public MapType getMap(final String key) { ++ return this.getMap(key, null); ++ } ++ ++ @Override ++ public MapType getMap(final String key, final MapType dfl) { ++ final JsonElement element = this.map.get(key); ++ if (element instanceof JsonObject) { ++ return new JsonMapType((JsonObject)element, this.compressed); ++ } ++ ++ return dfl; ++ } ++ ++ @Override ++ public void setMap(final String key, final MapType val) { ++ this.map.add(key, ((JsonMapType)val).map); ++ } ++ ++ @Override ++ public String getString(final String key) { ++ return this.getString(key, null); ++ } ++ ++ @Override ++ public String getString(final String key, final String dfl) { ++ final JsonElement element = this.map.get(key); ++ if (element instanceof JsonPrimitive) { ++ final JsonPrimitive primitive = (JsonPrimitive)element; ++ if (primitive.isString()) { ++ return primitive.getAsString(); ++ } else if (this.compressed && primitive.isNumber()) { ++ return primitive.getAsString(); ++ } ++ } ++ ++ return dfl; ++ } ++ ++ @Override ++ public void setString(final String key, final String val) { ++ this.map.addProperty(key, val); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/types/json/JsonTypeCompressedUtil.java b/src/main/java/ca/spottedleaf/dataconverter/types/json/JsonTypeCompressedUtil.java +new file mode 100644 +index 0000000000000000000000000000000000000000..9c3093b66b847b5248bde923243fce78842bf67f +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/types/json/JsonTypeCompressedUtil.java +@@ -0,0 +1,18 @@ ++package ca.spottedleaf.dataconverter.types.json; ++ ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.TypeUtil; ++ ++public final class JsonTypeCompressedUtil implements TypeUtil { ++ ++ @Override ++ public ListType createEmptyList() { ++ return new JsonListType(true); ++ } ++ ++ @Override ++ public MapType createEmptyMap() { ++ return new JsonMapType(true); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/types/json/JsonTypeUtil.java b/src/main/java/ca/spottedleaf/dataconverter/types/json/JsonTypeUtil.java +new file mode 100644 +index 0000000000000000000000000000000000000000..9410ae68395a09c7710bdbb2ccc6acf6633cad23 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/types/json/JsonTypeUtil.java +@@ -0,0 +1,81 @@ ++package ca.spottedleaf.dataconverter.types.json; ++ ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.TypeUtil; ++import ca.spottedleaf.dataconverter.types.nbt.NBTListType; ++import ca.spottedleaf.dataconverter.types.nbt.NBTMapType; ++import com.google.gson.JsonArray; ++import com.google.gson.JsonElement; ++import com.google.gson.JsonNull; ++import com.google.gson.JsonObject; ++import com.google.gson.JsonPrimitive; ++import com.google.gson.internal.Streams; ++import com.google.gson.stream.JsonReader; ++import java.io.StringReader; ++import java.util.Map; ++ ++public final class JsonTypeUtil implements TypeUtil { ++ ++ @Override ++ public ListType createEmptyList() { ++ return new JsonListType(false); ++ } ++ ++ @Override ++ public MapType createEmptyMap() { ++ return new JsonMapType(false); ++ } ++ ++ public static T copyJson(final T from) { ++ // This is stupidly inefficient. However, deepCopy() is not exposed in this gson version. ++ final String out = from.toString(); ++ ++ return (T)Streams.parse(new JsonReader(new StringReader(out))); ++ } ++ ++ private static Object convertToGenericNBT(final JsonElement element, final boolean compressed) { ++ if (element instanceof JsonObject) { ++ return convertJsonToNBT(new JsonMapType((JsonObject)element, compressed)); ++ } else if (element instanceof JsonArray) { ++ return convertJsonToNBT(new JsonListType((JsonArray)element, compressed)); ++ } else if (element instanceof JsonNull) { ++ return null; ++ } else { ++ final JsonPrimitive primitive = (JsonPrimitive)element; ++ if (primitive.isBoolean()) { ++ return primitive.getAsBoolean() ? Byte.valueOf((byte)1) : Byte.valueOf((byte)0); ++ } else if (primitive.isNumber()) { ++ return primitive.getAsNumber(); ++ } else if (primitive.isString()) { ++ return primitive.getAsString(); ++ } ++ } ++ ++ throw new IllegalStateException("Unrecognized type " + element); ++ } ++ ++ public static NBTMapType convertJsonToNBT(final JsonMapType json) { ++ final NBTMapType ret = new NBTMapType(); ++ for (final Map.Entry entry : json.map.entrySet()) { ++ final Object obj = convertToGenericNBT(entry.getValue(), json.compressed); ++ if (obj == null) { ++ continue; ++ } ++ ++ ret.setGeneric(entry.getKey(), obj); ++ } ++ ++ return ret; ++ } ++ ++ public static NBTListType convertJsonToNBT(final JsonListType json) { ++ final NBTListType ret = new NBTListType(); ++ ++ for (int i = 0, len = json.size(); i < len; ++i) { ++ ret.addGeneric(convertToGenericNBT(json.array.get(i), json.compressed)); ++ } ++ ++ return ret; ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/types/nbt/NBTListType.java b/src/main/java/ca/spottedleaf/dataconverter/types/nbt/NBTListType.java +new file mode 100644 +index 0000000000000000000000000000000000000000..3a0187de2c7b455cd15419da08026d5d9f72d35b +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/types/nbt/NBTListType.java +@@ -0,0 +1,433 @@ ++package ca.spottedleaf.dataconverter.types.nbt; ++ ++import ca.spottedleaf.dataconverter.types.ObjectType; ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import net.minecraft.nbt.ByteArrayTag; ++import net.minecraft.nbt.ByteTag; ++import net.minecraft.nbt.CompoundTag; ++import net.minecraft.nbt.DoubleTag; ++import net.minecraft.nbt.FloatTag; ++import net.minecraft.nbt.IntArrayTag; ++import net.minecraft.nbt.IntTag; ++import net.minecraft.nbt.ListTag; ++import net.minecraft.nbt.LongArrayTag; ++import net.minecraft.nbt.LongTag; ++import net.minecraft.nbt.NumericTag; ++import net.minecraft.nbt.ShortTag; ++import net.minecraft.nbt.StringTag; ++import net.minecraft.nbt.Tag; ++ ++public final class NBTListType implements ListType { ++ ++ private final ListTag list; ++ ++ public NBTListType() { ++ this.list = new ListTag(); ++ } ++ ++ public NBTListType(final ListTag tag) { ++ this.list = tag; ++ } ++ ++ @Override ++ public boolean equals(final Object obj) { ++ if (this == obj) { ++ return true; ++ } ++ if (obj == null || obj.getClass() != NBTListType.class) { ++ return false; ++ } ++ ++ return this.list.equals(((NBTListType)obj).list); ++ } ++ ++ @Override ++ public int hashCode() { ++ return this.list.hashCode(); ++ } ++ ++ @Override ++ public String toString() { ++ return "NBTListType{" + ++ "list=" + this.list + ++ '}'; ++ } ++ ++ public ListTag getTag() { ++ return this.list; ++ } ++ ++ @Override ++ public ListType copy() { ++ return new NBTListType(this.list.copy()); ++ } ++ ++ protected static ObjectType getType(final byte id) { ++ switch (id) { ++ case 0: // END ++ return ObjectType.NONE; ++ case 1: // BYTE ++ return ObjectType.BYTE; ++ case 2: // SHORT ++ return ObjectType.SHORT; ++ case 3: // INT ++ return ObjectType.INT; ++ case 4: // LONG ++ return ObjectType.LONG; ++ case 5: // FLOAT ++ return ObjectType.FLOAT; ++ case 6: // DOUBLE ++ return ObjectType.DOUBLE; ++ case 7: // BYTE_ARRAY ++ return ObjectType.BYTE_ARRAY; ++ case 8: // STRING ++ return ObjectType.STRING; ++ case 9: // LIST ++ return ObjectType.LIST; ++ case 10: // COMPOUND ++ return ObjectType.MAP; ++ case 11: // INT_ARRAY ++ return ObjectType.INT_ARRAY; ++ case 12: // LONG_ARRAY ++ return ObjectType.LONG_ARRAY; ++ default: ++ throw new IllegalStateException("Unknown type: " + id); ++ } ++ } ++ ++ @Override ++ public ObjectType getType() { ++ return getType(this.list.getElementType()); ++ } ++ ++ @Override ++ public int size() { ++ return this.list.size(); ++ } ++ ++ @Override ++ public void remove(final int index) { ++ this.list.remove(index); ++ } ++ ++ @Override ++ public Number getNumber(final int index) { ++ final Tag tag = this.list.get(index); // does bound checking for us ++ if (!(tag instanceof NumericTag)) { ++ throw new IllegalStateException(); ++ } ++ return ((NumericTag)tag).getAsNumber(); ++ } ++ ++ @Override ++ public byte getByte(final int index) { ++ final Tag tag = this.list.get(index); // does bound checking for us ++ if (!(tag instanceof NumericTag)) { ++ throw new IllegalStateException(); ++ } ++ return ((NumericTag)tag).getAsByte(); ++ } ++ ++ @Override ++ public void setByte(final int index, final byte to) { ++ this.list.set(index, ByteTag.valueOf(to)); ++ } ++ ++ @Override ++ public short getShort(final int index) { ++ final Tag tag = this.list.get(index); // does bound checking for us ++ if (!(tag instanceof NumericTag)) { ++ throw new IllegalStateException(); ++ } ++ return ((NumericTag)tag).getAsShort(); ++ } ++ ++ @Override ++ public void setShort(final int index, final short to) { ++ this.list.set(index, ShortTag.valueOf(to)); ++ } ++ ++ @Override ++ public int getInt(final int index) { ++ final Tag tag = this.list.get(index); // does bound checking for us ++ if (!(tag instanceof NumericTag)) { ++ throw new IllegalStateException(); ++ } ++ return ((NumericTag)tag).getAsInt(); ++ } ++ ++ @Override ++ public void setInt(final int index, final int to) { ++ this.list.set(index, IntTag.valueOf(to)); ++ } ++ ++ @Override ++ public long getLong(final int index) { ++ final Tag tag = this.list.get(index); // does bound checking for us ++ if (!(tag instanceof NumericTag)) { ++ throw new IllegalStateException(); ++ } ++ return ((NumericTag)tag).getAsLong(); ++ } ++ ++ @Override ++ public void setLong(final int index, final long to) { ++ this.list.set(index, LongTag.valueOf(to)); ++ } ++ ++ @Override ++ public float getFloat(final int index) { ++ final Tag tag = this.list.get(index); // does bound checking for us ++ if (!(tag instanceof NumericTag)) { ++ throw new IllegalStateException(); ++ } ++ return ((NumericTag)tag).getAsFloat(); ++ } ++ ++ @Override ++ public void setFloat(final int index, final float to) { ++ this.list.set(index, FloatTag.valueOf(to)); ++ } ++ ++ @Override ++ public double getDouble(final int index) { ++ final Tag tag = this.list.get(index); // does bound checking for us ++ if (!(tag instanceof NumericTag)) { ++ throw new IllegalStateException(); ++ } ++ return ((NumericTag)tag).getAsDouble(); ++ } ++ ++ @Override ++ public void setDouble(final int index, final double to) { ++ this.list.set(index, DoubleTag.valueOf(to)); ++ } ++ ++ @Override ++ public byte[] getBytes(final int index) { ++ final Tag tag = this.list.get(index); // does bound checking for us ++ if (!(tag instanceof ByteArrayTag)) { ++ throw new IllegalStateException(); ++ } ++ return ((ByteArrayTag)tag).getAsByteArray(); ++ } ++ ++ @Override ++ public void setBytes(final int index, final byte[] to) { ++ this.list.set(index, new ByteArrayTag(to)); ++ } ++ ++ @Override ++ public short[] getShorts(final int index) { ++ // NBT does not support shorts ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void setShorts(final int index, final short[] to) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public int[] getInts(final int index) { ++ final Tag tag = this.list.get(index); // does bound checking for us ++ if (!(tag instanceof IntArrayTag)) { ++ throw new IllegalStateException(); ++ } ++ return ((IntArrayTag)tag).getAsIntArray(); ++ } ++ ++ @Override ++ public void setInts(final int index, final int[] to) { ++ this.list.set(index, new IntArrayTag(to)); ++ } ++ ++ @Override ++ public long[] getLongs(final int index) { ++ final Tag tag = this.list.get(index); // does bound checking for us ++ if (!(tag instanceof LongArrayTag)) { ++ throw new IllegalStateException(); ++ } ++ return ((LongArrayTag)tag).getAsLongArray(); ++ } ++ ++ @Override ++ public void setLongs(final int index, final long[] to) { ++ this.list.set(index, new LongArrayTag(to)); ++ } ++ ++ @Override ++ public ListType getList(final int index) { ++ final Tag tag = this.list.get(index); // does bound checking for us ++ if (!(tag instanceof ListTag)) { ++ throw new IllegalStateException(); ++ } ++ return new NBTListType((ListTag)tag); ++ } ++ ++ @Override ++ public void setList(final int index, final ListType list) { ++ this.list.set(index, ((NBTListType)list).getTag()); ++ } ++ ++ @Override ++ public MapType getMap(final int index) { ++ final Tag tag = this.list.get(index); // does bound checking for us ++ if (!(tag instanceof CompoundTag)) { ++ throw new IllegalStateException(); ++ } ++ return new NBTMapType((CompoundTag)tag); ++ } ++ ++ @Override ++ public void setMap(final int index, final MapType to) { ++ this.list.set(index, ((NBTMapType)to).getTag()); ++ } ++ ++ @Override ++ public String getString(final int index) { ++ final Tag tag = this.list.get(index); // does bound checking for us ++ if (!(tag instanceof StringTag)) { ++ throw new IllegalStateException(); ++ } ++ return ((StringTag)tag).getAsString(); ++ } ++ ++ @Override ++ public void setString(final int index, final String to) { ++ this.list.set(index, StringTag.valueOf(to)); ++ } ++ ++ @Override ++ public void addByte(final byte b) { ++ this.list.add(ByteTag.valueOf(b)); ++ } ++ ++ @Override ++ public void addByte(final int index, final byte b) { ++ this.list.add(index, ByteTag.valueOf(b)); ++ } ++ ++ @Override ++ public void addShort(final short s) { ++ this.list.add(ShortTag.valueOf(s)); ++ } ++ ++ @Override ++ public void addShort(final int index, final short s) { ++ this.list.add(index, ShortTag.valueOf(s)); ++ } ++ ++ @Override ++ public void addInt(final int i) { ++ this.list.add(IntTag.valueOf(i)); ++ } ++ ++ @Override ++ public void addInt(final int index, final int i) { ++ this.list.add(index, IntTag.valueOf(i)); ++ } ++ ++ @Override ++ public void addLong(final long l) { ++ this.list.add(LongTag.valueOf(l)); ++ } ++ ++ @Override ++ public void addLong(final int index, final long l) { ++ this.list.add(index, LongTag.valueOf(l)); ++ } ++ ++ @Override ++ public void addFloat(final float f) { ++ this.list.add(FloatTag.valueOf(f)); ++ } ++ ++ @Override ++ public void addFloat(final int index, final float f) { ++ this.list.add(index, FloatTag.valueOf(f)); ++ } ++ ++ @Override ++ public void addDouble(final double d) { ++ this.list.add(DoubleTag.valueOf(d)); ++ } ++ ++ @Override ++ public void addDouble(final int index, final double d) { ++ this.list.add(index, DoubleTag.valueOf(d)); ++ } ++ ++ @Override ++ public void addByteArray(final byte[] arr) { ++ this.list.add(new ByteArrayTag(arr)); ++ } ++ ++ @Override ++ public void addByteArray(final int index, final byte[] arr) { ++ this.list.add(index, new ByteArrayTag(arr)); ++ } ++ ++ @Override ++ public void addShortArray(final short[] arr) { ++ // NBT does not support short[] ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void addShortArray(final int index, final short[] arr) { ++ // NBT does not support short[] ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void addIntArray(final int[] arr) { ++ this.list.add(new IntArrayTag(arr)); ++ } ++ ++ @Override ++ public void addIntArray(final int index, final int[] arr) { ++ this.list.add(index, new IntArrayTag(arr)); ++ } ++ ++ @Override ++ public void addLongArray(final long[] arr) { ++ this.list.add(new LongArrayTag(arr)); ++ } ++ ++ @Override ++ public void addLongArray(final int index, final long[] arr) { ++ this.list.add(index, new LongArrayTag(arr)); ++ } ++ ++ @Override ++ public void addList(final ListType list) { ++ this.list.add(((NBTListType)list).getTag()); ++ } ++ ++ @Override ++ public void addList(final int index, final ListType list) { ++ this.list.add(index, ((NBTListType)list).getTag()); ++ } ++ ++ @Override ++ public void addMap(final MapType map) { ++ this.list.add(((NBTMapType)map).getTag()); ++ } ++ ++ @Override ++ public void addMap(final int index, final MapType map) { ++ this.list.add(index, ((NBTMapType)map).getTag()); ++ } ++ ++ @Override ++ public void addString(final String string) { ++ this.list.add(StringTag.valueOf(string)); ++ } ++ ++ @Override ++ public void addString(final int index, final String string) { ++ this.list.add(index, StringTag.valueOf(string)); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/types/nbt/NBTMapType.java b/src/main/java/ca/spottedleaf/dataconverter/types/nbt/NBTMapType.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d31d33ea82bdf86da69c0b7114d0033210567289 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/types/nbt/NBTMapType.java +@@ -0,0 +1,433 @@ ++package ca.spottedleaf.dataconverter.types.nbt; ++ ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.ObjectType; ++import net.minecraft.nbt.ByteArrayTag; ++import net.minecraft.nbt.CompoundTag; ++import net.minecraft.nbt.IntArrayTag; ++import net.minecraft.nbt.ListTag; ++import net.minecraft.nbt.LongArrayTag; ++import net.minecraft.nbt.NumericTag; ++import net.minecraft.nbt.StringTag; ++import net.minecraft.nbt.Tag; ++ ++import java.util.Set; ++ ++public final class NBTMapType implements MapType { ++ ++ private final CompoundTag map; ++ ++ public NBTMapType() { ++ this.map = new CompoundTag(); ++ } ++ ++ public NBTMapType(final CompoundTag tag) { ++ this.map = tag; ++ } ++ ++ @Override ++ public boolean equals(final Object obj) { ++ if (this == obj) { ++ return true; ++ } ++ if (obj == null || obj.getClass() != NBTMapType.class) { ++ return false; ++ } ++ ++ return this.map.equals(((NBTMapType)obj).map); ++ } ++ ++ @Override ++ public int hashCode() { ++ return this.map.hashCode(); ++ } ++ ++ @Override ++ public String toString() { ++ return "NBTMapType{" + ++ "map=" + this.map + ++ '}'; ++ } ++ ++ @Override ++ public int size() { ++ return this.map.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return this.map.isEmpty(); ++ } ++ ++ @Override ++ public void clear() { ++ this.map.getAllKeys().clear(); ++ } ++ ++ @Override ++ public Set keys() { ++ return this.map.getAllKeys(); ++ } ++ ++ public CompoundTag getTag() { ++ return this.map; ++ } ++ ++ @Override ++ public MapType copy() { ++ return new NBTMapType(this.map.copy()); ++ } ++ ++ @Override ++ public boolean hasKey(final String key) { ++ return this.map.get(key) != null; ++ } ++ ++ @Override ++ public boolean hasKey(final String key, final ObjectType type) { ++ final Tag tag = this.map.get(key); ++ if (tag == null) { ++ return false; ++ } ++ ++ final ObjectType valueType = NBTListType.getType(tag.getId()); ++ ++ return valueType == type || (type == ObjectType.NUMBER && valueType.isNumber()); ++ } ++ ++ @Override ++ public void remove(final String key) { ++ this.map.remove(key); ++ } ++ ++ @Override ++ public Object getGeneric(final String key) { ++ final Tag tag = this.map.get(key); ++ if (tag == null) { ++ return null; ++ } ++ ++ switch (NBTListType.getType(tag.getId())) { ++ case BYTE: ++ case SHORT: ++ case INT: ++ case LONG: ++ case FLOAT: ++ case DOUBLE: ++ return ((NumericTag)tag).getAsNumber(); ++ case MAP: ++ return new NBTMapType((CompoundTag)tag); ++ case LIST: ++ return new NBTListType((ListTag)tag); ++ case STRING: ++ return ((StringTag)tag).getAsString(); ++ case BYTE_ARRAY: ++ return ((ByteArrayTag)tag).getAsByteArray(); ++ // Note: No short array tag! ++ case INT_ARRAY: ++ return ((IntArrayTag)tag).getAsIntArray(); ++ case LONG_ARRAY: ++ return ((LongArrayTag)tag).getAsLongArray(); ++ } ++ ++ throw new IllegalStateException("Unrecognized type " + tag); ++ } ++ ++ @Override ++ public Number getNumber(final String key) { ++ return this.getNumber(key, null); ++ } ++ ++ @Override ++ public Number getNumber(final String key, final Number dfl) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof NumericTag) { ++ return ((NumericTag)tag).getAsNumber(); ++ } ++ return dfl; ++ } ++ ++ @Override ++ public boolean getBoolean(final String key) { ++ return this.getByte(key) != 0; ++ } ++ ++ @Override ++ public boolean getBoolean(final String key, final boolean dfl) { ++ return this.getByte(key, dfl ? (byte)1 : (byte)0) != 0; ++ } ++ ++ @Override ++ public void setBoolean(final String key, final boolean val) { ++ this.setByte(key, val ? (byte)1 : (byte)0); ++ } ++ ++ @Override ++ public byte getByte(final String key) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof NumericTag) { ++ return ((NumericTag)tag).getAsByte(); ++ } ++ return 0; ++ } ++ ++ @Override ++ public byte getByte(final String key, final byte dfl) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof NumericTag) { ++ return ((NumericTag)tag).getAsByte(); ++ } ++ return dfl; ++ } ++ ++ @Override ++ public void setByte(final String key, final byte val) { ++ this.map.putByte(key, val); ++ } ++ ++ @Override ++ public short getShort(final String key) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof NumericTag) { ++ return ((NumericTag)tag).getAsShort(); ++ } ++ return 0; ++ } ++ ++ @Override ++ public short getShort(final String key, final short dfl) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof NumericTag) { ++ return ((NumericTag)tag).getAsShort(); ++ } ++ return dfl; ++ } ++ ++ @Override ++ public void setShort(final String key, final short val) { ++ this.map.putShort(key, val); ++ } ++ ++ @Override ++ public int getInt(final String key) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof NumericTag) { ++ return ((NumericTag)tag).getAsInt(); ++ } ++ return 0; ++ } ++ ++ @Override ++ public int getInt(final String key, final int dfl) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof NumericTag) { ++ return ((NumericTag)tag).getAsInt(); ++ } ++ return dfl; ++ } ++ ++ @Override ++ public void setInt(final String key, final int val) { ++ this.map.putInt(key, val); ++ } ++ ++ @Override ++ public long getLong(final String key) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof NumericTag) { ++ return ((NumericTag)tag).getAsLong(); ++ } ++ return 0; ++ } ++ ++ @Override ++ public long getLong(final String key, final long dfl) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof NumericTag) { ++ return ((NumericTag)tag).getAsLong(); ++ } ++ return dfl; ++ } ++ ++ @Override ++ public void setLong(final String key, final long val) { ++ this.map.putLong(key, val); ++ } ++ ++ @Override ++ public float getFloat(final String key) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof NumericTag) { ++ return ((NumericTag)tag).getAsFloat(); ++ } ++ return 0; ++ } ++ ++ @Override ++ public float getFloat(final String key, final float dfl) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof NumericTag) { ++ return ((NumericTag)tag).getAsFloat(); ++ } ++ return dfl; ++ } ++ ++ @Override ++ public void setFloat(final String key, final float val) { ++ this.map.putFloat(key, val); ++ } ++ ++ @Override ++ public double getDouble(final String key) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof NumericTag) { ++ return ((NumericTag)tag).getAsDouble(); ++ } ++ return 0; ++ } ++ ++ @Override ++ public double getDouble(final String key, final double dfl) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof NumericTag) { ++ return ((NumericTag)tag).getAsDouble(); ++ } ++ return dfl; ++ } ++ ++ @Override ++ public void setDouble(final String key, final double val) { ++ this.map.putDouble(key, val); ++ } ++ ++ @Override ++ public byte[] getBytes(final String key) { ++ return this.getBytes(key, null); ++ } ++ ++ @Override ++ public byte[] getBytes(final String key, final byte[] dfl) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof ByteArrayTag) { ++ return ((ByteArrayTag)tag).getAsByteArray(); ++ } ++ return dfl; ++ } ++ ++ @Override ++ public void setBytes(final String key, final byte[] val) { ++ this.map.putByteArray(key, val); ++ } ++ ++ @Override ++ public short[] getShorts(final String key) { ++ return this.getShorts(key, null); ++ } ++ ++ @Override ++ public short[] getShorts(final String key, final short[] dfl) { ++ // NBT does not support short array ++ return dfl; ++ } ++ ++ @Override ++ public void setShorts(final String key, final short[] val) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public int[] getInts(final String key) { ++ return this.getInts(key, null); ++ } ++ ++ @Override ++ public int[] getInts(final String key, final int[] dfl) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof IntArrayTag) { ++ return ((IntArrayTag)tag).getAsIntArray(); ++ } ++ return dfl; ++ } ++ ++ @Override ++ public void setInts(final String key, final int[] val) { ++ this.map.putIntArray(key, val); ++ } ++ ++ @Override ++ public long[] getLongs(final String key) { ++ return this.getLongs(key, null); ++ } ++ ++ @Override ++ public long[] getLongs(final String key, final long[] dfl) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof LongArrayTag) { ++ return ((LongArrayTag)tag).getAsLongArray(); ++ } ++ return dfl; ++ } ++ ++ @Override ++ public void setLongs(final String key, final long[] val) { ++ this.map.putLongArray(key, val); ++ } ++ ++ @Override ++ public ListType getListUnchecked(final String key) { ++ return this.getListUnchecked(key, null); ++ } ++ ++ @Override ++ public ListType getListUnchecked(final String key, final ListType dfl) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof ListTag) { ++ return new NBTListType((ListTag)tag); ++ } ++ return dfl; ++ } ++ ++ @Override ++ public void setList(final String key, final ListType val) { ++ this.map.put(key, ((NBTListType)val).getTag()); ++ } ++ ++ @Override ++ public MapType getMap(final String key) { ++ return this.getMap(key, null); ++ } ++ ++ @Override ++ public MapType getMap(final String key, final MapType dfl) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof CompoundTag) { ++ return new NBTMapType((CompoundTag)tag); ++ } ++ return dfl; ++ } ++ ++ @Override ++ public void setMap(final String key, final MapType val) { ++ this.map.put(key, ((NBTMapType)val).getTag()); ++ } ++ ++ @Override ++ public String getString(final String key) { ++ return this.getString(key, null); ++ } ++ ++ @Override ++ public String getString(final String key, final String dfl) { ++ final Tag tag = this.map.get(key); ++ if (tag instanceof StringTag) { ++ return ((StringTag)tag).getAsString(); ++ } ++ return dfl; ++ } ++ ++ @Override ++ public void setString(final String key, final String val) { ++ this.map.putString(key, val); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/types/nbt/NBTTypeUtil.java b/src/main/java/ca/spottedleaf/dataconverter/types/nbt/NBTTypeUtil.java +new file mode 100644 +index 0000000000000000000000000000000000000000..62c0f4073aff301bf5b3187e0d4446fd8d0ac475 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/types/nbt/NBTTypeUtil.java +@@ -0,0 +1,18 @@ ++package ca.spottedleaf.dataconverter.types.nbt; ++ ++import ca.spottedleaf.dataconverter.types.ListType; ++import ca.spottedleaf.dataconverter.types.MapType; ++import ca.spottedleaf.dataconverter.types.TypeUtil; ++ ++public final class NBTTypeUtil implements TypeUtil { ++ ++ @Override ++ public ListType createEmptyList() { ++ return new NBTListType(); ++ } ++ ++ @Override ++ public MapType createEmptyMap() { ++ return new NBTMapType(); ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/util/Int2IntArraySortedMap.java b/src/main/java/ca/spottedleaf/dataconverter/util/Int2IntArraySortedMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6596de3d9ebae583c252aa061f0cfdf8778ea1a5 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/util/Int2IntArraySortedMap.java +@@ -0,0 +1,77 @@ ++package ca.spottedleaf.dataconverter.util; ++ ++import it.unimi.dsi.fastutil.ints.Int2IntFunction; ++ ++import java.util.Arrays; ++ ++public class Int2IntArraySortedMap { ++ ++ protected int[] key; ++ protected int[] val; ++ protected int size; ++ ++ public Int2IntArraySortedMap() { ++ this.key = new int[8]; ++ this.val = new int[8]; ++ } ++ ++ public int put(final int key, final int value) { ++ final int index = Arrays.binarySearch(this.key, 0, this.size, key); ++ if (index >= 0) { ++ final int current = this.val[index]; ++ this.val[index] = value; ++ return current; ++ } ++ final int insert = -(index + 1); ++ // shift entries down ++ if (this.size >= this.val.length) { ++ this.key = Arrays.copyOf(this.key, this.key.length * 2); ++ this.val = Arrays.copyOf(this.val, this.val.length * 2); ++ } ++ System.arraycopy(this.key, insert, this.key, insert + 1, this.size - insert); ++ System.arraycopy(this.val, insert, this.val, insert + 1, this.size - insert); ++ ++this.size; ++ ++ this.key[insert] = key; ++ this.val[insert] = value; ++ ++ return 0; ++ } ++ ++ public int computeIfAbsent(final int key, final Int2IntFunction producer) { ++ final int index = Arrays.binarySearch(this.key, 0, this.size, key); ++ if (index >= 0) { ++ return this.val[index]; ++ } ++ final int insert = -(index + 1); ++ // shift entries down ++ if (this.size >= this.val.length) { ++ this.key = Arrays.copyOf(this.key, this.key.length * 2); ++ this.val = Arrays.copyOf(this.val, this.val.length * 2); ++ } ++ System.arraycopy(this.key, insert, this.key, insert + 1, this.size - insert); ++ System.arraycopy(this.val, insert, this.val, insert + 1, this.size - insert); ++ ++this.size; ++ ++ this.key[insert] = key; ++ ++ return this.val[insert] = producer.apply(key); ++ } ++ ++ public int get(final int key) { ++ final int index = Arrays.binarySearch(this.key, 0, this.size, key); ++ if (index < 0) { ++ return 0; ++ } ++ return this.val[index]; ++ } ++ ++ public int getFloor(final int key) { ++ final int index = Arrays.binarySearch(this.key, 0, this.size, key); ++ if (index < 0) { ++ final int insert = -(index + 1) - 1; ++ return insert < 0 ? 0 : this.val[insert]; ++ } ++ return this.val[index]; ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/util/Int2ObjectArraySortedMap.java b/src/main/java/ca/spottedleaf/dataconverter/util/Int2ObjectArraySortedMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..de9d632489609136c712a9adaee941fd38fad440 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/util/Int2ObjectArraySortedMap.java +@@ -0,0 +1,74 @@ ++package ca.spottedleaf.dataconverter.util; ++ ++import java.util.Arrays; ++import java.util.function.IntFunction; ++ ++public class Int2ObjectArraySortedMap { ++ ++ protected int[] key; ++ protected V[] val; ++ protected int size; ++ ++ public Int2ObjectArraySortedMap() { ++ this.key = new int[8]; ++ this.val = (V[])new Object[8]; ++ } ++ ++ public V put(final int key, final V value) { ++ final int index = Arrays.binarySearch(this.key, 0, this.size, key); ++ if (index >= 0) { ++ final V current = this.val[index]; ++ this.val[index] = value; ++ return current; ++ } ++ final int insert = -(index + 1); ++ // shift entries down ++ if (this.size >= this.val.length) { ++ this.key = Arrays.copyOf(this.key, this.key.length * 2); ++ this.val = Arrays.copyOf(this.val, this.val.length * 2); ++ } ++ System.arraycopy(this.key, insert, this.key, insert + 1, this.size - insert); ++ System.arraycopy(this.val, insert, this.val, insert + 1, this.size - insert); ++ ++ this.key[insert] = key; ++ this.val[insert] = value; ++ ++ return null; ++ } ++ ++ public V computeIfAbsent(final int key, final IntFunction producer) { ++ final int index = Arrays.binarySearch(this.key, 0, this.size, key); ++ if (index >= 0) { ++ return this.val[index]; ++ } ++ final int insert = -(index + 1); ++ // shift entries down ++ if (this.size >= this.val.length) { ++ this.key = Arrays.copyOf(this.key, this.key.length * 2); ++ this.val = Arrays.copyOf(this.val, this.val.length * 2); ++ } ++ System.arraycopy(this.key, insert, this.key, insert + 1, this.size - insert); ++ System.arraycopy(this.val, insert, this.val, insert + 1, this.size - insert); ++ ++ this.key[insert] = key; ++ ++ return this.val[insert] = producer.apply(key); ++ } ++ ++ public V get(final int key) { ++ final int index = Arrays.binarySearch(this.key, 0, this.size, key); ++ if (index < 0) { ++ return null; ++ } ++ return this.val[index]; ++ } ++ ++ public V getFloor(final int key) { ++ final int index = Arrays.binarySearch(this.key, 0, this.size, key); ++ if (index < 0) { ++ final int insert = -(index + 1); ++ return this.val[insert]; ++ } ++ return this.val[index]; ++ } ++} +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 +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/util/Long2IntArraySortedMap.java +@@ -0,0 +1,76 @@ ++package ca.spottedleaf.dataconverter.util; ++ ++import it.unimi.dsi.fastutil.longs.Long2IntFunction; ++import java.util.Arrays; ++ ++public class Long2IntArraySortedMap { ++ ++ protected long[] key; ++ protected int[] val; ++ protected int size; ++ ++ public Long2IntArraySortedMap() { ++ this.key = new long[8]; ++ this.val = new int[8]; ++ } ++ ++ public int put(final long key, final int value) { ++ final int index = Arrays.binarySearch(this.key, 0, this.size, key); ++ if (index >= 0) { ++ final int current = this.val[index]; ++ this.val[index] = value; ++ return current; ++ } ++ final int insert = -(index + 1); ++ // shift entries down ++ if (this.size >= this.val.length) { ++ this.key = Arrays.copyOf(this.key, this.key.length * 2); ++ this.val = Arrays.copyOf(this.val, this.val.length * 2); ++ } ++ System.arraycopy(this.key, insert, this.key, insert + 1, this.size - insert); ++ System.arraycopy(this.val, insert, this.val, insert + 1, this.size - insert); ++ ++this.size; ++ ++ this.key[insert] = key; ++ this.val[insert] = value; ++ ++ return 0; ++ } ++ ++ public int computeIfAbsent(final long key, final Long2IntFunction producer) { ++ final int index = Arrays.binarySearch(this.key, 0, this.size, key); ++ if (index >= 0) { ++ return this.val[index]; ++ } ++ final int insert = -(index + 1); ++ // shift entries down ++ if (this.size >= this.val.length) { ++ this.key = Arrays.copyOf(this.key, this.key.length * 2); ++ this.val = Arrays.copyOf(this.val, this.val.length * 2); ++ } ++ System.arraycopy(this.key, insert, this.key, insert + 1, this.size - insert); ++ System.arraycopy(this.val, insert, this.val, insert + 1, this.size - insert); ++ ++this.size; ++ ++ this.key[insert] = key; ++ ++ return this.val[insert] = producer.apply(key); ++ } ++ ++ public int get(final long key) { ++ final int index = Arrays.binarySearch(this.key, 0, this.size, key); ++ if (index < 0) { ++ return 0; ++ } ++ return this.val[index]; ++ } ++ ++ public int getFloor(final long key) { ++ final int index = Arrays.binarySearch(this.key, 0, this.size, key); ++ if (index < 0) { ++ final int insert = -(index + 1) - 1; ++ return insert < 0 ? 0 : this.val[insert]; ++ } ++ return this.val[index]; ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/util/Long2ObjectArraySortedMap.java b/src/main/java/ca/spottedleaf/dataconverter/util/Long2ObjectArraySortedMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6f634c8825589a23f46ad7b54354475c9a95bd1b +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/util/Long2ObjectArraySortedMap.java +@@ -0,0 +1,76 @@ ++package ca.spottedleaf.dataconverter.util; ++ ++import java.util.Arrays; ++import java.util.function.LongFunction; ++ ++public class Long2ObjectArraySortedMap { ++ ++ protected long[] key; ++ protected V[] val; ++ protected int size; ++ ++ public Long2ObjectArraySortedMap() { ++ this.key = new long[8]; ++ this.val = (V[])new Object[8]; ++ } ++ ++ public V put(final long key, final V value) { ++ final int index = Arrays.binarySearch(this.key, 0, this.size, key); ++ if (index >= 0) { ++ final V current = this.val[index]; ++ this.val[index] = value; ++ return current; ++ } ++ final int insert = -(index + 1); ++ // shift entries down ++ if (this.size >= this.val.length) { ++ this.key = Arrays.copyOf(this.key, this.key.length * 2); ++ this.val = Arrays.copyOf(this.val, this.val.length * 2); ++ } ++ System.arraycopy(this.key, insert, this.key, insert + 1, this.size - insert); ++ System.arraycopy(this.val, insert, this.val, insert + 1, this.size - insert); ++ ++this.size; ++ ++ this.key[insert] = key; ++ this.val[insert] = value; ++ ++ return null; ++ } ++ ++ public V computeIfAbsent(final long key, final LongFunction producer) { ++ final int index = Arrays.binarySearch(this.key, 0, this.size, key); ++ if (index >= 0) { ++ return this.val[index]; ++ } ++ final int insert = -(index + 1); ++ // shift entries down ++ if (this.size >= this.val.length) { ++ this.key = Arrays.copyOf(this.key, this.key.length * 2); ++ this.val = Arrays.copyOf(this.val, this.val.length * 2); ++ } ++ System.arraycopy(this.key, insert, this.key, insert + 1, this.size - insert); ++ System.arraycopy(this.val, insert, this.val, insert + 1, this.size - insert); ++ ++this.size; ++ ++ this.key[insert] = key; ++ ++ return this.val[insert] = producer.apply(key); ++ } ++ ++ public V get(final long key) { ++ final int index = Arrays.binarySearch(this.key, 0, this.size, key); ++ if (index < 0) { ++ return null; ++ } ++ return this.val[index]; ++ } ++ ++ public V getFloor(final long key) { ++ final int index = Arrays.binarySearch(this.key, 0, this.size, key); ++ if (index < 0) { ++ final int insert = -(index + 1) - 1; ++ return insert < 0 ? null : this.val[insert]; ++ } ++ return this.val[index]; ++ } ++} +diff --git a/src/main/java/ca/spottedleaf/dataconverter/util/NamespaceUtil.java b/src/main/java/ca/spottedleaf/dataconverter/util/NamespaceUtil.java +new file mode 100644 +index 0000000000000000000000000000000000000000..967ad1186cbc81a76a4958ea99d4eff37c15b48f +--- /dev/null ++++ b/src/main/java/ca/spottedleaf/dataconverter/util/NamespaceUtil.java +@@ -0,0 +1,24 @@ ++package ca.spottedleaf.dataconverter.util; ++ ++import net.minecraft.resources.ResourceLocation; ++ ++public final class NamespaceUtil { ++ ++ private NamespaceUtil() {} ++ ++ public static String correctNamespace(final String value) { ++ if (value == null) { ++ return null; ++ } ++ final ResourceLocation resourceLocation = ResourceLocation.tryParse(value); ++ return resourceLocation != null ? resourceLocation.toString() : value; ++ } ++ ++ public static String correctNamespaceOrNull(final String value) { ++ if (value == null) { ++ return null; ++ } ++ final String correct = correctNamespace(value); ++ 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; + + // 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 396c34c0866bf395b4d86361d96fe103c5d9ae7e..05af8f292a598a242a396afe4c2ddb2aa2c6b42e 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 +@@ -128,7 +128,7 @@ public class EntityStorage implements EntityPersistentStorage { + + private CompoundTag upgradeChunkTag(CompoundTag chunkTag) { + int i = 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 + } + + 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; ++ ++ 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; diff --git a/patches/server/0799-Use-Velocity-compression-and-cipher-natives.patch b/patches/server/0799-Use-Velocity-compression-and-cipher-natives.patch new file mode 100644 index 0000000000..6bc0af58c3 --- /dev/null +++ b/patches/server/0799-Use-Velocity-compression-and-cipher-natives.patch @@ -0,0 +1,371 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrew Steinborn +Date: Mon, 26 Jul 2021 02:15:17 -0400 +Subject: [PATCH] Use Velocity compression and cipher natives + + +diff --git a/build.gradle.kts b/build.gradle.kts +index 1876481a830228fd15fcd2a4be5f2c28bd7e51fe..b3687f632bbf06c933a6ef04dc2236ccf3c030b8 100644 +--- a/build.gradle.kts ++++ b/build.gradle.kts +@@ -25,6 +25,7 @@ repositories { + } + } + // Paper end ++ maven("https://repo.velocitypowered.com/snapshots/") // Paper + } + + dependencies { +@@ -62,6 +63,7 @@ dependencies { + implementation("io.netty:netty-all:4.1.65.Final") // Paper + + implementation("org.quiltmc:tiny-mappings-parser:0.3.0") // Paper - needed to read mappings for stacktrace deobfuscation ++ implementation("com.velocitypowered:velocity-native:1.1.0-SNAPSHOT") // Paper + + testImplementation("io.github.classgraph:classgraph:4.8.47") // Paper - mob goal test + testImplementation("junit:junit:4.13.1") +diff --git a/src/main/java/net/minecraft/network/CipherDecoder.java b/src/main/java/net/minecraft/network/CipherDecoder.java +index 06d545bc7206dd0d56cf27c31935c0f5ed21ef08..201e5eb2d0ec985ea60c59ac89593bd88ad3ee4d 100644 +--- a/src/main/java/net/minecraft/network/CipherDecoder.java ++++ b/src/main/java/net/minecraft/network/CipherDecoder.java +@@ -7,14 +7,30 @@ import java.util.List; + import javax.crypto.Cipher; + + public class CipherDecoder extends MessageToMessageDecoder { +- private final CipherBase cipher; ++ private final com.velocitypowered.natives.encryption.VelocityCipher cipher; // Paper + +- public CipherDecoder(Cipher cipher) { +- this.cipher = new CipherBase(cipher); ++ public CipherDecoder(com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper ++ this.cipher = cipher; // Paper + } + + @Override + protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List list) throws Exception { +- list.add(this.cipher.decipher(channelHandlerContext, byteBuf)); ++ // Paper start ++ ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), cipher, byteBuf); ++ try { ++ cipher.process(compatible); ++ list.add(compatible); ++ } catch (Exception e) { ++ compatible.release(); // compatible will never be used if we throw an exception ++ throw e; ++ } ++ // Paper end + } ++ ++ // Paper start ++ @Override ++ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { ++ cipher.close(); ++ } ++ // Paper end + } +diff --git a/src/main/java/net/minecraft/network/CipherEncoder.java b/src/main/java/net/minecraft/network/CipherEncoder.java +index 50a7058b18a8ca05363b73eaefbd812ef50d53f1..7319da27dfa5029c9893ec04bfd4325531af8a98 100644 +--- a/src/main/java/net/minecraft/network/CipherEncoder.java ++++ b/src/main/java/net/minecraft/network/CipherEncoder.java +@@ -4,16 +4,33 @@ import io.netty.buffer.ByteBuf; + import io.netty.channel.ChannelHandlerContext; + import io.netty.handler.codec.MessageToByteEncoder; + import javax.crypto.Cipher; ++import java.util.List; + +-public class CipherEncoder extends MessageToByteEncoder { +- private final CipherBase cipher; ++public class CipherEncoder extends io.netty.handler.codec.MessageToMessageEncoder { // Paper - change superclass ++ private final com.velocitypowered.natives.encryption.VelocityCipher cipher; // Paper + +- public CipherEncoder(Cipher cipher) { +- this.cipher = new CipherBase(cipher); ++ public CipherEncoder(com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper ++ this.cipher = cipher; // Paper + } + + @Override +- protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) throws Exception { +- this.cipher.encipher(byteBuf, byteBuf2); ++ protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List list) throws Exception { ++ // Paper start ++ ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), cipher, byteBuf); ++ try { ++ cipher.process(compatible); ++ list.add(compatible); ++ } catch (Exception e) { ++ compatible.release(); // compatible will never be used if we throw an exception ++ throw e; ++ } ++ // Paper end + } ++ ++ // Paper start ++ @Override ++ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { ++ cipher.close(); ++ } ++ // Paper end + } +diff --git a/src/main/java/net/minecraft/network/CompressionDecoder.java b/src/main/java/net/minecraft/network/CompressionDecoder.java +index efd05c8c1114aab4c237ccbc2e4e935a08c076ee..3569fc49e87b773dce0e59b9069797834ae32613 100644 +--- a/src/main/java/net/minecraft/network/CompressionDecoder.java ++++ b/src/main/java/net/minecraft/network/CompressionDecoder.java +@@ -12,13 +12,20 @@ public class CompressionDecoder extends ByteToMessageDecoder { + public static final int MAXIMUM_COMPRESSED_LENGTH = 2097152; + public static final int MAXIMUM_UNCOMPRESSED_LENGTH = 8388608; + private final Inflater inflater; ++ private final com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper + private int threshold; + private boolean validateDecompressed; + ++ // Paper start + public CompressionDecoder(int compressionThreshold, boolean bl) { ++ this(null, compressionThreshold, bl); ++ } ++ public CompressionDecoder(com.velocitypowered.natives.compression.VelocityCompressor compressor, int compressionThreshold, boolean bl) { + this.threshold = compressionThreshold; + this.validateDecompressed = bl; +- this.inflater = new Inflater(); ++ this.inflater = compressor == null ? new Inflater() : null; ++ this.compressor = compressor; ++ // Paper end + } + + @Override +@@ -39,6 +46,8 @@ public class CompressionDecoder extends ByteToMessageDecoder { + } + } + ++ // Paper start ++ if (this.inflater != null) { + byte[] bs = new byte[friendlyByteBuf.readableBytes()]; + friendlyByteBuf.readBytes(bs); + this.inflater.setInput(bs); +@@ -46,10 +55,36 @@ public class CompressionDecoder extends ByteToMessageDecoder { + this.inflater.inflate(cs); + list.add(Unpooled.wrappedBuffer(cs)); + this.inflater.reset(); ++ return; ++ } ++ ++ int claimedUncompressedSize = i; // OBFHELPER ++ ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), this.compressor, byteBuf); ++ ByteBuf uncompressed = com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(channelHandlerContext.alloc(), this.compressor, claimedUncompressedSize); ++ try { ++ this.compressor.inflate(compatibleIn, uncompressed, claimedUncompressedSize); ++ list.add(uncompressed); ++ byteBuf.clear(); ++ } catch (Exception e) { ++ uncompressed.release(); ++ throw e; ++ } finally { ++ compatibleIn.release(); ++ } ++ // Paper end + } + } + } + ++ // Paper start ++ @Override ++ public void handlerRemoved0(ChannelHandlerContext ctx) throws Exception { ++ if (this.compressor != null) { ++ this.compressor.close(); ++ } ++ } ++ // Paper end ++ + public void setThreshold(int compressionThreshold, boolean bl) { + this.threshold = compressionThreshold; + this.validateDecompressed = bl; +diff --git a/src/main/java/net/minecraft/network/CompressionEncoder.java b/src/main/java/net/minecraft/network/CompressionEncoder.java +index 524c0c674f63cfcb601416a18348f37aabb4e3ff..89bf5066b83e8d79c77c90fce1f7858c06b01730 100644 +--- a/src/main/java/net/minecraft/network/CompressionEncoder.java ++++ b/src/main/java/net/minecraft/network/CompressionEncoder.java +@@ -6,23 +6,38 @@ import io.netty.handler.codec.MessageToByteEncoder; + import java.util.zip.Deflater; + + public class CompressionEncoder extends MessageToByteEncoder { +- private final byte[] encodeBuf = new byte[8192]; ++ private final byte[] encodeBuf; // Paper + private final Deflater deflater; ++ private final com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper + private int threshold; + ++ // Paper start + public CompressionEncoder(int compressionThreshold) { ++ this(null, compressionThreshold); ++ } ++ public CompressionEncoder(com.velocitypowered.natives.compression.VelocityCompressor compressor, int compressionThreshold) { + this.threshold = compressionThreshold; +- this.deflater = new Deflater(); ++ if (compressor == null) { ++ this.encodeBuf = new byte[8192]; ++ this.deflater = new Deflater(); ++ } else { ++ this.encodeBuf = null; ++ this.deflater = null; ++ } ++ this.compressor = compressor; ++ // Paper end + } + + @Override +- protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) { ++ protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) throws Exception { // Paper + int i = byteBuf.readableBytes(); + FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(byteBuf2); + if (i < this.threshold) { + friendlyByteBuf.writeVarInt(0); + friendlyByteBuf.writeBytes(byteBuf); + } else { ++ // Paper start ++ if (this.deflater != null) { + byte[] bs = new byte[i]; + byteBuf.readBytes(bs); + friendlyByteBuf.writeVarInt(bs.length); +@@ -35,10 +50,48 @@ public class CompressionEncoder extends MessageToByteEncoder { + } + + this.deflater.reset(); ++ return; ++ } ++ ++ friendlyByteBuf.writeVarInt(i); ++ ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), this.compressor, byteBuf); ++ try { ++ this.compressor.deflate(compatibleIn, byteBuf2); ++ } finally { ++ compatibleIn.release(); ++ } ++ // Paper end + } + + } + ++ // Paper start ++ @Override ++ protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) throws Exception{ ++ if (this.compressor != null) { ++ // We allocate bytes to be compressed plus 1 byte. This covers two cases: ++ // ++ // - Compression ++ // According to https://github.com/ebiggers/libdeflate/blob/master/libdeflate.h#L103, ++ // if the data compresses well (and we do not have some pathological case) then the maximum ++ // size the compressed size will ever be is the input size minus one. ++ // - Uncompressed ++ // This is fairly obvious - we will then have one more than the uncompressed size. ++ int initialBufferSize = msg.readableBytes() + 1; ++ return com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(ctx.alloc(), this.compressor, initialBufferSize); ++ } ++ ++ return super.allocateBuffer(ctx, msg, preferDirect); ++ } ++ ++ @Override ++ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { ++ if (this.compressor != null) { ++ this.compressor.close(); ++ } ++ } ++ // Paper end ++ + public int getThreshold() { + return this.threshold; + } +diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java +index 0dae504f49a4a3c66a01eb03896833a010cd5821..5f43459884daea61178c3fdb913204e64f833f61 100644 +--- a/src/main/java/net/minecraft/network/Connection.java ++++ b/src/main/java/net/minecraft/network/Connection.java +@@ -657,11 +657,28 @@ public class Connection extends SimpleChannelInboundHandler> { + return networkmanager; + } + +- public void setEncryptionKey(Cipher decryptionCipher, Cipher encryptionCipher) { +- this.encrypted = true; +- this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptionCipher)); +- this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptionCipher)); ++ // Paper start ++// public void setEncryptionKey(Cipher decryptionCipher, Cipher encryptionCipher) { ++// this.encrypted = true; ++// this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptionCipher)); ++// this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptionCipher)); ++// } ++ ++ public void setupEncryption(javax.crypto.SecretKey key) throws net.minecraft.util.CryptException { ++ if (!this.encrypted) { ++ try { ++ com.velocitypowered.natives.encryption.VelocityCipher decryption = com.velocitypowered.natives.util.Natives.cipher.get().forDecryption(key); ++ com.velocitypowered.natives.encryption.VelocityCipher encryption = com.velocitypowered.natives.util.Natives.cipher.get().forEncryption(key); ++ ++ this.encrypted = true; ++ this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryption)); ++ this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryption)); ++ } catch (java.security.GeneralSecurityException e) { ++ throw new net.minecraft.util.CryptException(e); ++ } ++ } + } ++ // Paper end + + public boolean isEncrypted() { + return this.encrypted; +@@ -690,16 +707,17 @@ public class Connection extends SimpleChannelInboundHandler> { + + public void setupCompression(int compressionThreshold, boolean flag) { + if (compressionThreshold >= 0) { ++ com.velocitypowered.natives.compression.VelocityCompressor compressor = com.velocitypowered.natives.util.Natives.compress.get().create(-1); // Paper + if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder) { + ((CompressionDecoder) this.channel.pipeline().get("decompress")).setThreshold(compressionThreshold, flag); + } else { +- this.channel.pipeline().addBefore("decoder", "decompress", new CompressionDecoder(compressionThreshold, flag)); ++ this.channel.pipeline().addBefore("decoder", "decompress", new CompressionDecoder(compressor, compressionThreshold, flag)); // Paper + } + + if (this.channel.pipeline().get("compress") instanceof CompressionEncoder) { + ((CompressionEncoder) this.channel.pipeline().get("compress")).setThreshold(compressionThreshold); + } else { +- this.channel.pipeline().addBefore("encoder", "compress", new CompressionEncoder(compressionThreshold)); ++ this.channel.pipeline().addBefore("encoder", "compress", new CompressionEncoder(compressor, compressionThreshold)); // Paper + } + } else { + if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder) { +diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java +index 961660f6f9e00b93252519e38b74c66c53388ed2..a7046da8c097254907e01cd17f4107c8744f4a6e 100644 +--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java ++++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java +@@ -104,6 +104,11 @@ public class ServerConnectionListener { + ServerConnectionListener.LOGGER.info("Using default channel type"); + } + ++ // Paper start - indicate Velocity natives in use ++ ServerConnectionListener.LOGGER.info("Paper: Using " + com.velocitypowered.natives.util.Natives.compress.getLoadedVariant() + " compression from Velocity."); ++ ServerConnectionListener.LOGGER.info("Paper: Using " + com.velocitypowered.natives.util.Natives.cipher.getLoadedVariant() + " cipher from Velocity."); ++ // Paper end ++ + this.channels.add(((ServerBootstrap) ((ServerBootstrap) (new ServerBootstrap()).channel(oclass)).childHandler(new ChannelInitializer() { + protected void initChannel(Channel channel) { + try { +diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +index 45e77d96f673ce68cf15ce3d45fd1eeffed4d8d8..01fee879c946b6640da34d5890d686f0152437dc 100644 +--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +@@ -275,12 +275,14 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener + } + + SecretKey secretkey = packet.getSecretKey(privatekey); +- Cipher cipher = Crypt.getCipher(2, secretkey); +- Cipher cipher1 = Crypt.getCipher(1, secretkey); ++ // Paper start ++// Cipher cipher = Crypt.getCipher(2, secretkey); ++// Cipher cipher1 = Crypt.getCipher(1, secretkey); ++ // Paper end + + s = (new BigInteger(Crypt.digestData("", this.server.getKeyPair().getPublic(), secretkey))).toString(16); + this.state = ServerLoginPacketListenerImpl.State.AUTHENTICATING; +- this.connection.setEncryptionKey(cipher, cipher1); ++ this.connection.setupEncryption(secretkey); // Paper + } catch (CryptException cryptographyexception) { + throw new IllegalStateException("Protocol error", cryptographyexception); + } diff --git a/patches/server/0800-Always-parse-protochunk-light-sources-unless-it-is-m.patch b/patches/server/0800-Always-parse-protochunk-light-sources-unless-it-is-m.patch new file mode 100644 index 0000000000..263c8c5693 --- /dev/null +++ b/patches/server/0800-Always-parse-protochunk-light-sources-unless-it-is-m.patch @@ -0,0 +1,53 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Mon, 23 Aug 2021 04:50:05 -0700 +Subject: [PATCH] Always parse protochunk light sources unless it is marked as + non-lit + +Chunks not marked as lit will always go through the light engine, +so they should always have their block sources parsed. + +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 ad4081efec9c7eaf315ddb660f813f6ef3cfbb5b..7921ee2786d0d6a60d43786b20efc03a0f9178e3 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 +@@ -267,16 +267,33 @@ public class ChunkSerializer { + protochunk.setLightEngine(lightengine); + } + +- if (!flag && protochunk.getStatus().isOrAfter(ChunkStatus.LIGHT)) { +- Iterator iterator = BlockPos.betweenClosed(pos.getMinBlockX(), world.getMinBuildHeight(), pos.getMinBlockZ(), pos.getMaxBlockX(), world.getMaxBuildHeight() - 1, pos.getMaxBlockZ()).iterator(); ++ if (!flag) { // Paper - fix incorrect parsing of blocks that emit light - it should always parse it, unless the chunk is marked as lit ++ // Paper start - let's make sure the implementation isn't as slow as possible ++ int offX = pos.x << 4; ++ int offZ = pos.z << 4; ++ ++ int minChunkSection = io.papermc.paper.util.WorldUtil.getMinSection(world); ++ int maxChunkSection = io.papermc.paper.util.WorldUtil.getMaxSection(world); ++ ++ LevelChunkSection[] sections = achunksection; ++ for (int sectionY = minChunkSection; sectionY <= maxChunkSection; ++sectionY) { ++ LevelChunkSection section = sections[sectionY - minChunkSection]; ++ if (section == null || section.isEmpty()) { ++ // no sources in empty sections ++ continue; ++ } ++ int offY = sectionY << 4; + +- while (iterator.hasNext()) { +- BlockPos blockposition = (BlockPos) iterator.next(); ++ for (int index = 0; index < (16 * 16 * 16); ++index) { ++ if (section.states.get(index).getLightEmission() <= 0) { ++ continue; ++ } + +- if (((ChunkAccess) object).getBlockState(blockposition).getLightEmission() != 0) { +- protochunk.addLight(blockposition); ++ // index = x | (z << 4) | (y << 8) ++ protochunk.addLight(new BlockPos(offX | (index & 15), offY | (index >>> 8), offZ | ((index >>> 4) & 15))); + } + } ++ // Paper end + } + } + diff --git a/patches/server/0801-Reduce-worldgen-thread-worker-count-for-low-core-cou.patch b/patches/server/0801-Reduce-worldgen-thread-worker-count-for-low-core-cou.patch new file mode 100644 index 0000000000..5b9fbcac1f --- /dev/null +++ b/patches/server/0801-Reduce-worldgen-thread-worker-count-for-low-core-cou.patch @@ -0,0 +1,31 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Mon, 30 Aug 2021 04:26:40 -0700 +Subject: [PATCH] Reduce worldgen thread worker count for low core count CPUs + + +diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java +index 69faebb95924946f648cf9f86ff777d3274e3f28..505546d32eea4682452dbac02311433157f6a30e 100644 +--- a/src/main/java/net/minecraft/Util.java ++++ b/src/main/java/net/minecraft/Util.java +@@ -120,7 +120,19 @@ public class Util { + + private static ExecutorService makeExecutor(String s, int priorityModifier) { // Paper - add priority + // Paper start - use simpler thread pool that allows 1 thread +- int i = Math.min(8, Math.max(Runtime.getRuntime().availableProcessors() - 2, 1)); ++ // Paper start - also try to avoid suffocating the system with the worldgen workers ++ int cpus = Runtime.getRuntime().availableProcessors() / 2; ++ int i; ++ if (cpus <= 4) { ++ i = cpus <= 2 ? 1 : 2; ++ } else if (cpus <= 8) { ++ // [5, 8] ++ i = Math.max(3, cpus - 2); ++ } else { ++ i = cpus * 2 / 3; ++ } ++ i = Math.min(8, i); ++ // Paper end - also try to avoid suffocating the system with the worldgen workers + i = Integer.getInteger("Paper.WorkerThreadCount", i); + ExecutorService executorService; +