diff --git a/README.md b/README.md index 836dcd8f0..26b55747f 100644 --- a/README.md +++ b/README.md @@ -54,10 +54,10 @@ The code you contribute will be open-sourced under the GPLv3 license. If you pre 3. Once done, submit a **pull request** to **dev** branch for review. We appreciate your contributions! ## Differences Between Versions -| Version | Official Support | Max Players | Dev Builds | -|-------------------|------------------|-------------|------------| -| Community Edition | ❌ No | 30 | ❌ No | -| Premium Edition | ✔️ Yes | Unlimited | ✔️ Yes | +| Version | Official Support | Exclusive Features | Dev Builds | +|-------------------|------------------|--------------------|------------| +| Community Edition | ❌ No | ❌ No | ❌ No | +| Premium Edition | ✔️ Yes | ✔️ Yes | ✔️ Yes | ### 💖 Support the Developer Help sustain CraftEngine's development by going Premium! @@ -75,7 +75,7 @@ repositories { ``` ```kotlin dependencies { - compileOnly("net.momirealms:craft-engine-core:0.0.63") - compileOnly("net.momirealms:craft-engine-bukkit:0.0.63") + compileOnly("net.momirealms:craft-engine-core:0.0.64") + compileOnly("net.momirealms:craft-engine-bukkit:0.0.64") } ``` \ No newline at end of file diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts index 7519028f8..ec25aa893 100644 --- a/bukkit/build.gradle.kts +++ b/bukkit/build.gradle.kts @@ -101,6 +101,10 @@ tasks { relocate("com.google.common.jimfs", "net.momirealms.craftengine.libraries.jimfs") relocate("org.apache.commons", "net.momirealms.craftengine.libraries.commons") relocate("io.leangen.geantyref", "net.momirealms.craftengine.libraries.geantyref") + relocate("io.netty.handler.codec.http", "net.momirealms.craftengine.libraries.netty.handler.codec.http") + relocate("io.netty.handler.codec.rtsp", "net.momirealms.craftengine.libraries.netty.handler.codec.rtsp") + relocate("io.netty.handler.codec.spdy", "net.momirealms.craftengine.libraries.netty.handler.codec.spdy") + relocate("io.netty.handler.codec.http2", "net.momirealms.craftengine.libraries.netty.handler.codec.http2") } } diff --git a/bukkit/compatibility/build.gradle.kts b/bukkit/compatibility/build.gradle.kts index f6795ac32..520da0749 100644 --- a/bukkit/compatibility/build.gradle.kts +++ b/bukkit/compatibility/build.gradle.kts @@ -15,6 +15,7 @@ repositories { maven("https://repo.dmulloy2.net/repository/public/") // mcmmo required maven("https://repo.auxilor.io/repository/maven-public/") // eco maven("https://repo.hiusers.com/releases") // zaphkiel + maven("https://jitpack.io") // sxitem slimefun } dependencies { @@ -70,6 +71,14 @@ dependencies { compileOnly("com.github.Archy-X:AureliumSkills:Beta1.3.21") // Zaphkiel compileOnly("ink.ptms:ZaphkielAPI:2.1.0") + // WorldGuard + compileOnly(files("${rootProject.rootDir}/libs/worldguard-bukkit-7.0.14-dist.jar")) + // HeadDatabase + compileOnly("com.arcaniax:HeadDatabase-API:1.3.2") + // SXItem + compileOnly("com.github.Saukiya:SX-Item:4.4.6") + // Slimefun + compileOnly("io.github.Slimefun:Slimefun4:RC-32") } java { diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java index 303f3790b..f35396548 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java @@ -5,12 +5,14 @@ import net.momirealms.craftengine.bukkit.compatibility.item.*; import net.momirealms.craftengine.bukkit.compatibility.legacy.slimeworld.LegacySlimeFormatStorageAdaptor; import net.momirealms.craftengine.bukkit.compatibility.leveler.*; import net.momirealms.craftengine.bukkit.compatibility.model.bettermodel.BetterModelModel; +import net.momirealms.craftengine.bukkit.compatibility.model.bettermodel.BetterModelUtils; import net.momirealms.craftengine.bukkit.compatibility.model.modelengine.ModelEngineModel; import net.momirealms.craftengine.bukkit.compatibility.model.modelengine.ModelEngineUtils; import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicItemDropListener; import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicSkillHelper; import net.momirealms.craftengine.bukkit.compatibility.papi.PlaceholderAPIUtils; import net.momirealms.craftengine.bukkit.compatibility.permission.LuckPermsEventListeners; +import net.momirealms.craftengine.bukkit.compatibility.region.WorldGuardRegionCondition; import net.momirealms.craftengine.bukkit.compatibility.skript.SkriptHook; import net.momirealms.craftengine.bukkit.compatibility.slimeworld.SlimeFormatStorageAdaptor; import net.momirealms.craftengine.bukkit.compatibility.viaversion.ViaVersionUtils; @@ -18,11 +20,16 @@ import net.momirealms.craftengine.bukkit.compatibility.worldedit.WorldEditBlockR import net.momirealms.craftengine.bukkit.font.BukkitFontManager; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.core.block.BlockManager; import net.momirealms.craftengine.core.entity.furniture.ExternalModel; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.loot.LootConditions; import net.momirealms.craftengine.core.plugin.compatibility.CompatibilityManager; import net.momirealms.craftengine.core.plugin.compatibility.LevelerProvider; import net.momirealms.craftengine.core.plugin.compatibility.ModelProvider; +import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.context.condition.AlwaysFalseCondition; +import net.momirealms.craftengine.core.plugin.context.event.EventConditions; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.WorldManager; @@ -117,6 +124,23 @@ public class BukkitCompatibilityManager implements CompatibilityManager { new MythicItemDropListener(this.plugin); logHook("MythicMobs"); } + Key worldGuardRegion = Key.of("worldguard:region"); + if (this.isPluginEnabled("WorldGuard")) { + EventConditions.register(worldGuardRegion, new WorldGuardRegionCondition.FactoryImpl<>()); + LootConditions.register(worldGuardRegion, new WorldGuardRegionCondition.FactoryImpl<>()); + logHook("WorldGuard"); + } else { + EventConditions.register(worldGuardRegion, new AlwaysFalseCondition.FactoryImpl<>()); + LootConditions.register(worldGuardRegion, new AlwaysFalseCondition.FactoryImpl<>()); + } + if (this.isPluginEnabled("BetterModel")) { + BetterModelUtils.registerConstantBlockEntityRender(); + logHook("BetterModel"); + } + if (this.isPluginEnabled("ModelEngine")) { + ModelEngineUtils.registerConstantBlockEntityRender(); + logHook("ModelEngine"); + } } @Override @@ -224,8 +248,8 @@ public class BukkitCompatibilityManager implements CompatibilityManager { private void initWorldEditHook() { WorldEditBlockRegister weBlockRegister = new WorldEditBlockRegister(BukkitBlockManager.instance(), false); try { - for (Key newBlockId : BukkitBlockManager.instance().blockRegisterOrder()) { - weBlockRegister.register(newBlockId); + for (int i = 0; i < Config.serverSideBlocks(); i++) { + weBlockRegister.register(BlockManager.createCustomBlockKey(i)); } } catch (Exception e) { this.plugin.logger().warn("Failed to initialize world edit hook", e); @@ -250,6 +274,18 @@ public class BukkitCompatibilityManager implements CompatibilityManager { itemManager.registerExternalItemSource(new ZaphkielSource()); logHook("Zaphkiel"); } + if (this.isPluginEnabled("HeadDatabase")) { + itemManager.registerExternalItemSource(new HeadDatabaseSource()); + logHook("HeadDatabase"); + } + if (this.isPluginEnabled("SX-Item")) { + itemManager.registerExternalItemSource(new SXItemSource()); + logHook("SX-Item"); + } + if (this.isPluginEnabled("Slimefun")) { + itemManager.registerExternalItemSource(new SlimefunSource()); + logHook("Slimefun"); + } } private Plugin getPlugin(String name) { diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/HeadDatabaseSource.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/HeadDatabaseSource.java new file mode 100644 index 000000000..90f8763aa --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/HeadDatabaseSource.java @@ -0,0 +1,33 @@ +package net.momirealms.craftengine.bukkit.compatibility.item; + +import me.arcaniax.hdb.api.HeadDatabaseAPI; +import net.momirealms.craftengine.core.item.ExternalItemSource; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +public class HeadDatabaseSource implements ExternalItemSource { + private HeadDatabaseAPI api; + + @Override + public String plugin() { + return "headdatabase"; + } + + @Nullable + @Override + public ItemStack build(String id, ItemBuildContext context) { + if (api == null) { + api = new HeadDatabaseAPI(); + } + return api.getItemHead(id); + } + + @Override + public String id(ItemStack item) { + if (api == null) { + api = new HeadDatabaseAPI(); + } + return api.getItemID(item); + } +} diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/MythicMobsSource.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/MythicMobsSource.java index 67d974f20..8eb9e7d9c 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/MythicMobsSource.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/MythicMobsSource.java @@ -1,11 +1,18 @@ package net.momirealms.craftengine.bukkit.compatibility.item; +import io.lumine.mythic.api.adapters.AbstractPlayer; +import io.lumine.mythic.api.skills.SkillCaster; +import io.lumine.mythic.bukkit.BukkitAdapter; import io.lumine.mythic.bukkit.MythicBukkit; +import io.lumine.mythic.core.drops.DropMetadataImpl; import net.momirealms.craftengine.core.item.ExternalItemSource; import net.momirealms.craftengine.core.item.ItemBuildContext; +import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Nullable; +import java.util.Optional; + public class MythicMobsSource implements ExternalItemSource { private MythicBukkit mythicBukkit; @@ -20,7 +27,18 @@ public class MythicMobsSource implements ExternalItemSource { if (mythicBukkit == null || mythicBukkit.isClosed()) { this.mythicBukkit = MythicBukkit.inst(); } - return mythicBukkit.getItemManager().getItemStack(id); + return Optional.ofNullable(context.player()) + .map(p -> (Player) p.platformPlayer()) + .map(p -> { + AbstractPlayer target = BukkitAdapter.adapt(p); + SkillCaster caster = mythicBukkit.getSkillManager().getCaster(target); + DropMetadataImpl meta = new DropMetadataImpl(caster, target); + return mythicBukkit.getItemManager().getItem(id) + .map(i -> i.generateItemStack(meta, 1)) + .map(BukkitAdapter::adapt) + .orElse(null); + }) + .orElseGet(() -> mythicBukkit.getItemManager().getItemStack(id)); } @Override diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/SXItemSource.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/SXItemSource.java new file mode 100644 index 000000000..93d820fd0 --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/SXItemSource.java @@ -0,0 +1,29 @@ +package net.momirealms.craftengine.bukkit.compatibility.item; + +import github.saukiya.sxitem.SXItem; +import net.momirealms.craftengine.core.item.ExternalItemSource; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class SXItemSource implements ExternalItemSource { + + @Override + public String plugin() { + return "sx-item"; + } + + @Nullable + @Override + public ItemStack build(String id, ItemBuildContext context) { + return SXItem.getItemManager().getItem(id, Optional.ofNullable(context.player()).map(p -> (Player) p.platformPlayer()).orElse(null)); + } + + @Override + public String id(ItemStack item) { + return SXItem.getItemManager().getItemKey(item); + } +} diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/SlimefunSource.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/SlimefunSource.java new file mode 100644 index 000000000..d9c73986e --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/SlimefunSource.java @@ -0,0 +1,28 @@ +package net.momirealms.craftengine.bukkit.compatibility.item; + +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import net.momirealms.craftengine.core.item.ExternalItemSource; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class SlimefunSource implements ExternalItemSource { + + @Override + public String plugin() { + return "slimefun"; + } + + @Nullable + @Override + public ItemStack build(String id, ItemBuildContext context) { + return Optional.ofNullable(SlimefunItem.getById(id)).map(SlimefunItem::getItem).orElse(null); + } + + @Override + public String id(ItemStack item) { + return Optional.ofNullable(SlimefunItem.getByItem(item)).map(SlimefunItem::getId).orElse(null); + } +} diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/bettermodel/BetterModelBlockEntityElement.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/bettermodel/BetterModelBlockEntityElement.java new file mode 100644 index 000000000..1c9a0e5d3 --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/bettermodel/BetterModelBlockEntityElement.java @@ -0,0 +1,62 @@ +package net.momirealms.craftengine.bukkit.compatibility.model.bettermodel; + +import kr.toxicity.model.api.BetterModel; +import kr.toxicity.model.api.data.renderer.ModelRenderer; +import kr.toxicity.model.api.tracker.DummyTracker; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.World; +import org.bukkit.Location; +import org.joml.Vector3f; + +public class BetterModelBlockEntityElement implements BlockEntityElement { + private DummyTracker dummyTracker; + private final Location location; + private final BetterModelBlockEntityElementConfig config; + + public BetterModelBlockEntityElement(World world, BlockPos pos, BetterModelBlockEntityElementConfig config) { + this.config = config; + Vector3f position = config.position(); + this.location = new Location((org.bukkit.World) world.platformWorld(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, config.yaw(), config.pitch()); + this.dummyTracker = createDummyTracker(); + } + + private DummyTracker createDummyTracker() { + ModelRenderer modelRenderer = BetterModel.plugin().modelManager().renderer(this.config.model()); + if (modelRenderer == null) { + return null; + } else { + return modelRenderer.create(this.location); + } + } + + @Override + public void hide(Player player) { + if (this.dummyTracker != null) { + this.dummyTracker.remove((org.bukkit.entity.Player) player.platformPlayer()); + } + } + + @Override + public void show(Player player) { + if (this.dummyTracker != null) { + this.dummyTracker.spawn((org.bukkit.entity.Player) player.platformPlayer()); + } + } + + @Override + public void deactivate() { + if (this.dummyTracker != null) { + this.dummyTracker.close(); + this.dummyTracker = null; + } + } + + @Override + public void activate() { + if (this.dummyTracker == null) { + this.dummyTracker = createDummyTracker(); + } + } +} diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/bettermodel/BetterModelBlockEntityElementConfig.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/bettermodel/BetterModelBlockEntityElementConfig.java new file mode 100644 index 000000000..0b31cf939 --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/bettermodel/BetterModelBlockEntityElementConfig.java @@ -0,0 +1,61 @@ +package net.momirealms.craftengine.bukkit.compatibility.model.bettermodel; + +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.World; +import org.joml.Vector3f; + +import java.util.Map; + +public class BetterModelBlockEntityElementConfig implements BlockEntityElementConfig { + private final Vector3f position; + private final float yaw; + private final float pitch; + private final String model; + + public BetterModelBlockEntityElementConfig(String model, Vector3f position, float yaw, float pitch) { + this.pitch = pitch; + this.position = position; + this.yaw = yaw; + this.model = model; + } + + public String model() { + return model; + } + + public float pitch() { + return pitch; + } + + public Vector3f position() { + return position; + } + + public float yaw() { + return yaw; + } + + @Override + public BetterModelBlockEntityElement create(World world, BlockPos pos) { + return new BetterModelBlockEntityElement(world, pos, this); + } + + public static class Factory implements BlockEntityElementConfigFactory { + + @SuppressWarnings("unchecked") + @Override + public BlockEntityElementConfig create(Map arguments) { + String model = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("model"), "warning.config.block.state.entity_renderer.better_model.missing_model"); + return (BlockEntityElementConfig) new BetterModelBlockEntityElementConfig( + model, + ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch") + ); + } + } +} diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/bettermodel/BetterModelUtils.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/bettermodel/BetterModelUtils.java index 73e568774..507901517 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/bettermodel/BetterModelUtils.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/bettermodel/BetterModelUtils.java @@ -2,6 +2,8 @@ package net.momirealms.craftengine.bukkit.compatibility.model.bettermodel; import kr.toxicity.model.api.BetterModel; import kr.toxicity.model.api.data.renderer.ModelRenderer; +import net.momirealms.craftengine.bukkit.block.entity.renderer.element.BukkitBlockEntityElementConfigs; +import net.momirealms.craftengine.core.util.Key; import org.bukkit.entity.Entity; public class BetterModelUtils { @@ -13,4 +15,8 @@ public class BetterModelUtils { } renderer.create(base); } + + public static void registerConstantBlockEntityRender() { + BukkitBlockEntityElementConfigs.register(Key.of("craftengine:better_model"), new BetterModelBlockEntityElementConfig.Factory()); + } } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/modelengine/ModelEngineBlockEntityElement.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/modelengine/ModelEngineBlockEntityElement.java new file mode 100644 index 000000000..0b49726b7 --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/modelengine/ModelEngineBlockEntityElement.java @@ -0,0 +1,69 @@ +package net.momirealms.craftengine.bukkit.compatibility.model.modelengine; + +import com.ticxo.modelengine.api.ModelEngineAPI; +import com.ticxo.modelengine.api.entity.Dummy; +import com.ticxo.modelengine.api.model.ActiveModel; +import com.ticxo.modelengine.api.model.ModeledEntity; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.World; +import org.bukkit.Location; +import org.joml.Vector3f; + +// TODO not tested yet +public class ModelEngineBlockEntityElement implements BlockEntityElement { + private Dummy dummy; + private final Location location; + private final ModelEngineBlockEntityElementConfig config; + + public ModelEngineBlockEntityElement(World world, BlockPos pos, ModelEngineBlockEntityElementConfig config) { + this.config = config; + Vector3f position = config.position(); + this.location = new Location((org.bukkit.World) world.platformWorld(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, config.yaw(), config.pitch()); + this.dummy = createDummy(); + } + + private Dummy createDummy() { + ActiveModel activeModel = ModelEngineAPI.createActiveModel(config.model()); + if (activeModel == null) { + return null; + } else { + Dummy dummy = new Dummy<>(); + dummy.setLocation(this.location); + dummy.setDetectingPlayers(false); + ModeledEntity modeledEntity = ModelEngineAPI.createModeledEntity(dummy); + modeledEntity.addModel(activeModel, false); + return dummy; + } + } + + @Override + public void hide(Player player) { + if (this.dummy != null) { + this.dummy.setForceViewing((org.bukkit.entity.Player) player.platformPlayer(), true); + } + } + + @Override + public void show(Player player) { + if (this.dummy != null) { + this.dummy.setForceHidden((org.bukkit.entity.Player) player.platformPlayer(), true); + } + } + + @Override + public void deactivate() { + if (this.dummy != null) { + this.dummy.setRemoved(true); + this.dummy = null; + } + } + + @Override + public void activate() { + if (this.dummy == null) { + this.dummy = createDummy(); + } + } +} diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/modelengine/ModelEngineBlockEntityElementConfig.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/modelengine/ModelEngineBlockEntityElementConfig.java new file mode 100644 index 000000000..a8dbd2643 --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/modelengine/ModelEngineBlockEntityElementConfig.java @@ -0,0 +1,61 @@ +package net.momirealms.craftengine.bukkit.compatibility.model.modelengine; + +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.World; +import org.joml.Vector3f; + +import java.util.Map; + +public class ModelEngineBlockEntityElementConfig implements BlockEntityElementConfig { + private final Vector3f position; + private final float yaw; + private final float pitch; + private final String model; + + public ModelEngineBlockEntityElementConfig(String model, Vector3f position, float yaw, float pitch) { + this.pitch = pitch; + this.position = position; + this.yaw = yaw; + this.model = model; + } + + public String model() { + return model; + } + + public float pitch() { + return pitch; + } + + public Vector3f position() { + return position; + } + + public float yaw() { + return yaw; + } + + @Override + public ModelEngineBlockEntityElement create(World world, BlockPos pos) { + return new ModelEngineBlockEntityElement(world, pos, this); + } + + public static class Factory implements BlockEntityElementConfigFactory { + + @SuppressWarnings("unchecked") + @Override + public BlockEntityElementConfig create(Map arguments) { + String model = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("model"), "warning.config.block.state.entity_renderer.model_engine.missing_model"); + return (BlockEntityElementConfig) new ModelEngineBlockEntityElementConfig( + model, + ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch") + ); + } + } +} diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/modelengine/ModelEngineUtils.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/modelengine/ModelEngineUtils.java index b32074691..156537804 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/modelengine/ModelEngineUtils.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/modelengine/ModelEngineUtils.java @@ -3,6 +3,8 @@ package net.momirealms.craftengine.bukkit.compatibility.model.modelengine; import com.ticxo.modelengine.api.ModelEngineAPI; import com.ticxo.modelengine.api.model.ActiveModel; import com.ticxo.modelengine.api.model.ModeledEntity; +import net.momirealms.craftengine.bukkit.block.entity.renderer.element.BukkitBlockEntityElementConfigs; +import net.momirealms.craftengine.core.util.Key; import org.bukkit.entity.Entity; public class ModelEngineUtils { @@ -24,4 +26,8 @@ public class ModelEngineUtils { } return entityId; } + + public static void registerConstantBlockEntityRender() { + BukkitBlockEntityElementConfigs.register(Key.of("craftengine:model_engine"), new ModelEngineBlockEntityElementConfig.Factory()); + } } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicItemDrop.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicItemDrop.java index 634f341c2..9258e4214 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicItemDrop.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicItemDrop.java @@ -13,7 +13,7 @@ import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.core.item.CustomItem; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ReflectionUtils; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; @@ -33,7 +33,7 @@ public class MythicItemDrop extends ItemDrop implements IItemDrop { @Override public AbstractItemStack getDrop(DropMetadata dropMetadata, double amount) { - ItemBuildContext context = ItemBuildContext.EMPTY; + ItemBuildContext context = ItemBuildContext.empty(); SkillCaster caster = dropMetadata.getCaster(); if (caster != null && caster.getEntity() instanceof AbstractPlayer abstractPlayer) { Entity bukkitEntity = abstractPlayer.getBukkitEntity(); @@ -42,7 +42,7 @@ public class MythicItemDrop extends ItemDrop implements IItemDrop { context = ItemBuildContext.of(player); } } - int amountInt = MCUtils.fastFloor(amount + 0.5F); + int amountInt = MiscUtils.fastFloor(amount + 0.5F); ItemStack itemStack = this.customItem.buildItemStack(context, amountInt); return adapt(itemStack).amount(amountInt); } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/region/WorldGuardRegionCondition.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/region/WorldGuardRegionCondition.java new file mode 100644 index 000000000..86eeac55a --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/region/WorldGuardRegionCondition.java @@ -0,0 +1,98 @@ +package net.momirealms.craftengine.bukkit.compatibility.region; + +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.condition.ConditionFactory; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.WorldPosition; +import org.bukkit.World; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiFunction; +import java.util.function.Predicate; + +public class WorldGuardRegionCondition implements Condition { + private static final Key TYPE = Key.of("worldguard:region"); + private final MatchMode mode; + private final List regions; + + public WorldGuardRegionCondition(MatchMode mode, List regions) { + this.mode = mode; + this.regions = regions; + } + + @Override + public boolean test(CTX ctx) { + if (this.regions.isEmpty()) return false; + Optional optionalPos = ctx.getOptionalParameter(DirectContextParameters.POSITION); + if (optionalPos.isEmpty()) { + return false; + } + WorldPosition position = optionalPos.get(); + RegionManager regionManager = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt((World) position.world().platformWorld())); + if (regionManager != null) { + ApplicableRegionSet set = regionManager.getApplicableRegions(BlockVector3.at(position.x(), position.y(), position.z())); + List regionsAtThisPos = new ArrayList<>(set.size()); + for (ProtectedRegion region : set) { + String id = region.getId(); + regionsAtThisPos.add(id); + } + Predicate predicate = regionsAtThisPos::contains; + return this.mode.matcher.apply(predicate, this.regions); + } + return false; + } + + @Override + public Key type() { + return TYPE; + } + + public enum MatchMode { + ANY((p, regions) -> { + for (String region : regions) { + if (p.test(region)) { + return true; + } + } + return false; + }), + ALL((p, regions) -> { + for (String region : regions) { + if (!p.test(region)) { + return false; + } + } + return true; + }); + + private final BiFunction, List, Boolean> matcher; + + MatchMode(BiFunction, List, Boolean> matcher) { + this.matcher = matcher; + } + } + + public static class FactoryImpl implements ConditionFactory { + + @Override + public Condition create(Map arguments) { + int mode = ResourceConfigUtils.getAsInt(arguments.getOrDefault("mode", 1), "mode") - 1; + MatchMode matchMode = MatchMode.values()[mode]; + List regions = MiscUtils.getAsStringList(arguments.get("regions")); + return new WorldGuardRegionCondition<>(matchMode, regions); + } + } +} diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/expression/ExprCustomItem.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/expression/ExprCustomItem.java index 6ad45ad4c..ff22863b6 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/expression/ExprCustomItem.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/expression/ExprCustomItem.java @@ -33,7 +33,6 @@ public class ExprCustomItem extends SimpleExpression { private Expression itemIds; @Override - @SuppressWarnings("unchecked") public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { itemIds = exprs[0]; return true; @@ -49,7 +48,7 @@ public class ExprCustomItem extends SimpleExpression { if (object instanceof String string) { CustomItem customItem = CraftEngineItems.byId(Key.of(string)); if (customItem != null) { - ItemType itemType = new ItemType(customItem.buildItemStack(ItemBuildContext.EMPTY)); + ItemType itemType = new ItemType(customItem.buildItemStack(ItemBuildContext.empty())); items.add(itemType); } } diff --git a/bukkit/legacy/build.gradle.kts b/bukkit/legacy/build.gradle.kts index 2de912779..5703f4c6f 100644 --- a/bukkit/legacy/build.gradle.kts +++ b/bukkit/legacy/build.gradle.kts @@ -10,6 +10,8 @@ repositories { dependencies { // Platform compileOnly("io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT") + // authlib + compileOnly("com.mojang:authlib:6.0.58") } java { diff --git a/bukkit/legacy/src/main/java/net/momirealms/craftengine/bukkit/util/LegacyAuthLibUtils.java b/bukkit/legacy/src/main/java/net/momirealms/craftengine/bukkit/util/LegacyAuthLibUtils.java new file mode 100644 index 000000000..63a00b16e --- /dev/null +++ b/bukkit/legacy/src/main/java/net/momirealms/craftengine/bukkit/util/LegacyAuthLibUtils.java @@ -0,0 +1,16 @@ +package net.momirealms.craftengine.bukkit.util; + +import com.mojang.authlib.GameProfile; + +import java.util.UUID; + +public class LegacyAuthLibUtils { + + public static String getName(GameProfile profile) { + return profile.getName(); + } + + public static UUID getId(GameProfile profile) { + return profile.getId(); + } +} diff --git a/bukkit/loader/build.gradle.kts b/bukkit/loader/build.gradle.kts index 53736c927..11ecaf4e1 100644 --- a/bukkit/loader/build.gradle.kts +++ b/bukkit/loader/build.gradle.kts @@ -50,7 +50,7 @@ bukkit { name = "CraftEngine" apiVersion = "1.20" authors = listOf("XiaoMoMi") - contributors = listOf("jhqwqmc", "iqtesterr", "WhiteProject1", "Catnies", "xiaozhangup", "TamashiiMon", "Halogly", "ArubikU", "Maxsh001", "Sasha2294", "MrPanda8") + contributors = listOf("https://github.com/Xiao-MoMi/craft-engine/graphs/contributors") softDepend = listOf("PlaceholderAPI", "WorldEdit", "FastAsyncWorldEdit", "Skript") foliaSupported = true } @@ -81,5 +81,9 @@ tasks { relocate("org.apache.commons", "net.momirealms.craftengine.libraries.commons") relocate("io.leangen.geantyref", "net.momirealms.craftengine.libraries.geantyref") relocate("ca.spottedleaf.concurrentutil", "net.momirealms.craftengine.libraries.concurrentutil") + relocate("io.netty.handler.codec.http", "net.momirealms.craftengine.libraries.netty.handler.codec.http") + relocate("io.netty.handler.codec.rtsp", "net.momirealms.craftengine.libraries.netty.handler.codec.rtsp") + relocate("io.netty.handler.codec.spdy", "net.momirealms.craftengine.libraries.netty.handler.codec.spdy") + relocate("io.netty.handler.codec.http2", "net.momirealms.craftengine.libraries.netty.handler.codec.http2") } } diff --git a/bukkit/loader/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEnginePlugin.java b/bukkit/loader/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEnginePlugin.java index 5f9871d85..cbd3aacbe 100644 --- a/bukkit/loader/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEnginePlugin.java +++ b/bukkit/loader/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEnginePlugin.java @@ -8,7 +8,7 @@ public class BukkitCraftEnginePlugin extends JavaPlugin { public BukkitCraftEnginePlugin() { this.plugin = new BukkitCraftEngine(this); this.plugin.applyDependencies(); - this.plugin.setUpConfig(); + this.plugin.setUpConfigAndLocale(); } @Override diff --git a/bukkit/paper-loader/build.gradle.kts b/bukkit/paper-loader/build.gradle.kts index 8929b281f..ca26d2289 100644 --- a/bukkit/paper-loader/build.gradle.kts +++ b/bukkit/paper-loader/build.gradle.kts @@ -53,7 +53,7 @@ paper { name = "CraftEngine" apiVersion = "1.20" authors = listOf("XiaoMoMi") - contributors = listOf("jhqwqmc", "iqtesterrr", "WhiteProject1", "Catnies", "xiaozhangup", "TamashiiMon", "Halogly", "ArubikU", "Maxsh001", "Sasha2294", "MrPanda8") + contributors = listOf("https://github.com/Xiao-MoMi/craft-engine/graphs/contributors") foliaSupported = true serverDependencies { register("PlaceholderAPI") { @@ -86,6 +86,9 @@ paper { register("MythicMobs") { required = false } register("CustomFishing") { required = false } register("Zaphkiel") { required = false } + register("HeadDatabase") { required = false } + register("SX-Item") { required = false } + register("Slimefun") { required = false } // leveler register("AuraSkills") { required = false } @@ -154,5 +157,9 @@ tasks { relocate("org.apache.commons", "net.momirealms.craftengine.libraries.commons") relocate("io.leangen.geantyref", "net.momirealms.craftengine.libraries.geantyref") relocate("ca.spottedleaf.concurrentutil", "net.momirealms.craftengine.libraries.concurrentutil") + relocate("io.netty.handler.codec.http", "net.momirealms.craftengine.libraries.netty.handler.codec.http") + relocate("io.netty.handler.codec.rtsp", "net.momirealms.craftengine.libraries.netty.handler.codec.rtsp") + relocate("io.netty.handler.codec.spdy", "net.momirealms.craftengine.libraries.netty.handler.codec.spdy") + relocate("io.netty.handler.codec.http2", "net.momirealms.craftengine.libraries.netty.handler.codec.http2") } } diff --git a/bukkit/paper-loader/src/main/java/net/momirealms/craftengine/bukkit/plugin/PaperCraftEngineBootstrap.java b/bukkit/paper-loader/src/main/java/net/momirealms/craftengine/bukkit/plugin/PaperCraftEngineBootstrap.java index aadff5866..45eb0a35d 100644 --- a/bukkit/paper-loader/src/main/java/net/momirealms/craftengine/bukkit/plugin/PaperCraftEngineBootstrap.java +++ b/bukkit/paper-loader/src/main/java/net/momirealms/craftengine/bukkit/plugin/PaperCraftEngineBootstrap.java @@ -5,11 +5,11 @@ import io.papermc.paper.plugin.bootstrap.PluginBootstrap; import io.papermc.paper.plugin.bootstrap.PluginProviderContext; import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; import net.momirealms.craftengine.bukkit.plugin.agent.RuntimePatcher; -import net.momirealms.craftengine.bukkit.plugin.classpath.PaperClassPathAppender; +import net.momirealms.craftengine.bukkit.plugin.classpath.BukkitClassPathAppender; +import net.momirealms.craftengine.bukkit.plugin.classpath.PaperPluginClassPathAppender; import net.momirealms.craftengine.core.plugin.logger.PluginLogger; import net.momirealms.craftengine.core.plugin.logger.Slf4jPluginLogger; import net.momirealms.craftengine.core.util.ReflectionUtils; -import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; @@ -39,14 +39,24 @@ public class PaperCraftEngineBootstrap implements PluginBootstrap { } catch (ReflectiveOperationException e) { throw new RuntimeException("Failed to getLogger", e); } - this.plugin = new BukkitCraftEngine( - logger, - context.getDataDirectory(), - new PaperClassPathAppender(this.getClass().getClassLoader()) - ); + try { + this.plugin = new BukkitCraftEngine( + logger, + context.getDataDirectory(), + new BukkitClassPathAppender(), + new PaperPluginClassPathAppender(this.getClass().getClassLoader()) + ); + } catch (UnsupportedOperationException e) { + this.plugin = new BukkitCraftEngine( + logger, + context.getDataDirectory(), + new PaperPluginClassPathAppender(this.getClass().getClassLoader()), + new PaperPluginClassPathAppender(this.getClass().getClassLoader()) + ); + } this.plugin.applyDependencies(); - this.plugin.setUpConfig(); - if (VersionHelper.isOrAbove1_21_4()) { + this.plugin.setUpConfigAndLocale(); + if (isDatapackDiscoveryAvailable()) { new ModernEventHandler(context, this.plugin).register(); } else { try { @@ -58,6 +68,16 @@ public class PaperCraftEngineBootstrap implements PluginBootstrap { } } + private static boolean isDatapackDiscoveryAvailable() { + try { + Class eventsClass = Class.forName("io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents"); + eventsClass.getField("DATAPACK_DISCOVERY"); + return true; + } catch (ClassNotFoundException | NoSuchFieldException e) { + return false; + } + } + @Override public @NotNull JavaPlugin createPlugin(@NotNull PluginProviderContext context) { return new PaperCraftEnginePlugin(this); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/advancement/BukkitAdvancementManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/advancement/BukkitAdvancementManager.java index 38ead6b3f..e42a03083 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/advancement/BukkitAdvancementManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/advancement/BukkitAdvancementManager.java @@ -1,17 +1,26 @@ package net.momirealms.craftengine.bukkit.advancement; import com.google.gson.JsonElement; +import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; +import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.core.advancement.AbstractAdvancementManager; +import net.momirealms.craftengine.core.advancement.AdvancementType; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.config.IdSectionConfigParser; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.VersionHelper; import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; +import java.util.*; public class BukkitAdvancementManager extends AbstractAdvancementManager { private final BukkitCraftEngine plugin; @@ -33,7 +42,71 @@ public class BukkitAdvancementManager extends AbstractAdvancementManager { return this.advancementParser; } - public class AdvancementParser implements ConfigParser { + @Override + public void sendToast(Player player, Item icon, Component message, AdvancementType type) { + try { + Object displayInfo = CoreReflections.constructor$DisplayInfo.newInstance( + icon.getLiteralObject(), + ComponentUtils.adventureToMinecraft(message), // title + CoreReflections.instance$Component$empty, // description + VersionHelper.isOrAbove1_20_3() ? Optional.empty() : null, // background + CoreReflections.instance$AdvancementType$values[type.ordinal()], + true, // show toast + false, // announce to chat + true // hidden + ); + if (VersionHelper.isOrAbove1_20_2()) { + displayInfo = Optional.of(displayInfo); + } + Object resourceLocation = KeyUtils.toResourceLocation(Key.of("craftengine", "toast")); + Object criterion = VersionHelper.isOrAbove1_20_2() ? + CoreReflections.constructor$Criterion.newInstance(CoreReflections.constructor$ImpossibleTrigger.newInstance(), CoreReflections.constructor$ImpossibleTrigger$TriggerInstance.newInstance()) : + CoreReflections.constructor$Criterion.newInstance(CoreReflections.constructor$ImpossibleTrigger$TriggerInstance.newInstance()); + Map criteria = Map.of("impossible", criterion); + Object advancementProgress = CoreReflections.constructor$AdvancementProgress.newInstance(); + Object advancement; + if (VersionHelper.isOrAbove1_20_2()) { + Object advancementRequirements = VersionHelper.isOrAbove1_20_3() ? + CoreReflections.constructor$AdvancementRequirements.newInstance(List.of(List.of("impossible"))) : + CoreReflections.constructor$AdvancementRequirements.newInstance((Object) new String[][] {{"impossible"}}); + advancement = CoreReflections.constructor$Advancement.newInstance( + Optional.empty(), + displayInfo, + CoreReflections.instance$AdvancementRewards$EMPTY, + criteria, + advancementRequirements, + false + ); + CoreReflections.method$AdvancementProgress$update.invoke(advancementProgress, advancementRequirements); + advancement = CoreReflections.constructor$AdvancementHolder.newInstance(resourceLocation, advancement); + } else { + advancement = CoreReflections.constructor$Advancement.newInstance( + resourceLocation, + null, // parent + displayInfo, + CoreReflections.instance$AdvancementRewards$EMPTY, + criteria, + new String[][] {{"impossible"}}, + false + ); + CoreReflections.method$AdvancementProgress$update.invoke(advancementProgress, criteria, new String[][] {{"impossible"}}); + } + CoreReflections.method$AdvancementProgress$grantProgress.invoke(advancementProgress, "impossible"); + Map advancementsToGrant = new HashMap<>(); + advancementsToGrant.put(resourceLocation, advancementProgress); + Object grantPacket = VersionHelper.isOrAbove1_21_5() ? + NetworkReflections.constructor$ClientboundUpdateAdvancementsPacket.newInstance(false, Arrays.asList(advancement), new HashSet<>(), advancementsToGrant, true) : + NetworkReflections.constructor$ClientboundUpdateAdvancementsPacket.newInstance(false, Arrays.asList(advancement), new HashSet<>(), advancementsToGrant); + Object removePacket = VersionHelper.isOrAbove1_21_5() ? + NetworkReflections.constructor$ClientboundUpdateAdvancementsPacket.newInstance(false, new ArrayList<>(), new HashSet<>() {{add(resourceLocation);}}, new HashMap<>(), true) : + NetworkReflections.constructor$ClientboundUpdateAdvancementsPacket.newInstance(false, new ArrayList<>(), new HashSet<>() {{add(resourceLocation);}}, new HashMap<>()); + player.sendPackets(List.of(grantPacket, removePacket), false); + } catch (ReflectiveOperationException e) { + this.plugin.logger().warn("Failed to send toast for player " + player.name(), e); + } + } + + public class AdvancementParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"advancements", "advancement"}; @Override @@ -47,7 +120,7 @@ public class BukkitAdvancementManager extends AbstractAdvancementManager { } @Override - public void parseSection(Pack pack, Path path, Key id, Map section) { + public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (advancements.containsKey(id)) { throw new LocalizedResourceConfigException("warning.config.advancement.duplicate", path, id); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/BukkitAdaptors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/BukkitAdaptors.java index 6dcd0167a..c667f832e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/BukkitAdaptors.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/BukkitAdaptors.java @@ -3,37 +3,63 @@ package net.momirealms.craftengine.bukkit.api; import net.momirealms.craftengine.bukkit.entity.BukkitEntity; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; -import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.bukkit.world.BukkitWorld; -import net.momirealms.craftengine.core.world.WorldPosition; -import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; public final class BukkitAdaptors { private BukkitAdaptors() {} - public static BukkitServerPlayer adapt(final Player player) { + /** + * Adapts a Bukkit Player to a CraftEngine BukkitServerPlayer. + * This provides access to CraftEngine-specific player functionality and data. + * + * @param player the Bukkit Player to adapt, must not be null + * @return a non-null BukkitServerPlayer instance wrapping the provided player + */ + @NotNull + public static BukkitServerPlayer adapt(@NotNull final Player player) { return BukkitCraftEngine.instance().adapt(player); } - public static BukkitWorld adapt(final World world) { + /** + * Adapts a Bukkit World to a CraftEngine BukkitWorld. + * This enables CraftEngine world operations on Bukkit world instances. + * + * @param world the Bukkit World to adapt, must not be null + * @return a non-null BukkitWorld instance wrapping the provided world + */ + @NotNull + public static BukkitWorld adapt(@NotNull final World world) { return new BukkitWorld(world); } - public static BukkitEntity adapt(final Entity entity) { + /** + * Adapts a Bukkit Entity to a CraftEngine BukkitEntity. + * This provides CraftEngine entity functionality for Bukkit entities. + * + * @param entity the Bukkit Entity to adapt, must not be null + * @return a non-null BukkitEntity instance wrapping the provided entity + */ + @NotNull + public static BukkitEntity adapt(@NotNull final Entity entity) { return new BukkitEntity(entity); } - public static BukkitExistingBlock adapt(final Block block) { + /** + * Adapts a Bukkit Block to a CraftEngine BukkitExistingBlock. + * This enables CraftEngine block operations on Bukkit block instances. + * + * @param block the Bukkit Block to adapt, must not be null + * @return a non-null BukkitExistingBlock instance wrapping the provided block + */ + @NotNull + public static BukkitExistingBlock adapt(@NotNull final Block block) { return new BukkitExistingBlock(block); } - - public static Location toLocation(WorldPosition position) { - return LocationUtils.toLocation(position); - } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java index 678f1b710..d769ab88f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java @@ -27,10 +27,28 @@ import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Map; + public final class CraftEngineBlocks { private CraftEngineBlocks() {} + /** + * + * Returns an unmodifiable map of all currently loaded custom blocks. + * The map keys represent unique identifiers, and the values are the corresponding CustomBlock instances. + * + *

Important: Do not attempt to access this method during the onEnable phase + * as it will be empty. Instead, listen for the {@code CraftEngineReloadEvent} and use this method + * after the event is fired to obtain the complete block list. + * + * @return a non-null map containing all loaded custom blocks + */ + @NotNull + public static Map loadedBlocks() { + return BukkitBlockManager.instance().loadedBlocks(); + } + /** * Gets a custom block by ID * @@ -170,16 +188,14 @@ public final class CraftEngineBlocks { * @param player player who breaks the block * @param dropLoot whether to drop block loots * @param isMoving is moving - * @param playSound whether to play break sounds - * @param sendParticles whether to send break particles + * @param sendLevelEvent whether to send break particles and sounds * @return success or not */ public static boolean remove(@NotNull Block block, @Nullable Player player, boolean isMoving, boolean dropLoot, - boolean playSound, - boolean sendParticles) { + boolean sendLevelEvent) { ImmutableBlockState state = getCustomBlockState(block); if (state == null || state.isEmpty()) return false; World world = new BukkitWorld(block.getWorld()); @@ -188,24 +204,43 @@ public final class CraftEngineBlocks { if (dropLoot) { ContextHolder.Builder builder = new ContextHolder.Builder() .withParameter(DirectContextParameters.POSITION, position); - BukkitServerPlayer serverPlayer = BukkitCraftEngine.instance().adapt(player); + BukkitServerPlayer serverPlayer = null; if (player != null) { - builder.withParameter(DirectContextParameters.PLAYER, serverPlayer); + serverPlayer = BukkitCraftEngine.instance().adapt(player); + builder.withOptionalParameter(DirectContextParameters.PLAYER, serverPlayer); } for (Item item : state.getDrops(builder, world, serverPlayer)) { world.dropItemNaturally(position, item); } } - if (playSound) { - world.playBlockSound(position, state.settings().sounds().breakSound()); - } - if (sendParticles) { + if (sendLevelEvent) { FastNMS.INSTANCE.method$LevelAccessor$levelEvent(world.serverWorld(), WorldEvents.BLOCK_BREAK_EFFECT, LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), state.customBlockState().registryId()); } FastNMS.INSTANCE.method$Level$removeBlock(world.serverWorld(), LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), isMoving); return true; } + /** + * Removes a block from the world if it's custom + * + * @param block block to remove + * @param player player who breaks the block + * @param dropLoot whether to drop block loots + * @param isMoving is moving + * @param playSound whether to play break sounds + * @param sendParticles whether to send break particles + * @return success or not + */ + @Deprecated(forRemoval = true) + public static boolean remove(@NotNull Block block, + @Nullable Player player, + boolean isMoving, + boolean dropLoot, + boolean playSound, + boolean sendParticles) { + return remove(block, player, dropLoot, isMoving, playSound || sendParticles); + } + /** * Checks if a block is custom * @@ -251,4 +286,14 @@ public final class CraftEngineBlocks { public static BlockData getBukkitBlockData(@NotNull ImmutableBlockState blockState) { return BlockStateUtils.fromBlockData(blockState.customBlockState().literalObject()); } + + /** + * Checks if the block state is a vanilla block state + * + * @param id state id + * @return is vanilla block or not + */ + public static boolean isVanillaBlockState(int id) { + return BukkitBlockManager.instance().isVanillaBlockState(id); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java index 113fc0d93..d7367e5cd 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java @@ -27,11 +27,27 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.Map; public final class CraftEngineFurniture { private CraftEngineFurniture() {} + /** + * Returns an unmodifiable map of all currently loaded custom furniture. + * The map keys represent unique identifiers, and the values are the corresponding CustomFurniture instances. + * + *

Important: Do not attempt to access this method during the onEnable phase + * as it will be empty. Instead, listen for the {@code CraftEngineReloadEvent} and use this method + * after the event is fired to obtain the complete furniture list. + * + * @return a non-null map containing all loaded custom furniture + */ + @NotNull + public static Map loadedFurniture() { + return BukkitFurnitureManager.instance().loadedFurniture(); + } + /** * Gets custom furniture by ID * diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineImages.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineImages.java new file mode 100644 index 000000000..3a420339f --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineImages.java @@ -0,0 +1,40 @@ +package net.momirealms.craftengine.bukkit.api; + +import net.momirealms.craftengine.bukkit.font.BukkitFontManager; +import net.momirealms.craftengine.core.font.BitmapImage; +import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +public final class CraftEngineImages { + + private CraftEngineImages() {} + + /** + * Returns an unmodifiable map of all currently loaded custom images. + * The map keys represent unique identifiers, and the values are the corresponding BitmapImage instances. + * + *

Important: Do not attempt to access this method during the onEnable phase + * as it will be empty. Instead, listen for the {@code CraftEngineReloadEvent} and use this method + * after the event is fired to obtain the complete image list. + * + * @return a non-null map containing all loaded custom images + */ + @NotNull + public static Map loadedImages() { + return BukkitFontManager.instance().loadedImages(); + } + + /** + * Gets a custom image by ID + * + * @param id id + * @return the custom image + */ + @Nullable + public static BitmapImage byId(@NotNull Key id) { + return BukkitFontManager.instance().loadedImages().get(id); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineItems.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineItems.java index 64c628693..2fe0c11c5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineItems.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineItems.java @@ -8,10 +8,28 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Map; + public final class CraftEngineItems { private CraftEngineItems() {} + /** + * Returns an unmodifiable map of all currently loaded custom items. + * The map keys represent unique identifiers, and the values are the corresponding CustomItem instances. + * + *

Important: Do not attempt to access this method during the onEnable phase + * as it will be empty. Instead, listen for the {@code CraftEngineReloadEvent} and use this method + * after the event is fired to obtain the complete item list. + * + * @return a non-null map containing all loaded custom items + * @throws IllegalStateException if the BukkitItemManager instance is not available + */ + @NotNull + public static Map> loadedItems() { + return BukkitItemManager.instance().loadedItems(); + } + /** * Gets a custom item by ID * diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/AsyncResourcePackCacheEvent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/AsyncResourcePackCacheEvent.java new file mode 100644 index 000000000..35f1cdd4f --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/AsyncResourcePackCacheEvent.java @@ -0,0 +1,67 @@ +package net.momirealms.craftengine.bukkit.api.event; + +import net.momirealms.craftengine.core.pack.PackCacheData; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * This event is triggered when a user executes the "/ce reload pack" command. + *

+ * The event initiates a process that caches all resource content into a virtual file system + * to ensure optimal build performance. To add your resource pack through this event, + * you must use the {@link #registerExternalResourcePack(Path)} method every time this event is called. + *

+ *

+ * Important: The caching system will not update your resource pack if its file size or + * last modification time remains unchanged between reloads. Ensure these attributes change + * if you need the cache to recognize updates. + *

+ */ +public class AsyncResourcePackCacheEvent extends Event { + private static final HandlerList HANDLER_LIST = new HandlerList(); + private final PackCacheData cacheData; + + public AsyncResourcePackCacheEvent(@NotNull PackCacheData cacheData) { + super(true); + this.cacheData = cacheData; + } + + @NotNull + public PackCacheData cacheData() { + return this.cacheData; + } + + @NotNull + public static HandlerList getHandlerList() { + return HANDLER_LIST; + } + + @NotNull + public HandlerList getHandlers() { + return getHandlerList(); + } + + /** + * Adds an external resource pack to the cache. + *

+ * This method accepts either a .zip file or a directory path representing a resource pack. + * The resource pack will be added to the appropriate cache collection based on its type. + *

+ * + * @param path the file system path to the resource pack. Must be either a .zip file or a directory. + * @throws IllegalArgumentException if the provided path is neither a .zip file nor a directory. + */ + public void registerExternalResourcePack(@NotNull final Path path) { + if (Files.isRegularFile(path) && path.getFileName().endsWith(".zip")) { + this.cacheData.externalZips().add(path); + } else if (Files.isDirectory(path)) { + this.cacheData.externalFolders().add(path); + } else { + throw new IllegalArgumentException("Illegal resource pack path: " + path); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java index 24da42748..0a6cf5176 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java @@ -21,6 +21,7 @@ import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.sound.SoundSource; import net.momirealms.craftengine.core.util.Cancellable; import net.momirealms.craftengine.core.util.ItemUtils; import net.momirealms.craftengine.core.util.VersionHelper; @@ -44,13 +45,11 @@ import java.util.Optional; public final class BlockEventListener implements Listener { private final BukkitCraftEngine plugin; - private final boolean enableNoteBlockCheck; private final BukkitBlockManager manager; - public BlockEventListener(BukkitCraftEngine plugin, BukkitBlockManager manager, boolean enableNoteBlockCheck) { + public BlockEventListener(BukkitCraftEngine plugin, BukkitBlockManager manager) { this.plugin = plugin; this.manager = manager; - this.enableNoteBlockCheck = enableNoteBlockCheck; } @EventHandler(ignoreCancelled = true) @@ -74,18 +73,14 @@ public final class BlockEventListener implements Listener { // send sound if the placed block's sounds are removed if (Config.enableSoundSystem()) { Block block = event.getBlock(); - Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData()); - if (blockState != MBlocks.AIR$defaultState) { - Object ownerBlock = BlockStateUtils.getBlockOwner(blockState); - if (this.manager.isBlockSoundRemoved(ownerBlock)) { + Object blockState = BlockStateUtils.getBlockState(block); + if (blockState != MBlocks.AIR$defaultState && BlockStateUtils.isVanillaBlock(blockState)) { + Object soundType = FastNMS.INSTANCE.method$BlockBehaviour$BlockStateBase$getSoundType(blockState); + Object soundEvent = FastNMS.INSTANCE.field$SoundType$placeSound(soundType); + Object soundId = FastNMS.INSTANCE.field$SoundEvent$location(soundEvent); + if (this.manager.isPlaceSoundMissing(soundId)) { if (player.getInventory().getItemInMainHand().getType() != Material.DEBUG_STICK) { - try { - Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(ownerBlock); - Object placeSound = CoreReflections.field$SoundType$placeSound.get(soundType); - player.playSound(block.getLocation().add(0.5, 0.5, 0.5), FastNMS.INSTANCE.field$SoundEvent$location(placeSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f); - } catch (ReflectiveOperationException e) { - this.plugin.logger().warn("Failed to get sound type", e); - } + player.playSound(block.getLocation().add(0.5, 0.5, 0.5), soundId.toString(), SoundCategory.BLOCKS, 1f, 0.8f); } return; } @@ -93,23 +88,19 @@ public final class BlockEventListener implements Listener { } // resend sound if the clicked block is interactable on client side if (serverPlayer.shouldResendSound()) { - try { - Block block = event.getBlock(); - Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData()); - Object ownerBlock = BlockStateUtils.getBlockOwner(blockState); - Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(ownerBlock); - Object placeSound = CoreReflections.field$SoundType$placeSound.get(soundType); - player.playSound(block.getLocation().add(0.5, 0.5, 0.5), FastNMS.INSTANCE.field$SoundEvent$location(placeSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f); - } catch (ReflectiveOperationException e) { - this.plugin.logger().warn("Failed to get sound type", e); - } + Block block = event.getBlock(); + Object blockState = BlockStateUtils.getBlockState(block); + Object soundType = FastNMS.INSTANCE.method$BlockBehaviour$BlockStateBase$getSoundType(blockState); + Object soundEvent = FastNMS.INSTANCE.field$SoundType$placeSound(soundType); + Object soundId = FastNMS.INSTANCE.field$SoundEvent$location(soundEvent); + player.playSound(block.getLocation().add(0.5, 0.5, 0.5), soundId.toString(), SoundCategory.BLOCKS, 1f, 0.8f); } } @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) public void onPlayerBreak(BlockBreakEvent event) { org.bukkit.block.Block block = event.getBlock(); - Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData()); + Object blockState = BlockStateUtils.getBlockState(block); int stateId = BlockStateUtils.blockStateToId(blockState); Player player = event.getPlayer(); Location location = block.getLocation(); @@ -168,12 +159,15 @@ public final class BlockEventListener implements Listener { } // play sound - world.playBlockSound(position, state.settings().sounds().breakSound()); + serverPlayer.playSound(position, state.settings().sounds().breakSound(), SoundSource.BLOCK); } } else { // override vanilla block loots if (player.getGameMode() != GameMode.CREATIVE) { this.plugin.vanillaLootManager().getBlockLoot(stateId).ifPresent(it -> { + if (!event.isDropItems()) { + return; + } if (it.override()) { event.setDropItems(false); event.setExpToDrop(0); @@ -190,18 +184,13 @@ public final class BlockEventListener implements Listener { } }); } - // sound system if (Config.enableSoundSystem()) { - Object ownerBlock = BlockStateUtils.getBlockOwner(blockState); - if (this.manager.isBlockSoundRemoved(ownerBlock)) { - try { - Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(ownerBlock); - Object breakSound = CoreReflections.field$SoundType$breakSound.get(soundType); - block.getWorld().playSound(block.getLocation().add(0.5, 0.5, 0.5), FastNMS.INSTANCE.field$SoundEvent$location(breakSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f); - } catch (ReflectiveOperationException e) { - this.plugin.logger().warn("Failed to get sound type", e); - } + Object soundType = FastNMS.INSTANCE.method$BlockBehaviour$BlockStateBase$getSoundType(blockState); + Object soundEvent = FastNMS.INSTANCE.field$SoundType$breakSound(soundType); + Object soundId = FastNMS.INSTANCE.field$SoundEvent$location(soundEvent); + if (this.manager.isBreakSoundMissing(soundId)) { + player.playSound(block.getLocation().add(0.5, 0.5, 0.5), soundId.toString(), SoundCategory.BLOCKS, 1f, 0.8f); } } } @@ -258,22 +247,17 @@ public final class BlockEventListener implements Listener { } player.playSound(location, state.settings().sounds().stepSound().id().toString(), SoundCategory.BLOCKS, state.settings().sounds().stepSound().volume().get(), state.settings().sounds().stepSound().pitch().get()); } else if (Config.enableSoundSystem()) { - Object ownerBlock = BlockStateUtils.getBlockOwner(blockState); - if (this.manager.isBlockSoundRemoved(ownerBlock)) { - try { - Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(ownerBlock); - Object stepSound = CoreReflections.field$SoundType$stepSound.get(soundType); - player.playSound(player.getLocation(), FastNMS.INSTANCE.field$SoundEvent$location(stepSound).toString(), SoundCategory.BLOCKS, 0.15f, 1f); - } catch (ReflectiveOperationException e) { - this.plugin.logger().warn("Failed to get sound type", e); - } + Object soundType = FastNMS.INSTANCE.method$BlockBehaviour$BlockStateBase$getSoundType(blockState); + Object soundEvent = FastNMS.INSTANCE.field$SoundType$stepSound(soundType); + Object soundId = FastNMS.INSTANCE.field$SoundEvent$location(soundEvent); + if (this.manager.isStepSoundMissing(soundId)) { + player.playSound(player.getLocation(), soundId.toString(), SoundCategory.BLOCKS, 0.15f, 1f); } } } @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) public void onBlockPhysics(BlockPhysicsEvent event) { - if (!this.enableNoteBlockCheck) return; // for vanilla blocks if (event.getChangedType() == Material.NOTE_BLOCK) { Block block = event.getBlock(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java index f32cb3a5b..abcf5d08f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java @@ -2,138 +2,107 @@ package net.momirealms.craftengine.bukkit.block; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.momirealms.craftengine.bukkit.block.behavior.UnsafeCompositeBlockBehavior; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.injector.BlockGenerator; -import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.*; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; -import net.momirealms.craftengine.bukkit.util.BlockStateUtils; -import net.momirealms.craftengine.bukkit.util.KeyUtils; -import net.momirealms.craftengine.bukkit.util.RegistryUtils; -import net.momirealms.craftengine.bukkit.util.TagUtils; +import net.momirealms.craftengine.bukkit.util.*; import net.momirealms.craftengine.core.block.*; +import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviors; import net.momirealms.craftengine.core.block.behavior.EmptyBlockBehavior; import net.momirealms.craftengine.core.block.parser.BlockStateParser; -import net.momirealms.craftengine.core.plugin.config.StringKeyConstructor; -import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.loot.LootTable; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; +import net.momirealms.craftengine.core.plugin.context.function.Function; +import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.registry.Holder; -import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.sound.SoundData; -import net.momirealms.craftengine.core.sound.Sounds; +import net.momirealms.craftengine.core.sound.SoundSet; import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.chunk.PalettedContainer; import org.bukkit.Bukkit; import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.block.data.BlockData; import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.yaml.snakeyaml.LoaderOptions; -import org.yaml.snakeyaml.Yaml; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Field; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.*; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; public final class BukkitBlockManager extends AbstractBlockManager { + public static final Set CLIENT_SIDE_NOTE_BLOCKS = new HashSet<>(2048, 0.6f); + private static final Object ALWAYS_FALSE = FastNMS.INSTANCE.method$StatePredicate$always(false); + private static final Object ALWAYS_TRUE = FastNMS.INSTANCE.method$StatePredicate$always(true); private static BukkitBlockManager instance; private final BukkitCraftEngine plugin; - - // The total amount of blocks registered - private int customBlockCount; - private ImmutableBlockState[] stateId2ImmutableBlockStates; - // Minecraft objects - // Cached new blocks $ holders - private Map stateId2BlockHolder; - // This map is used to change the block states that are not necessarily needed into a certain block state - private Map blockAppearanceMapper; - // Record the amount of real blocks by block type - private Map registeredRealBlockSlots; - // A set of blocks that sounds have been removed - private Set affectedSoundBlocks; - private Map> affectedOpenableBlockSounds; - private Map soundMapper; - // A list to record the order of registration - private List blockRegisterOrder = new ObjectArrayList<>(); - // Event listeners - private BlockEventListener blockEventListener; - private FallingBlockRemoveListener fallingBlockRemoveListener; - // cached tag packet + // 事件监听器 + private final BlockEventListener blockEventListener; + // 用于缓存string形式的方块状态到原版方块状态 + private final Map blockStateCache = new HashMap<>(1024); + // 用于临时存储可燃烧自定义方块的列表 + private final List burnableBlocks = new ArrayList<>(); + // 可燃烧的方块 + private Map igniteOdds; + private Map burnOdds; + // 自定义客户端侧原版方块标签 + private Map> clientBoundTags = Map.of(); + private Map> previousClientBoundTags = Map.of(); + // 缓存的原版方块tag包 private Object cachedUpdateTagsPacket; - - private final List> blocksToDeceive = new ArrayList<>(); + // 被移除声音的原版方块 + private Set missingPlaceSounds = Set.of(); + private Set missingBreakSounds = Set.of(); + private Set missingHitSounds = Set.of(); + private Set missingStepSounds = Set.of(); + private Set missingInteractSoundBlocks = Set.of(); public BukkitBlockManager(BukkitCraftEngine plugin) { - super(plugin); - instance = this; + super(plugin, RegistryUtils.currentBlockRegistrySize(), Config.serverSideBlocks()); this.plugin = plugin; - this.initVanillaRegistry(); - this.loadMappingsAndAdditionalBlocks(); - this.registerBlocks(); - this.registerEmptyBlock(); + this.blockEventListener = new BlockEventListener(plugin, this); + this.registerServerSideCustomBlocks(Config.serverSideBlocks()); + EmptyBlock.initialize(); + instance = this; } @Override public void init() { this.initMirrorRegistry(); - this.deceiveBukkit(); - boolean enableNoteBlocks = this.blockAppearanceArranger.containsKey(BlockKeys.NOTE_BLOCK); - this.blockEventListener = new BlockEventListener(plugin, this, enableNoteBlocks); - if (enableNoteBlocks) { - this.recordVanillaNoteBlocks(); - } - this.fallingBlockRemoveListener = VersionHelper.isOrAbove1_20_3() ? new FallingBlockRemoveListener() : null; - this.stateId2ImmutableBlockStates = new ImmutableBlockState[this.customBlockCount]; - Arrays.fill(this.stateId2ImmutableBlockStates, EmptyBlock.INSTANCE.defaultState()); - this.resetPacketConsumers(); - } - - @Override - public String stateRegistryIdToStateSNBT(int id) { - return BlockStateUtils.idToBlockState(id).toString(); + this.initFireBlock(); + this.deceiveBukkitRegistry(); + this.markVanillaNoteBlocks(); + Arrays.fill(this.immutableBlockStates, EmptyBlock.INSTANCE.defaultState()); + this.plugin.networkManager().registerBlockStatePacketListeners(this.blockStateMappings); // 一定要预先初始化一次,预防id超出上限 } public static BukkitBlockManager instance() { return instance; } - public List blockRegisterOrder() { - return Collections.unmodifiableList(this.blockRegisterOrder); - } - @Override public void delayedInit() { Bukkit.getPluginManager().registerEvents(this.blockEventListener, this.plugin.javaPlugin()); - if (this.fallingBlockRemoveListener != null) { - Bukkit.getPluginManager().registerEvents(this.fallingBlockRemoveListener, this.plugin.javaPlugin()); - } } @Override public void unload() { super.unload(); + this.previousClientBoundTags = this.clientBoundTags; + this.clientBoundTags = new HashMap<>(); + for (DelegatingBlock block : this.burnableBlocks) { + this.igniteOdds.remove(block); + this.burnOdds.remove(block); + } + this.burnableBlocks.clear(); if (EmptyBlock.STATE != null) - Arrays.fill(this.stateId2ImmutableBlockStates, EmptyBlock.STATE); - for (DelegatingBlock block : this.registeredBlocks.values()) { + Arrays.fill(this.immutableBlockStates, EmptyBlock.STATE); + for (DelegatingBlock block : this.customBlocks) { block.behaviorDelegate().bindValue(EmptyBlockBehavior.INSTANCE); block.shapeDelegate().bindValue(BukkitBlockShape.STONE); DelegatingBlockState state = (DelegatingBlockState) FastNMS.INSTANCE.method$Block$defaultState(block); @@ -145,20 +114,29 @@ public final class BukkitBlockManager extends AbstractBlockManager { public void disable() { this.unload(); HandlerList.unregisterAll(this.blockEventListener); - if (this.fallingBlockRemoveListener != null) HandlerList.unregisterAll(this.fallingBlockRemoveListener); - } - - @Override - public Map soundMapper() { - return this.soundMapper; } @Override public void delayedLoad() { - this.resetPacketConsumers(); + this.plugin.networkManager().registerBlockStatePacketListeners(this.blockStateMappings); // 重置方块映射表 super.delayedLoad(); } + @Override + public BlockBehavior createBlockBehavior(CustomBlock customBlock, List> behaviorConfig) { + if (behaviorConfig == null || behaviorConfig.isEmpty()) { + return new EmptyBlockBehavior(); + } else if (behaviorConfig.size() == 1) { + return BlockBehaviors.fromMap(customBlock, behaviorConfig.getFirst()); + } else { + List behaviors = new ArrayList<>(); + for (Map config : behaviorConfig) { + behaviors.add((AbstractBlockBehavior) BlockBehaviors.fromMap(customBlock, config)); + } + return new UnsafeCompositeBlockBehavior(customBlock, behaviors); + } + } + @Override protected void resendTags() { // if there's no change @@ -186,47 +164,36 @@ public final class BukkitBlockManager extends AbstractBlockManager { if (state != null) { return state.customBlockState(); } + return createVanillaBlockState(blockState); + } + + @Override + public BlockStateWrapper createVanillaBlockState(String blockState) { + return this.blockStateCache.computeIfAbsent(blockState, k -> { + Object state = parseBlockState(k); + if (state == null) return null; + return BlockStateUtils.toBlockStateWrapper(state); + }); + } + + @Nullable + private Object parseBlockState(String state) { try { - BlockData blockData = Bukkit.createBlockData(blockState); - return BlockStateUtils.toBlockStateWrapper(blockData); - } catch (IllegalArgumentException e) { + Object registryOrLookUp = MBuiltInRegistries.BLOCK; + if (CoreReflections.method$Registry$asLookup != null) { + registryOrLookUp = CoreReflections.method$Registry$asLookup.invoke(registryOrLookUp); + } + Object result = CoreReflections.method$BlockStateParser$parseForBlock.invoke(null, registryOrLookUp, state, false); + return CoreReflections.method$BlockStateParser$BlockResult$blockState.invoke(result); + } catch (Exception e) { + Debugger.BLOCK.warn(() -> "Failed to create block state: " + state, e); return null; } } @Nullable public Object getMinecraftBlockHolder(int stateId) { - return this.stateId2BlockHolder.get(stateId); - } - - @NotNull - @Override - public ImmutableBlockState getImmutableBlockStateUnsafe(int stateId) { - return this.stateId2ImmutableBlockStates[stateId - BlockStateUtils.vanillaStateSize()]; - } - - @Nullable - @Override - public ImmutableBlockState getImmutableBlockState(int stateId) { - if (!BlockStateUtils.isVanillaBlock(stateId)) { - return this.stateId2ImmutableBlockStates[stateId - BlockStateUtils.vanillaStateSize()]; - } - return null; - } - - @Override - public void addBlockInternal(Key id, CustomBlock customBlock) { - // bind appearance and real state - for (ImmutableBlockState state : customBlock.variantProvider().states()) { - ImmutableBlockState previous = this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()]; - if (previous != null && !previous.isEmpty()) { - throw new LocalizedResourceConfigException("warning.config.block.state.bind_failed", state.toString(), previous.toString()); - } - this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()] = state; - this.tempBlockAppearanceConvertor.put(state.customBlockState().registryId(), state.vanillaBlockState().registryId()); - this.appearanceToRealState.computeIfAbsent(state.vanillaBlockState().registryId(), k -> new IntArrayList()).add(state.customBlockState().registryId()); - } - super.addBlockInternal(id, customBlock); + return this.customBlockHolders[stateId - BlockStateUtils.vanillaBlockStateCount()]; } @Override @@ -239,449 +206,312 @@ public final class BukkitBlockManager extends AbstractBlockManager { return BlockStateUtils.getBlockOwnerIdFromState(BlockStateUtils.idToBlockState(id)); } + @SuppressWarnings("unchecked") + private void initFireBlock() { + try { + this.igniteOdds = (Map) CoreReflections.field$FireBlock$igniteOdds.get(MBlocks.FIRE); + this.burnOdds = (Map) CoreReflections.field$FireBlock$burnOdds.get(MBlocks.FIRE); + } catch (IllegalAccessException e) { + this.plugin.logger().warn("Failed to get ignite odds", e); + } + } + @Override - public int availableAppearances(Key blockType) { - return Optional.ofNullable(this.registeredRealBlockSlots.get(blockType)).orElse(0); + protected void applyPlatformSettings(ImmutableBlockState state) { + DelegatingBlockState nmsState = (DelegatingBlockState) state.customBlockState().literalObject(); + nmsState.setBlockState(state); + Object nmsVisualState = state.vanillaBlockState().literalObject(); + + BlockSettings settings = state.settings(); + try { + CoreReflections.field$BlockStateBase$lightEmission.set(nmsState, settings.luminance()); + CoreReflections.field$BlockStateBase$burnable.set(nmsState, settings.burnable()); + CoreReflections.field$BlockStateBase$hardness.set(nmsState, settings.hardness()); + CoreReflections.field$BlockStateBase$replaceable.set(nmsState, settings.replaceable()); + Object mcMapColor = CoreReflections.method$MapColor$byId.invoke(null, settings.mapColor().id); + CoreReflections.field$BlockStateBase$mapColor.set(nmsState, mcMapColor); + CoreReflections.field$BlockStateBase$instrument.set(nmsState, CoreReflections.instance$NoteBlockInstrument$values[settings.instrument().ordinal()]); + CoreReflections.field$BlockStateBase$pushReaction.set(nmsState, CoreReflections.instance$PushReaction$values[settings.pushReaction().ordinal()]); + boolean canOcclude = settings.canOcclude() == Tristate.UNDEFINED ? BlockStateUtils.isOcclude(nmsVisualState) : settings.canOcclude().asBoolean(); + CoreReflections.field$BlockStateBase$canOcclude.set(nmsState, canOcclude); + boolean useShapeForLightOcclusion = settings.useShapeForLightOcclusion() == Tristate.UNDEFINED ? CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.getBoolean(nmsVisualState) : settings.useShapeForLightOcclusion().asBoolean(); + CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.set(nmsState, useShapeForLightOcclusion); + CoreReflections.field$BlockStateBase$isRedstoneConductor.set(nmsState, settings.isRedstoneConductor().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE); + CoreReflections.field$BlockStateBase$isSuffocating.set(nmsState, settings.isSuffocating().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE); + CoreReflections.field$BlockStateBase$isViewBlocking.set(nmsState, settings.isViewBlocking() == Tristate.UNDEFINED ? settings.isSuffocating().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE : (settings.isViewBlocking().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE)); + + DelegatingBlock nmsBlock = (DelegatingBlock) BlockStateUtils.getBlockOwner(nmsState); + ObjectHolder shapeHolder = nmsBlock.shapeDelegate(); + shapeHolder.bindValue(new BukkitBlockShape(nmsVisualState, Optional.ofNullable(state.settings().supportShapeBlockState()).map(it -> Objects.requireNonNull(createVanillaBlockState(it), "Illegal block state: " + it).literalObject()).orElse(null))); + ObjectHolder behaviorHolder = nmsBlock.behaviorDelegate(); + behaviorHolder.bindValue(state.behavior()); + + CoreReflections.field$BlockBehaviour$explosionResistance.set(nmsBlock, settings.resistance()); + CoreReflections.field$BlockBehaviour$friction.set(nmsBlock, settings.friction()); + CoreReflections.field$BlockBehaviour$speedFactor.set(nmsBlock, settings.speedFactor()); + CoreReflections.field$BlockBehaviour$jumpFactor.set(nmsBlock, settings.jumpFactor()); + CoreReflections.field$BlockBehaviour$soundType.set(nmsBlock, SoundUtils.toSoundType(settings.sounds())); + + CoreReflections.method$BlockStateBase$initCache.invoke(nmsState); + boolean isConditionallyFullOpaque = canOcclude & useShapeForLightOcclusion; + if (!VersionHelper.isOrAbove1_21_2()) { + CoreReflections.field$BlockStateBase$isConditionallyFullOpaque.set(nmsState, isConditionallyFullOpaque); + } + + if (VersionHelper.isOrAbove1_21_2()) { + int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$lightBlock.getInt(nmsVisualState); + CoreReflections.field$BlockStateBase$lightBlock.set(nmsState, blockLight); + boolean propagatesSkylightDown = settings.propagatesSkylightDown() == Tristate.UNDEFINED ? CoreReflections.field$BlockStateBase$propagatesSkylightDown.getBoolean(nmsVisualState) : settings.propagatesSkylightDown().asBoolean(); + CoreReflections.field$BlockStateBase$propagatesSkylightDown.set(nmsState, propagatesSkylightDown); + } else { + Object cache = CoreReflections.field$BlockStateBase$cache.get(nmsState); + int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$Cache$lightBlock.getInt(CoreReflections.field$BlockStateBase$cache.get(nmsVisualState)); + CoreReflections.field$BlockStateBase$Cache$lightBlock.set(cache, blockLight); + boolean propagatesSkylightDown = settings.propagatesSkylightDown() == Tristate.UNDEFINED ? CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.getBoolean(CoreReflections.field$BlockStateBase$cache.get(nmsVisualState)) : settings.propagatesSkylightDown().asBoolean(); + CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.set(cache, propagatesSkylightDown); + if (!isConditionallyFullOpaque) { + CoreReflections.field$BlockStateBase$opacityIfCached.set(nmsState, blockLight); + } + } + + CoreReflections.field$BlockStateBase$fluidState.set(nmsState, settings.fluidState() ? MFluids.WATER$defaultState : MFluids.EMPTY$defaultState); + CoreReflections.field$BlockStateBase$isRandomlyTicking.set(nmsState, settings.isRandomlyTicking()); + Object holder = BukkitCraftEngine.instance().blockManager().getMinecraftBlockHolder(state.customBlockState().registryId()); + Set tags = new HashSet<>(); + for (Key tag : settings.tags()) { + tags.add(CoreReflections.method$TagKey$create.invoke(null, MRegistries.BLOCK, KeyUtils.toResourceLocation(tag))); + } + CoreReflections.field$Holder$Reference$tags.set(holder, tags); + if (settings.burnable()) { + this.igniteOdds.put(nmsBlock, settings.burnChance()); + this.burnOdds.put(nmsBlock, settings.fireSpreadChance()); + this.burnableBlocks.add(nmsBlock); + } + } catch (ReflectiveOperationException e) { + this.plugin.logger().warn("Failed to apply platform block settings for block state " + state, e); + } } - @NotNull - public Map> blockAppearanceArranger() { - return this.blockAppearanceArranger; + private BlockSounds toBlockSounds(Object soundType) throws ReflectiveOperationException { + return new BlockSounds( + toSoundData(CoreReflections.field$SoundType$breakSound.get(soundType), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_0_8), + toSoundData(CoreReflections.field$SoundType$stepSound.get(soundType), SoundData.SoundValue.FIXED_0_15, SoundData.SoundValue.FIXED_1), + toSoundData(CoreReflections.field$SoundType$placeSound.get(soundType), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_0_8), + toSoundData(CoreReflections.field$SoundType$hitSound.get(soundType), SoundData.SoundValue.FIXED_0_5, SoundData.SoundValue.FIXED_0_5), + toSoundData(CoreReflections.field$SoundType$fallSound.get(soundType), SoundData.SoundValue.FIXED_0_5, SoundData.SoundValue.FIXED_0_75) + ); } - @NotNull - public Map> realBlockArranger() { - return this.realBlockArranger; + private SoundData toSoundData(Object soundEvent, SoundData.SoundValue volume, SoundData.SoundValue pitch) { + Key soundId = KeyUtils.resourceLocationToKey(FastNMS.INSTANCE.field$SoundEvent$location(soundEvent)); + return new SoundData(soundId, volume, pitch); } private void initMirrorRegistry() { int size = RegistryUtils.currentBlockRegistrySize(); BlockStateWrapper[] states = new BlockStateWrapper[size]; - for (int i = 0; i < size; i++) { - states[i] = new BukkitBlockStateWrapper(BlockStateUtils.idToBlockState(i), i); + for (int i = 0; i < this.vanillaBlockStateCount; i++) { + states[i] = new BukkitVanillaBlockStateWrapper(BlockStateUtils.idToBlockState(i), i); } - BlockRegistryMirror.init(states, new BukkitBlockStateWrapper(MBlocks.STONE$defaultState, BlockStateUtils.blockStateToId(MBlocks.STONE$defaultState))); - } - - private void registerEmptyBlock() { - Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.BLOCK).registerForHolder(ResourceKey.create(BuiltInRegistries.BLOCK.key().location(), Key.withDefaultNamespace("empty"))); - EmptyBlock emptyBlock = new EmptyBlock(Key.withDefaultNamespace("empty"), holder); - holder.bindValue(emptyBlock); - } - - private void resetPacketConsumers() { - Map finalMapping = new HashMap<>(this.blockAppearanceMapper); - int stoneId = BlockStateUtils.blockStateToId(MBlocks.STONE$defaultState); - for (int custom : this.internalId2StateId.values()) { - finalMapping.put(custom, stoneId); + for (int i = this.vanillaBlockStateCount; i < size; i++) { + states[i] = new BukkitCustomBlockStateWrapper(BlockStateUtils.idToBlockState(i), i); } - finalMapping.putAll(this.tempBlockAppearanceConvertor); - PacketConsumers.initBlocks(finalMapping, RegistryUtils.currentBlockRegistrySize()); + BlockRegistryMirror.init(states, states[BlockStateUtils.blockStateToId(MBlocks.STONE$defaultState)]); } - private void initVanillaRegistry() { - int vanillaStateCount = RegistryUtils.currentBlockRegistrySize(); - this.plugin.logger().info("Vanilla block count: " + vanillaStateCount); - BlockStateUtils.init(vanillaStateCount); - } - - @Override - protected CustomBlock.Builder platformBuilder(Key id) { - return BukkitCustomBlock.builder(id); - } - - @SuppressWarnings("unchecked") - private void registerBlocks() { - this.plugin.logger().info("Registering blocks. Please wait..."); + // 注册服务端侧的真实方块 + private void registerServerSideCustomBlocks(int count) { + // 这个会影响全局调色盘 + if (MiscUtils.ceilLog2(this.vanillaBlockStateCount + count) == MiscUtils.ceilLog2(this.vanillaBlockStateCount)) { + PalettedContainer.NEED_DOWNGRADE = false; + } try { - ImmutableMap.Builder builder1 = ImmutableMap.builder(); - ImmutableMap.Builder builder2 = ImmutableMap.builder(); - ImmutableMap.Builder> builder3 = ImmutableMap.builder(); - ImmutableMap.Builder builder4 = ImmutableMap.builder(); - Set affectedBlockSounds = new HashSet<>(); - Map> affectedDoors = new IdentityHashMap<>(); - Set affectedBlocks = new HashSet<>(); - List order = new ArrayList<>(); - unfreezeRegistry(); - - int counter = 0; - for (Map.Entry baseBlockAndItsCount : this.registeredRealBlockSlots.entrySet()) { - counter = registerBlockVariants(baseBlockAndItsCount, counter, builder1, builder2, builder3, builder4, affectedBlockSounds, order); + for (int i = 0; i < count; i++) { + Key customBlockId = BlockManager.createCustomBlockKey(i); + DelegatingBlock customBlock; + try { + customBlock = BlockGenerator.generateBlock(customBlockId); + } catch (Throwable t) { + CraftEngine.instance().logger().warn("Failed to generate custom block " + customBlockId, t); + break; + } + this.customBlocks[i] = customBlock; + try { + Object resourceLocation = KeyUtils.toResourceLocation(customBlockId); + Object blockHolder = CoreReflections.method$Registry$registerForHolder.invoke(null, MBuiltInRegistries.BLOCK, resourceLocation, customBlock); + this.customBlockHolders[i] = blockHolder; + CoreReflections.method$Holder$Reference$bindValue.invoke(blockHolder, customBlock); + CoreReflections.field$Holder$Reference$tags.set(blockHolder, Set.of()); + DelegatingBlockState newBlockState = (DelegatingBlockState) FastNMS.INSTANCE.method$Block$defaultState(customBlock); + this.customBlockStates[i] = newBlockState; + CoreReflections.method$IdMapper$add.invoke(CoreReflections.instance$Block$BLOCK_STATE_REGISTRY, newBlockState); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to register custom block " + customBlockId, e); + } } - + } finally { freezeRegistry(); - this.plugin.logger().info("Registered block count: " + counter); - this.customBlockCount = counter; - this.internalId2StateId = builder1.build(); - this.stateId2BlockHolder = builder2.build(); - this.realBlockArranger = builder3.build(); - this.registeredBlocks = builder4.build(); - this.blockRegisterOrder = ImmutableList.copyOf(order); - if (MCUtils.ceilLog2(BlockStateUtils.vanillaStateSize() + counter) == MCUtils.ceilLog2(BlockStateUtils.vanillaStateSize())) { - PalettedContainer.NEED_DOWNGRADE = false; - } - for (Object block : (Iterable) MBuiltInRegistries.BLOCK) { - Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(block); - if (affectedBlockSounds.contains(soundType)) { - Object state = FastNMS.INSTANCE.method$Block$defaultState(block); - if (BlockStateUtils.isVanillaBlock(state)) { - affectedBlocks.add(block); - } - } - } - - affectedBlocks.remove(MBlocks.FIRE); - affectedBlocks.remove(MBlocks.SOUL_FIRE); - - this.affectedSoundBlocks = ImmutableSet.copyOf(affectedBlocks); - - ImmutableMap.Builder soundMapperBuilder = ImmutableMap.builder(); - for (Object soundType : affectedBlockSounds) { - for (Field field : List.of(CoreReflections.field$SoundType$placeSound, CoreReflections.field$SoundType$fallSound, CoreReflections.field$SoundType$hitSound, CoreReflections.field$SoundType$stepSound, CoreReflections.field$SoundType$breakSound)) { - Object soundEvent = field.get(soundType); - Key previousId = Key.of(FastNMS.INSTANCE.field$SoundEvent$location(soundEvent).toString()); - soundMapperBuilder.put(previousId, Key.of(previousId.namespace(), "replaced." + previousId.value())); - } - } - - Predicate predicate = it -> this.realBlockArranger.containsKey(it); - Consumer soundCallback = s -> soundMapperBuilder.put(s, Key.of("replaced." + s.value())); - BiConsumer> affectedBlockCallback = affectedDoors::put; - Function soundMapper = (k) -> SoundData.of(k, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f)); - collectDoorSounds(predicate, Sounds.WOODEN_TRAPDOOR_OPEN, Sounds.WOODEN_TRAPDOOR_CLOSE, Sounds.WOODEN_TRAPDOORS, soundMapper, soundCallback, affectedBlockCallback); - collectDoorSounds(predicate, Sounds.NETHER_WOOD_TRAPDOOR_OPEN, Sounds.NETHER_WOOD_TRAPDOOR_CLOSE, Sounds.NETHER_TRAPDOORS, soundMapper, soundCallback, affectedBlockCallback); - collectDoorSounds(predicate, Sounds.BAMBOO_WOOD_TRAPDOOR_OPEN, Sounds.BAMBOO_WOOD_TRAPDOOR_CLOSE, Sounds.BAMBOO_TRAPDOORS, soundMapper, soundCallback, affectedBlockCallback); - collectDoorSounds(predicate, Sounds.CHERRY_WOOD_TRAPDOOR_OPEN, Sounds.CHERRY_WOOD_TRAPDOOR_CLOSE, Sounds.CHERRY_TRAPDOORS, soundMapper, soundCallback, affectedBlockCallback); - collectDoorSounds(predicate, Sounds.COPPER_TRAPDOOR_OPEN, Sounds.COPPER_TRAPDOOR_CLOSE, Sounds.COPPER_TRAPDOORS, soundMapper, soundCallback, affectedBlockCallback); - collectDoorSounds(predicate, Sounds.WOODEN_DOOR_OPEN, Sounds.WOODEN_DOOR_CLOSE, Sounds.WOODEN_DOORS, soundMapper, soundCallback, affectedBlockCallback); - collectDoorSounds(predicate, Sounds.NETHER_WOOD_DOOR_OPEN, Sounds.NETHER_WOOD_DOOR_CLOSE, Sounds.NETHER_DOORS, soundMapper, soundCallback, affectedBlockCallback); - collectDoorSounds(predicate, Sounds.BAMBOO_WOOD_DOOR_OPEN, Sounds.BAMBOO_WOOD_DOOR_CLOSE, Sounds.BAMBOO_DOORS, soundMapper, soundCallback, affectedBlockCallback); - collectDoorSounds(predicate, Sounds.CHERRY_WOOD_DOOR_OPEN, Sounds.CHERRY_WOOD_DOOR_CLOSE, Sounds.CHERRY_DOORS, soundMapper, soundCallback, affectedBlockCallback); - collectDoorSounds(predicate, Sounds.COPPER_DOOR_OPEN, Sounds.COPPER_DOOR_CLOSE, Sounds.COPPER_DOORS, soundMapper, soundCallback, affectedBlockCallback); - collectDoorSounds(predicate, Sounds.WOODEN_FENCE_GATE_OPEN, Sounds.WOODEN_FENCE_GATE_CLOSE, Sounds.WOODEN_FENCE_GATES, soundMapper, soundCallback, affectedBlockCallback); - collectDoorSounds(predicate, Sounds.NETHER_WOOD_FENCE_GATE_OPEN, Sounds.NETHER_WOOD_FENCE_GATE_CLOSE, Sounds.NETHER_FENCE_GATES, soundMapper, soundCallback, affectedBlockCallback); - collectDoorSounds(predicate, Sounds.BAMBOO_WOOD_FENCE_GATE_OPEN, Sounds.BAMBOO_WOOD_FENCE_GATE_CLOSE, Sounds.BAMBOO_FENCE_GATES, soundMapper, soundCallback, affectedBlockCallback); - collectDoorSounds(predicate, Sounds.CHERRY_WOOD_FENCE_GATE_OPEN, Sounds.CHERRY_WOOD_FENCE_GATE_CLOSE, Sounds.CHERRY_FENCE_GATES, soundMapper, soundCallback, affectedBlockCallback); - this.affectedOpenableBlockSounds = ImmutableMap.copyOf(affectedDoors); - this.soundMapper = soundMapperBuilder.buildKeepingLast(); - } catch (Throwable e) { - plugin.logger().warn("Failed to inject blocks.", e); - } - } - - private void collectDoorSounds(Predicate isUsedForCustomBlock, - Key openSound, - Key closeSound, - List doors, - Function soundMapper, - Consumer soundCallback, - BiConsumer> affectedBlockCallback) { - for (Key d : doors) { - if (isUsedForCustomBlock.test(d)) { - soundCallback.accept(openSound); - soundCallback.accept(closeSound); - for (Key door : doors) { - Object block = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(door)); - if (block != null) { - affectedBlockCallback.accept(block, Pair.of(soundMapper.apply(Key.of("replaced." + openSound.value())), soundMapper.apply(Key.of("replaced." + closeSound.value())))); - } - } - break; - } } } public Object cachedUpdateTagsPacket() { - return cachedUpdateTagsPacket; + return this.cachedUpdateTagsPacket; } - private void loadMappingsAndAdditionalBlocks() { - this.plugin.logger().info("Loading mappings.yml."); - Path mappingsFile = this.plugin.dataFolderPath().resolve("mappings.yml"); - if (!Files.exists(mappingsFile)) { - this.plugin.saveResource("mappings.yml"); - } - Path additionalFile = this.plugin.dataFolderPath().resolve("additional-real-blocks.yml"); - if (!Files.exists(additionalFile)) { - this.plugin.saveResource("additional-real-blocks.yml"); - } - Yaml yaml = new Yaml(new StringKeyConstructor(mappingsFile, new LoaderOptions())); - Map blockTypeCounter = new LinkedHashMap<>(); - try (InputStream is = Files.newInputStream(mappingsFile)) { - Map blockStateMappings = loadBlockStateMappings(yaml.load(is)); - this.validateBlockStateMappings(mappingsFile, blockStateMappings); - Map stateMap = new Int2ObjectOpenHashMap<>(); - Map appearanceMapper = new Int2IntOpenHashMap(); - Map> appearanceArranger = new HashMap<>(); - for (Map.Entry entry : blockStateMappings.entrySet()) { - this.processBlockStateMapping(mappingsFile, entry, stateMap, blockTypeCounter, appearanceMapper, appearanceArranger); - } - this.blockAppearanceMapper = ImmutableMap.copyOf(appearanceMapper); - this.blockAppearanceArranger = ImmutableMap.copyOf(appearanceArranger); - this.plugin.logger().info("Freed " + this.blockAppearanceMapper.size() + " block state appearances."); - } catch (IOException e) { - throw new RuntimeException("Failed to init mappings.yml", e); - } - try (InputStream is = Files.newInputStream(additionalFile)) { - this.registeredRealBlockSlots = this.buildRegisteredRealBlockSlots(blockTypeCounter, yaml.load(is)); - } catch (IOException e) { - throw new RuntimeException("Failed to init additional-real-blocks.yml", e); - } - } - - private void recordVanillaNoteBlocks() { + private void markVanillaNoteBlocks() { try { - Object resourceLocation = KeyUtils.toResourceLocation(BlockKeys.NOTE_BLOCK); - Object block = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, resourceLocation); + Object block = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(BlockKeys.NOTE_BLOCK)); Object stateDefinition = CoreReflections.field$Block$StateDefinition.get(block); @SuppressWarnings("unchecked") ImmutableList states = (ImmutableList) CoreReflections.field$StateDefinition$states.get(stateDefinition); - for (Object state : states) { - BlockStateUtils.CLIENT_SIDE_NOTE_BLOCKS.put(state, new Object()); - } + CLIENT_SIDE_NOTE_BLOCKS.addAll(states); } catch (ReflectiveOperationException e) { - plugin.logger().warn("Failed to init vanilla note block", e); - } - } - - @Nullable - public Key replaceSoundIfExist(Key id) { - return this.soundMapper.get(id); - } - - public boolean isBlockSoundRemoved(Object block) { - return this.affectedSoundBlocks.contains(block); - } - - public boolean isOpenableBlockSoundRemoved(Object block) { - return this.affectedOpenableBlockSounds.containsKey(block); - } - - public SoundData getRemovedOpenableBlockSound(Object block, boolean open) { - return open ? this.affectedOpenableBlockSounds.get(block).left() : this.affectedOpenableBlockSounds.get(block).right(); - } - - private Map loadBlockStateMappings(Map mappings) { - Map blockStateMappings = new LinkedHashMap<>(); - for (Map.Entry entry : mappings.entrySet()) { - if (entry.getValue() instanceof String afterValue) { - blockStateMappings.put(entry.getKey(), afterValue); - } - } - return blockStateMappings; - } - - private void validateBlockStateMappings(Path mappingFile, Map blockStateMappings) { - Map temp = new HashMap<>(blockStateMappings); - for (Map.Entry entry : temp.entrySet()) { - String state = entry.getValue(); - if (blockStateMappings.containsKey(state)) { - String after = blockStateMappings.remove(state); - plugin.logger().warn(mappingFile, "'" + state + ": " + after + "' is invalid because '" + state + "' has already been used as a base block."); - } - } - } - - private void processBlockStateMapping(Path mappingFile, - Map.Entry entry, - Map stateMap, - Map counter, - Map mapper, - Map> arranger) { - Object before = createBlockState(mappingFile, entry.getKey()); - Object after = createBlockState(mappingFile, entry.getValue()); - if (before == null || after == null) return; - - int beforeId = BlockStateUtils.blockStateToId(before); - int afterId = BlockStateUtils.blockStateToId(after); - - Integer previous = mapper.put(beforeId, afterId); - if (previous == null) { - Key key = blockOwnerFromString(entry.getKey()); - counter.compute(key, (k, count) -> count == null ? 1 : count + 1); - stateMap.put(beforeId, entry.getKey()); - stateMap.put(afterId, entry.getValue()); - arranger.computeIfAbsent(key, (k) -> new IntArrayList()).add(beforeId); - } else { - String previousState = stateMap.get(previous); - plugin.logger().warn(mappingFile, "Duplicate entry: '" + previousState + "' equals '" + entry.getKey() + "'"); - } - } - - private Key blockOwnerFromString(String stateString) { - int index = stateString.indexOf('['); - if (index == -1) { - return Key.of(stateString); - } else { - return Key.of(stateString.substring(0, index)); - } - } - - private Object createBlockState(Path mappingFile, String state) { - try { - Object registryOrLookUp = MBuiltInRegistries.BLOCK; - if (CoreReflections.method$Registry$asLookup != null) { - registryOrLookUp = CoreReflections.method$Registry$asLookup.invoke(registryOrLookUp); - } - Object result = CoreReflections.method$BlockStateParser$parseForBlock.invoke(null, registryOrLookUp, state, false); - return CoreReflections.method$BlockStateParser$BlockResult$blockState.invoke(result); - } catch (Exception e) { - this.plugin.logger().warn(mappingFile, "'" + state + "' is not a valid block state."); - return null; - } - } - - private LinkedHashMap buildRegisteredRealBlockSlots(Map counter, Map additionalYaml) { - LinkedHashMap map = new LinkedHashMap<>(counter); - for (Map.Entry entry : additionalYaml.entrySet()) { - Key blockType = Key.of(entry.getKey()); - if (entry.getValue() instanceof Integer i) { - int previous = map.getOrDefault(blockType, 0); - if (previous == 0) { - map.put(blockType, i); - this.plugin.logger().info("Loaded " + blockType + " with " + i + " real block states"); - } else { - map.put(blockType, i + previous); - this.plugin.logger().info("Loaded " + blockType + " with " + previous + " appearances and " + (i + previous) + " real block states"); - } - } - } - return map; - } - - private void unfreezeRegistry() throws IllegalAccessException { - CoreReflections.field$MappedRegistry$frozen.set(MBuiltInRegistries.BLOCK, false); - CoreReflections.field$MappedRegistry$unregisteredIntrusiveHolders.set(MBuiltInRegistries.BLOCK, new IdentityHashMap<>()); - } - - private void freezeRegistry() throws IllegalAccessException { - CoreReflections.field$MappedRegistry$frozen.set(MBuiltInRegistries.BLOCK, true); - } - - private int registerBlockVariants(Map.Entry blockWithCount, - int counter, - ImmutableMap.Builder builder1, - ImmutableMap.Builder builder2, - ImmutableMap.Builder> builder3, - ImmutableMap.Builder builder4, - Set affectSoundTypes, - List order) throws Exception { - Key clientSideBlockType = blockWithCount.getKey(); - boolean isNoteBlock = clientSideBlockType.equals(BlockKeys.NOTE_BLOCK); - Object clientSideBlock = getBlockFromRegistry(createResourceLocation(clientSideBlockType)); - int amount = blockWithCount.getValue(); - - List stateIds = new IntArrayList(); - - for (int i = 0; i < amount; i++) { - Key realBlockKey = createRealBlockKey(clientSideBlockType, i); - Object blockProperties = createBlockProperties(realBlockKey); - - Object newRealBlock; - Object newBlockState; - Object blockHolder; - Object resourceLocation = createResourceLocation(realBlockKey); - - try { - newRealBlock = BlockGenerator.generateBlock(clientSideBlockType, clientSideBlock, blockProperties); - } catch (Throwable throwable) { - this.plugin.logger().warn("Failed to generate dynamic block class", throwable); - continue; - } - - blockHolder = CoreReflections.method$Registry$registerForHolder.invoke(null, MBuiltInRegistries.BLOCK, resourceLocation, newRealBlock); - CoreReflections.method$Holder$Reference$bindValue.invoke(blockHolder, newRealBlock); - CoreReflections.field$Holder$Reference$tags.set(blockHolder, Set.of()); - - newBlockState = FastNMS.INSTANCE.method$Block$defaultState(newRealBlock); - CoreReflections.method$IdMapper$add.invoke(CoreReflections.instance$Block$BLOCK_STATE_REGISTRY, newBlockState); - - if (isNoteBlock) { - BlockStateUtils.CLIENT_SIDE_NOTE_BLOCKS.put(newBlockState, new Object()); - } - - int stateId = BlockStateUtils.vanillaStateSize() + counter; - - builder1.put(realBlockKey, stateId); - builder2.put(stateId, blockHolder); - builder4.put(realBlockKey, (DelegatingBlock) newRealBlock); - stateIds.add(stateId); - - this.blocksToDeceive.add(Tuple.of(newRealBlock, clientSideBlockType, isNoteBlock)); - order.add(realBlockKey); - counter++; - } - - builder3.put(clientSideBlockType, stateIds); - Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(clientSideBlock); - affectSoundTypes.add(soundType); - return counter; - } - - private Object createResourceLocation(Key key) { - return FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath(key.namespace(), key.value()); - } - - private Object getBlockFromRegistry(Object resourceLocation) { - return FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, resourceLocation); - } - - private Key createRealBlockKey(Key replacedBlock, int index) { - return Key.of(Key.DEFAULT_NAMESPACE, replacedBlock.value() + "_" + index); - } - - private Object createBlockProperties(Key realBlockKey) throws Exception { - Object blockProperties = CoreReflections.method$BlockBehaviour$Properties$of.invoke(null); - Object realBlockResourceLocation = createResourceLocation(realBlockKey); - Object realBlockResourceKey = CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.BLOCK, realBlockResourceLocation); - if (CoreReflections.field$BlockBehaviour$Properties$id != null) { - CoreReflections.field$BlockBehaviour$Properties$id.set(blockProperties, realBlockResourceKey); - } - return blockProperties; - } - - @SuppressWarnings("unchecked") - private void deceiveBukkit() { - try { - Map magicMap = (Map) CraftBukkitReflections.field$CraftMagicNumbers$BLOCK_MATERIAL.get(null); - Map factories = (Map) CraftBukkitReflections.field$CraftBlockStates$FACTORIES.get(null); - for (Tuple tuple : this.blocksToDeceive) { - deceiveBukkit(tuple.left(), tuple.mid(), tuple.right(), magicMap, factories); - } - this.blocksToDeceive.clear(); - } catch (ReflectiveOperationException e) { - this.plugin.logger().warn("Failed to deceive bukkit", e); - } - } - - private void deceiveBukkit(Object newBlock, Key replacedBlock, boolean isNoteBlock, Map magicMap, Map factories) { - if (isNoteBlock) { - magicMap.put(newBlock, Material.STONE); - } else { - Material material = org.bukkit.Registry.MATERIAL.get(new NamespacedKey(replacedBlock.namespace(), replacedBlock.value())); - if (CraftBukkitReflections.clazz$CraftBlockStates$BlockEntityStateFactory.isInstance(factories.get(material))) { - magicMap.put(newBlock, Material.STONE); - } else { - magicMap.put(newBlock, material); - } + this.plugin.logger().warn("Failed to init vanilla note block", e); } } @Override - protected int getBlockRegistryId(Key id) { + protected void setVanillaBlockTags(Key id, List tags) { Object block = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(id)); - return FastNMS.INSTANCE.method$IdMap$getId(MBuiltInRegistries.BLOCK, block).orElseThrow(() -> new IllegalStateException("Block " + id + " not found")); + this.clientBoundTags.put(FastNMS.INSTANCE.method$IdMap$getId(MBuiltInRegistries.BLOCK, block).orElseThrow(() -> new IllegalStateException("Block " + id + " not found")), tags); + } + + public boolean isPlaceSoundMissing(Object sound) { + return this.missingPlaceSounds.contains(sound); + } + + public boolean isBreakSoundMissing(Object sound) { + return this.missingBreakSounds.contains(sound); + } + + public boolean isHitSoundMissing(Object sound) { + return this.missingHitSounds.contains(sound); + } + + public boolean isStepSoundMissing(Object sound) { + return this.missingStepSounds.contains(sound); + } + + public boolean isInteractSoundMissing(Key blockType) { + return this.missingInteractSoundBlocks.contains(blockType); + } + + private void unfreezeRegistry() { + try { + CoreReflections.field$MappedRegistry$frozen.set(MBuiltInRegistries.BLOCK, false); + CoreReflections.field$MappedRegistry$unregisteredIntrusiveHolders.set(MBuiltInRegistries.BLOCK, new IdentityHashMap<>()); + } catch (IllegalAccessException e) { + this.plugin.logger().warn("Failed to unfreeze block registry", e); + } + } + + private void freezeRegistry() { + try { + CoreReflections.field$MappedRegistry$frozen.set(MBuiltInRegistries.BLOCK, true); + } catch (IllegalAccessException e) { + this.plugin.logger().warn("Failed to freeze block registry", e); + } + } + + @SuppressWarnings("unchecked") + private void deceiveBukkitRegistry() { + try { + Map magicMap = (Map) CraftBukkitReflections.field$CraftMagicNumbers$BLOCK_MATERIAL.get(null); + for (DelegatingBlock customBlock : this.customBlocks) { + magicMap.put(customBlock, Material.STONE); + } + } catch (ReflectiveOperationException e) { + this.plugin.logger().warn("Failed to deceive bukkit magic blocks", e); + } } @Override protected boolean isVanillaBlock(Key id) { - if (!id.namespace().equals("minecraft")) { + if (!id.namespace().equals("minecraft")) return false; - } - if (id.value().equals("air")) { + if (id.value().equals("air")) return true; - } return FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(id)) != MBlocks.AIR; } + + public boolean isBurnable(Object blockState) { + Object blockOwner = BlockStateUtils.getBlockOwner(blockState); + return this.igniteOdds.getOrDefault(blockOwner, 0) > 0; + } + + @Override + public int vanillaBlockStateCount() { + return this.vanillaBlockStateCount; + } + + @SuppressWarnings("DuplicatedCode") + @Override + protected void processSounds() { + Set affectedBlockSoundTypes = new HashSet<>(); + for (BlockStateWrapper vanillaBlockState : super.tempVisualBlockStatesInUse) { + affectedBlockSoundTypes.add(FastNMS.INSTANCE.method$BlockBehaviour$BlockStateBase$getSoundType(vanillaBlockState.literalObject())); + } + + Set placeSounds = new HashSet<>(); + Set breakSounds = new HashSet<>(); + Set stepSounds = new HashSet<>(); + Set hitSounds = new HashSet<>(); + + for (Object soundType : affectedBlockSoundTypes) { + placeSounds.add(FastNMS.INSTANCE.field$SoundEvent$location(FastNMS.INSTANCE.field$SoundType$placeSound(soundType))); + breakSounds.add(FastNMS.INSTANCE.field$SoundEvent$location(FastNMS.INSTANCE.field$SoundType$breakSound(soundType))); + stepSounds.add(FastNMS.INSTANCE.field$SoundEvent$location(FastNMS.INSTANCE.field$SoundType$stepSound(soundType))); + hitSounds.add(FastNMS.INSTANCE.field$SoundEvent$location(FastNMS.INSTANCE.field$SoundType$hitSound(soundType))); + } + + ImmutableMap.Builder soundReplacementBuilder = ImmutableMap.builder(); + for (Object soundId : placeSounds) { + Key previousId = KeyUtils.resourceLocationToKey(soundId); + soundReplacementBuilder.put(previousId, Key.of(previousId.namespace(), "replaced." + previousId.value())); + } + for (Object soundId : breakSounds) { + Key previousId = KeyUtils.resourceLocationToKey(soundId); + soundReplacementBuilder.put(previousId, Key.of(previousId.namespace(), "replaced." + previousId.value())); + } + for (Object soundId : stepSounds) { + Key previousId = KeyUtils.resourceLocationToKey(soundId); + soundReplacementBuilder.put(previousId, Key.of(previousId.namespace(), "replaced." + previousId.value())); + } + for (Object soundId : hitSounds) { + Key previousId = KeyUtils.resourceLocationToKey(soundId); + soundReplacementBuilder.put(previousId, Key.of(previousId.namespace(), "replaced." + previousId.value())); + } + + this.missingPlaceSounds = placeSounds; + this.missingBreakSounds = breakSounds; + this.missingHitSounds = hitSounds; + this.missingStepSounds = stepSounds; + + Set missingInteractSoundBlocks = new HashSet<>(); + + for (SoundSet soundSet : SoundSet.getAllSoundSets()) { + for (Key block : soundSet.blocks()) { + if (super.tempVisualBlocksInUse.contains(block)) { + Key openSound = soundSet.openSound(); + soundReplacementBuilder.put(openSound, Key.of(openSound.namespace(), "replaced." + openSound.value())); + Key closeSound = soundSet.closeSound(); + soundReplacementBuilder.put(closeSound, Key.of(closeSound.namespace(), "replaced." + closeSound.value())); + missingInteractSoundBlocks.addAll(soundSet.blocks()); + break; + } + } + } + + this.missingInteractSoundBlocks = missingInteractSoundBlocks; + this.soundReplacements = soundReplacementBuilder.buildKeepingLast(); + } + + @Override + protected CustomBlock createCustomBlock(@NotNull Holder.Reference holder, + @NotNull BlockStateVariantProvider variantProvider, + @NotNull Map>> events, + @Nullable LootTable lootTable) { + return new BukkitCustomBlock(holder, variantProvider, events, lootTable); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockStateWrapper.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockStateWrapper.java deleted file mode 100644 index cafb79317..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockStateWrapper.java +++ /dev/null @@ -1,23 +0,0 @@ -package net.momirealms.craftengine.bukkit.block; - -import net.momirealms.craftengine.core.block.BlockStateWrapper; - -public class BukkitBlockStateWrapper implements BlockStateWrapper { - private final Object blockState; - private final int registryId; - - public BukkitBlockStateWrapper(Object blockState, int registryId) { - this.blockState = blockState; - this.registryId = registryId; - } - - @Override - public Object literalObject() { - return this.blockState; - } - - @Override - public int registryId() { - return this.registryId; - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java index 84f1cbfe3..360e121ad 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java @@ -1,263 +1,27 @@ package net.momirealms.craftengine.bukkit.block; -import net.momirealms.craftengine.bukkit.block.behavior.UnsafeCompositeBlockBehavior; -import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries; -import net.momirealms.craftengine.bukkit.util.BlockStateUtils; -import net.momirealms.craftengine.bukkit.util.KeyUtils; -import net.momirealms.craftengine.bukkit.util.SoundUtils; -import net.momirealms.craftengine.core.block.*; -import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior; -import net.momirealms.craftengine.core.block.behavior.BlockBehaviors; -import net.momirealms.craftengine.core.block.behavior.EmptyBlockBehavior; -import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.block.AbstractCustomBlock; +import net.momirealms.craftengine.core.block.BlockStateVariantProvider; +import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.loot.LootTable; -import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.context.function.Function; -import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; -import net.momirealms.craftengine.core.registry.WritableRegistry; -import net.momirealms.craftengine.core.util.*; -import org.bukkit.Bukkit; -import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.List; +import java.util.Map; public final class BukkitCustomBlock extends AbstractCustomBlock { - private static final Object ALWAYS_FALSE = FastNMS.INSTANCE.method$StatePredicate$always(false); - private static final Object ALWAYS_TRUE = FastNMS.INSTANCE.method$StatePredicate$always(true); - private BukkitCustomBlock( - @NotNull Key id, + public BukkitCustomBlock( @NotNull Holder.Reference holder, - @NotNull Map> properties, - @NotNull Map appearances, - @NotNull Map variantMapper, - @NotNull BlockSettings settings, + @NotNull BlockStateVariantProvider variantProvider, @NotNull Map>> events, - @Nullable List> behavior, @Nullable LootTable lootTable ) { - super(id, holder, properties, appearances, variantMapper, settings, events, behavior, lootTable); - } - - @Override - protected BlockBehavior setupBehavior(List> behaviorConfig) { - if (behaviorConfig == null || behaviorConfig.isEmpty()) { - return new EmptyBlockBehavior(); - } else if (behaviorConfig.size() == 1) { - return BlockBehaviors.fromMap(this, behaviorConfig.getFirst()); - } else { - List behaviors = new ArrayList<>(); - for (Map config : behaviorConfig) { - behaviors.add((AbstractBlockBehavior) BlockBehaviors.fromMap(this, config)); - } - return new UnsafeCompositeBlockBehavior(this, behaviors); - } - } - - @SuppressWarnings("unchecked") - @Nullable - @Override - public LootTable lootTable() { - return (LootTable) super.lootTable(); - } - - @Override - protected void applyPlatformSettings() { - try { - for (ImmutableBlockState immutableBlockState : variantProvider().states()) { - if (immutableBlockState.vanillaBlockState() == null) { - CraftEngine.instance().logger().warn("Could not find vanilla block immutableBlockState for " + immutableBlockState + ". This might cause errors!"); - continue; - } else if (immutableBlockState.customBlockState() == null) { - CraftEngine.instance().logger().warn("Could not find custom block immutableBlockState for " + immutableBlockState + ". This might cause errors!"); - continue; - } - DelegatingBlockState nmsState = (DelegatingBlockState) immutableBlockState.customBlockState().literalObject(); - nmsState.setBlockState(immutableBlockState); - BlockSettings settings = immutableBlockState.settings(); - - // set block properties - CoreReflections.field$BlockStateBase$lightEmission.set(nmsState, settings.luminance()); - CoreReflections.field$BlockStateBase$burnable.set(nmsState, settings.burnable()); - CoreReflections.field$BlockStateBase$hardness.set(nmsState, settings.hardness()); - CoreReflections.field$BlockStateBase$replaceable.set(nmsState, settings.replaceable()); - Object mcMapColor = CoreReflections.method$MapColor$byId.invoke(null, settings.mapColor().id); - CoreReflections.field$BlockStateBase$mapColor.set(nmsState, mcMapColor); - Object mcInstrument = ((Object[]) CoreReflections.method$NoteBlockInstrument$values.invoke(null))[settings.instrument().ordinal()]; - CoreReflections.field$BlockStateBase$instrument.set(nmsState, mcInstrument); - Object pushReaction = ((Object[]) CoreReflections.method$PushReaction$values.invoke(null))[settings.pushReaction().ordinal()]; - CoreReflections.field$BlockStateBase$pushReaction.set(nmsState, pushReaction); - - boolean canOcclude = settings.canOcclude() == Tristate.UNDEFINED ? BlockStateUtils.isOcclude(immutableBlockState.vanillaBlockState().literalObject()) : settings.canOcclude().asBoolean(); - CoreReflections.field$BlockStateBase$canOcclude.set(nmsState, canOcclude); - - boolean useShapeForLightOcclusion = settings.useShapeForLightOcclusion() == Tristate.UNDEFINED ? CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.getBoolean(immutableBlockState.vanillaBlockState().literalObject()) : settings.useShapeForLightOcclusion().asBoolean(); - CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.set(nmsState, useShapeForLightOcclusion); - - CoreReflections.field$BlockStateBase$isRedstoneConductor.set(nmsState, settings.isRedstoneConductor().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE); - CoreReflections.field$BlockStateBase$isSuffocating.set(nmsState, settings.isSuffocating().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE); - CoreReflections.field$BlockStateBase$isViewBlocking.set(nmsState, settings.isViewBlocking() == Tristate.UNDEFINED ? settings.isSuffocating().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE : (settings.isViewBlocking().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE)); - - // set parent block properties - DelegatingBlock nmsBlock = (DelegatingBlock) BlockStateUtils.getBlockOwner(nmsState); - ObjectHolder shapeHolder = nmsBlock.shapeDelegate(); - shapeHolder.bindValue(new BukkitBlockShape(immutableBlockState.vanillaBlockState().literalObject(), Optional.ofNullable(immutableBlockState.settings().supportShapeBlockState()).map(it -> { - try { - Object blockState = BlockStateUtils.blockDataToBlockState(Bukkit.createBlockData(it)); - if (!BlockStateUtils.isVanillaBlock(blockState)) { - throw new IllegalArgumentException("BlockState is not a Vanilla block"); - } - return blockState; - } catch (IllegalArgumentException e) { - CraftEngine.instance().logger().warn("Illegal shape block state: " + it, e); - return null; - } - }).orElse(null))); - // bind behavior - ObjectHolder behaviorHolder = nmsBlock.behaviorDelegate(); - behaviorHolder.bindValue(super.behavior); - // set block side properties - CoreReflections.field$BlockBehaviour$explosionResistance.set(nmsBlock, settings.resistance()); - CoreReflections.field$BlockBehaviour$friction.set(nmsBlock, settings.friction()); - CoreReflections.field$BlockBehaviour$speedFactor.set(nmsBlock, settings.speedFactor()); - CoreReflections.field$BlockBehaviour$jumpFactor.set(nmsBlock, settings.jumpFactor()); - CoreReflections.field$BlockBehaviour$soundType.set(nmsBlock, SoundUtils.toSoundType(settings.sounds())); - // init cache - CoreReflections.method$BlockStateBase$initCache.invoke(nmsState); - boolean isConditionallyFullOpaque = canOcclude & useShapeForLightOcclusion; - if (!VersionHelper.isOrAbove1_21_2()) { - CoreReflections.field$BlockStateBase$isConditionallyFullOpaque.set(nmsState, isConditionallyFullOpaque); - } - // modify cache - if (VersionHelper.isOrAbove1_21_2()) { - int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$lightBlock.getInt(immutableBlockState.vanillaBlockState().literalObject()); - // set block light - CoreReflections.field$BlockStateBase$lightBlock.set(nmsState, blockLight); - // set propagates skylight - if (settings.propagatesSkylightDown() == Tristate.TRUE) { - CoreReflections.field$BlockStateBase$propagatesSkylightDown.set(nmsState, true); - } else if (settings.propagatesSkylightDown() == Tristate.FALSE) { - CoreReflections.field$BlockStateBase$propagatesSkylightDown.set(nmsState, false); - } else { - CoreReflections.field$BlockStateBase$propagatesSkylightDown.set(nmsState, CoreReflections.field$BlockStateBase$propagatesSkylightDown.getBoolean(immutableBlockState.vanillaBlockState().literalObject())); - } - } else { - Object cache = CoreReflections.field$BlockStateBase$cache.get(nmsState); - int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$Cache$lightBlock.getInt(CoreReflections.field$BlockStateBase$cache.get(immutableBlockState.vanillaBlockState().literalObject())); - // set block light - CoreReflections.field$BlockStateBase$Cache$lightBlock.set(cache, blockLight); - // set propagates skylight - if (settings.propagatesSkylightDown() == Tristate.TRUE) { - CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.set(cache, true); - } else if (settings.propagatesSkylightDown() == Tristate.FALSE) { - CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.set(cache, false); - } else { - CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.set(cache, CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.getBoolean(CoreReflections.field$BlockStateBase$cache.get(immutableBlockState.vanillaBlockState().literalObject()))); - } - if (!isConditionallyFullOpaque) { - CoreReflections.field$BlockStateBase$opacityIfCached.set(nmsState, blockLight); - } - } - // set fluid later - if (settings.fluidState()) { - CoreReflections.field$BlockStateBase$fluidState.set(nmsState, CoreReflections.method$FlowingFluid$getSource.invoke(MFluids.WATER, false)); - } else { - CoreReflections.field$BlockStateBase$fluidState.set(nmsState, MFluids.EMPTY$defaultState); - } - // set random tick later - CoreReflections.field$BlockStateBase$isRandomlyTicking.set(nmsState, settings.isRandomlyTicking()); - // bind tags - Object holder = BukkitCraftEngine.instance().blockManager().getMinecraftBlockHolder(immutableBlockState.customBlockState().registryId()); - Set tags = new HashSet<>(); - for (Key tag : settings.tags()) { - tags.add(CoreReflections.method$TagKey$create.invoke(null, MRegistries.BLOCK, KeyUtils.toResourceLocation(tag))); - } - CoreReflections.field$Holder$Reference$tags.set(holder, tags); - // set burning properties - if (settings.burnable()) { - CoreReflections.method$FireBlock$setFlammable.invoke(MBlocks.FIRE, nmsBlock, settings.burnChance(), settings.fireSpreadChance()); - } - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to init block settings", e); - } - } - - public static Builder builder(Key id) { - return new BuilderImpl(id); - } - - public static class BuilderImpl implements Builder { - protected final Key id; - protected Map> properties; - protected Map appearances; - protected Map variantMapper; - protected BlockSettings settings; - protected List> behavior; - protected LootTable lootTable; - protected Map>> events; - - public BuilderImpl(Key id) { - this.id = id; - } - - @Override - public Builder events(Map>> events) { - this.events = events; - return this; - } - - @Override - public Builder appearances(Map appearances) { - this.appearances = appearances; - return this; - } - - @Override - public Builder behavior(List> behavior) { - this.behavior = behavior; - return this; - } - - @Override - public Builder lootTable(LootTable lootTable) { - this.lootTable = lootTable; - return this; - } - - @Override - public Builder properties(Map> properties) { - this.properties = properties; - return this; - } - - @Override - public Builder settings(BlockSettings settings) { - this.settings = settings; - return this; - } - - @Override - public Builder variantMapper(Map variantMapper) { - this.variantMapper = variantMapper; - return this; - } - - @Override - public @NotNull CustomBlock build() { - // create or get block holder - Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.BLOCK).getOrRegisterForHolder(ResourceKey.create(BuiltInRegistries.BLOCK.key().location(), this.id)); - return new BukkitCustomBlock(this.id, holder, this.properties, this.appearances, this.variantMapper, this.settings, this.events, this.behavior, this.lootTable); - } + super(holder, variantProvider, events, lootTable); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlockStateWrapper.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlockStateWrapper.java new file mode 100644 index 000000000..f336c1cbb --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlockStateWrapper.java @@ -0,0 +1,46 @@ +package net.momirealms.craftengine.bukkit.block; + +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.core.block.AbstractBlockStateWrapper; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.util.Key; + +import java.util.Optional; + +public class BukkitCustomBlockStateWrapper extends AbstractBlockStateWrapper { + + public BukkitCustomBlockStateWrapper(Object blockState, int registryId) { + super(blockState, registryId); + } + + @Override + public Key ownerId() { + return getImmutableBlockState().map(state -> state.owner().value().id()).orElseGet(() -> BlockStateUtils.getBlockOwnerIdFromState(super.blockState)); + } + + @SuppressWarnings("unchecked") + @Override + public T getProperty(String propertyName) { + return (T) getImmutableBlockState().map(state -> { + Property property = state.owner().value().getProperty(propertyName); + if (property == null) + return null; + return state.getNullable(property); + }).orElse(null); + } + + @Override + public boolean hasProperty(String propertyName) { + return getImmutableBlockState().map(state -> state.owner().value().getProperty(propertyName) != null).orElse(false); + } + + @Override + public String getAsString() { + return getImmutableBlockState().map(ImmutableBlockState::toString).orElseGet(() -> BlockStateUtils.fromBlockData(super.blockState).getAsString()); + } + + public Optional getImmutableBlockState() { + return BlockStateUtils.getOptionalCustomBlockState(super.blockState); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitVanillaBlockStateWrapper.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitVanillaBlockStateWrapper.java new file mode 100644 index 000000000..9a13a5d13 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitVanillaBlockStateWrapper.java @@ -0,0 +1,36 @@ +package net.momirealms.craftengine.bukkit.block; + +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.core.block.AbstractBlockStateWrapper; +import net.momirealms.craftengine.core.block.StatePropertyAccessor; +import net.momirealms.craftengine.core.util.Key; + +public class BukkitVanillaBlockStateWrapper extends AbstractBlockStateWrapper { + private final StatePropertyAccessor accessor; + + public BukkitVanillaBlockStateWrapper(Object blockState, int registryId) { + super(blockState, registryId); + this.accessor = FastNMS.INSTANCE.createStatePropertyAccessor(blockState); + } + + @Override + public Key ownerId() { + return BlockStateUtils.getBlockOwnerIdFromState(super.blockState); + } + + @Override + public T getProperty(String propertyName) { + return this.accessor.getPropertyValue(propertyName); + } + + @Override + public boolean hasProperty(String propertyName) { + return this.accessor.hasProperty(propertyName); + } + + @Override + public String getAsString() { + return BlockStateUtils.fromBlockData(super.blockState).getAsString(); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/FallingBlockRemoveListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/FallingBlockRemoveListener.java deleted file mode 100644 index 5be81793d..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/FallingBlockRemoveListener.java +++ /dev/null @@ -1,51 +0,0 @@ -package net.momirealms.craftengine.bukkit.block; - -import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.util.BlockStateUtils; -import net.momirealms.craftengine.bukkit.world.BukkitWorld; -import net.momirealms.craftengine.core.block.ImmutableBlockState; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; -import net.momirealms.craftengine.core.world.WorldPosition; -import org.bukkit.entity.FallingBlock; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; - -import java.util.Optional; - -@SuppressWarnings("DuplicatedCode") -public final class FallingBlockRemoveListener implements Listener { - - @EventHandler - public void onFallingBlockBreak(org.bukkit.event.entity.EntityRemoveEvent event) { - if (event.getCause() == org.bukkit.event.entity.EntityRemoveEvent.Cause.DROP && event.getEntity() instanceof FallingBlock fallingBlock) { - try { - Object fallingBlockEntity = CraftBukkitReflections.field$CraftEntity$entity.get(fallingBlock); - boolean cancelDrop = (boolean) CoreReflections.field$FallingBlockEntity$cancelDrop.get(fallingBlockEntity); - if (cancelDrop) return; - Object blockState = CoreReflections.field$FallingBlockEntity$blockState.get(fallingBlockEntity); - Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState); - if (optionalCustomState.isEmpty()) return; - ImmutableBlockState customState = optionalCustomState.get(); - net.momirealms.craftengine.core.world.World world = new BukkitWorld(fallingBlock.getWorld()); - WorldPosition position = new WorldPosition(world, CoreReflections.field$Entity$xo.getDouble(fallingBlockEntity), CoreReflections.field$Entity$yo.getDouble(fallingBlockEntity), CoreReflections.field$Entity$zo.getDouble(fallingBlockEntity)); - ContextHolder.Builder builder = ContextHolder.builder() - .withParameter(DirectContextParameters.FALLING_BLOCK, true) - .withParameter(DirectContextParameters.POSITION, position); - for (Item item : customState.getDrops(builder, world, null)) { - world.dropItemNaturally(position, item); - } - Object entityData = CoreReflections.field$Entity$entityData.get(fallingBlockEntity); - boolean isSilent = (boolean) CoreReflections.method$SynchedEntityData$get.invoke(entityData, CoreReflections.instance$Entity$DATA_SILENT); - if (!isSilent) { - world.playBlockSound(position, customState.settings().sounds().destroySound()); - } - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to handle EntityRemoveEvent", e); - } - } - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/AbstractCanSurviveBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/AbstractCanSurviveBlockBehavior.java index 2ab895fe7..851371fa6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/AbstractCanSurviveBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/AbstractCanSurviveBlockBehavior.java @@ -7,7 +7,6 @@ import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; -import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldEvents; import net.momirealms.craftengine.core.world.WorldPosition; @@ -69,13 +68,8 @@ public abstract class AbstractCanSurviveBlockBehavior extends BukkitBlockBehavio FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(level, blockPos, thisBlock, this.delay); return state; } - if (!canSurvive(thisBlock, new Object[] {state, level, blockPos}, () -> true)) { - BlockPos pos = LocationUtils.fromBlockPos(blockPos); - ImmutableBlockState customState = optionalCustomState.get(); - net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); - WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos)); - world.playBlockSound(position, customState.settings().sounds().breakSound()); - FastNMS.INSTANCE.method$LevelAccessor$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, customState.customBlockState().registryId()); + if (!FastNMS.INSTANCE.method$BlockStateBase$canSurvive(state, level, blockPos)) { + FastNMS.INSTANCE.method$LevelAccessor$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, optionalCustomState.get().customBlockState().registryId()); return MBlocks.AIR$defaultState; } return state; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/AttachedStemBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/AttachedStemBlockBehavior.java new file mode 100644 index 000000000..f35d35159 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/AttachedStemBlockBehavior.java @@ -0,0 +1,99 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.DirectionUtils; +import net.momirealms.craftengine.core.block.BlockBehavior; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.properties.IntegerProperty; +import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.util.HorizontalDirection; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.VersionHelper; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.Callable; + +public class AttachedStemBlockBehavior extends BukkitBlockBehavior { + public static final Factory FACTORY = new Factory(); + private final Property facingProperty; + private final Key fruit; + private final Key stem; + + public AttachedStemBlockBehavior(CustomBlock customBlock, + Property facingProperty, + Key fruit, + Key stem) { + super(customBlock); + this.facingProperty = facingProperty; + this.fruit = fruit; + this.stem = stem; + } + + @Override + public boolean isPathFindable(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + return (VersionHelper.isOrAbove1_20_5() ? args[1] : args[3]).equals(CoreReflections.instance$PathComputationType$AIR) + && !FastNMS.INSTANCE.field$BlockBehavior$hasCollision(thisBlock) || (boolean) superMethod.call(); + } + + @Override + public Object updateShape(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object state = args[0]; + HorizontalDirection direction = DirectionUtils.fromNMSDirection(args[updateShape$direction]).toHorizontalDirection(); + Object neighborState = args[updateShape$neighborState]; + Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(state); + if (optionalCustomState.isEmpty() || direction != optionalCustomState.get().get(this.facingProperty)) { + return super.updateShape(thisBlock, args, superMethod); + } + Optional optionalCustomNeighborState = BlockStateUtils.getOptionalCustomBlockState(neighborState); + if (optionalCustomNeighborState.isPresent()) { + ImmutableBlockState customNeighborState = optionalCustomNeighborState.get(); + if (!customNeighborState.owner().value().id().equals(this.fruit)) { + Object stemBlock = resetStemBlock(); + if (stemBlock != null) return stemBlock; + } + } else { + if (this.stem.namespace().equals("minecraft")) { + Key neighborBlockId = BlockStateUtils.getBlockOwnerIdFromState(neighborState); + if (!neighborBlockId.equals(this.fruit)) { + Object stemBlock = resetStemBlock(); + if (stemBlock != null) return stemBlock; + } + } else { + Object stemBlock = resetStemBlock(); + if (stemBlock != null) return stemBlock; + } + } + return super.updateShape(thisBlock, args, superMethod); + } + + private Object resetStemBlock() { + Optional optionalStemBlock = BukkitBlockManager.instance().blockById(this.stem); + if (optionalStemBlock.isPresent()) { + CustomBlock stemBlock = optionalStemBlock.get(); + IntegerProperty ageProperty = (IntegerProperty) stemBlock.getProperty("age"); + if (ageProperty == null) return stemBlock.defaultState().customBlockState().literalObject(); + return stemBlock.defaultState().with(ageProperty, ageProperty.max).customBlockState().literalObject(); + } + return null; + } + + public static class Factory implements BlockBehaviorFactory { + + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + + @SuppressWarnings("unchecked") + Property facingProperty = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("facing"), "warning.config.block.behavior.attached_stem.missing_facing"); + Key fruit = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("fruit"), "warning.config.block.behavior.attached_stem.missing_fruit")); + Key stem = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("stem"), "warning.config.block.behavior.attached_stem.missing_stem")); + return new AttachedStemBlockBehavior(block, facingProperty, fruit, stem); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BouncingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BouncingBlockBehavior.java new file mode 100644 index 000000000..87d709036 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BouncingBlockBehavior.java @@ -0,0 +1,90 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.core.block.BlockBehavior; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.behavior.special.FallOnBlockBehavior; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.Vec3d; +import org.bukkit.entity.Entity; + +import java.util.Map; +import java.util.concurrent.Callable; + +public class BouncingBlockBehavior extends BukkitBlockBehavior implements FallOnBlockBehavior { + public static final Factory FACTORY = new Factory(); + private final double bounceHeight; + private final boolean syncPlayerPosition; + private final double fallDamageMultiplier; + + public BouncingBlockBehavior(CustomBlock customBlock, double bounceHeight, boolean syncPlayerPosition, double fallDamageMultiplier) { + super(customBlock); + this.bounceHeight = bounceHeight; + this.syncPlayerPosition = syncPlayerPosition; + this.fallDamageMultiplier = fallDamageMultiplier; + } + + @Override + public void fallOn(Object thisBlock, Object[] args, Callable superMethod) { + if (this.fallDamageMultiplier <= 0.0) return; + Object entity = args[3]; + Number fallDistance = (Number) args[4]; + FastNMS.INSTANCE.method$Entity$causeFallDamage( + entity, fallDistance.doubleValue() * this.fallDamageMultiplier, 1.0F, + FastNMS.INSTANCE.method$DamageSources$fall(FastNMS.INSTANCE.method$Entity$damageSources(entity)) + ); + } + + @Override + public void updateEntityMovementAfterFallOn(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object entity = args[1]; + if (FastNMS.INSTANCE.method$Entity$getSharedFlag(entity, 1)) { + superMethod.call(); + } else { + bounceUp(entity); + } + } + + private void bounceUp(Object entity) { + Vec3d deltaMovement = LocationUtils.fromVec(FastNMS.INSTANCE.method$Entity$getDeltaMovement(entity)); + if (deltaMovement.y < 0.0) { + double d = CoreReflections.clazz$LivingEntity.isInstance(entity) ? 1.0 : 0.8; + double y = -deltaMovement.y * this.bounceHeight * d; + FastNMS.INSTANCE.method$Entity$setDeltaMovement(entity, deltaMovement.x, y, deltaMovement.z); + if (CoreReflections.clazz$Player.isInstance(entity) && this.syncPlayerPosition + && /* 防抖 -> */ y > 0.035 /* <- 防抖 */ + ) { + // 这里一定要延迟 1t 不然就会出问题 + if (VersionHelper.isFolia()) { + Entity bukkitEntity = FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity); + bukkitEntity.getScheduler().runDelayed(BukkitCraftEngine.instance().javaPlugin(), + r -> FastNMS.INSTANCE.field$Entity$hurtMarked(entity, true), + null, 1L + ); + } else { + CraftEngine.instance().scheduler().sync().runLater( + () -> FastNMS.INSTANCE.field$Entity$hurtMarked(entity, true), + 1L + ); + } + } + } + } + + public static class Factory implements BlockBehaviorFactory { + + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + double bounceHeight = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("bounce-height", 0.66), "bounce-height"); + boolean syncPlayerPosition = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("sync-player-position", true), "sync-player-position"); + double fallDamageMultiplier = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("fall-damage-multiplier", 0.5), "fall-damage-multiplier"); + return new BouncingBlockBehavior(block, bounceHeight, syncPlayerPosition, fallDamageMultiplier); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BuddingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BuddingBlockBehavior.java new file mode 100644 index 000000000..0fab2a5ca --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BuddingBlockBehavior.java @@ -0,0 +1,99 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.*; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.DirectionUtils; +import net.momirealms.craftengine.core.block.BlockBehavior; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.properties.BooleanProperty; +import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.util.*; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +public class BuddingBlockBehavior extends BukkitBlockBehavior { + public static final Factory FACTORY = new Factory(); + private final float growthChance; + private final List blocks; + + public BuddingBlockBehavior(CustomBlock customBlock, float growthChance, List blocks) { + super(customBlock); + this.growthChance = growthChance; + this.blocks = blocks; + } + + @Override + public void randomTick(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + if (RandomUtils.generateRandomFloat(0, 1) >= growthChance) return; + Object nmsDirection = CoreReflections.instance$Direction$values[RandomUtils.generateRandomInt(0, 6)]; + Direction direction = DirectionUtils.fromNMSDirection(nmsDirection); + Object blockPos = FastNMS.INSTANCE.method$BlockPos$relative(args[2], nmsDirection); + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(args[1], blockPos); + if (canClusterGrowAtState(blockState)) { + Key blockId = blocks.getFirst(); + CustomBlock firstBlock = BukkitBlockManager.instance().blockById(blockId).orElse(null); + placeWithPropertyBlock(firstBlock, blockId, direction, nmsDirection, args[1], blockPos, blockState); + } else { + Key blockId = BlockStateUtils.getOptionalCustomBlockState(blockState) + .map(it -> it.owner().value().id()) + .orElseGet(() -> BlockStateUtils.getBlockOwnerIdFromState(blockState)); + int blockIdIndex = blocks.indexOf(blockId); + if (blockIdIndex < 0 || blockIdIndex == blocks.size() - 1) return; + Key nextBlockId = blocks.get(blockIdIndex + 1); + CustomBlock nextBlock = BukkitBlockManager.instance().blockById(nextBlockId).orElse(null); + placeWithPropertyBlock(nextBlock, nextBlockId, direction, nmsDirection, args[1], blockPos, blockState); + } + } + + @SuppressWarnings("unchecked") + private void placeWithPropertyBlock(CustomBlock customBlock, Key blockId, Direction direction, Object nmsDirection, Object level, Object blockPos, Object blockState) { + if (customBlock != null) { + ImmutableBlockState newState = customBlock.defaultState(); + Property facing = customBlock.getProperty("facing"); + if (facing != null) { + if (facing.valueClass() == Direction.class) { + newState = newState.with((Property) facing, direction); + } else if (facing.valueClass() == HorizontalDirection.class) { + if (!direction.axis().isHorizontal()) return; + newState = newState.with((Property) facing, direction.toHorizontalDirection()); + } + } + BooleanProperty waterlogged = (BooleanProperty) customBlock.getProperty("waterlogged"); + if (waterlogged != null) { + newState = newState.with(waterlogged, FastNMS.INSTANCE.method$FluidState$getType(FastNMS.INSTANCE.field$BlockBehaviour$BlockStateBase$fluidState(blockState)) == MFluids.WATER); + } + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, newState.customBlockState().literalObject(), 3); + } else if (blockId.namespace().equals("minecraft")) { + Object block = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", blockId.value())); + if (block == null) return; + Object newState = FastNMS.INSTANCE.method$Block$defaultState(block); + newState = FastNMS.INSTANCE.method$StateHolder$trySetValue(newState, MBlockStateProperties.WATERLOGGED, FastNMS.INSTANCE.method$FluidState$getType(FastNMS.INSTANCE.field$BlockBehaviour$BlockStateBase$fluidState(blockState)) == MFluids.WATER); + newState = FastNMS.INSTANCE.method$StateHolder$trySetValue(newState, MBlockStateProperties.FACING, (Comparable) nmsDirection); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, newState, 3); + } + } + + public static boolean canClusterGrowAtState(Object state) { + return FastNMS.INSTANCE.method$BlockStateBase$isAir(state) + || FastNMS.INSTANCE.method$BlockStateBase$isBlock(state, MBlocks.WATER) + && FastNMS.INSTANCE.field$FluidState$amount(FastNMS.INSTANCE.field$BlockBehaviour$BlockStateBase$fluidState(state)) == 8; + } + + public static class Factory implements BlockBehaviorFactory { + + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + float growthChance = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("growth-chance", 0.2), "growth-chance"); + List blocks = new ObjectArrayList<>(); + MiscUtils.getAsStringList(arguments.get("blocks")).forEach(s -> blocks.add(Key.of(s))); + return new BuddingBlockBehavior(block, growthChance, blocks); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java index d7b4b9b0d..000ecaccd 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java @@ -28,6 +28,21 @@ public class BukkitBlockBehaviors extends BlockBehaviors { public static final Key PRESSURE_PLATE_BLOCK = Key.from("craftengine:pressure_plate_block"); public static final Key DOUBLE_HIGH_BLOCK = Key.from("craftengine:double_high_block"); public static final Key CHANGE_OVER_TIME_BLOCK = Key.from("craftengine:change_over_time_block"); + public static final Key SIMPLE_STORAGE_BLOCK = Key.from("craftengine:simple_storage_block"); + public static final Key TOGGLEABLE_LAMP_BLOCK = Key.from("craftengine:toggleable_lamp_block"); + public static final Key SOFA_BLOCK = Key.from("craftengine:sofa_block"); + public static final Key BOUNCING_BLOCK = Key.from("craftengine:bouncing_block"); + public static final Key DIRECTIONAL_ATTACHED_BLOCK = Key.from("craftengine:directional_attached_block"); + public static final Key LIQUID_FLOWABLE_BLOCK = Key.from("craftengine:liquid_flowable_block"); + public static final Key SIMPLE_PARTICLE_BLOCK = Key.from("craftengine:simple_particle_block"); + public static final Key WALL_TORCH_PARTICLE_BLOCK = Key.from("craftengine:wall_torch_particle_block"); + public static final Key FENCE_BLOCK = Key.from("craftengine:fence_block"); + public static final Key BUTTON_BLOCK = Key.from("craftengine:button_block"); + public static final Key FACE_ATTACHED_HORIZONTAL_DIRECTIONAL_BLOCK = Key.from("craftengine:face_attached_horizontal_directional_block"); + public static final Key STEM_BLOCK = Key.from("craftengine:stem_block"); + public static final Key ATTACHED_STEM_BLOCK = Key.from("craftengine:attached_stem_block"); + public static final Key CHIME_BLOCK = Key.from("craftengine:chime_block"); + public static final Key BUDDING_BLOCK = Key.from("craftengine:budding_block"); public static void init() { register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE); @@ -54,5 +69,20 @@ public class BukkitBlockBehaviors extends BlockBehaviors { register(PRESSURE_PLATE_BLOCK, PressurePlateBlockBehavior.FACTORY); register(DOUBLE_HIGH_BLOCK, DoubleHighBlockBehavior.FACTORY); register(CHANGE_OVER_TIME_BLOCK, ChangeOverTimeBlockBehavior.FACTORY); + register(SIMPLE_STORAGE_BLOCK, SimpleStorageBlockBehavior.FACTORY); + register(TOGGLEABLE_LAMP_BLOCK, ToggleableLampBlockBehavior.FACTORY); + register(SOFA_BLOCK, SofaBlockBehavior.FACTORY); + register(BOUNCING_BLOCK, BouncingBlockBehavior.FACTORY); + register(DIRECTIONAL_ATTACHED_BLOCK, DirectionalAttachedBlockBehavior.FACTORY); + register(LIQUID_FLOWABLE_BLOCK, LiquidFlowableBlockBehavior.FACTORY); + register(SIMPLE_PARTICLE_BLOCK, SimpleParticleBlockBehavior.FACTORY); + register(WALL_TORCH_PARTICLE_BLOCK, WallTorchParticleBlockBehavior.FACTORY); + register(FENCE_BLOCK, FenceBlockBehavior.FACTORY); + register(BUTTON_BLOCK, ButtonBlockBehavior.FACTORY); + register(FACE_ATTACHED_HORIZONTAL_DIRECTIONAL_BLOCK, FaceAttachedHorizontalDirectionalBlockBehavior.FACTORY); + register(STEM_BLOCK, StemBlockBehavior.FACTORY); + register(ATTACHED_STEM_BLOCK, AttachedStemBlockBehavior.FACTORY); + register(CHIME_BLOCK, ChimeBlockBehavior.FACTORY); + register(BUDDING_BLOCK, BuddingBlockBehavior.FACTORY); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java index f20eab56d..8a807c7e0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java @@ -26,11 +26,13 @@ public class BushBlockBehavior extends AbstractCanSurviveBlockBehavior { protected final Set customBlocksCansSurviveOn; protected final boolean blacklistMode; protected final boolean stackable; + protected final int maxHeight; - public BushBlockBehavior(CustomBlock block, int delay, boolean blacklist, boolean stackable, List tagsCanSurviveOn, Set blockStatesCanSurviveOn, Set customBlocksCansSurviveOn) { + public BushBlockBehavior(CustomBlock block, int delay, boolean blacklist, boolean stackable, int maxHeight, List tagsCanSurviveOn, Set blockStatesCanSurviveOn, Set customBlocksCansSurviveOn) { super(block, delay); this.blacklistMode = blacklist; this.stackable = stackable; + this.maxHeight = maxHeight; this.tagsCanSurviveOn = tagsCanSurviveOn; this.blockStatesCanSurviveOn = blockStatesCanSurviveOn; this.customBlocksCansSurviveOn = customBlocksCansSurviveOn; @@ -42,9 +44,10 @@ public class BushBlockBehavior extends AbstractCanSurviveBlockBehavior { public BlockBehavior create(CustomBlock block, Map arguments) { Tuple, Set, Set> tuple = readTagsAndState(arguments, false); boolean stackable = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("stackable", false), "stackable"); + int maxHeight = ResourceConfigUtils.getAsInt(arguments.getOrDefault("max-height", 0), "max-height"); int delay = ResourceConfigUtils.getAsInt(arguments.getOrDefault("delay", 0), "delay"); boolean blacklistMode = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blacklist", false), "blacklist"); - return new BushBlockBehavior(block, delay, blacklistMode, stackable, tuple.left(), tuple.mid(), tuple.right()); + return new BushBlockBehavior(block, delay, blacklistMode, stackable, maxHeight,tuple.left(), tuple.mid(), tuple.right()); } } @@ -62,7 +65,7 @@ public class BushBlockBehavior extends AbstractCanSurviveBlockBehavior { if (material != null) { if (index == -1) { // vanilla - mcBlocks.addAll(BlockStateUtils.getAllVanillaBlockStates(blockType)); + mcBlocks.addAll(BlockStateUtils.getPossibleBlockStates(blockType)); } else { mcBlocks.add(BlockStateUtils.blockDataToBlockState(Bukkit.createBlockData(blockStateStr))); } @@ -96,7 +99,11 @@ public class BushBlockBehavior extends AbstractCanSurviveBlockBehavior { } else { ImmutableBlockState belowCustomState = optionalCustomState.get(); if (belowCustomState.owner().value() == super.customBlock) { - return this.stackable; + if (!this.stackable || this.maxHeight == 1) return false; + if (this.maxHeight > 1) { + return mayStackOn(world, belowPos); + } + return true; } if (this.customBlocksCansSurviveOn.contains(belowCustomState.owner().value().id().toString())) { return !this.blacklistMode; @@ -107,4 +114,21 @@ public class BushBlockBehavior extends AbstractCanSurviveBlockBehavior { } return this.blacklistMode; } + + protected boolean mayStackOn(Object world, Object belowPos) { + int count = 1; + Object cursorPos = LocationUtils.below(belowPos); + + while (count < this.maxHeight) { + Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, cursorPos); + Optional belowCustomState = BlockStateUtils.getOptionalCustomBlockState(belowState); + if (belowCustomState.isPresent() && belowCustomState.get().owner().value() == super.customBlock) { + count++; + cursorPos = LocationUtils.below(cursorPos); + } else { + break; + } + } + return count < this.maxHeight; + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ButtonBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ButtonBlockBehavior.java new file mode 100644 index 000000000..956830c86 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ButtonBlockBehavior.java @@ -0,0 +1,217 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntitySelectors; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MGameEvents; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.DirectionUtils; +import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.core.block.BlockBehavior; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.UpdateOption; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.properties.BooleanProperty; +import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.item.context.UseOnContext; +import net.momirealms.craftengine.core.sound.SoundData; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.HorizontalDirection; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.VersionHelper; + +import javax.annotation.Nullable; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.Callable; + +public class ButtonBlockBehavior extends BukkitBlockBehavior { + public static final Factory FACTORY = new Factory(); + private final BooleanProperty poweredProperty; + private final int ticksToStayPressed; + private final boolean canButtonBeActivatedByArrows; + private final SoundData buttonClickOnSound; + private final SoundData buttonClickOffSound; + + public ButtonBlockBehavior(CustomBlock customBlock, + BooleanProperty powered, + int ticksToStayPressed, + boolean canButtonBeActivatedByArrows, + SoundData buttonClickOnSound, + SoundData buttonClickOffSound) { + super(customBlock); + this.poweredProperty = powered; + this.ticksToStayPressed = ticksToStayPressed; + this.canButtonBeActivatedByArrows = canButtonBeActivatedByArrows; + this.buttonClickOnSound = buttonClickOnSound; + this.buttonClickOffSound = buttonClickOffSound; + } + + @Override + public InteractionResult useWithoutItem(UseOnContext context, ImmutableBlockState state) { + if (!state.get(this.poweredProperty)) { + press(BlockStateUtils.getBlockOwner(state.customBlockState().literalObject()), + state, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), + context.getPlayer() != null ? context.getPlayer().serverPlayer() : null); + return InteractionResult.SUCCESS_AND_CANCEL; + } + return InteractionResult.PASS; + } + + @Override + public void onExplosionHit(Object thisBlock, Object[] args, Callable superMethod) { + ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null); + if (blockState == null) return; + if (FastNMS.INSTANCE.method$Explosion$canTriggerBlocks(args[3]) && !blockState.get(this.poweredProperty)) { + press(thisBlock, blockState, args[1], args[2], null); + } + } + + @Override + public void affectNeighborsAfterRemoval(Object thisBlock, Object[] args, Callable superMethod) { + ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null); + if (blockState == null) return; + if (!(boolean) args[3] && blockState.get(this.poweredProperty)) { + updateNeighbours(thisBlock, blockState, args[1], args[2]); + } + } + + @Override + public void onRemove(Object thisBlock, Object[] args, Callable superMethod) { + ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null); + if (blockState == null) return; + if (!(boolean) args[4] && blockState.get(this.poweredProperty)) { + updateNeighbours(thisBlock, blockState, args[1], args[2]); + } + } + + @Override + public int getSignal(Object thisBlock, Object[] args, Callable superMethod) { + ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null); + if (blockState == null) return 0; + return blockState.get(this.poweredProperty) ? 15 : 0; + } + + @Override + public int getDirectSignal(Object thisBlock, Object[] args, Callable superMethod) { + ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null); + if (blockState == null) return 0; + return blockState.get(this.poweredProperty) + && FaceAttachedHorizontalDirectionalBlockBehavior.getConnectedDirection(blockState) + == DirectionUtils.fromNMSDirection(args[3]) ? 15 : 0; + } + + @Override + public boolean isSignalSource(Object thisBlock, Object[] args, Callable superMethod) { + return true; + } + + @Override + public void tick(Object thisBlock, Object[] args, Callable superMethod) { + Object state = args[0]; + Object level = args[1]; + Object pos = args[2]; + ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(state).orElse(null); + if (blockState == null) return; + if (blockState.get(this.poweredProperty)) { + checkPressed(thisBlock, state, level, pos); + } + } + + @Override + public void entityInside(Object thisBlock, Object[] args, Callable superMethod) { + Object state = args[0]; + Object level = args[1]; + Object pos = args[2]; + ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(state).orElse(null); + if (blockState == null) return; + if (this.canButtonBeActivatedByArrows && !blockState.get(this.poweredProperty)) { + checkPressed(thisBlock, state, level, pos); + } + } + + private void checkPressed(Object thisBlock, Object state, Object level, Object pos) { + Object arrow = this.canButtonBeActivatedByArrows ? FastNMS.INSTANCE.method$EntityGetter$getEntitiesOfClass( + level, CoreReflections.clazz$AbstractArrow, FastNMS.INSTANCE.method$AABB$move( + FastNMS.INSTANCE.method$VoxelShape$bounds(FastNMS.INSTANCE.method$BlockState$getShape( + state, level, pos, CoreReflections.instance$CollisionContext$empty + )), pos), MEntitySelectors.NO_SPECTATORS).stream().findFirst().orElse(null) : null; + boolean on = arrow != null; + ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(state).orElse(null); + if (blockState == null) return; + boolean poweredValue = blockState.get(this.poweredProperty); + if (on != poweredValue) { + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, blockState.with(this.poweredProperty, on).customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); + updateNeighbours(thisBlock, blockState, level, pos); + playSound(level, pos, on); + Object gameEvent = VersionHelper.isOrAbove1_20_5() + ? FastNMS.INSTANCE.method$Holder$direct(on ? MGameEvents.BLOCK_ACTIVATE : MGameEvents.BLOCK_DEACTIVATE) + : on ? MGameEvents.BLOCK_ACTIVATE : MGameEvents.BLOCK_DEACTIVATE; + FastNMS.INSTANCE.method$LevelAccessor$gameEvent(level, arrow, gameEvent, pos); + } + + if (on) { + FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(level, pos, thisBlock, this.ticksToStayPressed); + } + } + + private void updateNeighbours(Object thisBlock, ImmutableBlockState state, Object level, Object pos) { + Direction direction = FaceAttachedHorizontalDirectionalBlockBehavior.getConnectedDirection(state); + if (direction == null) return; + Direction opposite = direction.opposite(); + Object nmsDirection = DirectionUtils.toNMSDirection(opposite); + Object orientation = null; + if (VersionHelper.isOrAbove1_21_2()) { + @SuppressWarnings("unchecked") + Property facing = (Property) state.owner().value().getProperty("facing"); + if (facing != null) { + orientation = FastNMS.INSTANCE.method$ExperimentalRedstoneUtils$initialOrientation( + level, nmsDirection, opposite.axis().isHorizontal() ? CoreReflections.instance$Direction$UP : DirectionUtils.toNMSDirection(state.get(facing).toDirection()) + ); + } + } + FastNMS.INSTANCE.method$Level$updateNeighborsAt(level, pos, thisBlock, orientation); + FastNMS.INSTANCE.method$Level$updateNeighborsAt(level, FastNMS.INSTANCE.method$BlockPos$relative(pos, nmsDirection), thisBlock, orientation); + } + + private void playSound(Object level, Object pos, boolean on) { + SoundData soundData = getSound(on); + if (soundData == null) return; + Object sound = FastNMS.INSTANCE.constructor$SoundEvent(KeyUtils.toResourceLocation(soundData.id()), Optional.empty()); + FastNMS.INSTANCE.method$LevelAccessor$playSound(level, null, pos, sound, CoreReflections.instance$SoundSource$BLOCKS, soundData.volume().get(), soundData.pitch().get()); + } + + private SoundData getSound(boolean on) { + return on ? this.buttonClickOnSound : this.buttonClickOffSound; + } + + private void press(Object thisBlock, ImmutableBlockState state, Object level, Object pos, @Nullable Object player) { + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, state.with(this.poweredProperty, true).customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); + FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(level, pos, thisBlock, this.ticksToStayPressed); + playSound(level, pos, true); + Object gameEvent = VersionHelper.isOrAbove1_20_5() ? FastNMS.INSTANCE.method$Holder$direct(MGameEvents.BLOCK_ACTIVATE) : MGameEvents.BLOCK_ACTIVATE; + FastNMS.INSTANCE.method$LevelAccessor$gameEvent(level, player, gameEvent, pos); + } + + public static class Factory implements BlockBehaviorFactory { + + @SuppressWarnings({"unchecked", "DuplicatedCode"}) + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + BooleanProperty powered = (BooleanProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("powered"), "warning.config.block.behavior.button.missing_powered"); + int ticksToStayPressed = ResourceConfigUtils.getAsInt(arguments.getOrDefault("ticks-to-stay-pressed", 30), "ticks-to-stay-pressed"); + boolean canButtonBeActivatedByArrows = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-activated-by-arrows", true), "can-be-activated-by-arrows"); + Map sounds = (Map) arguments.get("sounds"); + SoundData buttonClickOnSound = null; + SoundData buttonClickOffSound = null; + if (sounds != null) { + buttonClickOnSound = Optional.ofNullable(sounds.get("on")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null); + buttonClickOffSound = Optional.ofNullable(sounds.get("off")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null); + } + return new ButtonBlockBehavior(block, powered, ticksToStayPressed, canButtonBeActivatedByArrows, buttonClickOnSound, buttonClickOffSound); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ChimeBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ChimeBlockBehavior.java new file mode 100644 index 000000000..4b286d52e --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ChimeBlockBehavior.java @@ -0,0 +1,45 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.core.block.BlockBehavior; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.sound.SoundData; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.Callable; + +public class ChimeBlockBehavior extends BukkitBlockBehavior { + public static final Factory FACTORY = new Factory(); + private final SoundData hitSound; + + public ChimeBlockBehavior(CustomBlock customBlock, SoundData hitSound) { + super(customBlock); + this.hitSound = hitSound; + } + + @Override + public void onProjectileHit(Object thisBlock, Object[] args, Callable superMethod) { + Object blockPos = FastNMS.INSTANCE.field$BlockHitResult$blockPos(args[2]); + Object sound = FastNMS.INSTANCE.constructor$SoundEvent(KeyUtils.toResourceLocation(hitSound.id()), Optional.empty()); + FastNMS.INSTANCE.method$LevelAccessor$playSound(args[0], null, blockPos, sound, CoreReflections.instance$SoundSource$BLOCKS, hitSound.volume().get(), hitSound.pitch().get()); + } + + public static class Factory implements BlockBehaviorFactory { + + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + SoundData hitSound = SoundData.create(ResourceConfigUtils.requireNonNullOrThrow( + Optional.ofNullable(ResourceConfigUtils.getAsMap(arguments.get("sounds"), "sounds")) + .map(sounds -> ResourceConfigUtils.get(sounds, "projectile-hit", "chime")) + .orElse(null), + "warning.config.block.behavior.chime.missing_sounds_projectile_hit" + ), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f)); + return new ChimeBlockBehavior(block, hitSound); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java index aa872018e..d932532cf 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ConcretePowderBlockBehavior.java @@ -129,7 +129,7 @@ public class ConcretePowderBlockBehavior extends BukkitBlockBehavior { Object mutablePos = CoreReflections.method$BlockPos$mutable.invoke(pos); int j = Direction.values().length; for (int k = 0; k < j; k++) { - Object direction = CoreReflections.instance$Directions[k]; + Object direction = CoreReflections.instance$Direction$values[k]; Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, mutablePos); if (direction != CoreReflections.instance$Direction$DOWN || canSolidify(blockState)) { CoreReflections.method$MutableBlockPos$setWithOffset.invoke(mutablePos, pos, direction); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java index 7e0746ead..2e6793e35 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java @@ -77,7 +77,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior { return minGrowLight; } - private static int getRawBrightness(Object level, Object pos) throws InvocationTargetException, IllegalAccessException { + public static int getRawBrightness(Object level, Object pos) throws InvocationTargetException, IllegalAccessException { return (int) CoreReflections.method$BlockAndTintGetter$getRawBrightness.invoke(level, pos, 0); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DirectionalAttachedBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DirectionalAttachedBlockBehavior.java new file mode 100644 index 000000000..958a1e37a --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DirectionalAttachedBlockBehavior.java @@ -0,0 +1,177 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.BlockTags; +import net.momirealms.craftengine.bukkit.util.DirectionUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.core.block.BlockBehavior; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.item.context.BlockPlaceContext; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.*; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.World; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; + +import java.util.*; +import java.util.concurrent.Callable; + +public class DirectionalAttachedBlockBehavior extends BukkitBlockBehavior { + public static final Factory FACTORY = new Factory(); + private final Property facingProperty; + private final boolean isSixDirection; + private final List tagsCanSurviveOn; + private final Set blockStatesCanSurviveOn; + private final Set customBlocksCansSurviveOn; + private final boolean blacklistMode; + + public DirectionalAttachedBlockBehavior(CustomBlock customBlock, + Property facingProperty, + boolean isSixDirection, + boolean blacklist, + List tagsCanSurviveOn, + Set blockStatesCanSurviveOn, + Set customBlocksCansSurviveOn) { + super(customBlock); + this.facingProperty = facingProperty; + this.isSixDirection = isSixDirection; + this.tagsCanSurviveOn = tagsCanSurviveOn; + this.blockStatesCanSurviveOn = blockStatesCanSurviveOn; + this.customBlocksCansSurviveOn = customBlocksCansSurviveOn; + this.blacklistMode = blacklist; + } + + @Override + public Object updateShape(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null); + if (state == null) return args[0]; + DirectionalAttachedBlockBehavior behavior = state.behavior().getAs(DirectionalAttachedBlockBehavior.class).orElse(null); + if (behavior == null) return state; + boolean flag; + if (isSixDirection) { + Direction direction = DirectionUtils.fromNMSDirection(args[updateShape$direction]).opposite(); + flag = direction == state.get(behavior.facingProperty); + } else { + HorizontalDirection direction = DirectionUtils.fromNMSDirection(args[updateShape$direction]).opposite().toHorizontalDirection(); + flag = direction == state.get(behavior.facingProperty); + } + return flag && !FastNMS.INSTANCE.method$BlockStateBase$canSurvive(args[0], args[updateShape$level], args[updateShape$blockPos]) ? MBlocks.AIR$defaultState : args[0]; + } + + @Override + public boolean canSurvive(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null); + if (state == null) return false; + DirectionalAttachedBlockBehavior behavior = state.behavior().getAs(DirectionalAttachedBlockBehavior.class).orElse(null); + if (behavior == null) return false; + Direction direction; + if (isSixDirection) { + direction = ((Direction) state.get(behavior.facingProperty)).opposite(); + } else { + direction = ((HorizontalDirection) state.get(behavior.facingProperty)).opposite().toDirection(); + } + BlockPos blockPos = LocationUtils.fromBlockPos(args[2]).relative(direction); + Object nmsPos = LocationUtils.toBlockPos(blockPos); + Object nmsState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(args[1], nmsPos); + return FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy(nmsState, args[1], nmsPos, DirectionUtils.toNMSDirection(direction), CoreReflections.instance$SupportType$FULL) + && mayPlaceOn(nmsState); + } + + private boolean mayPlaceOn(Object state) { + for (Object tag : this.tagsCanSurviveOn) { + if (FastNMS.INSTANCE.method$BlockStateBase$is(state, tag)) { + return !this.blacklistMode; + } + } + Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(state); + if (optionalCustomState.isEmpty()) { + if (!this.blockStatesCanSurviveOn.isEmpty() && this.blockStatesCanSurviveOn.contains(state)) { + return !this.blacklistMode; + } + } else { + ImmutableBlockState belowCustomState = optionalCustomState.get(); + if (this.customBlocksCansSurviveOn.contains(belowCustomState.owner().value().id().toString())) { + return !this.blacklistMode; + } + if (this.customBlocksCansSurviveOn.contains(belowCustomState.toString())) { + return !this.blacklistMode; + } + } + return this.blacklistMode; + } + + @SuppressWarnings("unchecked") + @Override + public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) { + DirectionalAttachedBlockBehavior behavior = state.behavior().getAs(DirectionalAttachedBlockBehavior.class).orElse(null); + if (behavior == null) return null; + World level = context.getLevel(); + BlockPos clickedPos = context.getClickedPos(); + for (Direction direction : context.getNearestLookingDirections()) { + if (isSixDirection) { + state = state.with((Property) behavior.facingProperty, direction.opposite()); + if (FastNMS.INSTANCE.method$BlockStateBase$canSurvive(state.customBlockState().literalObject(), level.serverWorld(), LocationUtils.toBlockPos(clickedPos))) { + return state; + } + } else if (direction.axis().isHorizontal()) { + state = state.with((Property) behavior.facingProperty, direction.opposite().toHorizontalDirection()); + if (FastNMS.INSTANCE.method$BlockStateBase$canSurvive(state.customBlockState().literalObject(), level.serverWorld(), LocationUtils.toBlockPos(clickedPos))) { + return state; + } + } + } + return null; + } + + public static class Factory implements BlockBehaviorFactory { + + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + Property facing = ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("facing"), "warning.config.block.behavior.directional_attached.missing_facing"); + boolean isHorizontalDirection = facing.valueClass() == HorizontalDirection.class; + boolean isDirection = facing.valueClass() == Direction.class; + if (!(isHorizontalDirection || isDirection)) { + throw new LocalizedResourceConfigException("warning.config.block.behavior.directional_attached.missing_facing"); + } + Tuple, Set, Set> tuple = readTagsAndState(arguments); + boolean blacklistMode = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blacklist", true), "blacklist"); + return new DirectionalAttachedBlockBehavior(block, facing, isDirection, blacklistMode, tuple.left(), tuple.mid(), tuple.right()); + } + } + + @SuppressWarnings("DuplicatedCode") + public static Tuple, Set, Set> readTagsAndState(Map arguments) { + List mcTags = new ArrayList<>(); + for (String tag : MiscUtils.getAsStringList(arguments.getOrDefault("attached-block-tags", List.of()))) { + mcTags.add(BlockTags.getOrCreate(Key.of(tag))); + } + Set mcBlocks = new HashSet<>(); + Set customBlocks = new HashSet<>(); + for (String blockStateStr : MiscUtils.getAsStringList(arguments.getOrDefault("attached-blocks", List.of()))) { + int index = blockStateStr.indexOf('['); + Key blockType = index != -1 ? Key.from(blockStateStr.substring(0, index)) : Key.from(blockStateStr); + Material material = Registry.MATERIAL.get(new NamespacedKey(blockType.namespace(), blockType.value())); + if (material != null) { + if (index == -1) { + // vanilla + mcBlocks.addAll(BlockStateUtils.getPossibleBlockStates(blockType)); + } else { + mcBlocks.add(BlockStateUtils.blockDataToBlockState(Bukkit.createBlockData(blockStateStr))); + } + } else { + // custom maybe + customBlocks.add(blockStateStr); + } + } + return new Tuple<>(mcTags, mcBlocks, customBlocks); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java index 864563451..677e9dd1c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java @@ -16,8 +16,8 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.UpdateOption; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.Property; -import net.momirealms.craftengine.core.block.state.properties.DoorHinge; -import net.momirealms.craftengine.core.block.state.properties.DoubleBlockHalf; +import net.momirealms.craftengine.core.block.properties.type.DoorHinge; +import net.momirealms.craftengine.core.block.properties.type.DoubleBlockHalf; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.entity.player.Player; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoubleHighBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoubleHighBlockBehavior.java index 14305b479..a60b2597f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoubleHighBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoubleHighBlockBehavior.java @@ -6,15 +6,16 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.block.*; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.Property; -import net.momirealms.craftengine.core.block.state.properties.DoubleBlockHalf; +import net.momirealms.craftengine.core.block.properties.type.DoubleBlockHalf; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.world.*; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldEvents; import java.util.Map; import java.util.concurrent.Callable; @@ -50,10 +51,6 @@ public class DoubleHighBlockBehavior extends BukkitBlockBehavior { if (anotherHalfCustomState != null && !anotherHalfCustomState.isEmpty()) return blockState; // 破坏 - BlockPos pos = LocationUtils.fromBlockPos(blockPos); - net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); - WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos)); - world.playBlockSound(position, customState.settings().sounds().breakSound()); FastNMS.INSTANCE.method$LevelAccessor$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, customState.customBlockState().registryId()); return MBlocks.AIR$defaultState; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FaceAttachedHorizontalDirectionalBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FaceAttachedHorizontalDirectionalBlockBehavior.java new file mode 100644 index 000000000..dae5a50b3 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FaceAttachedHorizontalDirectionalBlockBehavior.java @@ -0,0 +1,150 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.DirectionUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.core.block.BlockBehavior; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.block.properties.type.AnchorType; +import net.momirealms.craftengine.core.item.context.BlockPlaceContext; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.HorizontalDirection; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.Tuple; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.Callable; + +public class FaceAttachedHorizontalDirectionalBlockBehavior extends BukkitBlockBehavior { + public static final Factory FACTORY = new Factory(); + private final Property anchorTypeProperty; + private final Property facingProperty; + private final List tagsCanSurviveOn; + private final Set blockStatesCanSurviveOn; + private final Set customBlocksCansSurviveOn; + private final boolean blacklistMode; + + public FaceAttachedHorizontalDirectionalBlockBehavior(CustomBlock customBlock, + boolean blacklist, + List tagsCanSurviveOn, + Set blockStatesCanSurviveOn, + Set customBlocksCansSurviveOn, + Property anchorType, + Property facing) { + super(customBlock); + this.tagsCanSurviveOn = tagsCanSurviveOn; + this.blockStatesCanSurviveOn = blockStatesCanSurviveOn; + this.customBlocksCansSurviveOn = customBlocksCansSurviveOn; + this.blacklistMode = blacklist; + this.anchorTypeProperty = anchorType; + this.facingProperty = facing; + } + + @Override + public boolean canSurvive(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Direction direction = getConnectedDirection(BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null)); + if (direction == null) return false; + direction = direction.opposite(); + Object nmsDirection = DirectionUtils.toNMSDirection(direction); + Object targetPos = FastNMS.INSTANCE.method$BlockPos$relative(args[2], nmsDirection); + Object targetState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(args[1], targetPos); + return canAttach(args[1], targetPos, nmsDirection, targetState) && mayPlaceOn(targetState); + } + + @SuppressWarnings("unchecked") + @Override + public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) { + Property face = (Property) state.owner().value().getProperty("face"); + Property facing = (Property) state.owner().value().getProperty("facing"); + if (face == null || facing == null) return null; + for (Direction direction : context.getNearestLookingDirections()) { + if (direction.axis() == Direction.Axis.Y) { + state = state + .with(face, direction == Direction.UP ? AnchorType.CEILING : AnchorType.FLOOR) + .with(facing, context.getHorizontalDirection().toHorizontalDirection()); + } else { + state = state.with(face, AnchorType.WALL).with(facing, direction.opposite().toHorizontalDirection()); + } + if (FastNMS.INSTANCE.method$BlockStateBase$canSurvive(state.customBlockState().literalObject(), context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()))) { + return state; + } + } + return null; + } + + @Override + public Object updateShape(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Direction direction = getConnectedDirection(BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null)); + if (direction == null) return MBlocks.AIR$defaultState; + if (DirectionUtils.toNMSDirection(direction.opposite()) == args[updateShape$direction] && !FastNMS.INSTANCE.method$BlockStateBase$canSurvive(args[0], args[updateShape$level], args[updateShape$blockPos])) { + return MBlocks.AIR$defaultState; + } + return superMethod.call(); + } + + private boolean mayPlaceOn(Object state) { + for (Object tag : this.tagsCanSurviveOn) { + if (FastNMS.INSTANCE.method$BlockStateBase$is(state, tag)) { + return !this.blacklistMode; + } + } + Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(state); + if (optionalCustomState.isEmpty()) { + if (!this.blockStatesCanSurviveOn.isEmpty() && this.blockStatesCanSurviveOn.contains(state)) { + return !this.blacklistMode; + } + } else { + ImmutableBlockState belowCustomState = optionalCustomState.get(); + if (this.customBlocksCansSurviveOn.contains(belowCustomState.owner().value().id().toString())) { + return !this.blacklistMode; + } + if (this.customBlocksCansSurviveOn.contains(belowCustomState.toString())) { + return !this.blacklistMode; + } + } + return this.blacklistMode; + } + + public static boolean canAttach(Object level, Object targetPos, Object direction, Object targetState) { + return FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy( + targetState, level, targetPos, + FastNMS.INSTANCE.method$Direction$getOpposite(direction), + CoreReflections.instance$SupportType$FULL + ); + } + + @Nullable + public static Direction getConnectedDirection(ImmutableBlockState state) { + if (state == null) return null; + FaceAttachedHorizontalDirectionalBlockBehavior behavior = state.behavior().getAs(FaceAttachedHorizontalDirectionalBlockBehavior.class).orElse(null); + if (behavior == null) return null; + return switch (state.get(behavior.anchorTypeProperty)) { + case CEILING -> Direction.DOWN; + case FLOOR -> Direction.UP; + default -> state.get(behavior.facingProperty).toDirection(); + }; + } + + public static class Factory implements BlockBehaviorFactory { + + @SuppressWarnings("unchecked") + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + Property anchorType = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("face"), "warning.config.block.behavior.face_attached_horizontal_directional.missing_face"); + Property facing = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("facing"), "warning.config.block.behavior.face_attached_horizontal_directional.missing_facing"); + Tuple, Set, Set> tuple = DirectionalAttachedBlockBehavior.readTagsAndState(arguments); + boolean blacklistMode = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blacklist", true), "blacklist"); + return new FaceAttachedHorizontalDirectionalBlockBehavior(block, blacklistMode, tuple.left(), tuple.mid(), tuple.right(), anchorType, facing); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FallingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FallingBlockBehavior.java index 308e0168e..f71bbf15b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FallingBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FallingBlockBehavior.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.block.behavior; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; @@ -10,11 +11,8 @@ import net.momirealms.craftengine.core.block.BlockBehavior; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; @@ -26,11 +24,15 @@ public class FallingBlockBehavior extends BukkitBlockBehavior { public static final Factory FACTORY = new Factory(); private final float hurtAmount; private final int maxHurt; + private final SoundData landSound; + private final SoundData destroySound; - public FallingBlockBehavior(CustomBlock block, float hurtAmount, int maxHurt) { + public FallingBlockBehavior(CustomBlock block, float hurtAmount, int maxHurt, SoundData landSound, SoundData destroySound) { super(block); this.hurtAmount = hurtAmount; this.maxHurt = maxHurt; + this.landSound = landSound; + this.destroySound = destroySound; } @Override @@ -67,7 +69,7 @@ public class FallingBlockBehavior extends BukkitBlockBehavior { return; } Object blockState = args[0]; - Object fallingBlockEntity = CoreReflections.method$FallingBlockEntity$fall.invoke(null, world, blockPos, blockState); + Object fallingBlockEntity = FastNMS.INSTANCE.createInjectedFallingBlockEntity(world, blockPos, blockState); if (this.hurtAmount > 0 && this.maxHurt > 0) { CoreReflections.method$FallingBlockEntity$setHurtsEntities.invoke(fallingBlockEntity, this.hurtAmount, this.maxHurt); } @@ -76,28 +78,18 @@ public class FallingBlockBehavior extends BukkitBlockBehavior { @SuppressWarnings("DuplicatedCode") @Override public void onBrokenAfterFall(Object thisBlock, Object[] args) throws Exception { - // Use EntityRemoveEvent for 1.20.3+ - if (VersionHelper.isOrAbove1_20_3()) return; Object level = args[0]; Object fallingBlockEntity = args[2]; - boolean cancelDrop = (boolean) CoreReflections.field$FallingBlockEntity$cancelDrop.get(fallingBlockEntity); - if (cancelDrop) return; - Object blockState = CoreReflections.field$FallingBlockEntity$blockState.get(fallingBlockEntity); - Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState); - if (optionalCustomState.isEmpty()) return; - ImmutableBlockState customState = optionalCustomState.get(); - net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); - WorldPosition position = new WorldPosition(world, CoreReflections.field$Entity$xo.getDouble(fallingBlockEntity), CoreReflections.field$Entity$yo.getDouble(fallingBlockEntity), CoreReflections.field$Entity$zo.getDouble(fallingBlockEntity)); - ContextHolder.Builder builder = ContextHolder.builder() - .withParameter(DirectContextParameters.FALLING_BLOCK, true) - .withParameter(DirectContextParameters.POSITION, position); - for (Item item : customState.getDrops(builder, world, null)) { - world.dropItemNaturally(position, item); - } Object entityData = CoreReflections.field$Entity$entityData.get(fallingBlockEntity); - boolean isSilent = (boolean) CoreReflections.method$SynchedEntityData$get.invoke(entityData, CoreReflections.instance$Entity$DATA_SILENT); - if (!isSilent) { - world.playBlockSound(position, customState.settings().sounds().destroySound()); + if (!BaseEntityData.Silent.get(entityData)) { + Object blockState = CoreReflections.field$FallingBlockEntity$blockState.get(fallingBlockEntity); + Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState); + if (optionalCustomState.isEmpty()) return; + net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); + WorldPosition position = new WorldPosition(world, CoreReflections.field$Entity$xo.getDouble(fallingBlockEntity), CoreReflections.field$Entity$yo.getDouble(fallingBlockEntity), CoreReflections.field$Entity$zo.getDouble(fallingBlockEntity)); + if (this.destroySound != null) { + world.playBlockSound(position, this.destroySound); + } } } @@ -107,23 +99,33 @@ public class FallingBlockBehavior extends BukkitBlockBehavior { Object level = args[0]; Object pos = args[1]; Object entityData = CoreReflections.field$Entity$entityData.get(fallingBlock); - boolean isSilent = (boolean) CoreReflections.method$SynchedEntityData$get.invoke(entityData, CoreReflections.instance$Entity$DATA_SILENT); Object blockState = args[2]; int stateId = BlockStateUtils.blockStateToId(blockState); ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId); if (immutableBlockState == null || immutableBlockState.isEmpty()) return; - if (!isSilent) { + if (!BaseEntityData.Silent.get(entityData)) { net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); - world.playBlockSound(Vec3d.atCenterOf(LocationUtils.fromBlockPos(pos)), immutableBlockState.settings().sounds().landSound()); + if (this.landSound != null) { + world.playBlockSound(Vec3d.atCenterOf(LocationUtils.fromBlockPos(pos)), this.landSound); + } } } public static class Factory implements BlockBehaviorFactory { + + @SuppressWarnings("unchecked") @Override public BlockBehavior create(CustomBlock block, Map arguments) { float hurtAmount = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("hurt-amount", -1f), "hurt-amount"); int hurtMax = ResourceConfigUtils.getAsInt(arguments.getOrDefault("max-hurt", -1), "max-hurt"); - return new FallingBlockBehavior(block, hurtAmount, hurtMax); + Map sounds = (Map) arguments.get("sounds"); + SoundData fallSound = null; + SoundData destroySound = null; + if (sounds != null) { + fallSound = Optional.ofNullable(sounds.get("fall")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null); + destroySound = Optional.ofNullable(sounds.get("destroy")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null); + } + return new FallingBlockBehavior(block, hurtAmount, hurtMax, fallSound, destroySound); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FenceBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FenceBlockBehavior.java new file mode 100644 index 000000000..0a4de1cbf --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FenceBlockBehavior.java @@ -0,0 +1,150 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MTagKeys; +import net.momirealms.craftengine.bukkit.util.*; +import net.momirealms.craftengine.core.block.*; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.properties.BooleanProperty; +import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.context.BlockPlaceContext; +import net.momirealms.craftengine.core.item.context.UseOnContext; +import net.momirealms.craftengine.core.registry.Holder; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.HorizontalDirection; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.World; + +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.Callable; + +public class FenceBlockBehavior extends BukkitBlockBehavior { + public static final Factory FACTORY = new Factory(); + private final BooleanProperty northProperty; + private final BooleanProperty eastProperty; + private final BooleanProperty southProperty; + private final BooleanProperty westProperty; + private final Object connectableBlockTag; + private final boolean canLeash; + + public FenceBlockBehavior(CustomBlock customBlock, + BooleanProperty northProperty, + BooleanProperty eastProperty, + BooleanProperty southProperty, + BooleanProperty westProperty, + Object connectableBlockTag, + boolean canLeash) { + super(customBlock); + this.northProperty = northProperty; + this.eastProperty = eastProperty; + this.southProperty = southProperty; + this.westProperty = westProperty; + this.connectableBlockTag = connectableBlockTag; + this.canLeash = canLeash; + } + + @Override + public boolean isPathFindable(Object thisBlock, Object[] args, Callable superMethod) { + return false; + } + + public boolean connectsTo(BlockStateWrapper state, boolean isSideSolid, HorizontalDirection direction) { + boolean isSameFence = this.isSameFence(state); + boolean flag = CoreReflections.clazz$FenceGateBlock.isInstance(BlockStateUtils.getBlockOwner(state.literalObject())) + ? FastNMS.INSTANCE.method$FenceGateBlock$connectsToDirection(state.literalObject(), DirectionUtils.toNMSDirection(direction.toDirection())) + : FenceGateBlockBehavior.connectsToDirection(state, direction); + return !BlockUtils.isExceptionForConnection(state) && isSideSolid || isSameFence || flag; + } + + private boolean isSameFence(BlockStateWrapper state) { + Object blockState = state.literalObject(); + return FastNMS.INSTANCE.method$BlockStateBase$is(blockState, MTagKeys.Block$FENCES) + && FastNMS.INSTANCE.method$BlockStateBase$is(blockState, this.connectableBlockTag) + == FastNMS.INSTANCE.method$BlockStateBase$is(this.customBlock.defaultState().customBlockState().literalObject(), this.connectableBlockTag); + } + + @Override + public InteractionResult useWithoutItem(UseOnContext context, ImmutableBlockState state) { + if (!this.canLeash) return InteractionResult.PASS; + Player player = context.getPlayer(); + if (player == null) return InteractionResult.PASS; + if (FastNMS.INSTANCE.method$LeadItem$bindPlayerMobs(player.serverPlayer(), context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()))) { + player.swingHand(InteractionHand.MAIN_HAND); + return InteractionResult.SUCCESS; + } + return InteractionResult.PASS; + } + + @Override + public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) { + World level = context.getLevel(); + BlockPos clickedPos = context.getClickedPos(); + Object fluidState = FastNMS.INSTANCE.method$BlockGetter$getFluidState(level.serverWorld(), LocationUtils.toBlockPos(clickedPos)); + BlockPos blockPos = clickedPos.north(); + BlockPos blockPos1 = clickedPos.east(); + BlockPos blockPos2 = clickedPos.south(); + BlockPos blockPos3 = clickedPos.west(); + BlockStateWrapper blockState = level.getBlockAt(blockPos).blockState(); + BlockStateWrapper blockState1 = level.getBlockAt(blockPos1).blockState(); + BlockStateWrapper blockState2 = level.getBlockAt(blockPos2).blockState(); + BlockStateWrapper blockState3 = level.getBlockAt(blockPos3).blockState(); + BooleanProperty waterlogged = (BooleanProperty) state.owner().value().getProperty("waterlogged"); + if (waterlogged != null) { + state = state.with(waterlogged, FastNMS.INSTANCE.method$FluidState$getType(fluidState) == MFluids.WATER); + } + return state + .with(this.northProperty, this.connectsTo(blockState, FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy(blockState.literalObject(), level.serverWorld(), LocationUtils.toBlockPos(blockPos), CoreReflections.instance$Direction$SOUTH, CoreReflections.instance$SupportType$FULL), HorizontalDirection.SOUTH)) + .with(this.eastProperty, this.connectsTo(blockState1, FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy(blockState1.literalObject(), level.serverWorld(), LocationUtils.toBlockPos(blockPos1), CoreReflections.instance$Direction$WEST, CoreReflections.instance$SupportType$FULL), HorizontalDirection.WEST)) + .with(this.southProperty, this.connectsTo(blockState2, FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy(blockState2.literalObject(), level.serverWorld(), LocationUtils.toBlockPos(blockPos2), CoreReflections.instance$Direction$NORTH, CoreReflections.instance$SupportType$FULL), HorizontalDirection.NORTH)) + .with(this.westProperty, this.connectsTo(blockState3, FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy(blockState3.literalObject(), level.serverWorld(), LocationUtils.toBlockPos(blockPos3), CoreReflections.instance$Direction$EAST, CoreReflections.instance$SupportType$FULL), HorizontalDirection.EAST)); + } + + @Override + public Object updateShape(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Optional optionalState = BlockStateUtils.getOptionalCustomBlockState(args[0]); + BooleanProperty waterlogged = (BooleanProperty) optionalState + .map(BlockStateHolder::owner) + .map(Holder::value) + .map(block -> block.getProperty("waterlogged")) + .orElse(null); + if (waterlogged != null) { + FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleFluidTick(args[updateShape$level], args[updateShape$blockPos], MFluids.WATER, 5); + } + if (DirectionUtils.fromNMSDirection(args[updateShape$direction]).axis().isHorizontal() && optionalState.isPresent()) { + Direction direction = DirectionUtils.fromNMSDirection(args[updateShape$direction]); + ImmutableBlockState state = optionalState.get(); + if (state.owner() != null) { + BooleanProperty booleanProperty = (BooleanProperty) state.owner().value().getProperty(direction.name().toLowerCase(Locale.ROOT)); + if (booleanProperty != null) { + BlockStateWrapper wrapper = BlockStateUtils.toBlockStateWrapper(args[updateShape$neighborState]); + return state.with(booleanProperty, this.connectsTo(wrapper, FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy(wrapper.literalObject(), args[updateShape$level], args[5], DirectionUtils.toNMSDirection(direction.opposite()), CoreReflections.instance$SupportType$FULL), direction.opposite().toHorizontalDirection())).customBlockState().literalObject(); + } + } + } + return superMethod.call(); + } + + public static class Factory implements BlockBehaviorFactory { + + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + BooleanProperty north = (BooleanProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("north"), "warning.config.block.behavior.fence.missing_north"); + BooleanProperty east = (BooleanProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("east"), "warning.config.block.behavior.fence.missing_east"); + BooleanProperty south = (BooleanProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("south"), "warning.config.block.behavior.fence.missing_south"); + BooleanProperty west = (BooleanProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("west"), "warning.config.block.behavior.fence.missing_west"); + Object connectableBlockTag = FastNMS.INSTANCE.method$TagKey$create(MRegistries.BLOCK, KeyUtils.toResourceLocation(Key.of(arguments.getOrDefault("connectable-block-tag", "minecraft:wooden_fences").toString()))); + connectableBlockTag = connectableBlockTag != null ? connectableBlockTag : MTagKeys.Block$WOODEN_FENCES; + boolean canLeash = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-leash", false), "can-leash"); + return new FenceBlockBehavior(block, north, east, south, west, connectableBlockTag, canLeash); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FenceGateBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FenceGateBlockBehavior.java index 99f897792..03f0d1a11 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FenceGateBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/FenceGateBlockBehavior.java @@ -10,10 +10,7 @@ import net.momirealms.craftengine.bukkit.util.DirectionUtils; import net.momirealms.craftengine.bukkit.util.InteractUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.world.BukkitWorld; -import net.momirealms.craftengine.core.block.BlockBehavior; -import net.momirealms.craftengine.core.block.CustomBlock; -import net.momirealms.craftengine.core.block.ImmutableBlockState; -import net.momirealms.craftengine.core.block.UpdateOption; +import net.momirealms.craftengine.core.block.*; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.entity.player.InteractionResult; @@ -262,6 +259,22 @@ public class FenceGateBlockBehavior extends BukkitBlockBehavior { } } + public static boolean connectsToDirection(BlockStateWrapper state, HorizontalDirection direction) { + FenceGateBlockBehavior fence = BlockStateUtils.getOptionalCustomBlockState(state.literalObject()) + .map(ImmutableBlockState::behavior) + .flatMap(behavior -> behavior.getAs(FenceGateBlockBehavior.class)) + .orElse(null); + if (fence == null) return false; + Direction facing = null; + ImmutableBlockState customState = BlockStateUtils.getOptionalCustomBlockState(state.literalObject()).orElse(null); + if (customState == null) return false; + Property facingProperty = customState.owner().value().getProperty("facing"); + if (facingProperty != null && facingProperty.valueClass() == HorizontalDirection.class) { + facing = ((HorizontalDirection) customState.get(facingProperty)).toDirection(); + } + return facing != null && facing.axis() == direction.toDirection().clockWise().axis(); + } + public static class Factory implements BlockBehaviorFactory { @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java index 20833c67c..6903885ef 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/GrassBlockBehavior.java @@ -146,7 +146,7 @@ public class GrassBlockBehavior extends BukkitBlockBehavior { } if (FastNMS.INSTANCE.method$BlockStateBase$isAir(currentState)) { Object chunkGenerator = CoreReflections.method$ServerChunkCache$getGenerator.invoke(FastNMS.INSTANCE.method$ServerLevel$getChunkSource(world)); - Object placedFeature = CoreReflections.method$Holder$value.invoke(holder.get()); + Object placedFeature = FastNMS.INSTANCE.method$Holder$value(holder.get()); CoreReflections.method$PlacedFeature$place.invoke(placedFeature, world, chunkGenerator, random, nmsCurrentPos); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangingBlockBehavior.java index 594962aca..06724bc9f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangingBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangingBlockBehavior.java @@ -16,7 +16,7 @@ public class HangingBlockBehavior extends BushBlockBehavior { public static final Factory FACTORY = new Factory(); public HangingBlockBehavior(CustomBlock block, int delay, boolean blacklist, boolean stackable, List tagsCanSurviveOn, Set blocksCansSurviveOn, Set customBlocksCansSurviveOn) { - super(block, delay, blacklist, stackable, tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn); + super(block, delay, blacklist, stackable, -1, tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java index c863c19a6..85924d053 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java @@ -132,7 +132,7 @@ public class LeavesBlockBehavior extends BukkitBlockBehavior { Object mutablePos = CoreReflections.constructor$MutableBlockPos.newInstance(); int j = Direction.values().length; for (int k = 0; k < j; ++k) { - Object direction = CoreReflections.instance$Directions[k]; + Object direction = CoreReflections.instance$Direction$values[k]; CoreReflections.method$MutableBlockPos$setWithOffset.invoke(mutablePos, blockPos, direction); Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, mutablePos); i = Math.min(i, getDistanceAt(blockState) + 1); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LiquidFlowableBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LiquidFlowableBlockBehavior.java new file mode 100644 index 000000000..65edd4bdc --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LiquidFlowableBlockBehavior.java @@ -0,0 +1,49 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids; +import net.momirealms.craftengine.core.block.BlockBehavior; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.UpdateOption; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.behavior.special.PlaceLiquidBlockBehavior; +import net.momirealms.craftengine.core.world.WorldEvents; + +import java.util.Map; +import java.util.concurrent.Callable; + +public class LiquidFlowableBlockBehavior extends BukkitBlockBehavior implements PlaceLiquidBlockBehavior { + public static final Factory FACTORY = new Factory(); + + public LiquidFlowableBlockBehavior(CustomBlock customBlock) { + super(customBlock); + } + + @Override + public boolean canPlaceLiquid(Object thisBlock, Object[] args, Callable superMethod) { + return true; + } + + @Override + public boolean placeLiquid(Object thisBlock, Object[] args, Callable superMethod) { + Object level = args[0]; + Object pos = args[1]; + Object blockState = args[2]; + Object fluidState = args[3]; + Object fluidType = FastNMS.INSTANCE.method$FluidState$getType(fluidState); + if (fluidType == MFluids.LAVA || fluidType == MFluids.FLOWING_LAVA) { + FastNMS.INSTANCE.method$LevelAccessor$levelEvent(level, WorldEvents.LAVA_CONVERTS_BLOCK, pos, 0); + } else { + FastNMS.INSTANCE.method$Block$dropResources(blockState, level, pos); + } + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, FastNMS.INSTANCE.method$FluidState$createLegacyBlock(fluidState), UpdateOption.UPDATE_ALL.flags()); + return true; + } + + public static class Factory implements BlockBehaviorFactory { + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + return new LiquidFlowableBlockBehavior(block); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/PressurePlateBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/PressurePlateBlockBehavior.java index da6eb9d78..3d8263e77 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/PressurePlateBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/PressurePlateBlockBehavior.java @@ -4,11 +4,11 @@ import io.papermc.paper.event.entity.EntityInsideBlockEvent; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntitySelectors; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.DirectionUtils; import net.momirealms.craftengine.bukkit.util.EventUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; import net.momirealms.craftengine.core.block.BlockBehavior; import net.momirealms.craftengine.core.block.CustomBlock; @@ -20,7 +20,8 @@ import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.PressurePlateSensitivity; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.craftengine.core.world.*; +import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldEvents; import org.bukkit.GameEvent; import org.bukkit.util.Vector; @@ -66,10 +67,6 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior { return MBlocks.AIR$defaultState; } ImmutableBlockState customState = optionalCustomState.get(); - BlockPos pos = LocationUtils.fromBlockPos(blockPos); - net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); - WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos)); - world.playBlockSound(position, customState.settings().sounds().breakSound()); FastNMS.INSTANCE.method$LevelAccessor$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, customState.customBlockState().registryId()); return MBlocks.AIR$defaultState; } @@ -104,8 +101,6 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior { int signalForState = this.getSignalForState(state); if (signalForState == 0) { this.checkPressed(args[3], args[1], args[2], state, signalForState, thisBlock); - } else { - FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(args[1], args[2], thisBlock, this.pressedTime); } } @@ -115,7 +110,10 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior { case MOBS -> CoreReflections.clazz$LivingEntity; }; Object box = FastNMS.INSTANCE.method$AABB$move(CoreReflections.instance$BasePressurePlateBlock$TOUCH_AABB, pos); - return FastNMS.INSTANCE.method$EntityGetter$getEntitiesOfClass(level, box, clazz) > 0 ? 15 : 0; + return !FastNMS.INSTANCE.method$EntityGetter$getEntitiesOfClass( + level, clazz, box, + MEntitySelectors.NO_SPECTATORS.and(entity -> !FastNMS.INSTANCE.method$Entity$isIgnoringBlockTriggers(entity)) + ).isEmpty() ? 15 : 0; } private Object setSignalForState(Object state, int strength) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java index c56fa4050..fa3535e88 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java @@ -88,7 +88,7 @@ public class SaplingBlockBehavior extends BukkitBlockBehavior { return; } Object chunkGenerator = CoreReflections.method$ServerChunkCache$getGenerator.invoke(FastNMS.INSTANCE.method$ServerLevel$getChunkSource(world)); - Object configuredFeature = CoreReflections.method$Holder$value.invoke(holder.get()); + Object configuredFeature = FastNMS.INSTANCE.method$Holder$value(holder.get()); Object fluidState = FastNMS.INSTANCE.method$BlockGetter$getFluidState(world, blockPos); Object legacyState = CoreReflections.method$FluidState$createLegacyBlock.invoke(fluidState); FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, legacyState, UpdateOption.UPDATE_NONE.flags()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SimpleParticleBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SimpleParticleBlockBehavior.java new file mode 100644 index 000000000..fa0944580 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SimpleParticleBlockBehavior.java @@ -0,0 +1,64 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.block.entity.BukkitBlockEntityTypes; +import net.momirealms.craftengine.bukkit.block.entity.SimpleParticleBlockEntity; +import net.momirealms.craftengine.core.block.BlockBehavior; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.entity.BlockEntityType; +import net.momirealms.craftengine.core.block.entity.tick.BlockEntityTicker; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.particle.ParticleConfig; + +import java.util.List; +import java.util.Map; + +public class SimpleParticleBlockBehavior extends BukkitBlockBehavior implements EntityBlockBehavior { + public static final Factory FACTORY = new Factory(); + public final ParticleConfig[] particles; + public final int tickInterval; + + public SimpleParticleBlockBehavior(CustomBlock customBlock, ParticleConfig[] particles, int tickInterval) { + super(customBlock); + this.particles = particles; + this.tickInterval = tickInterval; + } + + public ParticleConfig[] particles() { + return this.particles; + } + + public int tickInterval() { + return tickInterval; + } + + @Override + public BlockEntityType blockEntityType() { + return EntityBlockBehavior.blockEntityTypeHelper(BukkitBlockEntityTypes.SIMPLE_PARTICLE); + } + + @Override + public BlockEntity createBlockEntity(BlockPos pos, ImmutableBlockState state) { + return new SimpleParticleBlockEntity(pos, state); + } + + @Override + public BlockEntityTicker createAsyncBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType blockEntityType) { + if (this.particles.length == 0) return null; + return EntityBlockBehavior.createTickerHelper(SimpleParticleBlockEntity::tick); + } + + public static class Factory implements BlockBehaviorFactory { + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + List particles = ResourceConfigUtils.parseConfigAsList(ResourceConfigUtils.get(arguments, "particles", "particle"), ParticleConfig::fromMap$blockEntity); + int tickInterval = ResourceConfigUtils.getAsInt(arguments.getOrDefault("tick-interval", 10), "tick-interval"); + return new SimpleParticleBlockBehavior(block, particles.toArray(new ParticleConfig[0]), tickInterval); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SimpleStorageBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SimpleStorageBlockBehavior.java new file mode 100644 index 000000000..07b33233f --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SimpleStorageBlockBehavior.java @@ -0,0 +1,212 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.block.entity.BukkitBlockEntityTypes; +import net.momirealms.craftengine.bukkit.block.entity.SimpleStorageBlockEntity; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.gui.BukkitInventory; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; +import net.momirealms.craftengine.core.block.BlockBehavior; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.entity.BlockEntityType; +import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.item.context.UseOnContext; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.sound.SoundData; +import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.CEWorld; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.Callable; + +public class SimpleStorageBlockBehavior extends BukkitBlockBehavior implements EntityBlockBehavior { + public static final Factory FACTORY = new Factory(); + private final String containerTitle; + private final int rows; + private final SoundData openSound; + private final SoundData closeSound; + private final boolean hasAnalogOutputSignal; + private final boolean canPlaceItem; + private final boolean canTakeItem; + @Nullable + private final Property openProperty; + + public SimpleStorageBlockBehavior(CustomBlock customBlock, + String containerTitle, + int rows, + SoundData openSound, + SoundData closeSound, + boolean hasAnalogOutputSignal, + boolean canPlaceItem, + boolean canTakeItem, + @Nullable Property openProperty) { + super(customBlock); + this.containerTitle = containerTitle; + this.rows = rows; + this.openSound = openSound; + this.closeSound = closeSound; + this.hasAnalogOutputSignal = hasAnalogOutputSignal; + this.canPlaceItem = canPlaceItem; + this.canTakeItem = canTakeItem; + this.openProperty = openProperty; + } + + @Override + public InteractionResult useWithoutItem(UseOnContext context, ImmutableBlockState state) { + CEWorld world = context.getLevel().storageWorld(); + net.momirealms.craftengine.core.entity.player.Player player = context.getPlayer(); + BlockEntity blockEntity = world.getBlockEntityAtIfLoaded(context.getClickedPos()); + if (player != null && blockEntity instanceof SimpleStorageBlockEntity entity) { + Player bukkitPlayer = (Player) player.platformPlayer(); + Optional.ofNullable(entity.inventory()).ifPresent(inventory -> { + entity.onPlayerOpen(player); + bukkitPlayer.openInventory(inventory); + new BukkitInventory(inventory).open(player, AdventureHelper.miniMessage().deserialize(this.containerTitle, PlayerOptionalContext.of(player).tagResolvers())); + }); + } + return InteractionResult.SUCCESS_AND_CANCEL; + } + + // 1.21.5+ + @Override + public void affectNeighborsAfterRemoval(Object thisBlock, Object[] args, Callable superMethod) { + Object level = args[1]; + Object pos = args[2]; + Object blockState = args[0]; + FastNMS.INSTANCE.method$Level$updateNeighbourForOutputSignal(level, pos, BlockStateUtils.getBlockOwner(blockState)); + } + + @Override + public void tick(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object world = args[1]; + Object blockPos = args[2]; + BlockPos pos = LocationUtils.fromBlockPos(blockPos); + World bukkitWorld = FastNMS.INSTANCE.method$Level$getCraftWorld(world); + CEWorld ceWorld = BukkitWorldManager.instance().getWorld(bukkitWorld.getUID()); + BlockEntity blockEntity = ceWorld.getBlockEntityAtIfLoaded(pos); + if (blockEntity instanceof SimpleStorageBlockEntity entity) { + entity.checkOpeners(world, blockPos, args[0]); + } + } + + @Override + public BlockEntityType blockEntityType() { + return EntityBlockBehavior.blockEntityTypeHelper(BukkitBlockEntityTypes.SIMPLE_STORAGE); + } + + @Override + public BlockEntity createBlockEntity(BlockPos pos, ImmutableBlockState state) { + return new SimpleStorageBlockEntity(pos, state); + } + + @NotNull + public String containerTitle() { + return this.containerTitle; + } + + @Nullable + public SoundData closeSound() { + return this.closeSound; + } + + @Nullable + public SoundData openSound() { + return this.openSound; + } + + public int rows() { + return this.rows; + } + + public boolean canPlaceItem() { + return this.canPlaceItem; + } + + public boolean canTakeItem() { + return this.canTakeItem; + } + + public @Nullable Property openProperty() { + return openProperty; + } + + @Override + public int getAnalogOutputSignal(Object thisBlock, Object[] args) { + if (!this.hasAnalogOutputSignal) return 0; + Object world = args[1]; + Object blockPos = args[2]; + BlockPos pos = LocationUtils.fromBlockPos(blockPos); + World bukkitWorld = FastNMS.INSTANCE.method$Level$getCraftWorld(world); + CEWorld ceWorld = BukkitWorldManager.instance().getWorld(bukkitWorld.getUID()); + BlockEntity blockEntity = ceWorld.getBlockEntityAtIfLoaded(pos); + if (blockEntity instanceof SimpleStorageBlockEntity entity) { + Inventory inventory = entity.inventory(); + if (inventory != null) { + float signal = 0.0F; + for (int i = 0; i < inventory.getSize(); i++) { + ItemStack item = inventory.getItem(i); + if (item != null) { + signal += (float) item.getAmount() / (float) (Math.min(inventory.getMaxStackSize(), item.getMaxStackSize())); + } + } + signal /= (float) inventory.getSize(); + return MiscUtils.lerpDiscrete(signal, 0, 15); + } + } + return 0; + } + + @Override + public boolean hasAnalogOutputSignal(Object thisBlock, Object[] args) { + return this.hasAnalogOutputSignal; + } + + @Override + public Object getContainer(Object thisBlock, Object[] args) { + CEWorld ceWorld = BukkitWorldManager.instance().getWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(args[1])); + BlockPos blockPos = LocationUtils.fromBlockPos(args[2]); + BlockEntity blockEntity = ceWorld.getBlockEntityAtIfLoaded(blockPos); + if (blockEntity instanceof SimpleStorageBlockEntity entity) { + return FastNMS.INSTANCE.method$CraftInventory$getInventory(entity.inventory()); + } + return null; + } + + public static class Factory implements BlockBehaviorFactory { + + @SuppressWarnings("unchecked") + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + String title = arguments.getOrDefault("title", "").toString(); + int rows = MiscUtils.clamp(ResourceConfigUtils.getAsInt(arguments.getOrDefault("rows", 1), "rows"), 1, 6); + Map sounds = (Map) arguments.get("sounds"); + boolean hasAnalogOutputSignal = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("has-signal", true), "has-signal"); + SoundData openSound = null; + SoundData closeSound = null; + if (sounds != null) { + openSound = Optional.ofNullable(sounds.get("open")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_0_5, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null); + closeSound = Optional.ofNullable(sounds.get("close")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_0_5, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null); + } + boolean canPlaceItem = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("allow-input", true), "allow-input"); + boolean canTakeItem = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("allow-output", true), "allow-output"); + Property property = (Property) block.getProperty("open"); + return new SimpleStorageBlockBehavior(block, title, rows, openSound, closeSound, hasAnalogOutputSignal, canPlaceItem, canTakeItem, property); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SlabBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SlabBlockBehavior.java index 3eaf4efcd..f44f55c3a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SlabBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SlabBlockBehavior.java @@ -10,7 +10,7 @@ import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.Property; -import net.momirealms.craftengine.core.block.state.properties.SlabType; +import net.momirealms.craftengine.core.block.properties.type.SlabType; import net.momirealms.craftengine.core.item.CustomItem; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.behavior.BlockBoundItemBehavior; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SofaBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SofaBlockBehavior.java new file mode 100644 index 000000000..4259fd8e4 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SofaBlockBehavior.java @@ -0,0 +1,112 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.DirectionUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.core.block.BlockBehavior; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.block.properties.type.SofaShape; +import net.momirealms.craftengine.core.item.context.BlockPlaceContext; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.HorizontalDirection; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.BlockPos; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.Callable; + +public class SofaBlockBehavior extends BukkitBlockBehavior { + public static final Factory FACTORY = new Factory(); + private final Property facingProperty; + private final Property shapeProperty; + + public SofaBlockBehavior(CustomBlock block, Property facing, Property shape) { + super(block); + this.facingProperty = facing; + this.shapeProperty = shape; + } + + @Override + public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) { + BlockPos clickedPos = context.getClickedPos(); + ImmutableBlockState blockState = state.owner().value().defaultState() + .with(this.facingProperty, context.getHorizontalDirection().toHorizontalDirection()); + if (super.waterloggedProperty != null) { + Object fluidState = FastNMS.INSTANCE.method$BlockGetter$getFluidState(context.getLevel().serverWorld(), LocationUtils.toBlockPos(clickedPos)); + blockState = blockState.with(this.waterloggedProperty, FastNMS.INSTANCE.method$FluidState$getType(fluidState) == MFluids.WATER); + } + return blockState.with(this.shapeProperty, getSofaShape(blockState, context.getLevel().serverWorld(), clickedPos)); + } + + @Override + public Object updateShape(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object level = args[updateShape$level]; + Object blockPos = args[updateShape$blockPos]; + Object blockState = args[0]; + Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState); + if (optionalCustomState.isEmpty()) return blockState; + ImmutableBlockState customState = optionalCustomState.get(); + if (super.waterloggedProperty != null && customState.get(this.waterloggedProperty)) { + FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleFluidTick(args[updateShape$level], args[updateShape$blockPos], MFluids.WATER, 5); + } + Direction direction = DirectionUtils.fromNMSDirection(VersionHelper.isOrAbove1_21_2() ? args[4] : args[1]); + SofaShape sofaShape = getSofaShape(customState, level, LocationUtils.fromBlockPos(blockPos)); + return direction.axis().isHorizontal() + ? customState.with(this.shapeProperty, sofaShape).customBlockState().literalObject() + : superMethod.call(); + } + + private SofaShape getSofaShape(ImmutableBlockState state, Object level, BlockPos pos) { + Direction direction = state.get(this.facingProperty).toDirection(); + Object relativeBlockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, LocationUtils.toBlockPos(pos.relative(direction.opposite()))); + Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(relativeBlockState); + if (optionalCustomState.isPresent()) { + ImmutableBlockState customState = optionalCustomState.get(); + Optional optionalStairsBlockBehavior = customState.behavior().getAs(SofaBlockBehavior.class); + if (optionalStairsBlockBehavior.isPresent()) { + SofaBlockBehavior stairsBlockBehavior = optionalStairsBlockBehavior.get(); + Direction direction1 = customState.get(stairsBlockBehavior.facingProperty).toDirection(); + if (direction1.axis() != state.get(this.facingProperty).toDirection().axis() && canTakeShape(state, level, pos, direction1)) { + if (direction1 == direction.counterClockWise()) { + return SofaShape.INNER_LEFT; + } + return SofaShape.INNER_RIGHT; + } + } + } + return SofaShape.STRAIGHT; + } + + private boolean canTakeShape(ImmutableBlockState state, Object level, BlockPos pos, Direction face) { + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, LocationUtils.toBlockPos(pos.relative(face))); + Optional optionalAnotherState = BlockStateUtils.getOptionalCustomBlockState(blockState); + if (optionalAnotherState.isEmpty()) { + return true; + } + ImmutableBlockState anotherState = optionalAnotherState.get(); + Optional optionalBehavior = anotherState.behavior().getAs(SofaBlockBehavior.class); + if (optionalBehavior.isEmpty()) { + return true; + } + SofaBlockBehavior anotherBehavior = optionalBehavior.get(); + return anotherState.get(anotherBehavior.facingProperty) != state.get(this.facingProperty); + } + + public static class Factory implements BlockBehaviorFactory { + + @Override + @SuppressWarnings("unchecked") + public BlockBehavior create(CustomBlock block, Map arguments) { + Property facing = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("facing"), "warning.config.block.behavior.sofa.missing_facing"); + Property shape = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("shape"), "warning.config.block.behavior.sofa.missing_shape"); + return new SofaBlockBehavior(block, facing, shape); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StairsBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StairsBlockBehavior.java index 2ad532eed..1fa475582 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StairsBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StairsBlockBehavior.java @@ -10,8 +10,8 @@ import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.Property; -import net.momirealms.craftengine.core.block.state.properties.SingleBlockHalf; -import net.momirealms.craftengine.core.block.state.properties.StairsShape; +import net.momirealms.craftengine.core.block.properties.type.SingleBlockHalf; +import net.momirealms.craftengine.core.block.properties.type.StairsShape; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.HorizontalDirection; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StemBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StemBlockBehavior.java new file mode 100644 index 000000000..69755b7ac --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StemBlockBehavior.java @@ -0,0 +1,139 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.DirectionUtils; +import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.core.block.BlockBehavior; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.UpdateOption; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.properties.IntegerProperty; +import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.util.*; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.Callable; + +public class StemBlockBehavior extends BukkitBlockBehavior { + public static final Factory FACTORY = new Factory(); + private final IntegerProperty ageProperty; + private final Key fruit; + private final Key attachedStem; + private final int minGrowLight; + private final Object tagMayPlaceFruit; + private final Object blockMayPlaceFruit; + + public StemBlockBehavior(CustomBlock customBlock, + IntegerProperty ageProperty, + Key fruit, + Key attachedStem, + int minGrowLight, + Object tagMayPlaceFruit, + Object blockMayPlaceFruit) { + super(customBlock); + this.ageProperty = ageProperty; + this.fruit = fruit; + this.attachedStem = attachedStem; + this.minGrowLight = minGrowLight; + this.tagMayPlaceFruit = tagMayPlaceFruit; + this.blockMayPlaceFruit = blockMayPlaceFruit; + } + + @Override + public boolean isPathFindable(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + return (VersionHelper.isOrAbove1_20_5() ? args[1] : args[3]).equals(CoreReflections.instance$PathComputationType$AIR) + && !FastNMS.INSTANCE.field$BlockBehavior$hasCollision(thisBlock) || (boolean) superMethod.call(); + } + + @Override + public void randomTick(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object state = args[0]; + Object level = args[1]; + Object pos = args[2]; + if (CropBlockBehavior.getRawBrightness(level, pos) < this.minGrowLight) return; + ImmutableBlockState customState = BlockStateUtils.getOptionalCustomBlockState(state).orElse(null); + if (customState == null || customState.isEmpty()) return; + int age = customState.get(ageProperty); + if (age < ageProperty.max) { + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, customState.with(ageProperty, age + 1).customBlockState().literalObject(), 2); + return; + } + Object randomDirection = CoreReflections.instance$Direction$values[RandomUtils.generateRandomInt(2, 6)]; + Object blockPos = FastNMS.INSTANCE.method$BlockPos$relative(pos, randomDirection); + if (!FastNMS.INSTANCE.method$BlockStateBase$isAir(FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, blockPos))) + return; + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, FastNMS.INSTANCE.method$BlockPos$relative(blockPos, CoreReflections.instance$Direction$DOWN)); + if (mayPlaceFruit(blockState)) { + Optional optionalFruit = BukkitBlockManager.instance().blockById(this.fruit); + Object fruitState = null; + if (optionalFruit.isPresent()) { + fruitState = optionalFruit.get().defaultState().customBlockState().literalObject(); + } else if (fruit.namespace().equals("minecraft")) { + fruitState = FastNMS.INSTANCE.method$Block$defaultState(FastNMS.INSTANCE.method$Registry$getValue( + MBuiltInRegistries.BLOCK, + FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", fruit.value()) + )); + } + Optional optionalAttachedStem = BukkitBlockManager.instance().blockById(this.attachedStem); + if (fruitState == null || optionalAttachedStem.isEmpty()) return; + CustomBlock attachedStem = optionalAttachedStem.get(); + @SuppressWarnings("unchecked") + Property facing = (Property) attachedStem.getProperty("facing"); + if (facing == null) return; + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, fruitState, UpdateOption.UPDATE_ALL.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, attachedStem.defaultState().with(facing, DirectionUtils.fromNMSDirection(randomDirection).toHorizontalDirection()).customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); + } + } + + @Override + public boolean isValidBoneMealTarget(Object thisBlock, Object[] args) { + ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(args[2]).orElse(null); + if (state == null || state.isEmpty()) return false; + return state.get(ageProperty) != ageProperty.max; + } + + @Override + public boolean isBoneMealSuccess(Object thisBlock, Object[] args) { + return true; + } + + @Override + public void performBoneMeal(Object thisBlock, Object[] args) { + ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(args[3]).orElse(null); + if (state == null || state.isEmpty()) return; + int min = Math.min(7, state.get(ageProperty) + RandomUtils.generateRandomInt(Math.min(ageProperty.min + 2, ageProperty.max), Math.min(ageProperty.max - 2, ageProperty.max))); + Object blockState = state.with(ageProperty, min).customBlockState().literalObject(); + FastNMS.INSTANCE.method$LevelWriter$setBlock(args[0], args[2], blockState, 2); + if (min >= ageProperty.max) { + FastNMS.INSTANCE.method$BlockBehaviour$BlockStateBase$randomTick(blockState, args[0], args[2]); + } + } + + private boolean mayPlaceFruit(Object blockState) { + boolean flag1 = tagMayPlaceFruit != null && FastNMS.INSTANCE.method$BlockStateBase$is(blockState, tagMayPlaceFruit); + boolean flag2 = blockMayPlaceFruit != null && FastNMS.INSTANCE.method$BlockStateBase$isBlock(blockState, blockMayPlaceFruit); + if (tagMayPlaceFruit == null && blockMayPlaceFruit == null) return true; + return flag1 || flag2; + } + + public static class Factory implements BlockBehaviorFactory { + + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + IntegerProperty ageProperty = (IntegerProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("age"), "warning.config.block.behavior.stem.missing_age"); + Key fruit = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("fruit"), "warning.config.block.behavior.stem.missing_fruit")); + Key attachedStem = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("attached-stem"), "warning.config.block.behavior.stem.missing_attached_stem")); + int minGrowLight = ResourceConfigUtils.getAsInt(arguments.getOrDefault("light-requirement", 9), "light-requirement"); + Object tagMayPlaceFruit = FastNMS.INSTANCE.method$TagKey$create(MRegistries.BLOCK, KeyUtils.toResourceLocation(Key.of(arguments.getOrDefault("may-place-fruit", "minecraft:dirt").toString()))); + Object blockMayPlaceFruit = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(Key.of(arguments.getOrDefault("may-place-fruit", "minecraft:farmland").toString()))); + return new StemBlockBehavior(block, ageProperty, fruit, attachedStem, minGrowLight, tagMayPlaceFruit, blockMayPlaceFruit); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ToggleableLampBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ToggleableLampBlockBehavior.java new file mode 100644 index 000000000..4fbb1b43f --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ToggleableLampBlockBehavior.java @@ -0,0 +1,100 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.core.block.BlockBehavior; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.item.context.UseOnContext; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.Callable; + +public class ToggleableLampBlockBehavior extends BukkitBlockBehavior { + public static final Factory FACTORY = new Factory(); + private final Property litProperty; + private final Property poweredProperty; + private final boolean canOpenWithHand; + + public ToggleableLampBlockBehavior(CustomBlock block, Property litProperty, Property poweredProperty, boolean canOpenWithHand) { + super(block); + this.litProperty = litProperty; + this.poweredProperty = poweredProperty; + this.canOpenWithHand = canOpenWithHand; + } + + @Override + public InteractionResult useWithoutItem(UseOnContext context, ImmutableBlockState state) { + if (!this.canOpenWithHand) { + return InteractionResult.PASS; + } + ToggleableLampBlockBehavior behavior = state.behavior().getAs(ToggleableLampBlockBehavior.class).orElse(null); + if (behavior == null) return InteractionResult.PASS; + FastNMS.INSTANCE.method$LevelWriter$setBlock( + context.getLevel().serverWorld(), + LocationUtils.toBlockPos(context.getClickedPos()), + state.cycle(behavior.litProperty).customBlockState().literalObject(), + 2 + ); + Optional.ofNullable(context.getPlayer()).ifPresent(p -> p.swingHand(context.getHand())); + return InteractionResult.SUCCESS_AND_CANCEL; + } + + @Override + public void onPlace(Object thisBlock, Object[] args, Callable superMethod) { + if (this.poweredProperty == null) return; + Object state = args[0]; + Object level = args[1]; + Object pos = args[2]; + Object oldState = args[3]; + if (FastNMS.INSTANCE.method$BlockState$getBlock(oldState) != FastNMS.INSTANCE.method$BlockState$getBlock(state) && CoreReflections.clazz$ServerLevel.isInstance(level)) { + Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(state); + if (optionalCustomState.isEmpty()) return; + checkAndFlip(optionalCustomState.get(), level, pos); + } + } + + @Override + public void neighborChanged(Object thisBlock, Object[] args, Callable superMethod) { + if (this.poweredProperty == null) return; + Object blockState = args[0]; + Object world = args[1]; + if (!CoreReflections.clazz$ServerLevel.isInstance(world)) return; + Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState); + if (optionalCustomState.isEmpty()) return; + Object blockPos = args[2]; + ImmutableBlockState customState = optionalCustomState.get(); + checkAndFlip(customState, world, blockPos); + } + + private void checkAndFlip(ImmutableBlockState customState, Object level, Object pos) { + boolean hasNeighborSignal = FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(level, pos); + boolean isPowered = customState.get(this.poweredProperty); + if (hasNeighborSignal != isPowered) { + ImmutableBlockState blockState = customState; + if (!isPowered) { + blockState = blockState.cycle(this.litProperty); + } + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, blockState.with(this.poweredProperty, hasNeighborSignal).customBlockState().literalObject(), 3); + } + + } + + @SuppressWarnings("unchecked") + public static class Factory implements BlockBehaviorFactory { + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + boolean canOpenWithHand = ResourceConfigUtils.getAsBoolean(ResourceConfigUtils.get(arguments, "can-open-with-hand", "can-toggle-with-hand"), "can-toggle-with-hand"); + Property lit = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("lit"), "warning.config.block.behavior.toggleable_lamp.missing_lit"); + Property powered = (Property) (canOpenWithHand ? block.getProperty("powered") : ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("powered"), "warning.config.block.behavior.toggleable_lamp.missing_powered")); + return new ToggleableLampBlockBehavior(block, lit, powered, canOpenWithHand); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/TrapDoorBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/TrapDoorBlockBehavior.java index 71db218b5..e945d546c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/TrapDoorBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/TrapDoorBlockBehavior.java @@ -15,7 +15,7 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.UpdateOption; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.Property; -import net.momirealms.craftengine.core.block.state.properties.SingleBlockHalf; +import net.momirealms.craftengine.core.block.properties.type.SingleBlockHalf; import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java index 4542f71d2..e2671bc94 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java @@ -4,15 +4,20 @@ import net.momirealms.craftengine.core.block.BlockBehavior; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior; +import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior; +import net.momirealms.craftengine.core.block.behavior.special.FallOnBlockBehavior; +import net.momirealms.craftengine.core.block.behavior.special.PlaceLiquidBlockBehavior; import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.item.context.UseOnContext; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Optional; import java.util.concurrent.Callable; -public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior { +public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior + implements FallOnBlockBehavior, PlaceLiquidBlockBehavior { private final AbstractBlockBehavior[] behaviors; public UnsafeCompositeBlockBehavior(CustomBlock customBlock, List behaviors) { @@ -20,6 +25,26 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior { this.behaviors = behaviors.toArray(new AbstractBlockBehavior[0]); } + @Override + public boolean canPlaceLiquid(Object thisBlock, Object[] args, Callable superMethod) { + for (AbstractBlockBehavior behavior : behaviors) { + if (behavior instanceof PlaceLiquidBlockBehavior) { + return behavior.canPlaceLiquid(thisBlock, args, superMethod); + } + } + return super.canPlaceLiquid(thisBlock, args, superMethod); + } + + @Override + public boolean placeLiquid(Object thisBlock, Object[] args, Callable superMethod) { + for (AbstractBlockBehavior behavior : behaviors) { + if (behavior instanceof PlaceLiquidBlockBehavior) { + return behavior.placeLiquid(thisBlock, args, superMethod); + } + } + return super.placeLiquid(thisBlock, args, superMethod); + } + @SuppressWarnings("unchecked") @Override public Optional getAs(Class tClass) { @@ -31,6 +56,22 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior { return Optional.empty(); } + @Nullable + @Override + public EntityBlockBehavior getEntityBehavior() { + EntityBlockBehavior target = null; + for (AbstractBlockBehavior behavior : this.behaviors) { + if (behavior instanceof EntityBlockBehavior entityBehavior) { + if (target == null) { + target = entityBehavior; + } else { + throw new IllegalArgumentException("Multiple entity block behaviors are not allowed"); + } + } + } + return target; + } + @Override public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) { for (AbstractBlockBehavior behavior : this.behaviors) { @@ -74,6 +115,18 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior { return previous; } + + @Override + public Object getContainer(Object thisBlock, Object[] args) throws Exception { + for (AbstractBlockBehavior behavior : this.behaviors) { + Object container = behavior.getContainer(thisBlock, args); + if (container != null) { + return container; + } + } + return null; + } + @Override public void tick(Object thisBlock, Object[] args, Callable superMethod) throws Exception { for (AbstractBlockBehavior behavior : this.behaviors) { @@ -264,6 +317,30 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior { return false; } + @Override + public boolean hasAnalogOutputSignal(Object thisBlock, Object[] args) throws Exception { + for (AbstractBlockBehavior behavior : this.behaviors) { + if (behavior.hasAnalogOutputSignal(thisBlock, args)) { + return true; + } + } + return false; + } + + @Override + public int getAnalogOutputSignal(Object thisBlock, Object[] args) throws Exception { + int signal = 0; + int count = 0; + for (AbstractBlockBehavior behavior : this.behaviors) { + int s = behavior.getAnalogOutputSignal(thisBlock, args); + if (s != 0) { + signal += s; + count++; + } + } + return count == 0 ? 0 : signal / count; + } + @Override public Object playerWillDestroy(Object thisBlock, Object[] args, Callable superMethod) throws Exception { Object previous = args[0]; @@ -282,4 +359,40 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior { behavior.spawnAfterBreak(thisBlock, args, superMethod); } } + + @Override + public void fallOn(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + for (AbstractBlockBehavior behavior : this.behaviors) { + if (behavior instanceof FallOnBlockBehavior f) { + f.fallOn(thisBlock, args, superMethod); + return; + } + } + FallOnBlockBehavior.super.fallOn(thisBlock, args, superMethod); + } + + @Override + public void updateEntityMovementAfterFallOn(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + for (AbstractBlockBehavior behavior : this.behaviors) { + if (behavior instanceof FallOnBlockBehavior f) { + f.updateEntityMovementAfterFallOn(thisBlock, args, superMethod); + return; + } + } + FallOnBlockBehavior.super.updateEntityMovementAfterFallOn(thisBlock, args, superMethod); + } + + @Override + public void stepOn(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + for (AbstractBlockBehavior behavior : this.behaviors) { + behavior.stepOn(thisBlock, args, superMethod); + } + } + + @Override + public void onProjectileHit(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + for (AbstractBlockBehavior behavior : this.behaviors) { + behavior.onProjectileHit(thisBlock, args, superMethod); + } + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/VerticalCropBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/VerticalCropBlockBehavior.java index 8e416ac4f..c42f10337 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/VerticalCropBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/VerticalCropBlockBehavior.java @@ -83,7 +83,7 @@ public class VerticalCropBlockBehavior extends BukkitBlockBehavior { @SuppressWarnings("unchecked") @Override public BlockBehavior create(CustomBlock block, Map arguments) { - Property ageProperty = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("age"), "warning.config.block.behavior.sugar_cane.missing_age"); + Property ageProperty = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("age"), "warning.config.block.behavior.vertical_crop.missing_age"); int maxHeight = ResourceConfigUtils.getAsInt(arguments.getOrDefault("max-height", 3), "max-height"); boolean direction = arguments.getOrDefault("direction", "up").toString().equalsIgnoreCase("up"); return new VerticalCropBlockBehavior(block, ageProperty, maxHeight, diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/WallTorchParticleBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/WallTorchParticleBlockBehavior.java new file mode 100644 index 000000000..85888fc1e --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/WallTorchParticleBlockBehavior.java @@ -0,0 +1,78 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.block.entity.BukkitBlockEntityTypes; +import net.momirealms.craftengine.bukkit.block.entity.WallTorchParticleBlockEntity; +import net.momirealms.craftengine.core.block.BlockBehavior; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.entity.BlockEntityType; +import net.momirealms.craftengine.core.block.entity.tick.BlockEntityTicker; +import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.HorizontalDirection; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.particle.ParticleConfig; + +import java.util.List; +import java.util.Map; + +public class WallTorchParticleBlockBehavior extends BukkitBlockBehavior implements EntityBlockBehavior { + public static final Factory FACTORY = new Factory(); + public final ParticleConfig[] particles; + public final int tickInterval; + public final Property facingProperty; + + public WallTorchParticleBlockBehavior(CustomBlock customBlock, ParticleConfig[] particles, int tickInterval, Property facingProperty) { + super(customBlock); + this.particles = particles; + this.tickInterval = tickInterval; + this.facingProperty = facingProperty; + } + + public ParticleConfig[] particles() { + return this.particles; + } + + public int tickInterval() { + return tickInterval; + } + + public Property facingProperty() { + return facingProperty; + } + + @Override + public BlockEntityType blockEntityType() { + return EntityBlockBehavior.blockEntityTypeHelper(BukkitBlockEntityTypes.WALL_TORCH_PARTICLE); + } + + @Override + public BlockEntity createBlockEntity(BlockPos pos, ImmutableBlockState state) { + return new WallTorchParticleBlockEntity(pos, state); + } + + @Override + public BlockEntityTicker createAsyncBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType blockEntityType) { + if (this.particles.length == 0) return null; + return EntityBlockBehavior.createTickerHelper(WallTorchParticleBlockEntity::tick); + } + + public static class Factory implements BlockBehaviorFactory { + @SuppressWarnings("unchecked") + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + List particles = ResourceConfigUtils.parseConfigAsList(ResourceConfigUtils.get(arguments, "particles", "particle"), ParticleConfig::fromMap$blockEntity); + int tickInterval = ResourceConfigUtils.getAsInt(arguments.getOrDefault("tick-interval", 10), "tick-interval"); + Property directionProperty = (Property) block.getProperty("facing"); + if (directionProperty == null) { + throw new LocalizedResourceConfigException("warning.config.block.behavior.wall_torch_particle.missing_facing"); + } + return new WallTorchParticleBlockBehavior(block, particles.toArray(new ParticleConfig[0]), tickInterval, directionProperty); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/AbstractAnimateTickBlockEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/AbstractAnimateTickBlockEntity.java new file mode 100644 index 000000000..90f863944 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/AbstractAnimateTickBlockEntity.java @@ -0,0 +1,14 @@ +package net.momirealms.craftengine.bukkit.block.entity; + +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.entity.BlockEntityType; +import net.momirealms.craftengine.core.world.BlockPos; + +public abstract class AbstractAnimateTickBlockEntity extends BlockEntity { + protected int tickCount; + + public AbstractAnimateTickBlockEntity(BlockEntityType type, BlockPos pos, ImmutableBlockState blockState) { + super(type, pos, blockState); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/BlockEntityHolder.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/BlockEntityHolder.java new file mode 100644 index 000000000..23ec8fb4c --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/BlockEntityHolder.java @@ -0,0 +1,28 @@ +package net.momirealms.craftengine.bukkit.block.entity; + +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; + +public class BlockEntityHolder implements InventoryHolder { + private final BlockEntity blockEntity; + private Inventory inventory; + + public BlockEntityHolder(BlockEntity entity) { + this.blockEntity = entity; + } + + public BlockEntity blockEntity() { + return blockEntity; + } + + public void setInventory(Inventory inventory) { + this.inventory = inventory; + } + + @Override + public @NotNull Inventory getInventory() { + return this.inventory; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/BukkitBlockEntityTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/BukkitBlockEntityTypes.java new file mode 100644 index 000000000..7c0456145 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/BukkitBlockEntityTypes.java @@ -0,0 +1,11 @@ +package net.momirealms.craftengine.bukkit.block.entity; + +import net.momirealms.craftengine.core.block.entity.BlockEntityType; +import net.momirealms.craftengine.core.block.entity.BlockEntityTypeKeys; +import net.momirealms.craftengine.core.block.entity.BlockEntityTypes; + +public class BukkitBlockEntityTypes extends BlockEntityTypes { + public static final BlockEntityType SIMPLE_STORAGE = register(BlockEntityTypeKeys.SIMPLE_STORAGE, SimpleStorageBlockEntity::new); + public static final BlockEntityType SIMPLE_PARTICLE = register(BlockEntityTypeKeys.SIMPLE_PARTICLE, SimpleParticleBlockEntity::new); + public static final BlockEntityType WALL_TORCH_PARTICLE = register(BlockEntityTypeKeys.WALL_TORCH_PARTICLE, WallTorchParticleBlockEntity::new); +} \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SimpleParticleBlockEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SimpleParticleBlockEntity.java new file mode 100644 index 000000000..30acd0be6 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SimpleParticleBlockEntity.java @@ -0,0 +1,45 @@ +package net.momirealms.craftengine.bukkit.block.entity; + +import net.momirealms.craftengine.bukkit.block.behavior.SimpleParticleBlockBehavior; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.ContextHolder; +import net.momirealms.craftengine.core.plugin.context.SimpleContext; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.particle.ParticleConfig; + +public class SimpleParticleBlockEntity extends AbstractAnimateTickBlockEntity { + private final SimpleParticleBlockBehavior behavior; + private final Context context = SimpleContext.of(ContextHolder.empty()); + + public SimpleParticleBlockEntity(BlockPos pos, ImmutableBlockState blockState) { + super(BukkitBlockEntityTypes.SIMPLE_PARTICLE, pos, blockState); + this.behavior = blockState.behavior().getAs(SimpleParticleBlockBehavior.class).orElseThrow(); + } + + public void animateTick(ImmutableBlockState state, World level, BlockPos pos) { + for (ParticleConfig particle : this.behavior.particles) { + Vec3d location = new Vec3d(super.pos.x() + particle.x.getDouble(context), super.pos.y() + particle.y.getDouble(context), super.pos.z() + particle.z.getDouble(context)); + level.spawnParticle( + location, + particle.particleType, + particle.count.getInt(context), + particle.xOffset.getDouble(context), + particle.yOffset.getDouble(context), + particle.zOffset.getDouble(context), + particle.speed.getDouble(context), + particle.particleData, + context + ); + } + } + + public static void tick(CEWorld ceWorld, BlockPos blockPos, ImmutableBlockState state, SimpleParticleBlockEntity particle) { + particle.tickCount++; + if (particle.tickCount % particle.behavior.tickInterval != 0) return; + particle.animateTick(state, ceWorld.world(), blockPos); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SimpleStorageBlockEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SimpleStorageBlockEntity.java new file mode 100644 index 000000000..fcb654c71 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SimpleStorageBlockEntity.java @@ -0,0 +1,218 @@ +package net.momirealms.craftengine.bukkit.block.entity; + +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; +import net.momirealms.craftengine.bukkit.block.behavior.SimpleStorageBlockBehavior; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.UpdateOption; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.sound.SoundData; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.ListTag; +import org.bukkit.GameEvent; +import org.bukkit.GameMode; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Optional; + +public class SimpleStorageBlockEntity extends BlockEntity { + private final SimpleStorageBlockBehavior behavior; + private final Inventory inventory; + private double maxInteractionDistance; + private boolean openState = false; + + public SimpleStorageBlockEntity(BlockPos pos, ImmutableBlockState blockState) { + super(BukkitBlockEntityTypes.SIMPLE_STORAGE, pos, blockState); + this.behavior = super.blockState.behavior().getAs(SimpleStorageBlockBehavior.class).orElseThrow(); + BlockEntityHolder holder = new BlockEntityHolder(this); + this.inventory = FastNMS.INSTANCE.createSimpleStorageContainer(holder, this.behavior.rows() * 9, this.behavior.canPlaceItem(), this.behavior.canTakeItem()); + holder.setInventory(this.inventory); + } + + @Override + protected void saveCustomData(CompoundTag tag) { + // 保存前先把所有打开此容器的玩家界面关闭 + this.inventory.close(); + ListTag itemsTag = new ListTag(); + @Nullable ItemStack[] storageContents = this.inventory.getStorageContents(); + for (int i = 0; i < storageContents.length; i++) { + if (storageContents[i] != null) { + if (VersionHelper.isOrAbove1_20_5()) { + int slot = i; + CoreReflections.instance$ItemStack$CODEC.encodeStart(MRegistryOps.SPARROW_NBT, FastNMS.INSTANCE.field$CraftItemStack$handle(storageContents[i])) + .ifSuccess(success -> { + CompoundTag itemTag = (CompoundTag) success; + itemTag.putInt("slot", slot); + itemsTag.add(itemTag); + }) + .ifError(error -> CraftEngine.instance().logger().severe("Error while saving storage item: " + error)); + } else { + Object nmsTag = FastNMS.INSTANCE.method$itemStack$save(FastNMS.INSTANCE.field$CraftItemStack$handle(storageContents[i]), FastNMS.INSTANCE.constructor$CompoundTag()); + CompoundTag itemTag = (CompoundTag) MRegistryOps.NBT.convertTo(MRegistryOps.SPARROW_NBT, nmsTag); + itemTag.putInt("slot", i); + itemsTag.add(itemTag); + } + } + } + tag.put("items", itemsTag); + } + + @Override + public void loadCustomData(CompoundTag tag) { + ListTag itemsTag = Optional.ofNullable(tag.getList("items")).orElseGet(ListTag::new); + ItemStack[] storageContents = new ItemStack[this.behavior.rows() * 9]; + for (int i = 0; i < itemsTag.size(); i++) { + CompoundTag itemTag = itemsTag.getCompound(i); + int slot = itemTag.getInt("slot"); + if (slot < 0 || slot >= storageContents.length) { + continue; + } + if (VersionHelper.isOrAbove1_20_5()) { + CoreReflections.instance$ItemStack$CODEC.parse(MRegistryOps.SPARROW_NBT, itemTag) + .resultOrPartial((s) -> CraftEngine.instance().logger().severe("Tried to load invalid item: '" + itemTag + "'. " + s)) + .ifPresent(nmsStack -> storageContents[slot] = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsStack)); + } else { + Object nmsTag = MRegistryOps.SPARROW_NBT.convertTo(MRegistryOps.NBT, itemTag); + Object itemStack = FastNMS.INSTANCE.method$ItemStack$of(nmsTag); + storageContents[slot] = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack); + } + } + this.inventory.setStorageContents(storageContents); + } + + public Inventory inventory() { + if (!isValid()) return null; + return this.inventory; + } + + public void onPlayerOpen(Player player) { + if (!isValidContainer()) return; + if (!player.isSpectatorMode()) { + // 有非观察者的人,那么就不触发开启音效和事件 + if (!hasNoViewer(this.inventory.getViewers())) return; + this.maxInteractionDistance = Math.max(player.getCachedInteractionRange(), this.maxInteractionDistance); + this.setOpen(player); + FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(super.world.world().serverWorld(), LocationUtils.toBlockPos(this.pos), BlockStateUtils.getBlockOwner(this.blockState.customBlockState().literalObject()), 5); + } + } + + public void onPlayerClose(Player player) { + if (!isValidContainer()) return; + if (!player.isSpectatorMode()) { + // 有非观察者的人,那么就不触发关闭音效和事件 + for (HumanEntity viewer : this.inventory.getViewers()) { + if (viewer.getGameMode() == GameMode.SPECTATOR || viewer == player.platformPlayer()) { + continue; + } + return; + } + this.maxInteractionDistance = 0; + this.setClose(player); + } + } + + private void setOpen(@Nullable Player player) { + this.updateOpenBlockState(true); + org.bukkit.World bukkitWorld = (org.bukkit.World) super.world.world().platformWorld(); + if (player != null) { + bukkitWorld.sendGameEvent((org.bukkit.entity.Player) player.platformPlayer(), GameEvent.CONTAINER_OPEN, new Vector(this.pos.x(), this.pos.y(), this.pos.z())); + } else { + bukkitWorld.sendGameEvent(null, GameEvent.CONTAINER_OPEN, new Vector(this.pos.x(), this.pos.y(), this.pos.z())); + } + this.openState = true; + SoundData soundData = this.behavior.openSound(); + if (soundData != null) { + super.world.world().playBlockSound(Vec3d.atCenterOf(this.pos), soundData); + } + } + + private void setClose(@Nullable Player player) { + this.updateOpenBlockState(false); + org.bukkit.World bukkitWorld = (org.bukkit.World) super.world.world().platformWorld(); + if (player != null) { + bukkitWorld.sendGameEvent((org.bukkit.entity.Player) player.platformPlayer(), GameEvent.CONTAINER_CLOSE, new Vector(this.pos.x(), this.pos.y(), this.pos.z())); + } else { + bukkitWorld.sendGameEvent(null, GameEvent.CONTAINER_CLOSE, new Vector(this.pos.x(), this.pos.y(), this.pos.z())); + } + this.openState = false; + SoundData soundData = this.behavior.closeSound(); + if (soundData != null) { + super.world.world().playBlockSound(Vec3d.atCenterOf(this.pos), soundData); + } + } + + private boolean hasNoViewer(List viewers) { + for (HumanEntity viewer : viewers) { + if (viewer.getGameMode() != GameMode.SPECTATOR) { + return false; + } + } + return true; + } + + private boolean isValidContainer() { + return this.isValid() && this.inventory != null && this.behavior != null; + } + + public void updateOpenBlockState(boolean open) { + ImmutableBlockState state = super.world.getBlockStateAtIfLoaded(this.pos); + if (state == null || state.behavior() != this.behavior) return; + Property property = this.behavior.openProperty(); + if (property == null) return; + super.world.world().setBlockAt(this.pos.x(), this.pos.y(), this.pos.z(), state.with(property, open), UpdateOption.UPDATE_ALL.flags()); + } + + public void checkOpeners(Object level, Object pos, Object blockState) { + if (!this.isValidContainer()) return; + double maxInteractionDistance = 0d; + List viewers = this.inventory.getViewers(); + int validViewers = 0; + for (HumanEntity viewer : viewers) { + if (viewer instanceof org.bukkit.entity.Player player) { + maxInteractionDistance = Math.max(BukkitAdaptors.adapt(player).getCachedInteractionRange(), maxInteractionDistance); + if (player.getGameMode() != GameMode.SPECTATOR) { + validViewers++; + } + } + } + boolean shouldOpen = validViewers != 0; + if (shouldOpen && !this.openState) { + this.setOpen(null); + } else if (!shouldOpen && this.openState) { + this.setClose(null); + } + + this.maxInteractionDistance = maxInteractionDistance; + if (!viewers.isEmpty()) { + FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(level, pos, BlockStateUtils.getBlockOwner(blockState), 5); + } + } + + @Override + public void preRemove() { + this.inventory.close(); + Vec3d pos = Vec3d.atCenterOf(this.pos); + for (ItemStack stack : this.inventory.getContents()) { + if (stack != null) { + super.world.world().dropItemNaturally(pos, BukkitItemManager.instance().wrap(stack)); + } + } + this.inventory.clear(); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/WallTorchParticleBlockEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/WallTorchParticleBlockEntity.java new file mode 100644 index 000000000..cdf545719 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/WallTorchParticleBlockEntity.java @@ -0,0 +1,54 @@ +package net.momirealms.craftengine.bukkit.block.entity; + +import net.momirealms.craftengine.bukkit.block.behavior.WallTorchParticleBlockBehavior; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.ContextHolder; +import net.momirealms.craftengine.core.plugin.context.SimpleContext; +import net.momirealms.craftengine.core.util.HorizontalDirection; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.particle.ParticleConfig; + +public class WallTorchParticleBlockEntity extends AbstractAnimateTickBlockEntity { + private final WallTorchParticleBlockBehavior behavior; + private final Context context = SimpleContext.of(ContextHolder.empty()); + + public WallTorchParticleBlockEntity(BlockPos pos, ImmutableBlockState blockState) { + super(BukkitBlockEntityTypes.WALL_TORCH_PARTICLE, pos, blockState); + this.behavior = blockState.behavior().getAs(WallTorchParticleBlockBehavior.class).orElseThrow(); + } + + public void animateTick(ImmutableBlockState state, World level, BlockPos pos) { + HorizontalDirection direction = state.get(this.behavior.facingProperty); + if (direction == null) return; + Vec3d center = Vec3d.atCenterOf(pos); + HorizontalDirection opposite = direction.opposite(); + for (ParticleConfig particle : this.behavior.particles) { + Vec3d location = new Vec3d( + center.x() + particle.x.getDouble(context) * opposite.stepX(), + center.y() + particle.y.getDouble(context), + center.z() + particle.z.getDouble(context) * opposite.stepZ() + ); + level.spawnParticle( + location, + particle.particleType, + particle.count.getInt(context), + particle.xOffset.getDouble(context), + particle.yOffset.getDouble(context), + particle.zOffset.getDouble(context), + particle.speed.getDouble(context), + particle.particleData, + context + ); + } + } + + public static void tick(CEWorld ceWorld, BlockPos blockPos, ImmutableBlockState state, WallTorchParticleBlockEntity particle) { + particle.tickCount++; + if (particle.tickCount % particle.behavior.tickInterval != 0) return; + particle.animateTick(state, ceWorld.world(), blockPos); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/BukkitBlockEntityElementConfigs.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/BukkitBlockEntityElementConfigs.java new file mode 100644 index 000000000..b8c12c41b --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/BukkitBlockEntityElementConfigs.java @@ -0,0 +1,14 @@ +package net.momirealms.craftengine.bukkit.block.entity.renderer.element; + +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigs; + +public class BukkitBlockEntityElementConfigs extends BlockEntityElementConfigs { + + static { + register(ITEM_DISPLAY, ItemDisplayBlockEntityElementConfig.FACTORY); + register(TEXT_DISPLAY, TextDisplayBlockEntityElementConfig.FACTORY); + } + + public static void init() { + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElement.java new file mode 100644 index 000000000..074bc87c5 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElement.java @@ -0,0 +1,42 @@ +package net.momirealms.craftengine.bukkit.block.entity.renderer.element; + +import it.unimi.dsi.fastutil.ints.IntList; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.world.BlockPos; +import org.joml.Vector3f; + +import java.util.List; +import java.util.UUID; + +public class ItemDisplayBlockEntityElement implements BlockEntityElement { + private final ItemDisplayBlockEntityElementConfig config; + private final Object cachedSpawnPacket; + private final Object cachedDespawnPacket; + private final int entityId; + + public ItemDisplayBlockEntityElement(ItemDisplayBlockEntityElementConfig config, BlockPos pos) { + int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + Vector3f position = config.position(); + this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityId, UUID.randomUUID(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, + config.xRot(), config.yRot(), MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 + ); + this.config = config; + this.cachedDespawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(IntList.of(entityId)); + this.entityId = entityId; + } + + @Override + public void hide(Player player) { + player.sendPacket(this.cachedDespawnPacket, false); + } + + @Override + public void show(Player player) { + player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), true); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElementConfig.java new file mode 100644 index 000000000..a42838b72 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElementConfig.java @@ -0,0 +1,132 @@ +package net.momirealms.craftengine.bukkit.block.entity.renderer.element; + +import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; +import net.momirealms.craftengine.core.entity.Billboard; +import net.momirealms.craftengine.core.entity.ItemDisplayContext; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.World; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.function.Function; + +public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementConfig { + public static final Factory FACTORY = new Factory(); + private final Function> lazyMetadataPacket; + private final Function> item; + private final Vector3f scale; + private final Vector3f position; + private final Vector3f translation; + private final float xRot; + private final float yRot; + private final Quaternionf rotation; + private final ItemDisplayContext displayContext; + private final Billboard billboard; + + public ItemDisplayBlockEntityElementConfig(Function> item, + Vector3f scale, + Vector3f position, + Vector3f translation, + float xRot, + float yRot, + Quaternionf rotation, + ItemDisplayContext displayContext, + Billboard billboard) { + this.item = item; + this.scale = scale; + this.position = position; + this.translation = translation; + this.xRot = xRot; + this.yRot = yRot; + this.rotation = rotation; + this.displayContext = displayContext; + this.billboard = billboard; + this.lazyMetadataPacket = player -> { + List dataValues = new ArrayList<>(); + ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(item.apply(player).getLiteralObject(), dataValues); + ItemDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(this.scale, dataValues); + ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(this.rotation, dataValues); + ItemDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(this.billboard.id(), dataValues); + ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(this.translation, dataValues); + ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(this.displayContext.id(), dataValues); + return dataValues; + }; + } + + @Override + public ItemDisplayBlockEntityElement create(World world, BlockPos pos) { + return new ItemDisplayBlockEntityElement(this, pos); + } + + public Item item(Player player) { + return this.item.apply(player); + } + + public Vector3f scale() { + return this.scale; + } + + public Vector3f translation() { + return this.translation; + } + + public Vector3f position() { + return this.position; + } + + public float yRot() { + return this.yRot; + } + + public float xRot() { + return this.xRot; + } + + public Billboard billboard() { + return billboard; + } + + public ItemDisplayContext displayContext() { + return displayContext; + } + + public Quaternionf rotation() { + return rotation; + } + + public List metadataValues(Player player) { + return this.lazyMetadataPacket.apply(player); + } + + public static class Factory implements BlockEntityElementConfigFactory { + + @SuppressWarnings("unchecked") + @Override + public BlockEntityElementConfig create(Map arguments) { + Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.item_display.missing_item")); + return (BlockEntityElementConfig) new ItemDisplayBlockEntityElementConfig( + player -> BukkitItemManager.instance().createWrappedItem(itemId, player), + ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"), + ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), + ResourceConfigUtils.getAsVector3f(arguments.get("translation"), "translation"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"), + ResourceConfigUtils.getAsQuaternionf(arguments.getOrDefault("rotation", 0f), "rotation"), + ItemDisplayContext.valueOf(arguments.getOrDefault("display-context", "none").toString().toUpperCase(Locale.ROOT)), + Billboard.valueOf(arguments.getOrDefault("billboard", "fixed").toString().toUpperCase(Locale.ROOT)) + ); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElement.java new file mode 100644 index 000000000..45a1c6c9d --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElement.java @@ -0,0 +1,42 @@ +package net.momirealms.craftengine.bukkit.block.entity.renderer.element; + +import it.unimi.dsi.fastutil.ints.IntList; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.world.BlockPos; +import org.joml.Vector3f; + +import java.util.List; +import java.util.UUID; + +public class TextDisplayBlockEntityElement implements BlockEntityElement { + private final TextDisplayBlockEntityElementConfig config; + private final Object cachedSpawnPacket; + private final Object cachedDespawnPacket; + private final int entityId; + + public TextDisplayBlockEntityElement(TextDisplayBlockEntityElementConfig config, BlockPos pos) { + int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + Vector3f position = config.position(); + this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityId, UUID.randomUUID(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, + config.xRot(), config.yRot(), MEntityTypes.TEXT_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 + ); + this.config = config; + this.cachedDespawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(IntList.of(entityId)); + this.entityId = entityId; + } + + @Override + public void hide(Player player) { + player.sendPacket(this.cachedDespawnPacket, false); + } + + @Override + public void show(Player player) { + player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), true); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElementConfig.java new file mode 100644 index 000000000..e707023ee --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElementConfig.java @@ -0,0 +1,123 @@ +package net.momirealms.craftengine.bukkit.block.entity.renderer.element; + +import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.bukkit.entity.data.TextDisplayEntityData; +import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; +import net.momirealms.craftengine.core.entity.Billboard; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.World; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.function.Function; + +public class TextDisplayBlockEntityElementConfig implements BlockEntityElementConfig { + public static final Factory FACTORY = new Factory(); + private final Function> lazyMetadataPacket; + private final String text; + private final Vector3f scale; + private final Vector3f position; + private final Vector3f translation; + private final float xRot; + private final float yRot; + private final Quaternionf rotation; + private final Billboard billboard; + + public TextDisplayBlockEntityElementConfig(String text, + Vector3f scale, + Vector3f position, + Vector3f translation, + float xRot, + float yRot, + Quaternionf rotation, + Billboard billboard) { + this.text = text; + this.scale = scale; + this.position = position; + this.translation = translation; + this.xRot = xRot; + this.yRot = yRot; + this.rotation = rotation; + this.billboard = billboard; + this.lazyMetadataPacket = player -> { + List dataValues = new ArrayList<>(); + TextDisplayEntityData.Text.addEntityDataIfNotDefaultValue(ComponentUtils.adventureToMinecraft(text(player)), dataValues); + TextDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(this.scale, dataValues); + TextDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(this.rotation, dataValues); + TextDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(this.billboard.id(), dataValues); + TextDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(this.translation, dataValues); + return dataValues; + }; + } + + @Override + public TextDisplayBlockEntityElement create(World world, BlockPos pos) { + return new TextDisplayBlockEntityElement(this, pos); + } + + public Component text(Player player) { + return AdventureHelper.miniMessage().deserialize(this.text, PlayerOptionalContext.of(player).tagResolvers()); + } + + public Vector3f scale() { + return this.scale; + } + + public Vector3f translation() { + return this.translation; + } + + public Vector3f position() { + return this.position; + } + + public float yRot() { + return this.yRot; + } + + public float xRot() { + return this.xRot; + } + + public Billboard billboard() { + return billboard; + } + + public Quaternionf rotation() { + return rotation; + } + + public List metadataValues(Player player) { + return this.lazyMetadataPacket.apply(player); + } + + public static class Factory implements BlockEntityElementConfigFactory { + + @SuppressWarnings("unchecked") + @Override + public BlockEntityElementConfig create(Map arguments) { + String text = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("text"), "warning.config.block.state.entity_renderer.text_display.missing_text"); + return (BlockEntityElementConfig) new TextDisplayBlockEntityElementConfig( + text, + ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"), + ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), + ResourceConfigUtils.getAsVector3f(arguments.get("translation"), "translation"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"), + ResourceConfigUtils.getAsQuaternionf(arguments.getOrDefault("rotation", 0f), "rotation"), + Billboard.valueOf(arguments.getOrDefault("billboard", "fixed").toString().toUpperCase(Locale.ROOT)) + ); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/DisplayEntityData.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/DisplayEntityData.java index a052feedb..b5f4bcd94 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/DisplayEntityData.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/DisplayEntityData.java @@ -15,10 +15,10 @@ public class DisplayEntityData extends BaseEntityData { public static final DisplayEntityData TransformationInterpolationDuration = of(DisplayEntityData.class, EntityDataValue.Serializers$INT, 0, VersionHelper.isOrAbove1_20_2()); public static final DisplayEntityData PositionRotationInterpolationDuration = of(DisplayEntityData.class, EntityDataValue.Serializers$INT, 0, VersionHelper.isOrAbove1_20_2()); - public static final DisplayEntityData Translation = of(DisplayEntityData.class, EntityDataValue.Serializers$VECTOR3, new Vector3f(0f), true); - public static final DisplayEntityData Scale = of(DisplayEntityData.class, EntityDataValue.Serializers$VECTOR3, new Vector3f(1f), true); - public static final DisplayEntityData RotationLeft = of(DisplayEntityData.class, EntityDataValue.Serializers$QUATERNION, new Quaternionf(0f, 0f, 0f, 1f), true); - public static final DisplayEntityData RotationRight = of(DisplayEntityData.class, EntityDataValue.Serializers$QUATERNION, new Quaternionf(0f, 0f, 0f, 1f), true); + public static final DisplayEntityData Translation = of(DisplayEntityData.class, EntityDataValue.Serializers$VECTOR3, new Vector3f(0f), true); + public static final DisplayEntityData Scale = of(DisplayEntityData.class, EntityDataValue.Serializers$VECTOR3, new Vector3f(1f), true); + public static final DisplayEntityData RotationLeft = of(DisplayEntityData.class, EntityDataValue.Serializers$QUATERNION, new Quaternionf(0f, 0f, 0f, 1f), true); + public static final DisplayEntityData RotationRight = of(DisplayEntityData.class, EntityDataValue.Serializers$QUATERNION, new Quaternionf(0f, 0f, 0f, 1f), true); /** * Billboard Constraints (0 = FIXED, 1 = VERTICAL, 2 = HORIZONTAL, 3 = CENTER) */ diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/EntityData.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/EntityData.java index da99028c4..0574a93f9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/EntityData.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/EntityData.java @@ -1,5 +1,7 @@ package net.momirealms.craftengine.bukkit.entity.data; +import net.momirealms.craftengine.bukkit.nms.FastNMS; + import java.util.List; public interface EntityData { @@ -27,6 +29,11 @@ public interface EntityData { list.add(EntityDataValue.create(id(), serializer(), entityDataAccessor(), value)); } + @SuppressWarnings("unchecked") + default T get(Object entityData) { + return (T) FastNMS.INSTANCE.method$SynchedEntityData$get(entityData, entityDataAccessor()); + } + static EntityData of(Class clazz, Object serializer, T defaultValue) { return new SimpleEntityData<>(clazz, serializer, defaultValue); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/EntityDataValue.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/EntityDataValue.java index fee5d034f..30d191d09 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/EntityDataValue.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/EntityDataValue.java @@ -71,7 +71,8 @@ public class EntityDataValue { if (VersionHelper.isOrAbove1_21_5()) Serializers$OPTIONAL_LIVING_ENTITY_REFERENCE = initSerializersByName("OPTIONAL_LIVING_ENTITY_REFERENCE"); else Serializers$OPTIONAL_LIVING_ENTITY_REFERENCE = null; Serializers$OPTIONAL_GLOBAL_POS = initSerializersByName("OPTIONAL_GLOBAL_POS"); - Serializers$COMPOUND_TAG = initSerializersByName("COMPOUND_TAG"); + if (!VersionHelper.isOrAbove1_21_9()) Serializers$COMPOUND_TAG = initSerializersByName("COMPOUND_TAG"); + else Serializers$COMPOUND_TAG = null; Serializers$VILLAGER_DATA = initSerializersByName("VILLAGER_DATA"); Serializers$OPTIONAL_UNSIGNED_INT = initSerializersByName("OPTIONAL_UNSIGNED_INT"); Serializers$POSE = initSerializersByName("POSE"); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index d9fb9c01d..b7e58f8be 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -15,7 +15,7 @@ import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.WorldPosition; import org.bukkit.*; @@ -337,6 +337,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { plugin.scheduler().sync().runDelayed(() -> tryLeavingSeat(player, entity), player.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); } + @SuppressWarnings("DuplicatedCode") protected void tryLeavingSeat(@NotNull Player player, @NotNull Entity vehicle) { Integer baseFurniture = vehicle.getPersistentDataContainer().get(FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER); if (baseFurniture == null) return; @@ -350,7 +351,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { plugin.logger().warn("Failed to get vector3f for player " + player.getName() + "'s seat"); return; } - Vector3f seatPos = MiscUtils.getAsVector3f(vector3f, "seat"); + Vector3f seatPos = ResourceConfigUtils.getAsVector3f(vector3f, "seat"); furniture.removeOccupiedSeat(seatPos); if (player.getVehicle() != null) return; @@ -375,6 +376,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { return (entity instanceof ArmorStand || entity instanceof ItemDisplay); } + @SuppressWarnings("DuplicatedCode") private boolean isSafeLocation(Location location) { World world = location.getWorld(); if (world == null) return false; @@ -386,6 +388,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { return world.getBlockAt(x, y + 1, z).isPassable(); } + @SuppressWarnings("DuplicatedCode") @Nullable private Location findSafeLocationNearby(Location center) { World world = center.getWorld(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBox.java index b76dcd24e..f5b3d87c1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBox.java @@ -8,7 +8,6 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkRefl import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.WorldPosition; @@ -84,7 +83,7 @@ public class CustomHitBox extends AbstractHitBox { @Override public HitBox create(Map arguments) { - Vector3f position = MiscUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); + Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1), "scale"); String type = (String) arguments.getOrDefault("entity-type", "slime"); EntityType entityType = Registry.ENTITY_TYPE.get(new NamespacedKey("minecraft", type)); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBox.java index 4e67ffd02..91754fd9b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBox.java @@ -9,7 +9,6 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityType import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.World; @@ -124,7 +123,7 @@ public class HappyGhastHitBox extends AbstractHitBox { boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); return new HappyGhastHitBox( HitBoxFactory.getSeats(arguments), - MiscUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"), + ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"), scale, canUseOn, blocksBuilding, canBeHitByProjectile, hardCollision ); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBox.java index 3fad43d0d..f8ae1852e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBox.java @@ -7,7 +7,6 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; @@ -93,7 +92,7 @@ public class InteractionHitBox extends AbstractHitBox { @Override public HitBox create(Map arguments) { - Vector3f position = MiscUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); + Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); float width; float height; if (arguments.containsKey("scale")) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBox.java index 96db4da42..9883004cc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBox.java @@ -171,7 +171,7 @@ public class ShulkerHitBox extends AbstractHitBox { } private static float getPhysicalPeek(float peek) { - return 0.5F - MCUtils.sin((0.5F + peek) * 3.1415927F) * 0.5F; + return 0.5F - MiscUtils.sin((0.5F + peek) * 3.1415927F) * 0.5F; } public boolean interactionEntity() { @@ -280,7 +280,7 @@ public class ShulkerHitBox extends AbstractHitBox { @Override public HitBox create(Map arguments) { - Vector3f position = MiscUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); + Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", "1"), "scale"); byte peek = (byte) ResourceConfigUtils.getAsInt(arguments.getOrDefault("peek", 0), "peek"); Direction directionEnum = Optional.ofNullable(arguments.get("direction")).map(it -> Direction.valueOf(it.toString().toUpperCase(Locale.ENGLISH))).orElse(Direction.UP); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index f6304a758..637fb20a5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -36,11 +36,17 @@ import java.lang.reflect.InvocationTargetException; import java.util.*; public class BukkitFontManager extends AbstractFontManager implements Listener { + private static BukkitFontManager instance; private final BukkitCraftEngine plugin; public BukkitFontManager(BukkitCraftEngine plugin) { super(plugin); this.plugin = plugin; + instance = this; + } + + public static BukkitFontManager instance() { + return instance; } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java index b7cb6fd32..aa514afdb 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java @@ -20,7 +20,6 @@ import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; import net.momirealms.craftengine.core.pack.AbstractPackManager; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.util.*; @@ -337,7 +336,7 @@ public class BukkitItemManager extends AbstractItemManager { @Override public ItemStack buildCustomItemStack(Key id, Player player) { - return Optional.ofNullable(this.customItemsById.get(id)).map(it -> it.buildItemStack(new ItemBuildContext(player, ContextHolder.EMPTY), 1)).orElse(null); + return Optional.ofNullable(this.customItemsById.get(id)).map(it -> it.buildItemStack(ItemBuildContext.of(player), 1)).orElse(null); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java index e928f9233..2cca3f5ae 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java @@ -131,7 +131,7 @@ public class AxeItemBehavior extends ItemBehavior { public static class Factory implements ItemBehaviorFactory { @Override - public ItemBehavior create(Pack pack, Path path, Key key, Map arguments) { + public ItemBehavior create(Pack pack, Path path, String node, Key key, Map arguments) { return INSTANCE; } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java index 3422f0352..f8cb7dfcb 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java @@ -23,6 +23,7 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.item.context.UseOnContext; import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.pack.PendingConfigSection; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.context.ContextHolder; @@ -73,7 +74,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { return InteractionResult.FAIL; } if (!context.canPlace()) { - return InteractionResult.FAIL; + return InteractionResult.PASS; } Player player = context.getPlayer(); @@ -89,7 +90,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { ImmutableBlockState blockStateToPlace = getPlacementState(context, block); if (blockStateToPlace == null) { - return InteractionResult.FAIL; + return InteractionResult.PASS; } BlockPos againstPos = context.getAgainstPos(); @@ -232,20 +233,24 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { return this.blockId; } + static void addPendingSection(Pack pack, Path path, String node, Key key, Map map) { + if (map.containsKey(key.toString())) { + // 防呆 + BukkitBlockManager.instance().blockParser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, MiscUtils.castToMap(map.get(key.toString()), false))); + } else { + BukkitBlockManager.instance().blockParser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, MiscUtils.castToMap(map, false))); + } + } + public static class Factory implements ItemBehaviorFactory { @Override - public ItemBehavior create(Pack pack, Path path, Key key, Map arguments) { + public ItemBehavior create(Pack pack, Path path, String node, Key key, Map arguments) { Object id = arguments.get("block"); if (id == null) { throw new LocalizedResourceConfigException("warning.config.item.behavior.block.missing_block", new IllegalArgumentException("Missing required parameter 'block' for block_item behavior")); } if (id instanceof Map map) { - if (map.containsKey(key.toString())) { - // 防呆 - BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map.get(key.toString()), false)); - } else { - BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false)); - } + addPendingSection(pack, path, node, key, map); return new BlockItemBehavior(key); } else { return new BlockItemBehavior(Key.of(id.toString())); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java index c045bfa96..5ae9e758d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java @@ -12,6 +12,7 @@ public class BukkitItemBehaviors extends ItemBehaviors { public static final Key COMPOSTABLE_ITEM = Key.from("craftengine:compostable_item"); public static final Key AXE_ITEM = Key.from("craftengine:axe_item"); public static final Key DOUBLE_HIGH_BLOCK_ITEM = Key.from("craftengine:double_high_block_item"); + public static final Key WALL_BLOCK_ITEM = Key.from("craftengine:wall_block_item"); public static final Key COMPASS_ITEM = Key.from("craftengine:compass_item"); public static final Key ENDER_EYE_ITEM = Key.from("craftengine:ender_eye_item"); public static final Key END_CRYSTAL_ITEM = Key.from("craftengine:end_crystal_item"); @@ -25,6 +26,7 @@ public class BukkitItemBehaviors extends ItemBehaviors { register(COMPOSTABLE_ITEM, CompostableItemBehavior.FACTORY); register(AXE_ITEM, AxeItemBehavior.FACTORY); register(DOUBLE_HIGH_BLOCK_ITEM, DoubleHighBlockItemBehavior.FACTORY); + register(WALL_BLOCK_ITEM, WallBlockItemBehavior.FACTORY); register(COMPASS_ITEM, CompassItemBehavior.FACTORY); register(ENDER_EYE_ITEM, EnderEyeItemBehavior.FACTORY); register(END_CRYSTAL_ITEM, EndCrystalItemBehavior.FACTORY); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/CompostableItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/CompostableItemBehavior.java index e4d92d241..50015c355 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/CompostableItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/CompostableItemBehavior.java @@ -79,7 +79,7 @@ public class CompostableItemBehavior extends ItemBehavior { public static class Factory implements ItemBehaviorFactory { @Override - public ItemBehavior create(Pack pack, Path path, Key key, Map arguments) { + public ItemBehavior create(Pack pack, Path path, String node, Key key, Map arguments) { double chance = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("chance", 0.55), "chance"); return new CompostableItemBehavior(chance); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/DoubleHighBlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/DoubleHighBlockItemBehavior.java index 0acf2bfb3..d33b45da1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/DoubleHighBlockItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/DoubleHighBlockItemBehavior.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.bukkit.item.behavior; -import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids; @@ -11,7 +10,6 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; import org.bukkit.Location; import org.bukkit.block.BlockState; @@ -40,18 +38,13 @@ public class DoubleHighBlockItemBehavior extends BlockItemBehavior { public static class Factory implements ItemBehaviorFactory { @Override - public ItemBehavior create(Pack pack, Path path, Key key, Map arguments) { + public ItemBehavior create(Pack pack, Path path, String node, Key key, Map arguments) { Object id = arguments.get("block"); if (id == null) { - throw new LocalizedResourceConfigException("warning.config.item.behavior.double_high.missing_block", new IllegalArgumentException("Missing required parameter 'block' for double_high_block_item behavior")); + throw new LocalizedResourceConfigException("warning.config.item.behavior.double_high.missing_block"); } if (id instanceof Map map) { - if (map.containsKey(key.toString())) { - // 防呆 - BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map.get(key.toString()), false)); - } else { - BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false)); - } + addPendingSection(pack, path, node, key, map); return new DoubleHighBlockItemBehavior(key); } else { return new DoubleHighBlockItemBehavior(Key.of(id.toString())); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FlintAndSteelItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FlintAndSteelItemBehavior.java index ab6521101..7505b3cb7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FlintAndSteelItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FlintAndSteelItemBehavior.java @@ -90,7 +90,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior { } // 且没有shift或者忽略潜行的可交互方块 if (!player.isSecondaryUseActive()) { - player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f)); + player.playSound(firePos, FLINT_SOUND, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f)); } } else { // 玩家觉得自定义方块不可燃,且点击了侧面,那么就要判断火源下方的方块是否可燃,如果不可燃,则补发声音 @@ -113,16 +113,16 @@ public class FlintAndSteelItemBehavior extends ItemBehavior { if (player.isSecondaryUseActive()) { // 如果底部不能燃烧,则燃烧点位为侧面,需要补发 if (!belowCanBurn) { - player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f)); + player.playSound(firePos, FLINT_SOUND, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f)); player.swingHand(context.getHand()); } } else { - player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f)); + player.playSound(firePos, FLINT_SOUND, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f)); } } else { // 如果底部方块不可燃烧才补发 if (!belowCanBurn) { - player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f)); + player.playSound(firePos, FLINT_SOUND, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f)); player.swingHand(context.getHand()); } } @@ -153,7 +153,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior { } } } - player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f)); + player.playSound(firePos, FLINT_SOUND, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f)); player.swingHand(context.getHand()); } return InteractionResult.PASS; @@ -161,7 +161,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior { public static class Factory implements ItemBehaviorFactory { @Override - public ItemBehavior create(Pack pack, Path path, Key id, Map arguments) { + public ItemBehavior create(Pack pack, Path path, String node, Key id, Map arguments) { return INSTANCE; } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index c7caee0a0..83b4bdef6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -20,6 +20,7 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.context.UseOnContext; import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.pack.PendingConfigSection; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; @@ -177,7 +178,7 @@ public class FurnitureItemBehavior extends ItemBehavior { public static class Factory implements ItemBehaviorFactory { @Override - public ItemBehavior create(Pack pack, Path path, Key key, Map arguments) { + public ItemBehavior create(Pack pack, Path path, String node, Key key, Map arguments) { Object id = arguments.get("furniture"); if (id == null) { throw new LocalizedResourceConfigException("warning.config.item.behavior.furniture.missing_furniture", new IllegalArgumentException("Missing required parameter 'furniture' for furniture_item behavior")); @@ -185,9 +186,9 @@ public class FurnitureItemBehavior extends ItemBehavior { if (id instanceof Map map) { if (map.containsKey(key.toString())) { // 防呆 - BukkitFurnitureManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map.get(key.toString()), false)); + BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, MiscUtils.castToMap(map.get(key.toString()), false))); } else { - BukkitFurnitureManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false)); + BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, MiscUtils.castToMap(map, false))); } return new FurnitureItemBehavior(key); } else { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java index 896c742af..11a852cac 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionBlockItemBehavior.java @@ -3,6 +3,8 @@ package net.momirealms.craftengine.bukkit.item.behavior; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids; +import net.momirealms.craftengine.bukkit.util.DirectionUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.player.InteractionResult; @@ -45,15 +47,19 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior { try { if (player == null) return InteractionResult.FAIL; Object blockHitResult = CoreReflections.method$Item$getPlayerPOVHitResult.invoke(null, world.serverWorld(), player.serverPlayer(), CoreReflections.instance$ClipContext$Fluid$SOURCE_ONLY); - Object blockPos = CoreReflections.field$BlockHitResul$blockPos.get(blockHitResult); + Object blockPos = FastNMS.INSTANCE.field$BlockHitResult$blockPos(blockHitResult); BlockPos above = new BlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) + offsetY, FastNMS.INSTANCE.field$Vec3i$z(blockPos)); - Direction direction = Direction.values()[(int) CoreReflections.method$Direction$ordinal.invoke(CoreReflections.field$BlockHitResul$direction.get(blockHitResult))]; - boolean miss = CoreReflections.field$BlockHitResul$miss.getBoolean(blockHitResult); + Direction direction = DirectionUtils.fromNMSDirection(FastNMS.INSTANCE.field$BlockHitResul$direction(blockHitResult)); + boolean miss = FastNMS.INSTANCE.field$BlockHitResul$miss(blockHitResult); Vec3d hitPos = LocationUtils.fromVec(CoreReflections.field$HitResult$location.get(blockHitResult)); + Object fluidType = FastNMS.INSTANCE.method$FluidState$getType(FastNMS.INSTANCE.method$BlockGetter$getFluidState(world.serverWorld(), blockPos)); + if (fluidType != MFluids.WATER && fluidType != MFluids.LAVA) { + return InteractionResult.PASS; + } if (miss) { return super.useOnBlock(new UseOnContext(player, hand, BlockHitResult.miss(hitPos, direction, above))); } else { - boolean inside = CoreReflections.field$BlockHitResul$inside.getBoolean(blockHitResult); + boolean inside = CoreReflections.field$BlockHitResult$inside.getBoolean(blockHitResult); return super.useOnBlock(new UseOnContext(player, hand, new BlockHitResult(hitPos, direction, above, inside))); } } catch (Exception e) { @@ -64,7 +70,7 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior { public static class Factory implements ItemBehaviorFactory { @Override - public ItemBehavior create(Pack pack, Path path, Key key, Map arguments) { + public ItemBehavior create(Pack pack, Path path, String node, Key key, Map arguments) { Object id = arguments.get("block"); if (id == null) { throw new LocalizedResourceConfigException("warning.config.item.behavior.liquid_collision.missing_block", new IllegalArgumentException("Missing required parameter 'block' for liquid_collision_block_item behavior")); @@ -73,9 +79,9 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior { if (id instanceof Map map) { if (map.containsKey(key.toString())) { // 防呆 - BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map.get(key.toString()), false)); + BukkitBlockManager.instance().blockParser().parseSection(pack, path, node, key, MiscUtils.castToMap(map.get(key.toString()), false)); } else { - BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false)); + BukkitBlockManager.instance().blockParser().parseSection(pack, path, node, key, MiscUtils.castToMap(map, false)); } return new LiquidCollisionBlockItemBehavior(key, offset); } else { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/WallBlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/WallBlockItemBehavior.java new file mode 100644 index 000000000..94b5254f5 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/WallBlockItemBehavior.java @@ -0,0 +1,49 @@ +package net.momirealms.craftengine.bukkit.item.behavior; + +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.item.behavior.ItemBehavior; +import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; +import net.momirealms.craftengine.core.item.context.BlockPlaceContext; +import net.momirealms.craftengine.core.item.context.UseOnContext; +import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.Key; + +import java.nio.file.Path; +import java.util.Map; + +public class WallBlockItemBehavior extends BlockItemBehavior { + public static final Factory FACTORY = new Factory(); + + public WallBlockItemBehavior(Key wallBlockId) { + super(wallBlockId); + } + + @Override + public InteractionResult useOnBlock(UseOnContext context) { + return this.place(new BlockPlaceContext(context)); + } + + public InteractionResult place(BlockPlaceContext context) { + if (context.getClickedFace().stepY() != 0) { + return InteractionResult.PASS; + } + return super.place(context); + } + + public static class Factory implements ItemBehaviorFactory { + @Override + public ItemBehavior create(Pack pack, Path path, String node, Key key, Map arguments) { + Object id = arguments.get("block"); + if (id == null) { + throw new LocalizedResourceConfigException("warning.config.item.behavior.wall_block.missing_block", new IllegalArgumentException("Missing required parameter 'block' for wall_block_item behavior")); + } + if (id instanceof Map map) { + addPendingSection(pack, path, node, key, map); + return new WallBlockItemBehavior(key); + } else { + return new WallBlockItemBehavior(Key.of(id.toString())); + } + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/BukkitItemFactory.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/BukkitItemFactory.java index c8f760d99..f836d910b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/BukkitItemFactory.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/BukkitItemFactory.java @@ -49,7 +49,7 @@ public abstract class BukkitItemFactory> extend case "1.21.4" -> { return new ComponentItemFactory1_21_4(plugin); } - case "1.21.5", "1.21.6", "1.21.7", "1.21.8" -> { + case "1.21.5", "1.21.6", "1.21.7", "1.21.8", "1.21.9" -> { return new ComponentItemFactory1_21_5(plugin); } default -> throw new IllegalStateException("Unsupported server version: " + plugin.serverVersion()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java index 447500af0..2231d8908 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java @@ -424,6 +424,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory getEnchantment(ComponentItemWrapper item, Key key) { Object enchant = item.getComponentExact(ComponentTypes.ENCHANTMENTS); + if (enchant == null) return Optional.empty(); try { Map map = EnchantmentUtils.toMap(enchant); Integer level = map.get(key.toString()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java index 172279912..83520ce08 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java @@ -190,7 +190,6 @@ public class UniversalItemFactory extends BukkitItemFactory { @Override protected void maxDamage(LegacyItemWrapper item, Integer damage) { - throw new UnsupportedOperationException("This feature is only available on 1.20.5+"); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java index 8b8df50b4..b3f763ce1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java @@ -17,7 +17,6 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.UpdateOption; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.util.MCUtils; import net.momirealms.craftengine.core.util.MiscUtils; import org.bukkit.Material; import org.bukkit.block.Block; @@ -119,7 +118,7 @@ public class DebugStickListener implements Listener { } private static T getRelative(Iterable elements, @Nullable T current, boolean inverse) { - return inverse ? MCUtils.findPreviousInIterable(elements, current) : MCUtils.findNextInIterable(elements, current); + return inverse ? MiscUtils.findPreviousInIterable(elements, current) : MiscUtils.findNextInIterable(elements, current); } private static > String getNameHelper(ImmutableBlockState state, Property property) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java index ce1500a38..9038391e2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java @@ -29,7 +29,7 @@ import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; -import net.momirealms.craftengine.core.sound.SoundData; +import net.momirealms.craftengine.core.sound.SoundSet; import net.momirealms.craftengine.core.sound.SoundSource; import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.BlockHitResult; @@ -41,6 +41,7 @@ import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Openable; +import org.bukkit.block.data.Powerable; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -219,14 +220,29 @@ public class ItemEventListener implements Listener { } } else { if (Config.enableSoundSystem() && hitResult != null) { - Object blockOwner = FastNMS.INSTANCE.method$BlockState$getBlock(blockState); - if (this.plugin.blockManager().isOpenableBlockSoundRemoved(blockOwner)) { + Key blockOwner = BlockStateUtils.getBlockOwnerIdFromState(blockState); + if (this.plugin.blockManager().isInteractSoundMissing(blockOwner)) { boolean hasItem = player.getInventory().getItemInMainHand().getType() != Material.AIR || player.getInventory().getItemInOffHand().getType() != Material.AIR; boolean flag = player.isSneaking() && hasItem; if (!flag) { if (blockData instanceof Openable openable) { - SoundData soundData = this.plugin.blockManager().getRemovedOpenableBlockSound(blockOwner, !openable.isOpen()); - serverPlayer.playSound(soundData.id(), SoundSource.BLOCK, soundData.volume().get(), soundData.pitch().get()); + SoundSet soundSet = SoundSet.getByBlock(blockOwner); + if (soundSet != null) { + serverPlayer.playSound( + Vec3d.atCenterOf(hitResult.getBlockPos()), + openable.isOpen() ? soundSet.closeSound() : soundSet.openSound(), + SoundSource.BLOCK, + 1, RandomUtils.generateRandomFloat(0.9f, 1)); + } + } else if (blockData instanceof Powerable powerable && !powerable.isPowered()) { + SoundSet soundSet = SoundSet.getByBlock(blockOwner); + if (soundSet != null) { + serverPlayer.playSound( + Vec3d.atCenterOf(hitResult.getBlockPos()), + soundSet.openSound(), + SoundSource.BLOCK, + 1, RandomUtils.generateRandomFloat(0.9f, 1)); + } } } } @@ -458,9 +474,9 @@ public class ItemEventListener implements Listener { if (foodData == null) return; event.setCancelled(true); int oldFoodLevel = player.getFoodLevel(); - if (foodData.nutrition() != 0) player.setFoodLevel(MCUtils.clamp(oldFoodLevel + foodData.nutrition(), 0, 20)); + if (foodData.nutrition() != 0) player.setFoodLevel(MiscUtils.clamp(oldFoodLevel + foodData.nutrition(), 0, 20)); float oldSaturation = player.getSaturation(); - if (foodData.saturation() != 0) player.setSaturation(MCUtils.clamp(oldSaturation, 0, 10)); + if (foodData.saturation() != 0) player.setSaturation(MiscUtils.clamp(oldSaturation, 0, 10)); } private boolean cancelEventIfHasInteraction(PlayerInteractEvent event, BukkitServerPlayer player, InteractionHand hand) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java index 11cd5b5a1..e385ffca4 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java @@ -200,7 +200,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { for (UniqueKey holder : holders) { Optional> buildableItem = BukkitItemManager.instance().getBuildableItem(holder.key()); if (buildableItem.isPresent()) { - ItemStack itemStack = buildableItem.get().buildItemStack(ItemBuildContext.EMPTY, 1); + ItemStack itemStack = buildableItem.get().buildItemStack(ItemBuildContext.empty(), 1); Object nmsStack = FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack); itemStacks.add(nmsStack); } else { @@ -258,7 +258,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { // 已经被替换过的数据包配方 private final Set replacedDatapackRecipes = new HashSet<>(); // 换成的数据包配方 - private Map> lastDatapackRecipes = Map.of(); + private Map lastDatapackRecipes = Map.of(); private Object lastRecipeManager = null; public BukkitRecipeManager(BukkitCraftEngine plugin) { @@ -336,7 +336,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { if (recipe instanceof CustomBrewingRecipe brewingRecipe) { if (!VersionHelper.isOrAbove1_20_2()) return; PotionMix potionMix = new PotionMix(new NamespacedKey(id.namespace(), id.value()), - brewingRecipe.result(ItemBuildContext.EMPTY), + brewingRecipe.result(ItemBuildContext.empty()), PotionMix.createPredicateChoice(container -> { Item wrapped = this.plugin.itemManager().wrap(container); return brewingRecipe.container().test(UniqueIdItem.of(wrapped)); @@ -386,18 +386,33 @@ public class BukkitRecipeManager extends AbstractRecipeManager { } boolean hasDisabledAny = !Config.disabledVanillaRecipes().isEmpty(); - for (Map.Entry> entry : this.lastDatapackRecipes.entrySet()) { + for (Map.Entry entry : this.lastDatapackRecipes.entrySet()) { + Key id = entry.getKey(); if (hasDisabledAny && Config.disabledVanillaRecipes().contains(entry.getKey())) { - this.recipesToUnregister.add(Pair.of(entry.getKey(), false)); + this.recipesToUnregister.add(Pair.of(id, false)); continue; } - markAsDataPackRecipe(entry.getKey()); - registerInternalRecipe(entry.getKey(), entry.getValue()); + + JsonObject jsonObject = entry.getValue(); + Key serializerType = Key.of(jsonObject.get("type").getAsString()); + @SuppressWarnings("unchecked") + RecipeSerializer> serializer = (RecipeSerializer>) BuiltInRegistries.RECIPE_SERIALIZER.getValue(serializerType); + if (serializer == null) { + continue; + } + + try { + Recipe recipe = serializer.readJson(id, jsonObject); + markAsDataPackRecipe(id); + registerInternalRecipe(id, recipe); + } catch (Exception e) { + this.plugin.logger().warn("Failed to load data pack recipe " + id + ". Json: " + jsonObject, e); + } } } @SuppressWarnings("unchecked") - private Map> scanResources() throws Throwable { + private Map scanResources() throws Throwable { Object fileToIdConverter = CoreReflections.methodHandle$FileToIdConverter$json.invokeExact((String) (VersionHelper.isOrAbove1_21() ? "recipe" : "recipes")); Object minecraftServer = FastNMS.INSTANCE.method$MinecraftServer$getServer(); Object packRepository = CoreReflections.methodHandle$MinecraftServer$getPackRepository.invokeExact(minecraftServer); @@ -406,7 +421,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { for (Object pack : selected) { packResources.add(CoreReflections.methodHandle$Pack$open.invokeExact(pack)); } - Map> recipes = new HashMap<>(); + Map recipes = new HashMap<>(); try (AutoCloseable resourceManager = (AutoCloseable) CoreReflections.methodHandle$MultiPackResourceManagerConstructor.invokeExact(CoreReflections.instance$PackType$SERVER_DATA, packResources)) { Map scannedResources = (Map) CoreReflections.methodHandle$FileToIdConverter$listMatchingResources.invokeExact(fileToIdConverter, resourceManager); @@ -414,17 +429,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { Key id = extractKeyFromResourceLocation(entry.getKey().toString()); Reader reader = (Reader) CoreReflections.methodHandle$Resource$openAsReader.invokeExact(entry.getValue()); JsonObject jsonObject = JsonParser.parseReader(reader).getAsJsonObject(); - Key serializerType = Key.of(jsonObject.get("type").getAsString()); - RecipeSerializer> serializer = (RecipeSerializer>) BuiltInRegistries.RECIPE_SERIALIZER.getValue(serializerType); - if (serializer == null) { - continue; - } - try { - Recipe recipe = serializer.readJson(id, jsonObject); - recipes.put(id, recipe); - } catch (Exception e) { - this.plugin.logger().warn("Failed to load data pack recipe " + id + ". Json: " + jsonObject, e); - } + recipes.put(id, jsonObject); } } catch (Throwable e) { this.plugin.logger().warn("Unknown error occurred when loading data pack recipes", e); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java index 78bf6203f..58392558e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java @@ -23,7 +23,8 @@ import net.momirealms.craftengine.core.item.recipe.input.SmithingInput; import net.momirealms.craftengine.core.item.setting.AnvilRepairItem; import net.momirealms.craftengine.core.item.setting.ItemEquipment; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.plugin.context.ContextHolder; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.function.Function; import net.momirealms.craftengine.core.util.*; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -261,7 +262,6 @@ public class RecipeEventListener implements Listener { @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onAnvilEvent(PrepareAnvilEvent event) { - if (event.getResult() == null) return; preProcess(event); processRepairable(event); processRename(event); @@ -271,6 +271,7 @@ public class RecipeEventListener implements Listener { 预处理会阻止一些不合理的原版材质造成的合并问题 */ private void preProcess(PrepareAnvilEvent event) { + if (event.getResult() == null) return; AnvilInventory inventory = event.getInventory(); ItemStack first = inventory.getFirstItem(); ItemStack second = inventory.getSecondItem(); @@ -318,10 +319,9 @@ public class RecipeEventListener implements Listener { return; } - if (firstCustom.isPresent()) { CustomItem firstCustomItem = firstCustom.get(); - if (firstCustomItem.settings().canRepair() == Tristate.FALSE) { + if (firstCustomItem.settings().repairable().anvilCombine() == Tristate.FALSE) { event.setResult(null); return; } @@ -373,7 +373,7 @@ public class RecipeEventListener implements Listener { Key firstId = wrappedFirst.id(); Optional> optionalCustomTool = wrappedFirst.getCustomItem(); // 物品无法被修复 - if (optionalCustomTool.isPresent() && optionalCustomTool.get().settings().canRepair() == Tristate.FALSE) { + if (optionalCustomTool.isPresent() && optionalCustomTool.get().settings().repairable().anvilRepair() == Tristate.FALSE) { return; } @@ -494,6 +494,7 @@ public class RecipeEventListener implements Listener { */ @SuppressWarnings("UnstableApiUsage") private void processRename(PrepareAnvilEvent event) { + if (event.getResult() == null) return; AnvilInventory inventory = event.getInventory(); ItemStack first = inventory.getFirstItem(); if (ItemStackUtils.isEmpty(first)) { @@ -609,20 +610,25 @@ public class RecipeEventListener implements Listener { inventory.setResult(null); return; } - CraftingInput input = getCraftingInput(inventory); - if (input == null) return; Player player = InventoryUtils.getPlayerFromInventoryEvent(event); BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); - if (craftingTableRecipe.hasVisualResult()) { - inventory.setResult(craftingTableRecipe.assembleVisual(input, new ItemBuildContext(serverPlayer, ContextHolder.EMPTY))); + ItemBuildContext itemBuildContext = ItemBuildContext.of(serverPlayer); + if (!craftingTableRecipe.canUse(itemBuildContext)) { + inventory.setResult(null); + return; + } + CraftingInput input = getCraftingInput(inventory); + if (input == null) return; + if (craftingTableRecipe.hasVisualResult() && VersionHelper.PREMIUM) { + inventory.setResult(craftingTableRecipe.assembleVisual(input, itemBuildContext)); } else { - inventory.setResult(craftingTableRecipe.assemble(input, new ItemBuildContext(serverPlayer, ContextHolder.EMPTY))); + inventory.setResult(craftingTableRecipe.assemble(input, itemBuildContext)); } } - @EventHandler(ignoreCancelled = true) + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onCraftingFinish(CraftItemEvent event) { - if (!Config.enableRecipeSystem()) return; + if (!Config.enableRecipeSystem() || !VersionHelper.PREMIUM) return; org.bukkit.inventory.Recipe recipe = event.getRecipe(); if (!(recipe instanceof CraftingRecipe craftingRecipe)) return; Key recipeId = Key.of(craftingRecipe.getKey().namespace(), craftingRecipe.getKey().value()); @@ -635,14 +641,19 @@ public class RecipeEventListener implements Listener { if (!(optionalRecipe.get() instanceof CustomCraftingTableRecipe craftingTableRecipe)) { return; } - if (!craftingTableRecipe.hasVisualResult()) { - return; - } - CraftingInput input = getCraftingInput(inventory); - if (input == null) return; Player player = InventoryUtils.getPlayerFromInventoryEvent(event); BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); - inventory.setResult(craftingTableRecipe.assemble(input, new ItemBuildContext(serverPlayer, ContextHolder.EMPTY))); + if (craftingTableRecipe.hasVisualResult()) { + CraftingInput input = getCraftingInput(inventory); + inventory.setResult(craftingTableRecipe.assemble(input, ItemBuildContext.of(serverPlayer))); + } + Function[] functions = craftingTableRecipe.craftingFunctions(); + if (functions != null) { + PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer); + for (Function function : functions) { + function.run(context); + } + } } private CraftingInput getCraftingInput(CraftingInventory inventory) { @@ -691,10 +702,16 @@ public class RecipeEventListener implements Listener { event.setResult(null); return; } + Player player = InventoryUtils.getPlayerFromInventoryEvent(event); + ItemBuildContext itemBuildContext = ItemBuildContext.of(BukkitAdaptors.adapt(player)); + if (!smithingTrimRecipe.canUse(itemBuildContext)) { + event.setResult(null); + return; + } + SmithingInput input = getSmithingInput(inventory); if (smithingTrimRecipe.matches(input)) { - Player player = InventoryUtils.getPlayerFromInventoryEvent(event); - ItemStack result = smithingTrimRecipe.assemble(getSmithingInput(inventory), new ItemBuildContext(BukkitAdaptors.adapt(player), ContextHolder.EMPTY)); + ItemStack result = smithingTrimRecipe.assemble(getSmithingInput(inventory), itemBuildContext); event.setResult(result); } else { event.setResult(null); @@ -718,7 +735,7 @@ public class RecipeEventListener implements Listener { SmithingInput input = getSmithingInput(inventory); if (smithingTransformRecipe.matches(input)) { Player player = InventoryUtils.getPlayerFromInventoryEvent(event); - ItemStack processed = smithingTransformRecipe.assemble(input, new ItemBuildContext(BukkitAdaptors.adapt(player), ContextHolder.EMPTY)); + ItemStack processed = smithingTransformRecipe.assemble(input, ItemBuildContext.of(BukkitAdaptors.adapt(player))); event.setResult(processed); } else { event.setResult(null); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java index 9dc95e83b..d38e58a24 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java @@ -14,6 +14,7 @@ import net.momirealms.craftengine.core.loot.VanillaLoot; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.config.IdSectionConfigParser; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; @@ -55,7 +56,6 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme HandlerList.unregisterAll(this); } - @SuppressWarnings("UnstableApiUsage") @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) public void onEntityDeath(EntityDeathEvent event) { Entity entity = event.getEntity(); @@ -91,8 +91,8 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme return this.vanillaLootParser; } - public class VanillaLootParser implements ConfigParser { - public static final String[] CONFIG_SECTION_NAME = new String[] {"vanilla-loots", "vanilla-loot", "vanilla_loots", "vanilla_loot"}; + public class VanillaLootParser extends IdSectionConfigParser { + public static final String[] CONFIG_SECTION_NAME = new String[] {"vanilla-loots", "vanilla-loot"}; @Override public int loadingSequence() { @@ -105,7 +105,7 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme } @Override - public void parseSection(Pack pack, Path path, Key id, Map section) { + public void parseSection(Pack pack, Path path, String node, Key id, Map section) { String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("type"), "warning.config.vanilla_loot.missing_type"); VanillaLoot.Type typeEnum; try { @@ -127,7 +127,7 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme VanillaLoot vanillaLoot = blockLoots.computeIfAbsent(BlockStateUtils.blockStateToId(blockState), k -> new VanillaLoot(VanillaLoot.Type.BLOCK)); vanillaLoot.addLootTable(lootTable); } else { - for (Object blockState : BlockStateUtils.getAllVanillaBlockStates(Key.of(target))) { + for (Object blockState : BlockStateUtils.getPossibleBlockStates(Key.of(target))) { if (blockState == MBlocks.AIR$defaultState) { throw new LocalizedResourceConfigException("warning.config.vanilla_loot.block.invalid_target", target); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java index 0ce69f22c..a8513327a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.pack; import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; +import net.momirealms.craftengine.bukkit.api.event.AsyncResourcePackCacheEvent; import net.momirealms.craftengine.bukkit.api.event.AsyncResourcePackGenerateEvent; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.command.feature.ReloadCommand; @@ -28,10 +29,17 @@ public class BukkitPackManager extends AbstractPackManager implements Listener { private final BukkitCraftEngine plugin; public BukkitPackManager(BukkitCraftEngine plugin) { - super(plugin, (rf, zp) -> { - AsyncResourcePackGenerateEvent endEvent = new AsyncResourcePackGenerateEvent(rf, zp); - EventUtils.fireAndForget(endEvent); - }); + super( + plugin, + (cd) -> { + AsyncResourcePackCacheEvent cacheEvent = new AsyncResourcePackCacheEvent(cd); + EventUtils.fireAndForget(cacheEvent); + }, + (rf, zp) -> { + AsyncResourcePackGenerateEvent endEvent = new AsyncResourcePackGenerateEvent(rf, zp); + EventUtils.fireAndForget(endEvent); + } + ); this.plugin = plugin; } @@ -45,6 +53,7 @@ public class BukkitPackManager extends AbstractPackManager implements Listener { public void onPlayerJoin(PlayerJoinEvent event) { if (Config.sendPackOnJoin() && !VersionHelper.isOrAbove1_20_2()) { Player player = BukkitAdaptors.adapt(event.getPlayer()); + if (player == null) return; this.sendResourcePack(player); } } @@ -81,7 +90,7 @@ public class BukkitPackManager extends AbstractPackManager implements Listener { return; } if (!Config.sendPackOnUpload()) return; - CraftEngine.instance().logger().info("Complete uploading resource pack"); + CraftEngine.instance().logger().info("Completed uploading resource pack"); for (BukkitServerPlayer player : this.plugin.networkManager().onlineUsers()) { sendResourcePack(player); } @@ -98,7 +107,7 @@ public class BukkitPackManager extends AbstractPackManager implements Listener { return; } if (dataList.size() == 1) { - ResourcePackDownloadData data = dataList.get(0); + ResourcePackDownloadData data = dataList.getFirst(); player.sendPacket(ResourcePackUtils.createPacket(data.uuid(), data.url(), data.sha1()), true); player.addResourcePackUUID(data.uuid()); } else { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java index 00b940138..fe991ca7f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java @@ -5,6 +5,7 @@ import net.momirealms.craftengine.bukkit.advancement.BukkitAdvancementManager; import net.momirealms.craftengine.bukkit.api.event.CraftEngineReloadEvent; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.block.behavior.BukkitBlockBehaviors; +import net.momirealms.craftengine.bukkit.block.entity.renderer.element.BukkitBlockEntityElementConfigs; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.BukkitHitBoxTypes; import net.momirealms.craftengine.bukkit.entity.projectile.BukkitProjectileManager; @@ -19,12 +20,10 @@ import net.momirealms.craftengine.bukkit.plugin.command.BukkitSenderFactory; import net.momirealms.craftengine.bukkit.plugin.gui.BukkitGuiManager; import net.momirealms.craftengine.bukkit.plugin.injector.*; import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager; -import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; import net.momirealms.craftengine.bukkit.plugin.scheduler.BukkitSchedulerAdapter; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.sound.BukkitSoundManager; import net.momirealms.craftengine.bukkit.util.EventUtils; -import net.momirealms.craftengine.bukkit.util.RegistryUtils; import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; import net.momirealms.craftengine.core.item.ItemManager; import net.momirealms.craftengine.core.plugin.CraftEngine; @@ -56,6 +55,7 @@ import org.jspecify.annotations.Nullable; import java.io.*; import java.net.URL; import java.net.URLConnection; +import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.Objects; @@ -72,26 +72,30 @@ public class BukkitCraftEngine extends CraftEngine { private final Path dataFolderPath; protected BukkitCraftEngine(JavaPlugin plugin) { - this(new JavaPluginLogger(plugin.getLogger()), plugin.getDataFolder().toPath().toAbsolutePath(), new ReflectionClassPathAppender(plugin.getClass().getClassLoader())); + this(new JavaPluginLogger(plugin.getLogger()), plugin.getDataFolder().toPath().toAbsolutePath(), + new ReflectionClassPathAppender(plugin.getClass().getClassLoader()), new ReflectionClassPathAppender(plugin.getClass().getClassLoader())); this.setJavaPlugin(plugin); } - protected BukkitCraftEngine(PluginLogger logger, Path dataFolderPath, ClassPathAppender classPathAppender) { + protected BukkitCraftEngine(PluginLogger logger, Path dataFolderPath, ClassPathAppender sharedClassPathAppender, ClassPathAppender privateClassPathAppender) { super((p) -> { CraftEngineReloadEvent event = new CraftEngineReloadEvent((BukkitCraftEngine) p); EventUtils.fireAndForget(event); }); instance = this; this.dataFolderPath = dataFolderPath; - super.classPathAppender = classPathAppender; + super.sharedClassPathAppender = sharedClassPathAppender; + super.privateClassPathAppender = privateClassPathAppender; super.logger = logger; super.platform = new BukkitPlatform(); super.scheduler = new BukkitSchedulerAdapter(this); - Class compatibilityClass = Objects.requireNonNull(ReflectionUtils.getClazz(COMPATIBILITY_CLASS), "Compatibility class not found"); - try { - super.compatibilityManager = (CompatibilityManager) Objects.requireNonNull(ReflectionUtils.getConstructor(compatibilityClass, 0)).newInstance(this); - } catch (ReflectiveOperationException e) { - logger().warn("Compatibility class could not be instantiated: " + compatibilityClass.getName()); + Class compatibilityClass = ReflectionUtils.getClazz(COMPATIBILITY_CLASS); + if (compatibilityClass != null) { + try { + super.compatibilityManager = (CompatibilityManager) Objects.requireNonNull(ReflectionUtils.getConstructor(compatibilityClass, 0)).newInstance(this); + } catch (ReflectiveOperationException e) { + logger().warn("Compatibility class could not be instantiated: " + compatibilityClass.getName()); + } } } @@ -99,9 +103,15 @@ public class BukkitCraftEngine extends CraftEngine { this.javaPlugin = javaPlugin; } - protected void setUpConfig() { - this.translationManager = new TranslationManagerImpl(this); + protected void setUpConfigAndLocale() { this.config = new Config(this); + this.config.updateConfigCache(); + // 先读取语言后,再重载语言文件系统 + this.config.loadForcedLocale(); + this.translationManager = new TranslationManagerImpl(this); + this.translationManager.reload(); + // 最后才加载完整的config配置 + this.config.loadFullSettings(); } public void injectRegistries() { @@ -141,8 +151,8 @@ public class BukkitCraftEngine extends CraftEngine { throw new InjectionException("Error initializing ProtectedFieldVisitor", e); } super.onPluginLoad(); - super.blockManager.init(); super.networkManager = new BukkitNetworkManager(this); + super.blockManager.init(); super.itemManager = new BukkitItemManager(this); this.successfullyLoaded = true; super.compatibilityManager().onLoad(); @@ -185,7 +195,7 @@ public class BukkitCraftEngine extends CraftEngine { BukkitBlockBehaviors.init(); BukkitItemBehaviors.init(); BukkitHitBoxTypes.init(); - PacketConsumers.initEntities(RegistryUtils.currentEntityTypeRegistrySize()); + BukkitBlockEntityElementConfigs.init(); super.packManager = new BukkitPackManager(this); super.senderFactory = new BukkitSenderFactory(this); super.recipeManager = new BukkitRecipeManager(this); @@ -201,6 +211,19 @@ public class BukkitCraftEngine extends CraftEngine { super.furnitureManager = new BukkitFurnitureManager(this); super.onPluginEnable(); super.compatibilityManager().onEnable(); + + // todo 未来版本移除 + Path legacyFile1 = this.dataFolderPath().resolve("additional-real-blocks.yml"); + Path legacyFile2 = this.dataFolderPath().resolve("mappings.yml"); + if (Files.exists(legacyFile1)) { + try { + Files.delete(legacyFile1); + Files.deleteIfExists(legacyFile2); + this.saveResource("resources/internal/configuration/mappings.yml"); + } catch (IOException e) { + this.logger.warn("Failed to delete legacy files", e); + } + } } @Override @@ -325,6 +348,11 @@ public class BukkitCraftEngine extends CraftEngine { return (BukkitPackManager) packManager; } + @Override + public BukkitFontManager fontManager() { + return (BukkitFontManager) fontManager; + } + @SuppressWarnings("ResultOfMethodCallIgnored") @Override public void saveResource(String resourcePath) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitPlatform.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitPlatform.java index e1055644a..dfccb590f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitPlatform.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitPlatform.java @@ -2,13 +2,20 @@ package net.momirealms.craftengine.bukkit.plugin; import com.google.gson.JsonElement; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps; +import net.momirealms.craftengine.bukkit.util.ParticleUtils; +import net.momirealms.craftengine.bukkit.world.particle.BukkitParticleType; import net.momirealms.craftengine.core.plugin.Platform; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.particle.ParticleType; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.Tag; import org.bukkit.Bukkit; +import org.bukkit.Particle; import java.util.Map; @@ -51,4 +58,22 @@ public class BukkitPlatform implements Platform { public Tag javaToSparrowNBT(Object object) { return MRegistryOps.JAVA.convertTo(MRegistryOps.SPARROW_NBT, object); } + + @Override + public World getWorld(String name) { + org.bukkit.World world = Bukkit.getWorld(name); + if (world == null) { + return null; + } + return BukkitAdaptors.adapt(world); + } + + @Override + public ParticleType getParticleType(Key name) { + Particle particle = ParticleUtils.getParticle(name); + if (particle == null) { + throw new IllegalArgumentException("Invalid particle: " + name); + } + return new BukkitParticleType(particle, name); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/agent/BlocksAgent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/agent/BlocksAgent.java index cac4429ba..9b8f17333 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/agent/BlocksAgent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/agent/BlocksAgent.java @@ -34,7 +34,7 @@ public final class BlocksAgent { Method injectRegistries = plugin.getClass().getMethod("injectRegistries"); injectRegistries.invoke(plugin); } catch (Exception e) { - throw new RuntimeException("Failed to inject registries", e); + e.printStackTrace(); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/BukkitClassPathAppender.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/BukkitClassPathAppender.java new file mode 100644 index 000000000..804947546 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/BukkitClassPathAppender.java @@ -0,0 +1,37 @@ +package net.momirealms.craftengine.bukkit.plugin.classpath; + +import net.momirealms.craftengine.core.plugin.classpath.ClassPathAppender; +import net.momirealms.craftengine.core.plugin.classpath.URLClassLoaderAccess; +import org.bukkit.Bukkit; + +import java.net.MalformedURLException; +import java.net.URLClassLoader; +import java.nio.file.Path; + +public class BukkitClassPathAppender implements ClassPathAppender { + private final URLClassLoaderAccess libraryClassLoaderAccess; + + public BukkitClassPathAppender() { + // 这个类加载器用于加载重定位后的依赖库,这样所有插件都能访问到 + ClassLoader bukkitClassLoader = Bukkit.class.getClassLoader(); + if (bukkitClassLoader instanceof URLClassLoader urlClassLoader) { + this.libraryClassLoaderAccess = URLClassLoaderAccess.create(urlClassLoader); + } else { + // ignite会把Bukkit放置于EmberClassLoader中,获取其父DynamicClassLoader + if (bukkitClassLoader.getClass().getName().equals("space.vectrix.ignite.launch.ember.EmberClassLoader") && bukkitClassLoader.getParent() instanceof URLClassLoader urlClassLoader) { + this.libraryClassLoaderAccess = URLClassLoaderAccess.create(urlClassLoader); + } else { + throw new UnsupportedOperationException("Unsupported classloader " + bukkitClassLoader.getClass()); + } + } + } + + @Override + public void addJarToClasspath(Path file) { + try { + this.libraryClassLoaderAccess.addURL(file.toUri().toURL()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/PaperClassPathAppender.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/PaperClassPathAppender.java deleted file mode 100644 index 885b223cd..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/PaperClassPathAppender.java +++ /dev/null @@ -1,57 +0,0 @@ -package net.momirealms.craftengine.bukkit.plugin.classpath; - -import net.momirealms.craftengine.core.plugin.classpath.ClassPathAppender; -import net.momirealms.craftengine.core.plugin.classpath.URLClassLoaderAccess; -import net.momirealms.craftengine.core.util.ReflectionUtils; -import org.bukkit.Bukkit; - -import java.lang.reflect.Field; -import java.net.MalformedURLException; -import java.net.URLClassLoader; -import java.nio.file.Path; -import java.util.Optional; - -public class PaperClassPathAppender implements ClassPathAppender { - public static final Class clazz$PaperPluginClassLoader = ReflectionUtils.getClazz( - "io.papermc.paper.plugin.entrypoint.classloader.PaperPluginClassLoader" - ); - public static final Field field$PaperPluginClassLoader$libraryLoader = Optional.ofNullable(clazz$PaperPluginClassLoader) - .map(it -> ReflectionUtils.getDeclaredField(it, URLClassLoader.class, 0)) - .orElse(null); - private final URLClassLoaderAccess libraryClassLoaderAccess; - - // todo 是否有更好的方法让库被其他插件共享 - public PaperClassPathAppender(ClassLoader classLoader) { - // 这个类加载器用于加载重定位后的依赖库,这样所有插件都能访问到 - ClassLoader bukkitClassLoader = Bukkit.class.getClassLoader(); - if (bukkitClassLoader instanceof URLClassLoader urlClassLoader) { - this.libraryClassLoaderAccess = URLClassLoaderAccess.create(urlClassLoader); - } else { - // ignite会把Bukkit放置于EmberClassLoader中,获取其父DynamicClassLoader - if (bukkitClassLoader.getClass().getName().equals("space.vectrix.ignite.launch.ember.EmberClassLoader") && bukkitClassLoader.getParent() instanceof URLClassLoader urlClassLoader) { - this.libraryClassLoaderAccess = URLClassLoaderAccess.create(urlClassLoader); - return; - } - try { - // 最次的方案,使用paper自带的classloader去加载依赖,这种情况会发生依赖隔离 - if (clazz$PaperPluginClassLoader != null && clazz$PaperPluginClassLoader.isInstance(classLoader)) { - URLClassLoader libraryClassLoader = (URLClassLoader) field$PaperPluginClassLoader$libraryLoader.get(classLoader); - this.libraryClassLoaderAccess = URLClassLoaderAccess.create(libraryClassLoader); - } else { - throw new IllegalStateException("ClassLoader is not instance of URLClassLoader"); - } - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Failed to instantiate PaperPluginClassLoader", e); - } - } - } - - @Override - public void addJarToClasspath(Path file) { - try { - this.libraryClassLoaderAccess.addURL(file.toUri().toURL()); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/PaperPluginClassPathAppender.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/PaperPluginClassPathAppender.java new file mode 100644 index 000000000..aa9bcb53a --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/PaperPluginClassPathAppender.java @@ -0,0 +1,44 @@ +package net.momirealms.craftengine.bukkit.plugin.classpath; + +import net.momirealms.craftengine.core.plugin.classpath.ClassPathAppender; +import net.momirealms.craftengine.core.plugin.classpath.URLClassLoaderAccess; +import net.momirealms.craftengine.core.util.ReflectionUtils; + +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.Optional; + +public class PaperPluginClassPathAppender implements ClassPathAppender { + public static final Class clazz$PaperPluginClassLoader = ReflectionUtils.getClazz( + "io.papermc.paper.plugin.entrypoint.classloader.PaperPluginClassLoader" + ); + public static final Field field$PaperPluginClassLoader$libraryLoader = Optional.ofNullable(clazz$PaperPluginClassLoader) + .map(it -> ReflectionUtils.getDeclaredField(it, URLClassLoader.class, 0)) + .orElse(null); + private final URLClassLoaderAccess libraryClassLoaderAccess; + + public PaperPluginClassPathAppender(ClassLoader classLoader) { + try { + // 使用paper自带的classloader去加载依赖,这种情况会发生依赖隔离 + if (clazz$PaperPluginClassLoader != null && clazz$PaperPluginClassLoader.isInstance(classLoader)) { + URLClassLoader libraryClassLoader = (URLClassLoader) field$PaperPluginClassLoader$libraryLoader.get(classLoader); + this.libraryClassLoaderAccess = URLClassLoaderAccess.create(libraryClassLoader); + } else { + throw new IllegalStateException("ClassLoader is not instance of URLClassLoader"); + } + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to instantiate PaperPluginClassLoader", e); + } + } + + @Override + public void addJarToClasspath(Path file) { + try { + this.libraryClassLoaderAccess.addURL(file.toUri().toURL()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java index 008bb53b8..14c4d9378 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java @@ -55,11 +55,13 @@ public class BukkitCommandManager extends AbstractCommandManager new DisableResourceCommand(this, plugin), new ListResourceCommand(this, plugin), new UploadPackCommand(this, plugin), - new SendResourcePackCommand(this, plugin) + new SendResourcePackCommand(this, plugin), + new DebugSaveDefaultResourcesCommand(this, plugin), + new DebugCleanCacheCommand(this, plugin) )); final LegacyPaperCommandManager manager = (LegacyPaperCommandManager) getCommandManager(); manager.settings().set(ManagerSetting.ALLOW_UNSAFE_REGISTRATION, true); - if (manager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) { + if (manager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER) && manager.hasCapability(CloudBukkitCapabilities.BRIGADIER)) { manager.registerBrigadier(); manager.brigadierManager().setNativeNumberSuggestions(true); } else if (manager.hasCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugAppearanceStateUsageCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugAppearanceStateUsageCommand.java index f47eb842e..78b10ff30 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugAppearanceStateUsageCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugAppearanceStateUsageCommand.java @@ -5,7 +5,8 @@ import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.NamedTextColor; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; -import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.core.block.BlockStateWrapper; +import net.momirealms.craftengine.core.pack.allocator.VisualBlockStateAllocator; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.util.Key; @@ -34,37 +35,39 @@ public class DebugAppearanceStateUsageCommand extends BukkitCommandFeature() { @Override public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { - return CompletableFuture.completedFuture(plugin().blockManager().blockAppearanceArranger().keySet().stream().map(it -> Suggestion.suggestion(it.toString())).toList()); + return CompletableFuture.completedFuture(plugin().blockManager().blockStateArranger().keySet().stream().map(it -> Suggestion.suggestion(it.toString())).toList()); } })) .handler(context -> { String data = context.get("id"); BukkitBlockManager blockManager = plugin().blockManager(); Key baseBlockId = Key.of(data); - List appearances = blockManager.blockAppearanceArranger().get(baseBlockId); + List appearances = blockManager.blockStateArranger().get(baseBlockId); if (appearances == null) return; int i = 0; Component block = Component.text(baseBlockId + ": "); plugin().senderFactory().wrap(context.sender()).sendMessage(block); - + VisualBlockStateAllocator allocator = blockManager.blockParser().visualBlockStateAllocator(); List batch = new ArrayList<>(); - for (int appearance : appearances) { + for (BlockStateWrapper appearance : appearances) { Component text = Component.text("|"); - List reals = blockManager.appearanceToRealStates(appearance); - if (reals == null || reals.isEmpty()) { + List reals = blockManager.appearanceToRealStates(appearance.registryId()); + if (reals.isEmpty()) { Component hover = Component.text(baseBlockId.value() + ":" + i).color(NamedTextColor.GREEN); - hover = hover.append(Component.newline()).append(Component.text(BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(appearance)).getAsString()).color(NamedTextColor.GREEN)); + hover = hover.append(Component.newline()).append(Component.text(appearance.getAsString()).color(NamedTextColor.GREEN)); text = text.color(NamedTextColor.GREEN).hoverEvent(HoverEvent.showText(hover)); } else { - Component hover = Component.text(baseBlockId.value() + ":" + i).color(NamedTextColor.RED); + boolean isFixed = allocator.isForcedState(appearance); + NamedTextColor namedTextColor = isFixed ? NamedTextColor.RED : NamedTextColor.YELLOW; + Component hover = Component.text(baseBlockId.value() + ":" + i).color(namedTextColor); List hoverChildren = new ArrayList<>(); hoverChildren.add(Component.newline()); - hoverChildren.add(Component.text(BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(appearance)).getAsString()).color(NamedTextColor.RED)); + hoverChildren.add(Component.text(appearance.getAsString()).color(namedTextColor)); for (int real : reals) { hoverChildren.add(Component.newline()); hoverChildren.add(Component.text(blockManager.getImmutableBlockStateUnsafe(real).toString()).color(NamedTextColor.GRAY)); } - text = text.color(NamedTextColor.RED).hoverEvent(HoverEvent.showText(hover.children(hoverChildren))); + text = text.color(namedTextColor).hoverEvent(HoverEvent.showText(hover.children(hoverChildren))); } batch.add(text); i++; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugCleanCacheCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugCleanCacheCommand.java new file mode 100644 index 000000000..bf41e6f10 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugCleanCacheCommand.java @@ -0,0 +1,241 @@ +package net.momirealms.craftengine.bukkit.plugin.command.feature; + +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.font.BukkitFontManager; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; +import net.momirealms.craftengine.core.block.BlockStateWrapper; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.font.BitmapImage; +import net.momirealms.craftengine.core.item.CustomItem; +import net.momirealms.craftengine.core.pack.allocator.IdAllocator; +import net.momirealms.craftengine.core.pack.allocator.VisualBlockStateAllocator; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; +import net.momirealms.craftengine.core.util.FileUtils; +import net.momirealms.craftengine.core.util.Key; +import org.bukkit.command.CommandSender; +import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.Command; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +public class DebugCleanCacheCommand extends BukkitCommandFeature { + + public DebugCleanCacheCommand(CraftEngineCommandManager commandManager, CraftEngine plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { + return builder + .optional("type", StringParser.stringComponent().suggestionProvider(new SuggestionProvider<>() { + @Override + public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { + return CompletableFuture.completedFuture(List.of(Suggestion.suggestion("custom-model-data"), Suggestion.suggestion("custom-block-states"), Suggestion.suggestion("visual-block-states"), Suggestion.suggestion("font"), Suggestion.suggestion("all"))); + } + })) + .handler(context -> { + if (this.plugin().isReloading()) { + context.sender().sendMessage("The plugin is reloading. Please wait until the process is complete."); + return; + } + String type = context.getOrDefault("type", "all"); + switch (type) { + case "custom-model-data" -> handleCustomModelData(context); + case "font", "images" -> handleFont(context); + case "custom-block-states" -> handleCustomBlockState(context); + case "visual-block-states" -> handleVisualBlockState(context); + case "all" -> { + handleCustomModelData(context); + handleFont(context); + handleCustomBlockState(context); + handleVisualBlockState(context); + } + } + }); + } + + @Override + public String getFeatureID() { + return "debug_clean_cache"; + } + + private void handleVisualBlockState(CommandContext context) { + BukkitBlockManager instance = BukkitBlockManager.instance(); + Set ids = new HashSet<>(); + for (CustomBlock customBlock : instance.loadedBlocks().values()) { + for (ImmutableBlockState state : customBlock.variantProvider().states()) { + ids.add(state.vanillaBlockState()); + } + } + VisualBlockStateAllocator visualBlockStateAllocator = instance.blockParser().visualBlockStateAllocator(); + List removed = visualBlockStateAllocator.cleanupUnusedIds(i -> !ids.contains(i)); + try { + visualBlockStateAllocator.saveToCache(); + } catch (IOException e) { + this.plugin().logger().warn("Error while saving visual block states allocation", e); + } + for (String id : removed) { + this.plugin().logger().info("Cleaned unsued block appearance: " + id); + } + context.sender().sendMessage("Cleaned " + removed.size() + " unused block state appearances"); + } + + private void handleCustomBlockState(CommandContext context) { + BukkitBlockManager instance = BukkitBlockManager.instance(); + Set ids = new HashSet<>(); + for (CustomBlock customBlock : instance.loadedBlocks().values()) { + for (ImmutableBlockState state : customBlock.variantProvider().states()) { + ids.add(state.toString()); + } + } + IdAllocator idAllocator = instance.blockParser().internalIdAllocator(); + List removed = idAllocator.cleanupUnusedIds(i -> !ids.contains(i)); + try { + idAllocator.saveToCache(); + } catch (IOException e) { + this.plugin().logger().warn("Error while saving custom block states allocation", e); + } + for (String id : removed) { + this.plugin().logger().info("Cleaned unsued block state: " + id); + } + context.sender().sendMessage("Cleaned " + removed.size() + " unused custom block states"); + } + + private void handleFont(CommandContext context) { + BukkitFontManager instance = this.plugin().fontManager(); + Map> idsMap = new HashMap<>(); + for (BitmapImage image : instance.loadedImages().values()) { + Set ids = idsMap.computeIfAbsent(image.font(), k -> new HashSet<>()); + String id = image.id().toString(); + ids.add(id); + for (int i = 0; i < image.rows(); i++) { + for (int j = 0; j < image.columns(); j++) { + String imageArgs = id + ":" + i + ":" + j; + ids.add(imageArgs); + } + } + } + int total = 0; + for (Map.Entry entry : getAllCachedFont().entrySet()) { + Key font = entry.getKey(); + Set ids = idsMap.getOrDefault(font, Collections.emptySet()); + List removed = entry.getValue().cleanupUnusedIds(i -> !ids.contains(i)); + try { + entry.getValue().saveToCache(); + } catch (IOException e) { + this.plugin().logger().warn("Error while saving codepoint allocation for font " + font.asString(), e); + return; + } + for (String id : removed) { + this.plugin().logger().info("Cleaned unsued image: " + id); + } + total += removed.size(); + } + context.sender().sendMessage("Cleaned " + total + " unused codepoints"); + } + + private void handleCustomModelData(CommandContext context) { + BukkitItemManager instance = BukkitItemManager.instance(); + Map> idsMap = new HashMap<>(); + for (CustomItem item : instance.loadedItems().values()) { + Set ids = idsMap.computeIfAbsent(item.clientBoundMaterial(), k -> new HashSet<>()); + ids.add(item.id().asString()); + } + int total = 0; + for (Map.Entry entry : getAllCachedCustomModelData().entrySet()) { + Set ids = idsMap.getOrDefault(entry.getKey(), Collections.emptySet()); + List removed = entry.getValue().cleanupUnusedIds(i -> !ids.contains(i)); + total += removed.size(); + try { + entry.getValue().saveToCache(); + } catch (IOException e) { + this.plugin().logger().warn("Error while saving custom model data allocation for material " + entry.getKey().asString(), e); + return; + } + for (String id : removed) { + this.plugin().logger().info("Cleaned unsued item: " + id); + } + } + context.sender().sendMessage("Cleaned " + total + " unused custom model data"); + } + + public Map getAllCachedCustomModelData() { + Path cacheDir = CraftEngine.instance().dataFolderPath().resolve("cache").resolve("custom-model-data"); + + Map idAllocators = new HashMap<>(); + try (Stream files = Files.list(cacheDir)) { + files.filter(this::isJsonFile) + .forEach(file -> processIdAllocatorFile("minecraft", file, idAllocators)); + + } catch (IOException e) { + CraftEngine.instance().logger().warn("Failed to process: " + cacheDir.getFileName(), e); + } + + return idAllocators; + } + + public Map getAllCachedFont() { + Path cacheDir = CraftEngine.instance().dataFolderPath().resolve("cache").resolve("font"); + + try { + List namespaces = FileUtils.collectNamespaces(cacheDir); + Map idAllocators = new HashMap<>(); + + for (Path namespace : namespaces) { + processNamespace(namespace, idAllocators); + } + return idAllocators; + + } catch (IOException e) { + CraftEngine.instance().logger().warn("Failed to load cached id allocators from: " + cacheDir, e); + return Collections.emptyMap(); + } + } + + private void processNamespace(Path namespace, Map idAllocators) { + if (!Files.isDirectory(namespace)) { + return; + } + + try (Stream files = Files.list(namespace)) { + files.filter(this::isJsonFile) + .forEach(file -> processIdAllocatorFile(namespace.getFileName().toString(), file, idAllocators)); + + } catch (IOException e) { + CraftEngine.instance().logger().warn("Failed to process namespace: " + namespace.getFileName(), e); + } + } + + private boolean isJsonFile(Path file) { + return Files.isRegularFile(file) && file.getFileName().toString().endsWith(".json"); + } + + private void processIdAllocatorFile(String namespaceName, Path file, Map idAllocators) { + try { + String fileName = FileUtils.pathWithoutExtension(file.getFileName().toString()); + + Key font = Key.of(namespaceName, fileName); + IdAllocator allocator = new IdAllocator(file); + allocator.loadFromCache(); + + idAllocators.put(font, allocator); + + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to load id allocator from: " + file, e); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugRealStateUsageCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugRealStateUsageCommand.java index 01531251d..14e9888d9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugRealStateUsageCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugRealStateUsageCommand.java @@ -5,22 +5,17 @@ import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.NamedTextColor; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; +import net.momirealms.craftengine.core.block.BlockManager; import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.pack.allocator.IdAllocator; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; -import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.plugin.config.Config; import org.bukkit.command.CommandSender; -import org.checkerframework.checker.nullness.qual.NonNull; import org.incendo.cloud.Command; -import org.incendo.cloud.context.CommandContext; -import org.incendo.cloud.context.CommandInput; -import org.incendo.cloud.parser.standard.StringParser; -import org.incendo.cloud.suggestion.Suggestion; -import org.incendo.cloud.suggestion.SuggestionProvider; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CompletableFuture; public class DebugRealStateUsageCommand extends BukkitCommandFeature { @@ -31,34 +26,22 @@ public class DebugRealStateUsageCommand extends BukkitCommandFeature assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { return builder - .required("id", StringParser.stringComponent(StringParser.StringMode.GREEDY_FLAG_YIELDING).suggestionProvider(new SuggestionProvider<>() { - @Override - public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { - return CompletableFuture.completedFuture(plugin().blockManager().blockAppearanceArranger().keySet().stream().map(it -> Suggestion.suggestion(it.toString())).toList()); - } - })) .handler(context -> { - String data = context.get("id"); BukkitBlockManager blockManager = plugin().blockManager(); - Key baseBlockId = Key.of(data); - List reals = blockManager.realBlockArranger().get(baseBlockId); - if (reals == null) return; - int i = 0; - Component block = Component.text(baseBlockId + ": "); - plugin().senderFactory().wrap(context.sender()).sendMessage(block); - + plugin().senderFactory().wrap(context.sender()).sendMessage(Component.text("Serverside block state usage:")); List batch = new ArrayList<>(100); - for (int real : reals) { - ImmutableBlockState state = blockManager.getImmutableBlockStateUnsafe(real); + IdAllocator idAllocator = blockManager.blockParser().internalIdAllocator(); + for (int i = 0; i < Config.serverSideBlocks(); i++) { + ImmutableBlockState state = blockManager.getImmutableBlockStateUnsafe(i + blockManager.vanillaBlockStateCount()); if (state.isEmpty()) { - Component hover = Component.text("craftengine:" + baseBlockId.value() + "_" + i).color(NamedTextColor.GREEN); + Component hover = Component.text(BlockManager.createCustomBlockKey(i).asString()).color(NamedTextColor.GREEN); batch.add(Component.text("|").color(NamedTextColor.GREEN).hoverEvent(HoverEvent.showText(hover))); } else { - Component hover = Component.text("craftengine:" + baseBlockId.value() + "_" + i).color(NamedTextColor.RED); + NamedTextColor namedTextColor = idAllocator.isForced(state.toString()) ? NamedTextColor.RED : NamedTextColor.YELLOW; + Component hover = Component.text(BlockManager.createCustomBlockKey(i).asString()).color(namedTextColor); hover = hover.append(Component.newline()).append(Component.text(state.toString()).color(NamedTextColor.GRAY)); - batch.add(Component.text("|").color(NamedTextColor.RED).hoverEvent(HoverEvent.showText(hover))); + batch.add(Component.text("|").color(namedTextColor).hoverEvent(HoverEvent.showText(hover))); } - i++; if (batch.size() == 100) { plugin().senderFactory().wrap(context.sender()) .sendMessage(Component.text("").children(batch)); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSaveDefaultResourcesCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSaveDefaultResourcesCommand.java new file mode 100644 index 000000000..75525786e --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSaveDefaultResourcesCommand.java @@ -0,0 +1,30 @@ +package net.momirealms.craftengine.bukkit.plugin.command.feature; + +import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; +import net.momirealms.craftengine.core.pack.AbstractPackManager; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.Command; + +public class DebugSaveDefaultResourcesCommand extends BukkitCommandFeature { + + public DebugSaveDefaultResourcesCommand(CraftEngineCommandManager commandManager, CraftEngine plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { + return builder + .handler(context -> { + AbstractPackManager packManager = (AbstractPackManager) CraftEngine.instance().packManager(); + packManager.saveDefaultConfigs(); + context.sender().sendMessage("Saved default configs"); + }); + } + + @Override + public String getFeatureID() { + return "debug_save_default_resources"; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugTargetBlockCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugTargetBlockCommand.java index 4ed380551..c684296a8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugTargetBlockCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugTargetBlockCommand.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; @@ -10,6 +11,7 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.sender.Sender; +import net.momirealms.craftengine.core.plugin.config.Config; import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.command.CommandSender; @@ -44,15 +46,14 @@ public class DebugTargetBlockCommand extends BukkitCommandFeature Sender sender = plugin().senderFactory().wrap(context.sender()); sender.sendMessage(Component.text(bData)); int id = BlockStateUtils.blockStateToId(blockState); - - Object holder = BukkitBlockManager.instance().getMinecraftBlockHolder(id); - if (holder != null) { + if (!BlockStateUtils.isVanillaBlock(id)) { + Object holder = BukkitBlockManager.instance().getMinecraftBlockHolder(id); ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(id); if (immutableBlockState != null) { sender.sendMessage(Component.text(immutableBlockState.toString())); } ImmutableBlockState dataInCache = plugin().worldManager().getWorld(block.getWorld().getUID()).getBlockStateAtIfLoaded(LocationUtils.toBlockPos(block.getLocation())); - sender.sendMessage(Component.text("cache-state: " + !dataInCache.isEmpty())); + sender.sendMessage(Component.text("cache-state: " + (dataInCache != null && !dataInCache.isEmpty()))); try { @SuppressWarnings("unchecked") Set tags = (Set) CoreReflections.field$Holder$Reference$tags.get(holder); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchRecipePlayerCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchRecipePlayerCommand.java index 1082e366a..e1163bcb0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchRecipePlayerCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchRecipePlayerCommand.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.item.recipe.Recipe; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.locale.MessageConstants; +import net.momirealms.craftengine.core.util.ItemUtils; import net.momirealms.craftengine.core.util.Key; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -30,7 +31,7 @@ public class SearchRecipePlayerCommand extends BukkitCommandFeature item = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND); - if (item == null) { + if (ItemUtils.isEmpty(item)) { handleFeedback(context, MessageConstants.COMMAND_SEARCH_RECIPE_NO_ITEM); return; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchUsagePlayerCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchUsagePlayerCommand.java index 9425e7a3c..9cd21ae36 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchUsagePlayerCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchUsagePlayerCommand.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.item.recipe.Recipe; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.locale.MessageConstants; +import net.momirealms.craftengine.core.util.ItemUtils; import net.momirealms.craftengine.core.util.Key; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -30,7 +31,7 @@ public class SearchUsagePlayerCommand extends BukkitCommandFeature item = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND); - if (item.isEmpty()) { + if (ItemUtils.isEmpty(item)) { handleFeedback(context, MessageConstants.COMMAND_SEARCH_USAGE_NO_ITEM); return; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java index e5f42e169..437855e70 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java @@ -1,15 +1,10 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; -import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; -import org.bukkit.Location; -import org.bukkit.block.data.BlockData; import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; import org.incendo.cloud.Command; -import org.incendo.cloud.parser.standard.IntegerParser; public class TestCommand extends BukkitCommandFeature { @@ -20,22 +15,9 @@ public class TestCommand extends BukkitCommandFeature { @Override public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { return builder - .required("start", IntegerParser.integerParser(0)) - .senderType(Player.class) .handler(context -> { - Player sender = context.sender(); - int start = context.get("start"); - int x = sender.getChunk().getX() * 16; - int z = sender.getChunk().getZ() * 16; - int y = (sender.getLocation().getBlockY() / 16) * 16; - for (int a = 0; a < 16; a++) { - for (int b = 0; b < 16; b++) { - for (int c = 0; c < 16; c++) { - BlockData blockData = BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(start + a + b * 16 + c * 256)); - sender.getWorld().setBlockData(new Location(sender.getWorld(), x + a, y + b, z + c), blockData); - } - } - } + // DO NOT PUSH ANY CODE FOR TEST COMMAND + // 禁止推送含有实现的Test指令 }); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TotemAnimationCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TotemAnimationCommand.java index 9584a7fd1..7001785f0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TotemAnimationCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TotemAnimationCommand.java @@ -61,7 +61,7 @@ public class TotemAnimationCommand extends BukkitCommandFeature { handleFeedback(context, MessageConstants.COMMAND_TOTEM_NOT_TOTEM, Component.text(key.toString())); return; } - Item item = customItem.buildItem(ItemBuildContext.EMPTY); + Item item = customItem.buildItem(ItemBuildContext.empty()); if (VersionHelper.isOrAbove1_21_2()) { if (context.flags().contains("sound_location")) { String soundResourceLocation = context.flags().getValue("sound_location").get().toString(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/gui/BukkitGuiManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/gui/BukkitGuiManager.java index 26b31cebc..65feeb73d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/gui/BukkitGuiManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/gui/BukkitGuiManager.java @@ -1,16 +1,17 @@ package net.momirealms.craftengine.bukkit.plugin.gui; import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.bukkit.block.entity.BlockEntityHolder; +import net.momirealms.craftengine.bukkit.block.entity.SimpleStorageBlockEntity; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.bukkit.util.InventoryUtils; import net.momirealms.craftengine.bukkit.util.LegacyInventoryUtils; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.gui.*; -import net.momirealms.craftengine.core.util.ReflectionUtils; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -18,16 +19,18 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.inventory.InventoryDragEvent; -import org.bukkit.inventory.InventoryView; -import org.bukkit.inventory.MenuType; +import org.bukkit.event.player.PlayerQuitEvent; public class BukkitGuiManager implements GuiManager, Listener { - private static final boolean useNewOpenInventory = ReflectionUtils.getDeclaredMethod(InventoryView.class, void.class, new String[]{"open"}) != null; +// private static final boolean useNewOpenInventory = ReflectionUtils.getDeclaredMethod(InventoryView.class, void.class, new String[]{"open"}) != null; + private static BukkitGuiManager instance; private final BukkitCraftEngine plugin; public BukkitGuiManager(BukkitCraftEngine plugin) { this.plugin = plugin; + instance = this; } @Override @@ -40,21 +43,21 @@ public class BukkitGuiManager implements GuiManager, Listener { HandlerList.unregisterAll(this); } - @SuppressWarnings("UnstableApiUsage") +// @SuppressWarnings("UnstableApiUsage") @Override public void openInventory(net.momirealms.craftengine.core.entity.player.Player player, GuiType guiType) { Player bukkitPlayer = (Player) player.platformPlayer(); - if (useNewOpenInventory) { - switch (guiType) { - case ANVIL -> MenuType.ANVIL.create(bukkitPlayer).open(); - case LOOM -> MenuType.LOOM.create(bukkitPlayer).open(); - case ENCHANTMENT -> MenuType.ENCHANTMENT.create(bukkitPlayer).open(); - case CRAFTING -> MenuType.CRAFTER_3X3.create(bukkitPlayer).open(); - case CARTOGRAPHY -> MenuType.CARTOGRAPHY_TABLE.create(bukkitPlayer).open(); - case SMITHING -> MenuType.SMITHING.create(bukkitPlayer).open(); - case GRINDSTONE -> MenuType.GRINDSTONE.create(bukkitPlayer).open(); - } - } else { +// if (useNewOpenInventory) { +// switch (guiType) { +// case ANVIL -> MenuType.ANVIL.create(bukkitPlayer).open(); +// case LOOM -> MenuType.LOOM.create(bukkitPlayer).open(); +// case ENCHANTMENT -> MenuType.ENCHANTMENT.create(bukkitPlayer).open(); +// case CRAFTING -> MenuType.CRAFTING.create(bukkitPlayer).open(); +// case CARTOGRAPHY -> MenuType.CARTOGRAPHY_TABLE.create(bukkitPlayer).open(); +// case SMITHING -> MenuType.SMITHING.create(bukkitPlayer).open(); +// case GRINDSTONE -> MenuType.GRINDSTONE.create(bukkitPlayer).open(); +// } +// } else { switch (guiType) { case ANVIL -> LegacyInventoryUtils.openAnvil(bukkitPlayer); case LOOM -> LegacyInventoryUtils.openLoom(bukkitPlayer); @@ -64,7 +67,7 @@ public class BukkitGuiManager implements GuiManager, Listener { case ENCHANTMENT -> LegacyInventoryUtils.openEnchanting(bukkitPlayer); case CARTOGRAPHY -> LegacyInventoryUtils.openCartographyTable(bukkitPlayer); } - } +// } } @Override @@ -83,8 +86,8 @@ public class BukkitGuiManager implements GuiManager, Listener { @Override public Inventory createInventory(Gui gui, int size) { - CraftEngineInventoryHolder holder = new CraftEngineInventoryHolder(gui); - org.bukkit.inventory.Inventory inventory = Bukkit.createInventory(holder, size); + CraftEngineGUIHolder holder = new CraftEngineGUIHolder(gui); + org.bukkit.inventory.Inventory inventory = FastNMS.INSTANCE.createSimpleStorageContainer(holder, size, false, false); holder.holder().bindValue(inventory); return new BukkitInventory(inventory); } @@ -92,13 +95,11 @@ public class BukkitGuiManager implements GuiManager, Listener { @EventHandler(ignoreCancelled = true) public void onInventoryClick(InventoryClickEvent event) { org.bukkit.inventory.Inventory inventory = event.getInventory(); - if (!CraftBukkitReflections.clazz$MinecraftInventory.isInstance(FastNMS.INSTANCE.method$CraftInventory$getInventory(inventory))) { + if (!InventoryUtils.isCustomContainer(inventory)) return; + if (!(inventory.getHolder() instanceof CraftEngineGUIHolder craftEngineGUIHolder)) { return; } - if (!(inventory.getHolder() instanceof CraftEngineInventoryHolder craftEngineInventoryHolder)) { - return; - } - AbstractGui gui = (AbstractGui) craftEngineInventoryHolder.gui(); + AbstractGui gui = (AbstractGui) craftEngineGUIHolder.gui(); Player player = (Player) event.getWhoClicked(); if (event.getClickedInventory() == player.getInventory()) { gui.handleInventoryClick(new BukkitClick(event, gui, new BukkitInventory(player.getInventory()))); @@ -110,10 +111,8 @@ public class BukkitGuiManager implements GuiManager, Listener { @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) public void onInventoryDrag(InventoryDragEvent event) { org.bukkit.inventory.Inventory inventory = event.getInventory(); - if (!CraftBukkitReflections.clazz$MinecraftInventory.isInstance(FastNMS.INSTANCE.method$CraftInventory$getInventory(inventory))) { - return; - } - if (!(inventory.getHolder() instanceof CraftEngineInventoryHolder)) { + if (!InventoryUtils.isCustomContainer(inventory)) return; + if (!(inventory.getHolder() instanceof CraftEngineGUIHolder)) { return; } for (int raw : event.getRawSlots()) { @@ -123,4 +122,34 @@ public class BukkitGuiManager implements GuiManager, Listener { } } } + + // 处理自定义容器的关闭音效 + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) + public void onInventoryClose(InventoryCloseEvent event) { + org.bukkit.inventory.Inventory inventory = event.getInventory(); + if (!InventoryUtils.isCustomContainer(inventory)) return; + if (!(inventory.getHolder() instanceof BlockEntityHolder holder)) { + return; + } + if (event.getPlayer() instanceof Player player && holder.blockEntity() instanceof SimpleStorageBlockEntity simpleStorageBlockEntity) { + simpleStorageBlockEntity.onPlayerClose(this.plugin.adapt(player)); + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) + public void onInventoryClose(PlayerQuitEvent event) { + Player player = event.getPlayer(); + org.bukkit.inventory.Inventory inventory = player.getInventory(); + if (!InventoryUtils.isCustomContainer(inventory)) return; + if (!(inventory.getHolder() instanceof BlockEntityHolder holder)) { + return; + } + if (holder.blockEntity() instanceof SimpleStorageBlockEntity simpleStorageBlockEntity) { + simpleStorageBlockEntity.onPlayerClose(this.plugin.adapt(player)); + } + } + + public static BukkitGuiManager instance() { + return instance; + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/gui/CraftEngineInventoryHolder.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/gui/CraftEngineGUIHolder.java similarity index 86% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/gui/CraftEngineInventoryHolder.java rename to bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/gui/CraftEngineGUIHolder.java index 50938e21b..a35679a4b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/gui/CraftEngineInventoryHolder.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/gui/CraftEngineGUIHolder.java @@ -6,11 +6,11 @@ import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.jetbrains.annotations.NotNull; -public class CraftEngineInventoryHolder implements InventoryHolder { +public class CraftEngineGUIHolder implements InventoryHolder { private final ObjectHolder inventory; private final Gui gui; - public CraftEngineInventoryHolder(Gui gui) { + public CraftEngineGUIHolder(Gui gui) { this.inventory = new ObjectHolder<>(); this.gui = gui; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockGenerator.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockGenerator.java index e05bada17..70f1823bb 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockGenerator.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockGenerator.java @@ -17,12 +17,15 @@ import net.momirealms.craftengine.bukkit.block.BukkitBlockShape; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries; +import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.bukkit.util.NoteBlockChainUpdateUtils; import net.momirealms.craftengine.core.block.BlockBehavior; import net.momirealms.craftengine.core.block.BlockKeys; import net.momirealms.craftengine.core.block.BlockShape; import net.momirealms.craftengine.core.block.DelegatingBlock; import net.momirealms.craftengine.core.block.behavior.EmptyBlockBehavior; +import net.momirealms.craftengine.core.block.behavior.special.FallOnBlockBehavior; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.Key; @@ -62,6 +65,7 @@ public final class BlockGenerator { .implement(CoreReflections.clazz$Fallable) .implement(CoreReflections.clazz$BonemealableBlock) .implement(CoreReflections.clazz$SimpleWaterloggedBlock) + .implement(CoreReflections.clazz$WorldlyContainerHolder) // internal interfaces .method(ElementMatchers.named("behaviorDelegate")) .intercept(FieldAccessor.ofField("behaviorHolder")) @@ -89,12 +93,21 @@ public final class BlockGenerator { // rotate .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$rotate)) .intercept(MethodDelegation.to(RotateInterceptor.INSTANCE)) + // hasAnalogOutputSignal + .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$hasAnalogOutputSignal)) + .intercept(MethodDelegation.to(HasAnalogOutputSignalInterceptor.INSTANCE)) + // getAnalogOutputSignal + .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$getAnalogOutputSignal)) + .intercept(MethodDelegation.to(GetAnalogOutputSignalInterceptor.INSTANCE)) // tick .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$tick)) .intercept(MethodDelegation.to(TickInterceptor.INSTANCE)) // isValidBoneMealTarget .method(ElementMatchers.is(CoreReflections.method$BonemealableBlock$isValidBonemealTarget)) .intercept(MethodDelegation.to(IsValidBoneMealTargetInterceptor.INSTANCE)) + // getContainer + .method(ElementMatchers.is(CoreReflections.method$WorldlyContainerHolder$getContainer)) + .intercept(MethodDelegation.to(GetContainerInterceptor.INSTANCE)) // isBoneMealSuccess .method(ElementMatchers.is(CoreReflections.method$BonemealableBlock$isBonemealSuccess)) .intercept(MethodDelegation.to(IsBoneMealSuccessInterceptor.INSTANCE)) @@ -149,7 +162,20 @@ public final class BlockGenerator { .intercept(MethodDelegation.to(PlayerWillDestroyInterceptor.INSTANCE)) // spawnAfterBreak .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$spawnAfterBreak)) - .intercept(MethodDelegation.to(SpawnAfterBreakInterceptor.INSTANCE)); + .intercept(MethodDelegation.to(SpawnAfterBreakInterceptor.INSTANCE)) + // fallOn + .method(ElementMatchers.is(CoreReflections.method$Block$fallOn)) + .intercept(MethodDelegation.to(FallOnInterceptor.INSTANCE)) + // updateEntityMovementAfterFallOn + .method(ElementMatchers.is(CoreReflections.method$Block$updateEntityMovementAfterFallOn)) + .intercept(MethodDelegation.to(UpdateEntityMovementAfterFallOnInterceptor.INSTANCE)) + // stepOn + .method(ElementMatchers.is(CoreReflections.method$Block$stepOn)) + .intercept(MethodDelegation.to(StepOnInterceptor.INSTANCE)) + // onProjectileHit + .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$onProjectileHit)) + .intercept(MethodDelegation.to(OnProjectileHitInterceptor.INSTANCE)) + ; // 1.21.5+ if (CoreReflections.method$BlockBehaviour$affectNeighborsAfterRemoval != null) { builder = builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$affectNeighborsAfterRemoval)) @@ -175,6 +201,30 @@ public final class BlockGenerator { field$CraftEngineBlock$isTripwire = clazz$CraftEngineBlock.getField("isClientSideTripwire"); } + public static DelegatingBlock generateBlock(Key blockId) throws Throwable { + ObjectHolder behaviorHolder = new ObjectHolder<>(EmptyBlockBehavior.INSTANCE); + ObjectHolder shapeHolder = new ObjectHolder<>(STONE_SHAPE); + Object newBlockInstance = constructor$CraftEngineBlock.invoke(createEmptyBlockProperties(blockId)); + field$CraftEngineBlock$behavior.set(newBlockInstance, behaviorHolder); + field$CraftEngineBlock$shape.set(newBlockInstance, shapeHolder); + Object stateDefinitionBuilder = CoreReflections.constructor$StateDefinition$Builder.newInstance(newBlockInstance); + Object stateDefinition = CoreReflections.method$StateDefinition$Builder$create.invoke(stateDefinitionBuilder, + (Function) FastNMS.INSTANCE::method$Block$defaultState, BlockStateGenerator.instance$StateDefinition$Factory); + CoreReflections.field$Block$StateDefinition.set(newBlockInstance, stateDefinition); + CoreReflections.field$Block$defaultBlockState.set(newBlockInstance, ((ImmutableList) CoreReflections.field$StateDefinition$states.get(stateDefinition)).getFirst()); + return (DelegatingBlock) newBlockInstance; + } + + private static Object createEmptyBlockProperties(Key id) throws ReflectiveOperationException { + Object blockProperties = CoreReflections.method$BlockBehaviour$Properties$of.invoke(null); + Object resourceLocation = KeyUtils.toResourceLocation(id); + Object resourceKey = FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.BLOCK, resourceLocation); + if (CoreReflections.field$BlockBehaviour$Properties$id != null) { + CoreReflections.field$BlockBehaviour$Properties$id.set(blockProperties, resourceKey); + } + return blockProperties; + } + public static Object generateBlock(Key replacedBlock, Object ownerBlock, Object properties) throws Throwable { Object ownerProperties = CoreReflections.field$BlockBehaviour$properties.get(ownerBlock); CoreReflections.field$BlockBehaviour$Properties$hasCollision.set(properties, CoreReflections.field$BlockBehaviour$Properties$hasCollision.get(ownerProperties)); @@ -441,6 +491,51 @@ public final class BlockGenerator { } } + public static class GetContainerInterceptor { + public static final GetContainerInterceptor INSTANCE = new GetContainerInterceptor(); + + @RuntimeType + public Object intercept(@This Object thisObj, @AllArguments Object[] args) { + ObjectHolder holder = ((DelegatingBlock) thisObj).behaviorDelegate(); + try { + return holder.value().getContainer(thisObj, args); + } catch (Exception e) { + CraftEngine.instance().logger().severe("Failed to run getContainer", e); + return null; + } + } + } + + public static class HasAnalogOutputSignalInterceptor { + public static final HasAnalogOutputSignalInterceptor INSTANCE = new HasAnalogOutputSignalInterceptor(); + + @RuntimeType + public boolean intercept(@This Object thisObj, @AllArguments Object[] args) { + ObjectHolder holder = ((DelegatingBlock) thisObj).behaviorDelegate(); + try { + return holder.value().hasAnalogOutputSignal(thisObj, args); + } catch (Exception e) { + CraftEngine.instance().logger().severe("Failed to run hasAnalogOutputSignal", e); + return false; + } + } + } + + public static class GetAnalogOutputSignalInterceptor { + public static final GetAnalogOutputSignalInterceptor INSTANCE = new GetAnalogOutputSignalInterceptor(); + + @RuntimeType + public int intercept(@This Object thisObj, @AllArguments Object[] args) { + ObjectHolder holder = ((DelegatingBlock) thisObj).behaviorDelegate(); + try { + return holder.value().getAnalogOutputSignal(thisObj, args); + } catch (Exception e) { + CraftEngine.instance().logger().severe("Failed to run getAnalogOutputSignal", e); + return 0; + } + } + } + public static class PerformBoneMealInterceptor { public static final PerformBoneMealInterceptor INSTANCE = new PerformBoneMealInterceptor(); @@ -476,7 +571,7 @@ public final class BlockGenerator { public void intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable superMethod) { ObjectHolder holder = ((DelegatingBlock) thisObj).behaviorDelegate(); try { - holder.value().onExplosionHit(thisObj, args, superMethod); + holder.value().onExplosionHit(thisObj, args, () -> null); superMethod.call(); } catch (Exception e) { CraftEngine.instance().logger().severe("Failed to run onExplosionHit", e); @@ -644,4 +739,68 @@ public final class BlockGenerator { } } } -} + + public static class StepOnInterceptor { + public static final StepOnInterceptor INSTANCE = new StepOnInterceptor(); + + @RuntimeType + public void intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable superMethod) { + ObjectHolder holder = ((DelegatingBlock) thisObj).behaviorDelegate(); + try { + holder.value().stepOn(thisObj, args, superMethod); + } catch (Exception e) { + CraftEngine.instance().logger().severe("Failed to run stepOn", e); + } + } + } + + public static class FallOnInterceptor { + public static final FallOnInterceptor INSTANCE = new FallOnInterceptor(); + + @RuntimeType + public void intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable superMethod) { + ObjectHolder holder = ((DelegatingBlock) thisObj).behaviorDelegate(); + try { + if (holder.value() instanceof FallOnBlockBehavior behavior) { + behavior.fallOn(thisObj, args, superMethod); + } else { + superMethod.call(); + } + } catch (Exception e) { + CraftEngine.instance().logger().severe("Failed to run fallOn", e); + } + } + } + + public static class UpdateEntityMovementAfterFallOnInterceptor { + public static final UpdateEntityMovementAfterFallOnInterceptor INSTANCE = new UpdateEntityMovementAfterFallOnInterceptor(); + + @RuntimeType + public void intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable superMethod) { + ObjectHolder holder = ((DelegatingBlock) thisObj).behaviorDelegate(); + try { + if (holder.value() instanceof FallOnBlockBehavior behavior) { + behavior.updateEntityMovementAfterFallOn(thisObj, args, superMethod); + } else { + superMethod.call(); + } + } catch (Exception e) { + CraftEngine.instance().logger().severe("Failed to run updateEntityMovementAfterFallOn", e); + } + } + } + + public static class OnProjectileHitInterceptor { + public static final OnProjectileHitInterceptor INSTANCE = new OnProjectileHitInterceptor(); + + @RuntimeType + public void intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable superMethod) { + ObjectHolder holder = ((DelegatingBlock) thisObj).behaviorDelegate(); + try { + holder.value().onProjectileHit(thisObj, args, superMethod); + } catch (Exception e) { + CraftEngine.instance().logger().severe("Failed to run onProjectileHit", e); + } + } + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockStateGenerator.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockStateGenerator.java index 131b1dc1e..2c2e4d036 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockStateGenerator.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockStateGenerator.java @@ -64,15 +64,17 @@ public final class BlockStateGenerator { .method(ElementMatchers.is(CoreReflections.method$StateHolder$getValue)) .intercept(MethodDelegation.to(GetPropertyValueInterceptor.INSTANCE)) .method(ElementMatchers.is(CoreReflections.method$StateHolder$setValue)) - .intercept(MethodDelegation.to(SetPropertyValueInterceptor.INSTANCE)); + .intercept(MethodDelegation.to(SetPropertyValueInterceptor.INSTANCE)) + .method(ElementMatchers.is(CoreReflections.method$BlockStateBase$isBlock)) + .intercept(MethodDelegation.to(IsBlockInterceptor.INSTANCE)); Class clazz$CraftEngineBlock = stateBuilder.make().load(BlockStateGenerator.class.getClassLoader()).getLoaded(); constructor$CraftEngineBlockState = VersionHelper.isOrAbove1_20_5() ? MethodHandles.publicLookup().in(clazz$CraftEngineBlock) - .findConstructor(clazz$CraftEngineBlock, MethodType.methodType(void.class, CoreReflections.clazz$Block, Reference2ObjectArrayMap.class, MapCodec.class)) - .asType(MethodType.methodType(CoreReflections.clazz$BlockState, CoreReflections.clazz$Block, Reference2ObjectArrayMap.class, MapCodec.class)) : + .findConstructor(clazz$CraftEngineBlock, MethodType.methodType(void.class, CoreReflections.clazz$Block, Reference2ObjectArrayMap.class, MapCodec.class)) + .asType(MethodType.methodType(CoreReflections.clazz$BlockState, CoreReflections.clazz$Block, Reference2ObjectArrayMap.class, MapCodec.class)) : MethodHandles.publicLookup().in(clazz$CraftEngineBlock) - .findConstructor(clazz$CraftEngineBlock, MethodType.methodType(void.class, CoreReflections.clazz$Block, ImmutableMap.class, MapCodec.class)) - .asType(MethodType.methodType(CoreReflections.clazz$BlockState, CoreReflections.clazz$Block, ImmutableMap.class, MapCodec.class)); + .findConstructor(clazz$CraftEngineBlock, MethodType.methodType(void.class, CoreReflections.clazz$Block, ImmutableMap.class, MapCodec.class)) + .asType(MethodType.methodType(CoreReflections.clazz$BlockState, CoreReflections.clazz$Block, ImmutableMap.class, MapCodec.class)); String generatedFactoryClassName = packageWithName.substring(0, packageWithName.lastIndexOf('.')) + ".CraftEngineStateFactory"; DynamicType.Builder factoryBuilder = byteBuddy @@ -121,8 +123,10 @@ public final class BlockStateGenerator { if (!item.isEmpty()) { lootBuilder.withParameter(DirectContextParameters.ITEM_IN_HAND, item); } - BukkitServerPlayer player = optionalPlayer != null ? BukkitCraftEngine.instance().adapt(FastNMS.INSTANCE.method$ServerPlayer$getBukkitEntity(optionalPlayer)) : null; + if (player != null) { + lootBuilder.withParameter(DirectContextParameters.PLAYER, player); + } Float radius = (Float) FastNMS.INSTANCE.method$LootParams$Builder$getOptionalParameter(builder, MLootContextParams.EXPLOSION_RADIUS); if (radius != null) { lootBuilder.withParameter(DirectContextParameters.EXPLOSION_RADIUS, radius); @@ -181,6 +185,23 @@ public final class BlockStateGenerator { } } + public static class IsBlockInterceptor { + public static final IsBlockInterceptor INSTANCE = new IsBlockInterceptor(); + + @RuntimeType + public boolean intercept(@This Object thisObj, @AllArguments Object[] args) { + DelegatingBlockState customState = (DelegatingBlockState) thisObj; + ImmutableBlockState thisState = customState.blockState(); + if (thisState == null) return false; + if (FastNMS.INSTANCE.method$Block$defaultState(args[0]) instanceof DelegatingBlockState holder) { + ImmutableBlockState holderState = holder.blockState(); + if (holderState == null) return false; + return holderState.owner().equals(thisState.owner()); + } + return false; + } + } + public static class CreateStateInterceptor { public static final CreateStateInterceptor INSTANCE = new CreateStateInterceptor(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java index 1149aa9b9..d474df0f8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java @@ -240,7 +240,7 @@ public final class RecipeInjector { if (input2.count() != 1 || !isDamageableItem(input2)) return false; if (!input1.id().equals(input2.id())) return false; Optional> customItem = input1.getCustomItem(); - return customItem.isEmpty() || customItem.get().settings().canRepair() != Tristate.FALSE; + return customItem.isEmpty() || customItem.get().settings().repairable().craftingTable() != Tristate.FALSE; } private static boolean isDamageableItem(Item item) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java index 89e4bfea1..b1e7d7ff1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java @@ -14,15 +14,16 @@ import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.matcher.ElementMatchers; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.util.BlockStateUtils; -import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.block.BlockStateWrapper; +import net.momirealms.craftengine.core.block.DelegatingBlockState; import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.ReflectionUtils; import net.momirealms.craftengine.core.util.SectionPosUtils; +import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.SectionPos; import net.momirealms.craftengine.core.world.chunk.CEChunk; @@ -33,7 +34,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.List; -import java.util.Optional; +import java.util.Objects; import java.util.concurrent.Callable; import java.util.function.Consumer; @@ -214,55 +215,104 @@ public final class WorldStorageInjector { } } + @SuppressWarnings("DuplicatedCode") private static void compareAndUpdateBlockState(int x, int y, int z, Object newState, Object previousState, InjectedHolder holder) { - try { - Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(newState); - CESection section = holder.ceSection(); - // 如果是原版方块 - if (optionalCustomState.isEmpty()) { - // 那么应该清空自定义块 - ImmutableBlockState previous = section.setBlockState(x, y, z, EmptyBlock.STATE); - // 处理 自定义块 -> 原版块 - if (!previous.isEmpty()) { - holder.ceChunk().setDirty(true); - if (Config.enableLightSystem()) { - // 自定义块到原版块,只需要判断旧块是否和客户端一直 - BlockStateWrapper wrapper = previous.vanillaBlockState(); - if (wrapper != null) { - updateLight(holder, wrapper.literalObject(), previousState, x, y, z); + CESection section = holder.ceSection(); + if (newState instanceof DelegatingBlockState delegatingBlockState) { + ImmutableBlockState newImmutableBlockState = delegatingBlockState.blockState(); + if (newImmutableBlockState == null) return; + ImmutableBlockState previousImmutableBlockState = section.setBlockState(x, y, z, newImmutableBlockState); + if (previousImmutableBlockState == newImmutableBlockState) return; + // 处理 自定义块到自定义块或原版块到自定义块 + CEChunk chunk = holder.ceChunk(); + chunk.setDirty(true); + // 如果两个方块没有相同的主人 且 旧方块有方块实体 + if (!previousImmutableBlockState.isEmpty()) { + if (previousImmutableBlockState.owner() != newImmutableBlockState.owner() && previousImmutableBlockState.hasBlockEntity()) { + BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); + BlockEntity blockEntity = chunk.getBlockEntity(pos, false); + if (blockEntity != null) { + try { + blockEntity.preRemove(); + } catch (Throwable t) { + CraftEngine.instance().logger().warn("Error removing block entity " + blockEntity.getClass().getName(), t); } + chunk.removeBlockEntity(pos); } } - } else { - ImmutableBlockState immutableBlockState = optionalCustomState.get(); - ImmutableBlockState previousImmutableBlockState = section.setBlockState(x, y, z, immutableBlockState); - if (previousImmutableBlockState == immutableBlockState) return; - // 处理 自定义块到自定义块或原版块到自定义块 - holder.ceChunk().setDirty(true); - // 不可能!绝对不可能! - if (immutableBlockState.isEmpty()) return; - // 如果新方块的光照属性和客户端认为的不同 + if (previousImmutableBlockState.hasConstantBlockEntityRenderer()) { + BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); + chunk.removeConstantBlockEntityRenderer(pos); + } + } + if (newImmutableBlockState.hasBlockEntity()) { + BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); + BlockEntity blockEntity = chunk.getBlockEntity(pos, false); + if (blockEntity != null && !blockEntity.isValidBlockState(newImmutableBlockState)) { + chunk.removeBlockEntity(pos); + blockEntity = null; + } + if (blockEntity == null) { + blockEntity = Objects.requireNonNull(newImmutableBlockState.behavior().getEntityBehavior()).createBlockEntity(pos, newImmutableBlockState); + if (blockEntity != null) { + chunk.addBlockEntity(blockEntity); + } + } else { + blockEntity.setBlockState(newImmutableBlockState); + // 方块类型未变,仅更新状态,选择性更新ticker + chunk.replaceOrCreateTickingBlockEntity(blockEntity); + chunk.createDynamicBlockEntityRenderer(blockEntity); + } + } + if (newImmutableBlockState.hasConstantBlockEntityRenderer()) { + BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); + chunk.addConstantBlockEntityRenderer(pos, newImmutableBlockState); + } + // 如果新方块的光照属性和客户端认为的不同 + if (Config.enableLightSystem()) { + if (previousImmutableBlockState.isEmpty()) { + // 原版块到自定义块,只需要判断新块是否和客户端视觉一致 + updateLight(holder, newImmutableBlockState.vanillaBlockState().literalObject(), newState, x, y, z); + } else { + // 自定义块到自定义块 + updateLight$complex(holder, newImmutableBlockState.vanillaBlockState().literalObject(), newState, previousState, x, y, z); + } + } + } else { + // 如果是原版方块 + // 那么应该清空自定义块 + ImmutableBlockState previous = section.setBlockState(x, y, z, EmptyBlock.STATE); + // 处理 自定义块 -> 原版块 + if (previous != null && !previous.isEmpty()) { + CEChunk chunk = holder.ceChunk(); + chunk.setDirty(true); + if (previous.hasBlockEntity()) { + BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); + BlockEntity blockEntity = chunk.getBlockEntity(pos, false); + if (blockEntity != null) { + blockEntity.preRemove(); + chunk.removeBlockEntity(pos); + } + } + if (previous.hasConstantBlockEntityRenderer()) { + BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); + chunk.removeConstantBlockEntityRenderer(pos); + } if (Config.enableLightSystem()) { - if (previousImmutableBlockState.isEmpty()) { - // 原版块到自定义块,只需要判断新块是否和客户端视觉一致 - updateLight(holder, immutableBlockState.vanillaBlockState().literalObject(), newState, x, y, z); - } else { - // 自定义块到自定义块 - updateLight$complex(holder, immutableBlockState.vanillaBlockState().literalObject(), newState, previousState, x, y, z); + // 自定义块到原版块,只需要判断旧块是否和客户端一直 + BlockStateWrapper wrapper = previous.vanillaBlockState(); + if (wrapper != null) { + updateLight(holder, wrapper.literalObject(), previousState, x, y, z); } } } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to intercept setBlockState", e); } } @SuppressWarnings("DuplicatedCode") - protected static void updateLight(@This InjectedHolder thisObj, Object clientState, Object serverState, int x, int y, int z) { - CEWorld world = thisObj.ceChunk().world(); - Object blockPos = LocationUtils.toBlockPos(x, y, z); - Object serverWorld = world.world().serverWorld(); - if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(serverState, clientState, serverWorld, blockPos)) { + private static void updateLight(@This InjectedHolder thisObj, Object clientState, Object serverState, int x, int y, int z) { + CEWorld world = thisObj.ceChunk().world; + if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(serverState, clientState)) { SectionPos sectionPos = thisObj.cePos(); List pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, 15); world.sectionLightUpdated(pos); @@ -270,18 +320,16 @@ public final class WorldStorageInjector { } @SuppressWarnings("DuplicatedCode") - protected static void updateLight$complex(@This InjectedHolder thisObj, Object newClientState, Object newServerState, Object oldServerState, int x, int y, int z) { - CEWorld world = thisObj.ceChunk().world(); - Object blockPos = LocationUtils.toBlockPos(x, y, z); - Object serverWorld = world.world().serverWorld(); + private static void updateLight$complex(@This InjectedHolder thisObj, Object newClientState, Object newServerState, Object oldServerState, int x, int y, int z) { + CEWorld world = thisObj.ceChunk().world; // 如果客户端新状态和服务端新状态光照属性不同 - if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(newClientState, newServerState, serverWorld, blockPos)) { + if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(newClientState, newServerState)) { SectionPos sectionPos = thisObj.cePos(); List pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, 15); world.sectionLightUpdated(pos); return; } - if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(newServerState, oldServerState, serverWorld, blockPos)) { + if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(newServerState, oldServerState)) { SectionPos sectionPos = thisObj.cePos(); List pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, 15); world.sectionLightUpdated(pos); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 8a3f5d23f..733013ca6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -1,29 +1,93 @@ package net.momirealms.craftengine.bukkit.plugin.network; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.mojang.authlib.GameProfile; +import com.mojang.datafixers.util.Either; import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.netty.channel.*; import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntList; +import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks; +import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; +import net.momirealms.craftengine.bukkit.api.event.FurnitureAttemptBreakEvent; +import net.momirealms.craftengine.bukkit.api.event.FurnitureBreakEvent; +import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent; +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; +import net.momirealms.craftengine.bukkit.entity.projectile.BukkitProjectileManager; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.item.behavior.FurnitureItemBehavior; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIdFinder; +import net.momirealms.craftengine.bukkit.plugin.injector.ProtectedFieldVisitor; +import net.momirealms.craftengine.bukkit.plugin.network.handler.*; import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIds1_20; import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIds1_20_5; +import net.momirealms.craftengine.bukkit.plugin.network.id.PlayPacketIdHelper; +import net.momirealms.craftengine.bukkit.plugin.network.listener.ByteBufferPacketListener; +import net.momirealms.craftengine.bukkit.plugin.network.listener.ByteBufferPacketListenerHolder; +import net.momirealms.craftengine.bukkit.plugin.network.listener.NMSPacketListener; +import net.momirealms.craftengine.bukkit.plugin.network.payload.DiscardedPayload; +import net.momirealms.craftengine.bukkit.plugin.network.payload.Payload; +import net.momirealms.craftengine.bukkit.plugin.network.payload.PayloadHelper; +import net.momirealms.craftengine.bukkit.plugin.network.payload.UnknownPayload; import net.momirealms.craftengine.bukkit.plugin.reflection.leaves.LeavesReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.LibraryReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.*; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.plugin.user.FakeBukkitServerPlayer; -import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.bukkit.util.*; +import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; +import net.momirealms.craftengine.core.advancement.network.AdvancementHolder; +import net.momirealms.craftengine.core.advancement.network.AdvancementProgress; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.font.FontManager; +import net.momirealms.craftengine.core.font.IllegalCharacterProcessResult; +import net.momirealms.craftengine.core.item.CustomItem; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.behavior.ItemBehavior; +import net.momirealms.craftengine.core.item.context.UseOnContext; +import net.momirealms.craftengine.core.item.recipe.network.legacy.LegacyRecipeHolder; +import net.momirealms.craftengine.core.item.recipe.network.modern.RecipeBookEntry; +import net.momirealms.craftengine.core.item.recipe.network.modern.display.RecipeDisplay; +import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData; +import net.momirealms.craftengine.core.pack.host.ResourcePackHost; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.CooldownData; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.plugin.network.*; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.*; -import org.bukkit.Bukkit; +import net.momirealms.craftengine.core.world.*; +import net.momirealms.craftengine.core.world.chunk.CEChunk; +import net.momirealms.craftengine.core.world.chunk.ChunkStatus; +import net.momirealms.craftengine.core.world.chunk.Palette; +import net.momirealms.craftengine.core.world.chunk.PalettedContainer; +import net.momirealms.craftengine.core.world.chunk.packet.BlockEntityData; +import net.momirealms.craftengine.core.world.chunk.packet.MCSection; +import net.momirealms.craftengine.core.world.collision.AABB; +import net.momirealms.sparrow.nbt.Tag; +import org.bukkit.*; +import org.bukkit.World; +import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -31,49 +95,33 @@ import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; import org.bukkit.persistence.PersistentDataType; import org.bukkit.plugin.messaging.PluginMessageListener; +import org.bukkit.util.RayTraceResult; +import org.bukkit.util.Vector; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; public class BukkitNetworkManager implements NetworkManager, Listener, PluginMessageListener { private static BukkitNetworkManager instance; - private static final Map, TriConsumer> NMS_PACKET_HANDLERS = new HashMap<>(); - // only for game stage for the moment - private static BiConsumer[] S2C_GAME_BYTE_BUFFER_PACKET_HANDLERS; - private static BiConsumer[] C2S_GAME_BYTE_BUFFER_PACKET_HANDLERS; + private final BukkitCraftEngine plugin; + private final Map, NMSPacketListener> nmsPacketListeners = new IdentityHashMap<>(128); - private static void registerNMSPacketConsumer(final TriConsumer function, @Nullable Class packet) { - if (packet == null) return; - NMS_PACKET_HANDLERS.put(packet, function); - } - - private static void registerS2CByteBufPacketConsumer(final BiConsumer function, int id) { - if (id == -1) return; - if (id < 0 || id >= S2C_GAME_BYTE_BUFFER_PACKET_HANDLERS.length) { - throw new IllegalArgumentException("Invalid packet id: " + id); - } - S2C_GAME_BYTE_BUFFER_PACKET_HANDLERS[id] = function; - } - - private static void registerC2SByteBufPacketConsumer(final BiConsumer function, int id) { - if (id == -1) return; - if (id < 0 || id >= C2S_GAME_BYTE_BUFFER_PACKET_HANDLERS.length) { - throw new IllegalArgumentException("Invalid packet id: " + id); - } - C2S_GAME_BYTE_BUFFER_PACKET_HANDLERS[id] = function; - } + private final ByteBufferPacketListenerHolder[] s2cGamePacketListeners; + private final ByteBufferPacketListenerHolder[] c2sGamePacketListeners; private final TriConsumer packetConsumer; private final TriConsumer, Object> packetsConsumer; private final TriConsumer immediatePacketConsumer; private final TriConsumer, Runnable> immediatePacketsConsumer; - private final BukkitCraftEngine plugin; private final Map users = new ConcurrentHashMap<>(); private final Map onlineUsers = new ConcurrentHashMap<>(); @@ -88,23 +136,23 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes private static final String PACKET_ENCODER = "craftengine_encoder"; private static final String PACKET_DECODER = "craftengine_decoder"; - private static boolean hasModelEngine; - private static boolean hasViaVersion; + private final boolean hasModelEngine; + + private int[] blockStateRemapper; + private int[] modBlockStateRemapper; @SuppressWarnings("unchecked") public BukkitNetworkManager(BukkitCraftEngine plugin) { instance = this; - S2C_GAME_BYTE_BUFFER_PACKET_HANDLERS = new BiConsumer[PacketIdFinder.s2cGamePackets()]; - C2S_GAME_BYTE_BUFFER_PACKET_HANDLERS = new BiConsumer[PacketIdFinder.c2sGamePackets()]; - Arrays.fill(S2C_GAME_BYTE_BUFFER_PACKET_HANDLERS, Handlers.DO_NOTHING); - Arrays.fill(C2S_GAME_BYTE_BUFFER_PACKET_HANDLERS, Handlers.DO_NOTHING); - hasModelEngine = Bukkit.getPluginManager().getPlugin("ModelEngine") != null; - hasViaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion") != null; + this.s2cGamePacketListeners = new ByteBufferPacketListenerHolder[PlayPacketIdHelper.count(PacketFlow.CLIENTBOUND)]; + this.c2sGamePacketListeners = new ByteBufferPacketListenerHolder[PlayPacketIdHelper.count(PacketFlow.SERVERBOUND)]; + this.hasModelEngine = Bukkit.getPluginManager().getPlugin("ModelEngine") != null; this.plugin = plugin; // set up packet id this.packetIds = VersionHelper.isOrAbove1_20_5() ? new PacketIds1_20_5() : new PacketIds1_20(); // register packet handlers - this.registerPacketHandlers(); + this.registerPacketListeners(); + PayloadHelper.registerDataTypes(); // set up packet senders this.packetConsumer = FastNMS.INSTANCE::method$Connection$send; this.packetsConsumer = ((connection, packets, sendListener) -> { @@ -154,6 +202,32 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes return instance; } + @Override + public int remapBlockState(int stateId, boolean enableMod) { + return enableMod ? this.modBlockStateRemapper[stateId] : this.blockStateRemapper[stateId]; + } + + private void registerNMSPacketConsumer(final NMSPacketListener listener, @Nullable Class packet) { + if (packet == null) return; + this.nmsPacketListeners.put(packet, listener); + } + + private void registerS2CGamePacketListener(final ByteBufferPacketListener listener, int id, String name) { + if (id == -1) return; + if (id < 0 || id >= this.s2cGamePacketListeners.length) { + throw new IllegalArgumentException("Invalid packet id: " + id); + } + this.s2cGamePacketListeners[id] = new ByteBufferPacketListenerHolder(name, listener); + } + + private void registerC2SGamePacketListener(final ByteBufferPacketListener listener, int id, String name) { + if (id == -1) return; + if (id < 0 || id >= this.c2sGamePacketListeners.length) { + throw new IllegalArgumentException("Invalid packet id: " + id); + } + this.c2sGamePacketListeners[id] = new ByteBufferPacketListenerHolder(name, listener); + } + public void addFakePlayer(Player player) { FakeBukkitServerPlayer fakePlayer = new FakeBukkitServerPlayer(this.plugin); fakePlayer.setPlayer(player); @@ -187,64 +261,145 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } } - private void registerPacketHandlers() { - registerNMSPacketConsumer(PacketConsumers.PLAYER_INFO_UPDATE, NetworkReflections.clazz$ClientboundPlayerInfoUpdatePacket); - registerNMSPacketConsumer(PacketConsumers.PLAYER_ACTION, NetworkReflections.clazz$ServerboundPlayerActionPacket); - registerNMSPacketConsumer(PacketConsumers.SWING_HAND, NetworkReflections.clazz$ServerboundSwingPacket); - registerNMSPacketConsumer(PacketConsumers.HELLO_C2S, NetworkReflections.clazz$ServerboundHelloPacket); - registerNMSPacketConsumer(PacketConsumers.USE_ITEM_ON, NetworkReflections.clazz$ServerboundUseItemOnPacket); - registerNMSPacketConsumer(PacketConsumers.PICK_ITEM_FROM_BLOCK, NetworkReflections.clazz$ServerboundPickItemFromBlockPacket); - registerNMSPacketConsumer(PacketConsumers.SET_CREATIVE_SLOT, NetworkReflections.clazz$ServerboundSetCreativeModeSlotPacket); - registerNMSPacketConsumer(PacketConsumers.LOGIN, NetworkReflections.clazz$ClientboundLoginPacket); - registerNMSPacketConsumer(PacketConsumers.RESPAWN, NetworkReflections.clazz$ClientboundRespawnPacket); - registerNMSPacketConsumer(PacketConsumers.SYNC_ENTITY_POSITION, NetworkReflections.clazz$ClientboundEntityPositionSyncPacket); - registerNMSPacketConsumer(PacketConsumers.PICK_ITEM_FROM_ENTITY, NetworkReflections.clazz$ServerboundPickItemFromEntityPacket); - registerNMSPacketConsumer(PacketConsumers.RENAME_ITEM, NetworkReflections.clazz$ServerboundRenameItemPacket); - registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, NetworkReflections.clazz$ServerboundSignUpdatePacket); - registerNMSPacketConsumer(PacketConsumers.EDIT_BOOK, NetworkReflections.clazz$ServerboundEditBookPacket); - registerNMSPacketConsumer(PacketConsumers.CUSTOM_PAYLOAD, NetworkReflections.clazz$ServerboundCustomPayloadPacket); - registerNMSPacketConsumer(PacketConsumers.RESOURCE_PACK_RESPONSE, NetworkReflections.clazz$ServerboundResourcePackPacket); - registerNMSPacketConsumer(PacketConsumers.ENTITY_EVENT, NetworkReflections.clazz$ClientboundEntityEventPacket); - registerNMSPacketConsumer(PacketConsumers.MOVE_POS_AND_ROTATE_ENTITY, NetworkReflections.clazz$ClientboundMoveEntityPacket$PosRot); - registerNMSPacketConsumer(PacketConsumers.MOVE_POS_ENTITY, NetworkReflections.clazz$ClientboundMoveEntityPacket$Pos); - registerNMSPacketConsumer(PacketConsumers.ROTATE_HEAD, NetworkReflections.clazz$ClientboundRotateHeadPacket); - registerNMSPacketConsumer(PacketConsumers.SET_ENTITY_MOTION, NetworkReflections.clazz$ClientboundSetEntityMotionPacket); - registerNMSPacketConsumer(PacketConsumers.FINISH_CONFIGURATION, NetworkReflections.clazz$ClientboundFinishConfigurationPacket); - registerNMSPacketConsumer(PacketConsumers.LOGIN_FINISHED, NetworkReflections.clazz$ClientboundLoginFinishedPacket); - registerNMSPacketConsumer(PacketConsumers.UPDATE_TAGS, NetworkReflections.clazz$ClientboundUpdateTagsPacket); - registerNMSPacketConsumer(PacketConsumers.CONTAINER_CLICK_1_21_5, VersionHelper.isOrAbove1_21_5() ? NetworkReflections.clazz$ServerboundContainerClickPacket : null); - registerS2CByteBufPacketConsumer(PacketConsumers.FORGET_LEVEL_CHUNK, this.packetIds.clientboundForgetLevelChunkPacket()); - registerS2CByteBufPacketConsumer(PacketConsumers.LEVEL_CHUNK_WITH_LIGHT, this.packetIds.clientboundLevelChunkWithLightPacket()); - registerS2CByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket()); - registerS2CByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket()); - registerS2CByteBufPacketConsumer(VersionHelper.isOrAbove1_21_4() ? PacketConsumers.LEVEL_PARTICLE_1_21_4 : (VersionHelper.isOrAbove1_20_5() ? PacketConsumers.LEVEL_PARTICLE_1_20_5 : PacketConsumers.LEVEL_PARTICLE_1_20), this.packetIds.clientboundLevelParticlesPacket()); - registerS2CByteBufPacketConsumer(PacketConsumers.LEVEL_EVENT, this.packetIds.clientboundLevelEventPacket()); - registerS2CByteBufPacketConsumer(VersionHelper.isOrAbove1_20_3() ? PacketConsumers.OPEN_SCREEN_1_20_3 : PacketConsumers.OPEN_SCREEN_1_20, this.packetIds.clientboundOpenScreenPacket()); - registerS2CByteBufPacketConsumer(VersionHelper.isOrAbove1_20_3() ? PacketConsumers.SET_TITLE_TEXT_1_20_3 : PacketConsumers.SET_TITLE_TEXT_1_20, this.packetIds.clientboundSetTitleTextPacket()); - registerS2CByteBufPacketConsumer(VersionHelper.isOrAbove1_20_3() ? PacketConsumers.SET_SUBTITLE_TEXT_1_20_3 : PacketConsumers.SET_SUBTITLE_TEXT_1_20, this.packetIds.clientboundSetSubtitleTextPacket()); - registerS2CByteBufPacketConsumer(VersionHelper.isOrAbove1_20_3() ? PacketConsumers.SET_ACTIONBAR_TEXT_1_20_3 : PacketConsumers.SET_ACTIONBAR_TEXT_1_20, this.packetIds.clientboundSetActionBarTextPacket()); - registerS2CByteBufPacketConsumer(VersionHelper.isOrAbove1_20_3() ? PacketConsumers.BOSS_EVENT_1_20_3 : PacketConsumers.BOSS_EVENT_1_20, this.packetIds.clientboundBossEventPacket()); - registerS2CByteBufPacketConsumer(VersionHelper.isOrAbove1_20_3() ? PacketConsumers.SYSTEM_CHAT_1_20_3 : PacketConsumers.SYSTEM_CHAT_1_20, this.packetIds.clientboundSystemChatPacket()); - registerS2CByteBufPacketConsumer(VersionHelper.isOrAbove1_20_3() ? PacketConsumers.TAB_LIST_1_20_3 : PacketConsumers.TAB_LIST_1_20, this.packetIds.clientboundTabListPacket()); - registerS2CByteBufPacketConsumer(VersionHelper.isOrAbove1_20_3() ? PacketConsumers.TEAM_1_20_3 : PacketConsumers.TEAM_1_20, this.packetIds.clientboundSetPlayerTeamPacket()); - registerS2CByteBufPacketConsumer(VersionHelper.isOrAbove1_20_3() ? PacketConsumers.SET_OBJECTIVE_1_20_3 : PacketConsumers.SET_OBJECTIVE_1_20, this.packetIds.clientboundSetObjectivePacket()); - registerS2CByteBufPacketConsumer(PacketConsumers.SET_SCORE_1_20_3, VersionHelper.isOrAbove1_20_3() ? this.packetIds.clientboundSetScorePacket() : -1); - registerS2CByteBufPacketConsumer(PacketConsumers.ADD_RECIPE_BOOK, this.packetIds.clientboundRecipeBookAddPacket()); - registerS2CByteBufPacketConsumer(PacketConsumers.PLACE_GHOST_RECIPE, this.packetIds.clientboundPlaceGhostRecipePacket()); - registerS2CByteBufPacketConsumer(PacketConsumers.UPDATE_RECIPES, this.packetIds.clientboundUpdateRecipesPacket()); - registerS2CByteBufPacketConsumer(PacketConsumers.UPDATE_ADVANCEMENTS, this.packetIds.clientboundUpdateAdvancementsPacket()); - registerS2CByteBufPacketConsumer(PacketConsumers.REMOVE_ENTITY, this.packetIds.clientboundRemoveEntitiesPacket()); - registerS2CByteBufPacketConsumer(PacketConsumers.ADD_ENTITY, this.packetIds.clientboundAddEntityPacket()); - registerS2CByteBufPacketConsumer(PacketConsumers.SOUND, this.packetIds.clientboundSoundPacket()); - registerS2CByteBufPacketConsumer(PacketConsumers.SET_ENTITY_DATA, this.packetIds.clientboundSetEntityDataPacket()); - registerS2CByteBufPacketConsumer(PacketConsumers.CONTAINER_SET_CONTENT, this.packetIds.clientboundContainerSetContentPacket()); - registerS2CByteBufPacketConsumer(PacketConsumers.CONTAINER_SET_SLOT, this.packetIds.clientboundContainerSetSlotPacket()); - registerS2CByteBufPacketConsumer(PacketConsumers.SET_CURSOR_ITEM, this.packetIds.clientboundSetCursorItemPacket()); - registerS2CByteBufPacketConsumer(PacketConsumers.SET_EQUIPMENT, this.packetIds.clientboundSetEquipmentPacket()); - registerS2CByteBufPacketConsumer(PacketConsumers.SET_PLAYER_INVENTORY_1_21_2, this.packetIds.clientboundSetPlayerInventoryPacket()); - registerC2SByteBufPacketConsumer(PacketConsumers.SET_CREATIVE_MODE_SLOT, this.packetIds.serverboundSetCreativeModeSlotPacket()); - registerC2SByteBufPacketConsumer(PacketConsumers.CONTAINER_CLICK_1_20, VersionHelper.isOrAbove1_21_5() ? -1 : this.packetIds.serverboundContainerClickPacket()); - registerC2SByteBufPacketConsumer(PacketConsumers.INTERACT_ENTITY, this.packetIds.serverboundInteractPacket()); + public void registerBlockStatePacketListeners(int[] blockStateMappings) { + int stoneId = BlockStateUtils.blockStateToId(MBlocks.STONE$defaultState); + int vanillaBlocks = BlockStateUtils.vanillaBlockStateCount(); + int[] newMappings = new int[blockStateMappings.length]; + int[] newMappingsMOD = new int[blockStateMappings.length]; + for (int i = 0; i < vanillaBlocks; i++) { + int mappedId = blockStateMappings[i]; + if (mappedId != -1) { + newMappings[i] = mappedId; + newMappingsMOD[i] = mappedId; + } else { + newMappings[i] = i; + newMappingsMOD[i] = i; + } + } + for (int i = vanillaBlocks; i < blockStateMappings.length; i++) { + int mappedId = blockStateMappings[i]; + if (mappedId != -1) { + newMappings[i] = mappedId; + } else { + newMappings[i] = stoneId; + } + newMappingsMOD[i] = i; + } + this.blockStateRemapper = newMappings; + this.modBlockStateRemapper = newMappingsMOD; + registerS2CGamePacketListener(new LevelChunkWithLightListener(newMappings, newMappingsMOD, newMappings.length, RegistryUtils.currentBiomeRegistrySize()), this.packetIds.clientboundLevelChunkWithLightPacket(), "ClientboundLevelChunkWithLightPacket"); + registerS2CGamePacketListener(new SectionBlockUpdateListener(newMappings, newMappingsMOD), this.packetIds.clientboundSectionBlocksUpdatePacket(), "ClientboundSectionBlocksUpdatePacket"); + registerS2CGamePacketListener(new BlockUpdateListener(newMappings, newMappingsMOD), this.packetIds.clientboundBlockUpdatePacket(), "ClientboundBlockUpdatePacket"); + registerS2CGamePacketListener( + VersionHelper.isOrAbove1_21_4() ? + new LevelParticleListener1_21_4(newMappings, newMappingsMOD) : + (VersionHelper.isOrAbove1_20_5() ? + new LevelParticleListener1_20_5(newMappings, newMappingsMOD) : + new LevelParticleListener1_20(newMappings, newMappingsMOD)), + this.packetIds.clientboundLevelParticlesPacket(), "ClientboundLevelParticlesPacket" + ); + registerS2CGamePacketListener(new LevelEventListener(newMappings, newMappingsMOD), this.packetIds.clientboundLevelEventPacket(), "ClientboundLevelEventPacket"); + } + + private void registerPacketListeners() { + registerNMSPacketConsumer(new PlayerInfoUpdateListener(), NetworkReflections.clazz$ClientboundPlayerInfoUpdatePacket); + registerNMSPacketConsumer(new PlayerActionListener(), NetworkReflections.clazz$ServerboundPlayerActionPacket); + registerNMSPacketConsumer(new SwingListener(), NetworkReflections.clazz$ServerboundSwingPacket); + registerNMSPacketConsumer(new HelloListener(), NetworkReflections.clazz$ServerboundHelloPacket); + registerNMSPacketConsumer(new UseItemOnListener(), NetworkReflections.clazz$ServerboundUseItemOnPacket); + registerNMSPacketConsumer(new PickItemFromBlockListener(), NetworkReflections.clazz$ServerboundPickItemFromBlockPacket); + registerNMSPacketConsumer(new PickItemFromEntityListener(), NetworkReflections.clazz$ServerboundPickItemFromEntityPacket); + registerNMSPacketConsumer(new SetCreativeSlotListener(), NetworkReflections.clazz$ServerboundSetCreativeModeSlotPacket); + registerNMSPacketConsumer(new LoginListener(), NetworkReflections.clazz$ClientboundLoginPacket); + registerNMSPacketConsumer(new RespawnListener(), NetworkReflections.clazz$ClientboundRespawnPacket); + registerNMSPacketConsumer(new SyncEntityPositionListener(), NetworkReflections.clazz$ClientboundEntityPositionSyncPacket); + registerNMSPacketConsumer(new RenameItemListener(), NetworkReflections.clazz$ServerboundRenameItemPacket); + registerNMSPacketConsumer(new SignUpdateListener(), NetworkReflections.clazz$ServerboundSignUpdatePacket); + registerNMSPacketConsumer(new EditBookListener(), NetworkReflections.clazz$ServerboundEditBookPacket); + registerNMSPacketConsumer(new CustomPayloadListener1_20_2(), VersionHelper.isOrAbove1_20_2() ? NetworkReflections.clazz$ServerboundCustomPayloadPacket : null); + registerNMSPacketConsumer(new ResourcePackResponseListener(), NetworkReflections.clazz$ServerboundResourcePackPacket); + registerNMSPacketConsumer(new EntityEventListener(), NetworkReflections.clazz$ClientboundEntityEventPacket); + registerNMSPacketConsumer(new MovePosAndRotateEntityListener(), NetworkReflections.clazz$ClientboundMoveEntityPacket$PosRot); + registerNMSPacketConsumer(new MovePosEntityListener(), NetworkReflections.clazz$ClientboundMoveEntityPacket$Pos); + registerNMSPacketConsumer(new RotateHeadListener(), NetworkReflections.clazz$ClientboundRotateHeadPacket); + registerNMSPacketConsumer(new SetEntityMotionListener(), NetworkReflections.clazz$ClientboundSetEntityMotionPacket); + registerNMSPacketConsumer(new FinishConfigurationListener(), NetworkReflections.clazz$ClientboundFinishConfigurationPacket); + registerNMSPacketConsumer(new LoginFinishedListener(), NetworkReflections.clazz$ClientboundLoginFinishedPacket); + registerNMSPacketConsumer(new UpdateTagsListener(), NetworkReflections.clazz$ClientboundUpdateTagsPacket); + registerNMSPacketConsumer(new ContainerClickListener1_21_5(), VersionHelper.isOrAbove1_21_5() ? NetworkReflections.clazz$ServerboundContainerClickPacket : null); + registerS2CGamePacketListener(new ForgetLevelChunkListener(), this.packetIds.clientboundForgetLevelChunkPacket(), "ClientboundForgetLevelChunkPacket"); + registerS2CGamePacketListener(new SetScoreListener1_20_3(), VersionHelper.isOrAbove1_20_3() ? this.packetIds.clientboundSetScorePacket() : -1, "ClientboundSetScorePacket"); + registerS2CGamePacketListener(new AddRecipeBookListener(), this.packetIds.clientboundRecipeBookAddPacket(), "ClientboundRecipeBookAddPacket"); + registerS2CGamePacketListener(new PlaceGhostRecipeListener(), this.packetIds.clientboundPlaceGhostRecipePacket(), "ClientboundPlaceGhostRecipePacket"); + registerS2CGamePacketListener(new UpdateRecipesListener(), this.packetIds.clientboundUpdateRecipesPacket(), "ClientboundUpdateRecipesPacket"); + registerS2CGamePacketListener(new UpdateAdvancementsListener(), this.packetIds.clientboundUpdateAdvancementsPacket(), "ClientboundUpdateAdvancementsPacket"); + registerS2CGamePacketListener(new RemoveEntityListener(), this.packetIds.clientboundRemoveEntitiesPacket(), "ClientboundRemoveEntitiesPacket"); + registerS2CGamePacketListener(new SoundListener(), this.packetIds.clientboundSoundPacket(), "ClientboundSoundPacket"); + registerS2CGamePacketListener(new ContainerSetContentListener(), this.packetIds.clientboundContainerSetContentPacket(), "ClientboundContainerSetContentPacket"); + registerS2CGamePacketListener(new ContainerSetSlotListener(), this.packetIds.clientboundContainerSetSlotPacket(), "ClientboundContainerSetSlotPacket"); + registerS2CGamePacketListener(new SetCursorItemListener(), this.packetIds.clientboundSetCursorItemPacket(), "ClientboundSetCursorItemPacket"); + registerS2CGamePacketListener(new SetEquipmentListener(), this.packetIds.clientboundSetEquipmentPacket(), "ClientboundSetEquipmentPacket"); + registerS2CGamePacketListener(new SetPlayerInventoryListener1_21_2(), VersionHelper.isOrAbove1_21_2() ? this.packetIds.clientboundSetPlayerInventoryPacket() : -1, "ClientboundSetPlayerInventoryPacket"); + registerS2CGamePacketListener(new SetEntityDataListener(), this.packetIds.clientboundSetEntityDataPacket(), "ClientboundSetEntityDataPacket"); + registerC2SGamePacketListener(new SetCreativeModeSlotListener(), this.packetIds.serverboundSetCreativeModeSlotPacket(), "ServerboundSetCreativeModeSlotPacket"); + registerC2SGamePacketListener(new ContainerClick1_20(), VersionHelper.isOrAbove1_21_5() ? -1 : this.packetIds.serverboundContainerClickPacket(), "ServerboundContainerClickPacket"); + registerC2SGamePacketListener(new InteractEntityListener(), this.packetIds.serverboundInteractPacket(), "ServerboundInteractPacket"); + registerC2SGamePacketListener(new CustomPayloadListener1_20(), VersionHelper.isOrAbove1_20_2() ? -1 : this.packetIds.serverboundCustomPayloadPacket(), "ServerboundCustomPayloadPacket"); + registerS2CGamePacketListener(new AddEntityListener(RegistryUtils.currentEntityTypeRegistrySize()), this.packetIds.clientboundAddEntityPacket(), "ClientboundAddEntityPacket"); + registerS2CGamePacketListener( + VersionHelper.isOrAbove1_20_3() ? + new OpenScreenListener1_20_3() : + new OpenScreenListener1_20(), + this.packetIds.clientboundOpenScreenPacket(), "ClientboundOpenScreenPacket" + ); + registerS2CGamePacketListener( + VersionHelper.isOrAbove1_20_3() ? + new SystemChatListener1_20_3() : + new SystemChatListener1_20(), + this.packetIds.clientboundSystemChatPacket(), "ClientboundSystemChatPacket" + ); + registerS2CGamePacketListener( + VersionHelper.isOrAbove1_20_3() ? + new SetActionBarListener1_20_3() : + new SetActionBarListener1_20(), + this.packetIds.clientboundSetActionBarTextPacket(), "ClientboundSetActionBarTextPacket" + ); + registerS2CGamePacketListener( + VersionHelper.isOrAbove1_20_3() ? + new TabListListener1_20_3() : + new TabListListener1_20(), + this.packetIds.clientboundTabListPacket(), "ClientboundTabListPacket" + ); + registerS2CGamePacketListener( + VersionHelper.isOrAbove1_20_3() ? + new SetTitleListener1_20_3() : + new SetTitleListener1_20(), + this.packetIds.clientboundSetTitleTextPacket(), "ClientboundSetTitleTextPacket" + ); + registerS2CGamePacketListener( + VersionHelper.isOrAbove1_20_3() ? + new SetSubtitleListener1_20_3() : + new SetSubtitleListener1_20(), + this.packetIds.clientboundSetSubtitleTextPacket(), "ClientboundSetSubtitleTextPacket" + ); + registerS2CGamePacketListener( + VersionHelper.isOrAbove1_20_3() ? + new BossEventListener1_20_3() : + new BossEventListener1_20(), + this.packetIds.clientboundBossEventPacket(), "ClientboundBossEventPacket" + ); + registerS2CGamePacketListener( + VersionHelper.isOrAbove1_20_3() ? + new TeamListener1_20_3() : + new TeamListener1_20(), + this.packetIds.clientboundSetPlayerTeamPacket(), "ClientboundSetPlayerTeamPacket" + ); + registerS2CGamePacketListener( + VersionHelper.isOrAbove1_20_3() ? + new SetObjectiveListener1_20_3() : + new SetObjectiveListener1_20(), + this.packetIds.clientboundSetObjectivePacket(), "ClientboundSetObjectivePacket" + ); } @EventHandler(priority = EventPriority.LOWEST) @@ -380,17 +535,13 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } } - public static boolean hasModelEngine() { + public boolean hasModelEngine() { return hasModelEngine; } - public static boolean hasViaVersion() { - return hasViaVersion; - } - public void simulatePacket(@NotNull NetWorkUser player, Object packet) { Channel channel = player.nettyChannel(); - if (channel.isOpen()) { + if (channel != null && channel.isOpen()) { List handlerNames = channel.pipeline().names(); if (handlerNames.contains("via-encoder")) { channel.pipeline().context("via-decoder").fireChannelRead(packet); @@ -523,9 +674,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes String encoderName = pipeline.names().contains("outbound_config") ? "outbound_config" : "encoder"; pipeline.addBefore(encoderName, PACKET_ENCODER, new PluginChannelEncoder(user)); - channel.closeFuture().addListener((ChannelFutureListener) future -> { - handleDisconnection(user.nettyChannel()); - }); + channel.closeFuture().addListener((ChannelFutureListener) future -> handleDisconnection(user.nettyChannel())); setUser(channel, user); } @@ -706,7 +855,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes private void onNMSPacketReceive(NetWorkUser user, NMSPacketEvent event, Object packet) { Debugger.PACKET.debug(() -> "[C->S]" + packet.getClass()); - handleNMSPacket(user, event, packet); + handleReceiveNMSPacket(user, event, packet); } private void onNMSPacketSend(NetWorkUser player, NMSPacketEvent event, Object packet) { @@ -717,25 +866,54 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } } else { Debugger.PACKET.debug(() -> "[S->C]" + packet.getClass()); - handleNMSPacket(player, event, packet); + handleSendNMSPacket(player, event, packet); } } - protected void handleNMSPacket(NetWorkUser user, NMSPacketEvent event, Object packet) { - Optional.ofNullable(NMS_PACKET_HANDLERS.get(packet.getClass())) - .ifPresent(function -> function.accept(user, event, packet)); + protected void handleReceiveNMSPacket(NetWorkUser user, NMSPacketEvent event, Object packet) { + NMSPacketListener nmsPacketListener = this.nmsPacketListeners.get(packet.getClass()); + if (nmsPacketListener != null) { + try { + nmsPacketListener.onPacketReceive(user, event, packet); + } catch (Throwable t) { + this.plugin.logger().warn("An error occurred when handling packet " + packet.getClass(), t); + } + } + } + + protected void handleSendNMSPacket(NetWorkUser user, NMSPacketEvent event, Object packet) { + NMSPacketListener nmsPacketListener = this.nmsPacketListeners.get(packet.getClass()); + if (nmsPacketListener != null) { + try { + nmsPacketListener.onPacketSend(user, event, packet); + } catch (Throwable t) { + this.plugin.logger().warn("An error occurred when handling packet " + packet.getClass(), t); + } + } } protected void handleS2CByteBufPacket(NetWorkUser user, ByteBufPacketEvent event) { int packetID = event.packetID(); - Optional.ofNullable(S2C_GAME_BYTE_BUFFER_PACKET_HANDLERS[packetID]) - .ifPresent(function -> function.accept(user, event)); + ByteBufferPacketListenerHolder holder = this.s2cGamePacketListeners[packetID]; + if (holder != null) { + try { + holder.listener().onPacketSend(user, event); + } catch (Throwable t) { + this.plugin.logger().warn("An error occurred when handling packet " + holder.id(), t); + } + } } protected void handleC2SByteBufPacket(NetWorkUser user, ByteBufPacketEvent event) { int packetID = event.packetID(); - Optional.ofNullable(C2S_GAME_BYTE_BUFFER_PACKET_HANDLERS[packetID]) - .ifPresent(function -> function.accept(user, event)); + ByteBufferPacketListenerHolder holder = this.c2sGamePacketListeners[packetID]; + if (holder != null) { + try { + holder.listener().onPacketReceive(user, event); + } catch (Throwable t) { + this.plugin.logger().warn("An error occurred when handling packet " + holder.id(), t); + } + } } private void compress(ChannelHandlerContext ctx, ByteBuf input) { @@ -754,7 +932,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes private void decompress(ChannelHandlerContext ctx, ByteBuf input, ByteBuf output) { ChannelHandler decompressor = ctx.pipeline().get("decompress"); if (decompressor != null) { - ByteBuf temp = (ByteBuf) callDecode(decompressor, ctx, input).get(0); + ByteBuf temp = (ByteBuf) callDecode(decompressor, ctx, input).getFirst(); try { output.clear().writeBytes(temp); } finally { @@ -781,13 +959,2755 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes return output; } - @FunctionalInterface - public interface Handlers extends BiConsumer { - Handlers DO_NOTHING = doNothing(); + /* + * + * + * Packet Listener Implementations + * + * + */ + public static class HelloListener implements NMSPacketListener { - static Handlers doNothing() { - return (user, byteBufPacketEvent) -> { + @SuppressWarnings("unchecked") + @Override + public void onPacketReceive(NetWorkUser user, NMSPacketEvent event, Object packet) { + BukkitServerPlayer player = (BukkitServerPlayer) user; + String name; + try { + name = (String) NetworkReflections.methodHandle$ServerboundHelloPacket$nameGetter.invokeExact(packet); + } catch (Throwable t) { + CraftEngine.instance().logger().severe("Failed to get name from ServerboundHelloPacket", t); + return; + } + player.setUnverifiedName(name); + if (VersionHelper.isOrAbove1_20_2()) { + UUID uuid; + try { + uuid = (UUID) NetworkReflections.methodHandle$ServerboundHelloPacket$uuidGetter.invokeExact(packet); + } catch (Throwable t) { + CraftEngine.instance().logger().severe("Failed to get uuid from ServerboundHelloPacket", t); + return; + } + player.setUnverifiedUUID(uuid); + } else { + Optional uuid; + try { + uuid = (Optional) NetworkReflections.methodHandle$ServerboundHelloPacket$uuidGetter.invokeExact(packet); + } catch (Throwable t) { + CraftEngine.instance().logger().severe("Failed to get uuid from ServerboundHelloPacket", t); + return; + } + if (uuid.isPresent()) { + player.setUnverifiedUUID(uuid.get()); + } else { + player.setUnverifiedUUID(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8))); + } + } + } + } + + public static class PlayerActionListener implements NMSPacketListener { + + @Override + public void onPacketReceive(NetWorkUser user, NMSPacketEvent event, Object packet) { + BukkitServerPlayer player = (BukkitServerPlayer) user; + Player platformPlayer = player.platformPlayer(); + World world = platformPlayer.getWorld(); + Object blockPos = FastNMS.INSTANCE.field$ServerboundPlayerActionPacket$pos(packet); + BlockPos pos = LocationUtils.fromBlockPos(blockPos); + if (VersionHelper.isFolia()) { + platformPlayer.getScheduler().run(BukkitCraftEngine.instance().javaPlugin(), (t) -> { + try { + handlePlayerActionPacketOnMainThread(player, world, pos, packet); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ServerboundPlayerActionPacket", e); + } + }, () -> {}); + } else { + handlePlayerActionPacketOnMainThread(player, world, pos, packet); + } + } + + private static void handlePlayerActionPacketOnMainThread(BukkitServerPlayer player, World world, BlockPos pos, Object packet) { + Object action = FastNMS.INSTANCE.field$ServerboundPlayerActionPacket$action(packet); + if (action == NetworkReflections.instance$ServerboundPlayerActionPacket$Action$START_DESTROY_BLOCK) { + Object serverLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(world); + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(serverLevel, LocationUtils.toBlockPos(pos)); + int stateId = BlockStateUtils.blockStateToId(blockState); + // not a custom block + if (BlockStateUtils.isVanillaBlock(stateId)) { + if (Config.enableSoundSystem()) { + Object soundType = FastNMS.INSTANCE.method$BlockBehaviour$BlockStateBase$getSoundType(blockState); + Object soundEvent = FastNMS.INSTANCE.field$SoundType$hitSound(soundType); + Object soundId = FastNMS.INSTANCE.field$SoundEvent$location(soundEvent); + if (BukkitBlockManager.instance().isHitSoundMissing(soundId)) { + player.startMiningBlock(pos, blockState, null); + return; + } + } + if (player.isMiningBlock()) { + player.stopMiningBlock(); + } else { + player.setClientSideCanBreakBlock(true); + } + return; + } + if (player.isAdventureMode()) { + if (Config.simplifyAdventureBreakCheck()) { + ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId); + if (!player.canBreak(pos, state.vanillaBlockState().literalObject())) { + player.preventMiningBlock(); + return; + } + } else { + if (!player.canBreak(pos, null)) { + player.preventMiningBlock(); + return; + } + } + } + player.startMiningBlock(pos, blockState, BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId)); + } else if (action == NetworkReflections.instance$ServerboundPlayerActionPacket$Action$ABORT_DESTROY_BLOCK) { + if (player.isMiningBlock()) { + player.abortMiningBlock(); + } + } else if (action == NetworkReflections.instance$ServerboundPlayerActionPacket$Action$STOP_DESTROY_BLOCK) { + if (player.isMiningBlock()) { + player.stopMiningBlock(); + } + } + } + } + + public static class SwingListener implements NMSPacketListener { + + @Override + public void onPacketReceive(NetWorkUser user, NMSPacketEvent event, Object packet) { + BukkitServerPlayer player = (BukkitServerPlayer) user; + if (!player.isMiningBlock()) return; + Object hand = FastNMS.INSTANCE.field$ServerboundSwingPacket$hand(packet); + if (hand == CoreReflections.instance$InteractionHand$MAIN_HAND) { + player.onSwingHand(); + } + } + } + + public static class UseItemOnListener implements NMSPacketListener { + + @Override + public void onPacketReceive(NetWorkUser user, NMSPacketEvent event, Object packet) { + BukkitServerPlayer player = (BukkitServerPlayer) user; + if (player.isMiningBlock()) { + player.stopMiningBlock(); + } + } + } + + public static class PlayerInfoUpdateListener implements NMSPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { + if (!Config.interceptPlayerInfo()) return; + List entries = FastNMS.INSTANCE.field$ClientboundPlayerInfoUpdatePacket$entries(packet); + if (entries instanceof MarkedArrayList) { + return; + } + EnumSet> enums = FastNMS.INSTANCE.field$ClientboundPlayerInfoUpdatePacket$actions(packet); + outer: { + for (Object entry : enums) { + if (entry == NetworkReflections.instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_DISPLAY_NAME) { + break outer; + } + } + return; + } + boolean isChanged = false; + List newEntries = new MarkedArrayList<>(); + for (Object entry : entries) { + Object mcComponent = FastNMS.INSTANCE.field$ClientboundPlayerInfoUpdatePacket$Entry$displayName(entry); + if (mcComponent == null) { + newEntries.add(entry); + } else { + String json = ComponentUtils.minecraftToJson(mcComponent); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); + if (tokens.isEmpty()) { + newEntries.add(entry); + } else { + Object newEntry = FastNMS.INSTANCE.constructor$ClientboundPlayerInfoUpdatePacket$Entry(entry, + ComponentUtils.adventureToMinecraft(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); + newEntries.add(newEntry); + isChanged = true; + } + } + } + if (isChanged) { + event.replacePacket(FastNMS.INSTANCE.constructor$ClientboundPlayerInfoUpdatePacket(enums, newEntries)); + } + } + } + + public static class PickItemFromBlockListener implements NMSPacketListener { + + @Override + public void onPacketReceive(NetWorkUser user, NMSPacketEvent event, Object packet) { + Player player = (Player) user.platformPlayer(); + if (player == null) return; + Object pos; + try { + pos = NetworkReflections.methodHandle$ServerboundPickItemFromBlockPacket$posGetter.invokeExact(packet); + } catch (Throwable e) { + CraftEngine.instance().logger().warn("Failed to get pos from ServerboundPickItemFromBlockPacket", e); + return; + } + if (VersionHelper.isFolia()) { + int x = FastNMS.INSTANCE.field$Vec3i$x(pos); + int z = FastNMS.INSTANCE.field$Vec3i$z(pos); + BukkitCraftEngine.instance().scheduler().sync().run(() -> { + try { + handlePickItemFromBlockPacketOnMainThread(player, pos); + } catch (Throwable e) { + CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromBlockPacket on region thread", e); + } + }, player.getWorld(), x >> 4, z >> 4); + } else { + BukkitCraftEngine.instance().scheduler().sync().run(() -> { + try { + handlePickItemFromBlockPacketOnMainThread(player, pos); + } catch (Throwable e) { + CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromBlockPacket on main thread", e); + } + }); + } + } + + private static void handlePickItemFromBlockPacketOnMainThread(Player player, Object pos) throws Throwable { + Object serverLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld()); + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(serverLevel, pos); + ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState)); + if (state == null) return; + Key itemId = state.settings().itemId(); + if (itemId == null) return; + pickItem(player, itemId, pos, null); + } + } + + public static class PickItemFromEntityListener implements NMSPacketListener { + + @Override + public void onPacketReceive(NetWorkUser user, NMSPacketEvent event, Object packet) { + int entityId; + try { + entityId = (int) NetworkReflections.methodHandle$ServerboundPickItemFromEntityPacket$idGetter.invokeExact(packet); + } catch (Throwable e) { + CraftEngine.instance().logger().warn("Failed to get entityId from ServerboundPickItemFromEntityPacket", e); + return; + } + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId); + if (furniture == null) return; + Player player = (Player) user.platformPlayer(); + if (player == null) return; + if (VersionHelper.isFolia()) { + player.getScheduler().run(BukkitCraftEngine.instance().javaPlugin(), (t) -> { + try { + handlePickItemFromEntityOnMainThread(player, furniture); + } catch (Throwable e) { + CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromEntityPacket on region thread", e); + } + }, () -> {}); + } else { + BukkitCraftEngine.instance().scheduler().sync().run(() -> { + try { + handlePickItemFromEntityOnMainThread(player, furniture); + } catch (Throwable e) { + CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromEntityPacket on main thread", e); + } + }); + } + } + + private static void handlePickItemFromEntityOnMainThread(Player player, BukkitFurniture furniture) throws Throwable { + Key itemId = furniture.config().settings().itemId(); + if (itemId == null) return; + pickItem(player, itemId, null, FastNMS.INSTANCE.method$CraftEntity$getHandle(furniture.baseEntity())); + } + } + + private static void pickItem(Player player, Key itemId, @Nullable Object blockPos, @Nullable Object entity) throws Throwable { + ItemStack itemStack = BukkitCraftEngine.instance().itemManager().buildCustomItemStack(itemId, BukkitCraftEngine.instance().adapt(player)); + if (itemStack == null) { + CraftEngine.instance().logger().warn("Item: " + itemId + " is not a valid item"); + return; + } + assert CoreReflections.method$ServerGamePacketListenerImpl$tryPickItem != null; + if (VersionHelper.isOrAbove1_21_5()) { + CoreReflections.method$ServerGamePacketListenerImpl$tryPickItem.invoke( + CoreReflections.methodHandle$ServerPlayer$connectionGetter.invokeExact(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)), + FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack), blockPos, entity, true); + } else { + CoreReflections.method$ServerGamePacketListenerImpl$tryPickItem.invoke( + CoreReflections.methodHandle$ServerPlayer$connectionGetter.invokeExact(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)), FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack)); + } + } + + + public static class SetCreativeSlotListener implements NMSPacketListener { + + @Override + public void onPacketReceive(NetWorkUser user, NMSPacketEvent event, Object packet) { + if (VersionHelper.isOrAbove1_21_4()) return; + if (!user.isOnline()) return; + BukkitServerPlayer player = (BukkitServerPlayer) user; + if (VersionHelper.isFolia()) { + player.platformPlayer().getScheduler().run(BukkitCraftEngine.instance().javaPlugin(), (t) -> { + try { + handleSetCreativeSlotPacketOnMainThread(player, packet); + } catch (Throwable e) { + CraftEngine.instance().logger().warn("Failed to handle ServerboundSetCreativeModeSlotPacket on region thread", e); + } + }, () -> {}); + } else { + try { + handleSetCreativeSlotPacketOnMainThread(player, packet); + } catch (Throwable e) { + CraftEngine.instance().logger().warn("Failed to handle ServerboundSetCreativeModeSlotPacket on main thread", e); + } + } + } + + private static void handleSetCreativeSlotPacketOnMainThread(BukkitServerPlayer player, Object packet) throws Throwable { + Player bukkitPlayer = player.platformPlayer(); + if (bukkitPlayer == null) return; + if (bukkitPlayer.getGameMode() != GameMode.CREATIVE) return; + int slot = VersionHelper.isOrAbove1_20_5() ? (short) NetworkReflections.methodHandle$ServerboundSetCreativeModeSlotPacket$slotNumGetter.invokeExact(packet) : (int) NetworkReflections.methodHandle$ServerboundSetCreativeModeSlotPacket$slotNumGetter.invokeExact(packet); + if (slot < 36 || slot > 44) return; + ItemStack item = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(NetworkReflections.methodHandle$ServerboundSetCreativeModeSlotPacket$itemStackGetter.invokeExact(packet)); + if (ItemStackUtils.isEmpty(item)) return; + if (slot - 36 != bukkitPlayer.getInventory().getHeldItemSlot()) { + return; + } + double interactionRange = player.getCachedInteractionRange(); + // do ray trace to get current block + RayTraceResult result = bukkitPlayer.rayTraceBlocks(interactionRange, FluidCollisionMode.NEVER); + if (result == null) return; + Block hitBlock = result.getHitBlock(); + if (hitBlock == null) return; + ImmutableBlockState state = CraftEngineBlocks.getCustomBlockState(hitBlock); + // not a custom block + if (state == null || state.isEmpty()) return; + Key itemId = state.settings().itemId(); + // no item available + if (itemId == null) return; + Object vanillaBlock = FastNMS.INSTANCE.method$BlockState$getBlock(state.vanillaBlockState().literalObject()); + Object vanillaBlockItem = FastNMS.INSTANCE.method$Block$asItem(vanillaBlock); + if (vanillaBlockItem == null) return; + Key addItemId = KeyUtils.namespacedKey2Key(item.getType().getKey()); + Key blockItemId = KeyUtils.resourceLocationToKey(FastNMS.INSTANCE.method$Registry$getKey(MBuiltInRegistries.ITEM, vanillaBlockItem)); + if (!addItemId.equals(blockItemId)) return; + ItemStack itemStack = BukkitCraftEngine.instance().itemManager().buildCustomItemStack(itemId, player); + if (ItemStackUtils.isEmpty(itemStack)) { + CraftEngine.instance().logger().warn("Item: " + itemId + " is not a valid item"); + return; + } + PlayerInventory inventory = bukkitPlayer.getInventory(); + int sameItemSlot = -1; + int emptySlot = -1; + for (int i = 0; i < 9 + 27; i++) { + ItemStack invItem = inventory.getItem(i); + if (ItemStackUtils.isEmpty(invItem)) { + if (emptySlot == -1 && i < 9) emptySlot = i; + continue; + } + if (invItem.getType().equals(itemStack.getType()) && invItem.getItemMeta().equals(itemStack.getItemMeta())) { + if (sameItemSlot == -1) sameItemSlot = i; + } + } + if (sameItemSlot != -1) { + if (sameItemSlot < 9) { + inventory.setHeldItemSlot(sameItemSlot); + ItemStack previousItem = inventory.getItem(slot - 36); + BukkitCraftEngine.instance().scheduler().sync().runDelayed(() -> inventory.setItem(slot - 36, previousItem)); + } else { + ItemStack sameItem = inventory.getItem(sameItemSlot); + int finalSameItemSlot = sameItemSlot; + BukkitCraftEngine.instance().scheduler().sync().runDelayed(() -> { + inventory.setItem(finalSameItemSlot, new ItemStack(Material.AIR)); + inventory.setItem(slot - 36, sameItem); + }); + } + } else { + if (item.getAmount() == 1) { + if (ItemStackUtils.isEmpty(inventory.getItem(slot - 36))) { + BukkitCraftEngine.instance().scheduler().sync().runDelayed(() -> inventory.setItem(slot - 36, itemStack)); + return; + } + if (emptySlot != -1) { + inventory.setHeldItemSlot(emptySlot); + inventory.setItem(emptySlot, itemStack); + } else { + BukkitCraftEngine.instance().scheduler().sync().runDelayed(() -> inventory.setItem(slot - 36, itemStack)); + } + } + } + } + } + + public static class LoginListener implements NMSPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { + BukkitServerPlayer player = (BukkitServerPlayer) user; + player.setConnectionState(ConnectionState.PLAY); + Object dimensionKey; + try { + if (!VersionHelper.isOrAbove1_20_2()) { + dimensionKey = NetworkReflections.methodHandle$ClientboundLoginPacket$dimensionGetter.invokeExact(packet); + } else { + Object commonInfo = NetworkReflections.methodHandle$ClientboundLoginPacket$commonPlayerSpawnInfoGetter.invokeExact(packet); + dimensionKey = NetworkReflections.methodHandle$CommonPlayerSpawnInfo$dimensionGetter.invokeExact(commonInfo); + } + } catch (Throwable t) { + CraftEngine.instance().logger().warn("Failed to get dimensionKey from ClientboundLoginPacket", t); + return; + } + Object location = FastNMS.INSTANCE.field$ResourceKey$location(dimensionKey); + World world = Bukkit.getWorld(Objects.requireNonNull(NamespacedKey.fromString(location.toString()))); + if (world != null) { + int sectionCount = (world.getMaxHeight() - world.getMinHeight()) / 16; + player.setClientSideSectionCount(sectionCount); + player.setClientSideDimension(Key.of(location.toString())); + } else { + CraftEngine.instance().logger().warn("Failed to handle ClientboundLoginPacket: World " + location + " does not exist"); + } + } + } + + public static class RespawnListener implements NMSPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { + BukkitServerPlayer player = (BukkitServerPlayer) user; + player.clearView(); + Object dimensionKey; + try { + if (!VersionHelper.isOrAbove1_20_2()) { + dimensionKey = NetworkReflections.methodHandle$ClientboundRespawnPacket$dimensionGetter.invokeExact(packet); + } else { + Object commonInfo = NetworkReflections.methodHandle$ClientboundRespawnPacket$commonPlayerSpawnInfoGetter.invokeExact(packet); + dimensionKey = NetworkReflections.methodHandle$CommonPlayerSpawnInfo$dimensionGetter.invokeExact(commonInfo); + } + } catch (Throwable t) { + CraftEngine.instance().logger().warn("Failed to get dimensionKey from ClientboundRespawnPacket", t); + return; + } + Object location = FastNMS.INSTANCE.field$ResourceKey$location(dimensionKey); + World world = Bukkit.getWorld(Objects.requireNonNull(NamespacedKey.fromString(location.toString()))); + if (world != null) { + int sectionCount = (world.getMaxHeight() - world.getMinHeight()) / 16; + player.setClientSideSectionCount(sectionCount); + player.setClientSideDimension(Key.of(location.toString())); + player.clearTrackedChunks(); + } else { + CraftEngine.instance().logger().warn("Failed to handle ClientboundRespawnPacket: World " + location + " does not exist"); + } + } + } + + // 1.21.2+ + public static class SyncEntityPositionListener implements NMSPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { + int entityId = FastNMS.INSTANCE.method$ClientboundEntityPositionSyncPacket$id(packet); + EntityPacketHandler handler = user.entityPacketHandlers().get(entityId); + if (handler != null) { + handler.handleSyncEntityPosition(user, event, packet); + } + } + } + + public static class RenameItemListener implements NMSPacketListener { + + @Override + public void onPacketReceive(NetWorkUser user, NMSPacketEvent event, Object packet) { + if (!Config.filterAnvil()) return; + if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_ANVIL)) { + return; + } + String message; + try { + message = (String) NetworkReflections.methodHandle$ServerboundRenameItemPacket$nameGetter.invokeExact(packet); + } catch (Throwable t) { + CraftEngine.instance().logger().warn("Failed to get message from ServerboundRenameItemPacket", t); + return; + } + if (message != null && !message.isEmpty()) { + // check bypass + FontManager manager = CraftEngine.instance().fontManager(); + IllegalCharacterProcessResult result = manager.processIllegalCharacters(message); + if (result.has()) { + try { + NetworkReflections.methodHandle$ServerboundRenameItemPacket$nameSetter.invokeExact(packet, result.text()); + } catch (Throwable e) { + CraftEngine.instance().logger().warn("Failed to set field 'name' for ServerboundRenameItemPacket", e); + } + } + } + } + } + + public static class SignUpdateListener implements NMSPacketListener { + + @Override + public void onPacketReceive(NetWorkUser user, NMSPacketEvent event, Object packet) { + if (!Config.filterSign()) return; + // check bypass + if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_SIGN)) { + return; + } + String[] lines; + try { + lines = (String[]) NetworkReflections.methodHandle$ServerboundSignUpdatePacket$linesGetter.invokeExact(packet); + } catch (Throwable t) { + CraftEngine.instance().logger().warn("Failed to get lines from ServerboundSignUpdatePacket", t); + return; + } + FontManager manager = CraftEngine.instance().fontManager(); + if (!manager.isDefaultFontInUse()) return; + for (int i = 0; i < lines.length; i++) { + String line = lines[i]; + if (line != null && !line.isEmpty()) { + IllegalCharacterProcessResult result = manager.processIllegalCharacters(line); + if (result.has()) { + lines[i] = result.text(); + } + } + } + } + } + + public static class EditBookListener implements NMSPacketListener { + + @SuppressWarnings("unchecked") + @Override + public void onPacketReceive(NetWorkUser user, NMSPacketEvent event, Object packet) { + if (!Config.filterBook()) return; + FontManager manager = CraftEngine.instance().fontManager(); + if (!manager.isDefaultFontInUse()) return; + // check bypass + if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_BOOK)) { + return; + } + + boolean changed = false; + + List pages; + try { + pages = (List) NetworkReflections.methodHandle$ServerboundEditBookPacket$pagesGetter.invokeExact(packet); + } catch (Throwable t) { + CraftEngine.instance().logger().warn("Failed to get pages from ServerboundEditBookPacket", t); + return; + } + List newPages = new ArrayList<>(pages.size()); + Optional title; + try { + title = (Optional) NetworkReflections.methodHandle$ServerboundEditBookPacket$titleGetter.invokeExact(packet); + } catch (Throwable t) { + CraftEngine.instance().logger().warn("Failed to get title from ServerboundEditBookPacket", t); + return; + } + Optional newTitle; + + if (title.isPresent()) { + String titleStr = title.get(); + Pair result = processClientString(titleStr, manager); + newTitle = Optional.of(result.right()); + if (result.left()) { + changed = true; + } + } else { + newTitle = Optional.empty(); + } + + for (String page : pages) { + Pair result = processClientString(page, manager); + newPages.add(result.right()); + if (result.left()) { + changed = true; + } + } + + if (changed) { + try { + Object newPacket = NetworkReflections.constructor$ServerboundEditBookPacket.newInstance( + (int) NetworkReflections.methodHandle$ServerboundEditBookPacket$slotGetter.invokeExact(packet), + newPages, + newTitle + ); + event.replacePacket(newPacket); + } catch (Throwable t) { + CraftEngine.instance().logger().warn("Failed to construct ServerboundEditBookPacket", t); + } + } + } + + private static Pair processClientString(String original, FontManager manager) { + if (original.isEmpty()) { + return Pair.of(false, original); + } + int[] codepoints = CharacterUtils.charsToCodePoints(original.toCharArray()); + int[] newCodepoints = new int[codepoints.length]; + boolean hasIllegal = false; + for (int i = 0; i < codepoints.length; i++) { + int codepoint = codepoints[i]; + if (manager.isIllegalCodepoint(codepoint)) { + newCodepoints[i] = '*'; + hasIllegal = true; + } else { + newCodepoints[i] = codepoint; + } + } + return hasIllegal ? Pair.of(true, new String(newCodepoints, 0, newCodepoints.length)) : Pair.of(false, original); + } + } + + public static class CustomPayloadListener1_20_2 implements NMSPacketListener { + + @Override + public void onPacketReceive(NetWorkUser user, NMSPacketEvent event, Object packet) { + if (!VersionHelper.isOrAbove1_20_2()) return; + Object payload; + try { + payload = NetworkReflections.methodHandle$ServerboundCustomPayloadPacket$payloadGetter.invokeExact(packet); + } catch (Throwable t) { + CraftEngine.instance().logger().warn("Failed to get payload from ServerboundCustomPayloadPacket", t); + return; + } + Payload clientPayload; + if (VersionHelper.isOrAbove1_20_5() && NetworkReflections.clazz$DiscardedPayload.isInstance(payload)) { + clientPayload = DiscardedPayload.from(payload); + } else if (!VersionHelper.isOrAbove1_20_5() && NetworkReflections.clazz$ServerboundCustomPayloadPacket$UnknownPayload.isInstance(payload)) { + clientPayload = UnknownPayload.from(payload); + } else { + return; + } + if (clientPayload == null || !clientPayload.channel().equals(NetworkManager.MOD_CHANNEL_KEY)) return; + PayloadHelper.handleReceiver(clientPayload, user); + } + } + + public static class ResourcePackResponseListener implements NMSPacketListener { + + @Override + public void onPacketReceive(NetWorkUser user, NMSPacketEvent event, Object packet) { + Object action = FastNMS.INSTANCE.field$ServerboundResourcePackPacket$action(packet); + + if (VersionHelper.isOrAbove1_20_3()) { + UUID uuid = FastNMS.INSTANCE.field$ServerboundResourcePackPacket$id(packet); + if (!user.isResourcePackLoading(uuid)) { + // 不是CraftEngine发送的资源包,不管 + return; + } + } + + if (action == null) { + user.kick(Component.text("Corrupted ResourcePackResponse Packet")); + return; + } + + // 检查是否是拒绝 + if (Config.kickOnDeclined()) { + if (action == NetworkReflections.instance$ServerboundResourcePackPacket$Action$DECLINED || action == NetworkReflections.instance$ServerboundResourcePackPacket$Action$DISCARDED) { + user.kick(Component.translatable("multiplayer.requiredTexturePrompt.disconnect")); + return; + } + } + + // 检查是否失败 + if (Config.kickOnFailedApply()) { + if (action == NetworkReflections.instance$ServerboundResourcePackPacket$Action$FAILED_DOWNLOAD + || (VersionHelper.isOrAbove1_20_3() && action == NetworkReflections.instance$ServerboundResourcePackPacket$Action$INVALID_URL)) { + user.kick(Component.translatable("multiplayer.requiredTexturePrompt.disconnect")); + return; + } + } + + boolean isTerminal = action != NetworkReflections.instance$ServerboundResourcePackPacket$Action$ACCEPTED && action != NetworkReflections.instance$ServerboundResourcePackPacket$Action$DOWNLOADED; + if (isTerminal && VersionHelper.isOrAbove1_20_2()) { + event.setCancelled(true); + Object packetListener = FastNMS.INSTANCE.method$Connection$getPacketListener(user.connection()); + if (!CoreReflections.clazz$ServerConfigurationPacketListenerImpl.isInstance(packetListener)) return; + // 主线程上处理这个包 + CraftEngine.instance().scheduler().executeSync(() -> { + try { + // 当客户端发出多次成功包的时候,finish会报错,我们忽略他 + NetworkReflections.methodHandle$ServerCommonPacketListener$handleResourcePackResponse.invokeExact(packetListener, packet); + CoreReflections.methodHandle$ServerConfigurationPacketListenerImpl$finishCurrentTask.invokeExact(packetListener, CoreReflections.instance$ServerResourcePackConfigurationTask$TYPE); + } catch (Throwable e) { + Debugger.RESOURCE_PACK.warn(() -> "Cannot finish current task", e); + } + }); + } + } + } + + public static class EntityEventListener implements NMSPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { + Object player = user.serverPlayer(); + if (player == null) return; + int entityId; + try { + entityId = (int) NetworkReflections.methodHandle$ClientboundEntityEventPacket$entityIdGetter.invokeExact(packet); + } catch (Throwable t) { + CraftEngine.instance().logger().warn("Failed to get entity id from ClientboundEntityEventPacket", t); + return; + } + if (entityId != FastNMS.INSTANCE.method$Entity$getId(player)) return; + byte eventId; + try { + eventId = (byte) NetworkReflections.methodHandle$ClientboundEntityEventPacket$eventIdGetter.invokeExact(packet); + } catch (Throwable t) { + CraftEngine.instance().logger().warn("Failed to get event id from ClientboundEntityEventPacket", t); + return; + } + if (eventId >= 24 && eventId <= 28) { + CraftEngine.instance().fontManager().refreshEmojiSuggestions(user.uuid()); + } + } + } + + public static class MovePosAndRotateEntityListener implements NMSPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { + int entityId = ProtectedFieldVisitor.get().field$ClientboundMoveEntityPacket$entityId(packet); + if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) { + event.setCancelled(true); + } + EntityPacketHandler handler = user.entityPacketHandlers().get(entityId); + if (handler != null) { + handler.handleMoveAndRotate(user, event, packet); + } + } + } + + public static class MovePosEntityListener implements NMSPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { + int entityId = ProtectedFieldVisitor.get().field$ClientboundMoveEntityPacket$entityId(packet); + EntityPacketHandler handler = user.entityPacketHandlers().get(entityId); + if (handler != null) { + handler.handleMove(user, event, packet); + } + } + } + + public static class RotateHeadListener implements NMSPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { + int entityId; + try { + entityId = (int) NetworkReflections.methodHandle$ClientboundRotateHeadPacket$entityIdGetter.invokeExact(packet); + } catch (Throwable t) { + CraftEngine.instance().logger().warn("Failed to get entity id from ClientboundRotateHeadPacket", t); + return; + } + if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) { + event.setCancelled(true); + } + } + } + + public static class SetEntityMotionListener implements NMSPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { + if (!VersionHelper.isOrAbove1_21_6()) return; + int entityId; + try { + entityId = (int) NetworkReflections.methodHandle$ClientboundSetEntityMotionPacket$idGetter.invokeExact(packet); + } catch (Throwable t) { + CraftEngine.instance().logger().warn("Failed to get entity id from ClientboundSetEntityMotionPacket", t); + return; + } + if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) { + event.setCancelled(true); + } + } + } + + public static class FinishConfigurationListener implements NMSPacketListener { + + @SuppressWarnings("unchecked") + @Override + public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { + if (!VersionHelper.isOrAbove1_20_2() || !Config.sendPackOnJoin()) { + // 防止后期调试进配置阶段造成问题 + user.setShouldProcessFinishConfiguration(false); + return; + } + + if (!user.shouldProcessFinishConfiguration()) return; + Object packetListener = FastNMS.INSTANCE.method$Connection$getPacketListener(user.connection()); + if (!CoreReflections.clazz$ServerConfigurationPacketListenerImpl.isInstance(packetListener)) { + return; + } + + // 防止后续加入的JoinWorldTask再次处理 + user.setShouldProcessFinishConfiguration(false); + + // 检查用户UUID是否已经校验 + if (!user.isUUIDVerified()) { + if (Config.strictPlayerUuidValidation()) { + TranslationManager.instance().log("warning.network.resource_pack.unverified_uuid", user.name(), user.uuid().toString()); + user.kick(Component.translatable("disconnect.loginFailedInfo").arguments(Component.translatable("argument.uuid.invalid"))); + return; + } + if (Config.debugResourcePack()) { + TranslationManager.instance().log("warning.network.resource_pack.unverified_uuid", user.name(), user.uuid().toString()); + } + } + + // 取消 ClientboundFinishConfigurationPacket,让客户端发呆,并结束掉当前的进入世界任务 + event.setCancelled(true); + try { + CoreReflections.methodHandle$ServerConfigurationPacketListenerImpl$finishCurrentTask.invokeExact(packetListener, CoreReflections.instance$JoinWorldTask$TYPE); + } catch (Throwable e) { + CraftEngine.instance().logger().warn("Failed to finish current task for " + user.name(), e); + } + + if (VersionHelper.isOrAbove1_20_5()) { + // 1.20.5+开始会检查是否结束需要重新设置回去,不然不会发keepAlive包 + try { + CoreReflections.methodHandle$ServerCommonPacketListenerImpl$closedSetter.invokeExact(packetListener, false); + } catch (Throwable e) { + CraftEngine.instance().logger().warn("Failed to set the 'closed' field of ServerCommonPacketListenerImpl for" + user.name(), e); + } + } + + // 请求资源包 + ResourcePackHost host = CraftEngine.instance().packManager().resourcePackHost(); + host.requestResourcePackDownloadLink(user.uuid()).whenComplete((dataList, t) -> { + if (t != null) { + CraftEngine.instance().logger().warn("Failed to get pack data for player " + user.name(), t); + FastNMS.INSTANCE.method$ServerConfigurationPacketListenerImpl$returnToWorld(packetListener); + return; + } + if (dataList.isEmpty()) { + FastNMS.INSTANCE.method$ServerConfigurationPacketListenerImpl$returnToWorld(packetListener); + return; + } + Queue configurationTasks; + try { + configurationTasks = (Queue) CoreReflections.methodHandle$ServerConfigurationPacketListenerImpl$configurationTasksGetter.invokeExact(packetListener); + } catch (Throwable e) { + CraftEngine.instance().logger().warn("Failed to get configuration tasks for player " + user.name(), e); + FastNMS.INSTANCE.method$ServerConfigurationPacketListenerImpl$returnToWorld(packetListener); + return; + } + // 向配置阶段连接的任务重加入资源包的任务 + for (ResourcePackDownloadData data : dataList) { + configurationTasks.add(FastNMS.INSTANCE.constructor$ServerResourcePackConfigurationTask(ResourcePackUtils.createServerResourcePackInfo(data.uuid(), data.url(), data.sha1()))); + user.addResourcePackUUID(data.uuid()); + } + // 最后再加入一个 JoinWorldTask 并开始资源包任务 + FastNMS.INSTANCE.method$ServerConfigurationPacketListenerImpl$returnToWorld(packetListener); + }); + } + } + + public static class LoginFinishedListener implements NMSPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { + GameProfile gameProfile = FastNMS.INSTANCE.field$ClientboundLoginFinishedPacket$gameProfile(packet); + if (VersionHelper.isOrAbove1_21_9()) { + user.setVerifiedName(gameProfile.name()); + user.setVerifiedUUID(gameProfile.id()); + } else { + user.setVerifiedName(LegacyAuthLibUtils.getName(gameProfile)); + user.setVerifiedUUID(LegacyAuthLibUtils.getId(gameProfile)); + } + } + } + + public static class UpdateTagsListener implements NMSPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { + Object modifiedPacket = BukkitBlockManager.instance().cachedUpdateTagsPacket(); + if (packet.equals(modifiedPacket) || modifiedPacket == null) return; + event.replacePacket(modifiedPacket); + } + } + + public static class ContainerClickListener1_21_5 implements NMSPacketListener { + + @Override + public void onPacketReceive(NetWorkUser user, NMSPacketEvent event, Object packet) { + BukkitServerPlayer player = (BukkitServerPlayer) user; + int containerId = FastNMS.INSTANCE.field$ServerboundContainerClickPacket$containerId(packet); + int stateId = FastNMS.INSTANCE.field$ServerboundContainerClickPacket$stateId(packet); + short slotNum = FastNMS.INSTANCE.field$ServerboundContainerClickPacket$slotNum(packet); + byte buttonNum = FastNMS.INSTANCE.field$ServerboundContainerClickPacket$buttonNum(packet); + Object clickType = FastNMS.INSTANCE.field$ServerboundContainerClickPacket$clickType(packet); + @SuppressWarnings("unchecked") + Int2ObjectMap changedSlots = FastNMS.INSTANCE.field$ServerboundContainerClickPacket$changedSlots(packet); + Int2ObjectMap newChangedSlots = new Int2ObjectOpenHashMap<>(changedSlots.size()); + for (Int2ObjectMap.Entry entry : changedSlots.int2ObjectEntrySet()) { + newChangedSlots.put(entry.getIntKey(), FastNMS.INSTANCE.constructor$InjectedHashedStack(entry.getValue(), player)); + } + Object carriedItem = FastNMS.INSTANCE.constructor$InjectedHashedStack(FastNMS.INSTANCE.field$ServerboundContainerClickPacket$carriedItem(packet), player); + event.replacePacket(FastNMS.INSTANCE.constructor$ServerboundContainerClickPacket(containerId, stateId, slotNum, buttonNum, clickType, Int2ObjectMaps.unmodifiable(newChangedSlots), carriedItem)); + } + } + + public static class ForgetLevelChunkListener implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + BukkitServerPlayer player = (BukkitServerPlayer) user; + FriendlyByteBuf buf = event.getBuffer(); + CEWorld ceWorld = BukkitWorldManager.instance().getWorld(player.world().uuid()); + if (VersionHelper.isOrAbove1_20_2()) { + long chunkPos = buf.readLong(); + user.removeTrackedChunk(chunkPos); + CEChunk ceChunk = ceWorld.getChunkAtIfLoaded(chunkPos); + if (ceChunk != null) { + ceChunk.despawnBlockEntities(player); + } + } else { + int x = buf.readInt(); + int y = buf.readInt(); + user.removeTrackedChunk(ChunkPos.asLong(x, y)); + CEChunk ceChunk = ceWorld.getChunkAtIfLoaded(x, y); + if (ceChunk != null) { + ceChunk.despawnBlockEntities(player); + } + } + } + } + + public static class LevelChunkWithLightListener implements ByteBufferPacketListener { + private static BiConsumer> biomeRemapper = null; + + public static void setBiomeRemapper(BiConsumer> remapper) { + biomeRemapper = remapper; + } + + public static void remapBiomes(NetWorkUser user, PalettedContainer biomes) { + if (biomeRemapper != null) { + biomeRemapper.accept(user, biomes); + } + } + + private final int[] blockStateMapper; + private final int[] modBlockStateMapper; + private final IntIdentityList biomeList; + private final IntIdentityList blockList; + + public LevelChunkWithLightListener(int[] blockStateMapper, int[] modBlockStateMapper, int blockRegistrySize, int biomeRegistrySize) { + this.blockStateMapper = blockStateMapper; + this.modBlockStateMapper = modBlockStateMapper; + this.biomeList = new IntIdentityList(biomeRegistrySize); + this.blockList = new IntIdentityList(blockRegistrySize); + } + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + BukkitServerPlayer player = (BukkitServerPlayer) user; + FriendlyByteBuf buf = event.getBuffer(); + int chunkX = buf.readInt(); + int chunkZ = buf.readInt(); + boolean named = !VersionHelper.isOrAbove1_20_2(); + // ClientboundLevelChunkPacketData + int heightmapsCount = 0; + Map heightmapsMap = null; + net.momirealms.sparrow.nbt.Tag heightmaps = null; + if (VersionHelper.isOrAbove1_21_5()) { + heightmapsMap = new HashMap<>(); + heightmapsCount = buf.readVarInt(); + for (int i = 0; i < heightmapsCount; i++) { + int key = buf.readVarInt(); + long[] value = buf.readLongArray(); + heightmapsMap.put(key, value); + } + } else { + heightmaps = buf.readNbt(named); + } + + int varInt = buf.readVarInt(); + byte[] buffer = new byte[varInt]; + buf.readBytes(buffer); + int blockEntitiesDataCount = buf.readVarInt(); + List blockEntitiesData = new ArrayList<>(); + for (int i = 0; i < blockEntitiesDataCount; i++) { + byte packedXZ = buf.readByte(); + short y = buf.readShort(); + int type = buf.readVarInt(); + Tag tag = buf.readNbt(named); + BlockEntityData blockEntityData = new BlockEntityData(packedXZ, y, type, tag); + blockEntitiesData.add(blockEntityData); + } + // ClientboundLightUpdatePacketData + BitSet skyYMask = buf.readBitSet(); + BitSet blockYMask = buf.readBitSet(); + BitSet emptySkyYMask = buf.readBitSet(); + BitSet emptyBlockYMask = buf.readBitSet(); + List skyUpdates = buf.readByteArrayList(2048); + List blockUpdates = buf.readByteArrayList(2048); + // 开始处理 + if (user.clientModEnabled()) { + ByteBuf byteBuf = Unpooled.copiedBuffer(buffer); + FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(byteBuf); + FriendlyByteBuf newBuf = new FriendlyByteBuf(Unpooled.buffer()); + for (int i = 0, count = player.clientSideSectionCount(); i < count; i++) { + MCSection mcSection = new MCSection(user.clientBlockList(), this.blockList, this.biomeList); + mcSection.readPacket(friendlyByteBuf); + PalettedContainer container = mcSection.blockStateContainer(); + remapBiomes(user, mcSection.biomeContainer()); + Palette palette = container.data().palette(); + if (palette.canRemap()) { + palette.remap(s -> this.modBlockStateMapper[s]); + } else { + for (int j = 0; j < 4096; j++) { + int state = container.get(j); + int newState = this.modBlockStateMapper[state]; + if (newState != state) { + container.set(j, newState); + } + } + } + mcSection.writePacket(newBuf); + } + buffer = newBuf.array(); + } else { + ByteBuf byteBuf = Unpooled.copiedBuffer(buffer); + FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(byteBuf); + FriendlyByteBuf newBuf = new FriendlyByteBuf(Unpooled.buffer()); + for (int i = 0, count = player.clientSideSectionCount(); i < count; i++) { + MCSection mcSection = new MCSection(user.clientBlockList(), this.blockList, this.biomeList); + mcSection.readPacket(friendlyByteBuf); + PalettedContainer container = mcSection.blockStateContainer(); + remapBiomes(user, mcSection.biomeContainer()); + Palette palette = container.data().palette(); + if (palette.canRemap()) { + palette.remap(s -> this.blockStateMapper[s]); + } else { + for (int j = 0; j < 4096; j++) { + int state = container.get(j); + int newState = this.blockStateMapper[state]; + if (newState != state) { + container.set(j, newState); + } + } + } + mcSection.writePacket(newBuf); + } + buffer = newBuf.array(); + } + + // 开始修改 + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeInt(chunkX); + buf.writeInt(chunkZ); + if (VersionHelper.isOrAbove1_21_5()) { + buf.writeVarInt(heightmapsCount); + for (Map.Entry entry : heightmapsMap.entrySet()) { + buf.writeVarInt(entry.getKey()); + buf.writeLongArray(entry.getValue()); + } + } else { + buf.writeNbt(heightmaps, named); + } + buf.writeVarInt(buffer.length); + buf.writeBytes(buffer); + buf.writeVarInt(blockEntitiesDataCount); + for (BlockEntityData blockEntityData : blockEntitiesData) { + buf.writeByte(blockEntityData.packedXZ()); + buf.writeShort(blockEntityData.y()); + buf.writeVarInt(blockEntityData.type()); + buf.writeNbt(blockEntityData.tag(), named); + } + buf.writeBitSet(skyYMask); + buf.writeBitSet(blockYMask); + buf.writeBitSet(emptySkyYMask); + buf.writeBitSet(emptyBlockYMask); + buf.writeByteArrayList(skyUpdates); + buf.writeByteArrayList(blockUpdates); + + ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); + // 记录加载的区块 + player.addTrackedChunk(chunkPos.longKey, new ChunkStatus()); + + CEWorld ceWorld = BukkitWorldManager.instance().getWorld(player.world().uuid()); + CEChunk ceChunk = ceWorld.getChunkAtIfLoaded(chunkPos.longKey); + if (ceChunk != null) { + ceChunk.spawnBlockEntities(player); + } + } + } + + public static class SectionBlockUpdateListener implements ByteBufferPacketListener { + private final int[] blockStateMapper; + private final int[] modBlockStateMapper; + + public SectionBlockUpdateListener(int[] blockStateMapper, int[] modBlockStateMapper) { + this.blockStateMapper = blockStateMapper; + this.modBlockStateMapper = modBlockStateMapper; + } + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (user.clientModEnabled()) { + FriendlyByteBuf buf = event.getBuffer(); + long pos = buf.readLong(); + int blocks = buf.readVarInt(); + short[] positions = new short[blocks]; + int[] states = new int[blocks]; + for (int i = 0; i < blocks; i++) { + long k = buf.readVarLong(); + positions[i] = (short) ((int) (k & 4095L)); + states[i] = modBlockStateMapper[((int) (k >>> 12))]; + } + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeLong(pos); + buf.writeVarInt(blocks); + for (int i = 0; i < blocks; i++) { + buf.writeVarLong((long) states[i] << 12 | positions[i]); + } + event.setChanged(true); + } else { + FriendlyByteBuf buf = event.getBuffer(); + long pos = buf.readLong(); + int blocks = buf.readVarInt(); + short[] positions = new short[blocks]; + int[] states = new int[blocks]; + for (int i = 0; i < blocks; i++) { + long k = buf.readVarLong(); + positions[i] = (short) ((int) (k & 4095L)); + states[i] = blockStateMapper[((int) (k >>> 12))]; + } + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeLong(pos); + buf.writeVarInt(blocks); + for (int i = 0; i < blocks; i++) { + buf.writeVarLong((long) states[i] << 12 | positions[i]); + } + event.setChanged(true); + } + } + } + + public static class BlockUpdateListener implements ByteBufferPacketListener { + private final int[] blockStateMapper; + private final int[] modBlockStateMapper; + + public BlockUpdateListener(int[] blockStateMapper, int[] modBlockStateMapper) { + this.blockStateMapper = blockStateMapper; + this.modBlockStateMapper = modBlockStateMapper; + } + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + FriendlyByteBuf buf = event.getBuffer(); + BlockPos pos = buf.readBlockPos(); + int before = buf.readVarInt(); + if (user.clientModEnabled() && !BlockStateUtils.isVanillaBlock(before)) { + return; + } + int state = user.clientModEnabled() ? modBlockStateMapper[before] : blockStateMapper[before]; + if (state == before) { + return; + } + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeBlockPos(pos); + buf.writeVarInt(state); + } + } + + public static class LevelParticleListener1_21_4 implements ByteBufferPacketListener { + private final int[] blockStateMapper; + private final int[] modBlockStateMapper; + + public LevelParticleListener1_21_4(int[] blockStateMapper, int[] modBlockStateMapper) { + this.blockStateMapper = blockStateMapper; + this.modBlockStateMapper = modBlockStateMapper; + } + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + FriendlyByteBuf buf = event.getBuffer(); + boolean overrideLimiter = buf.readBoolean(); + boolean alwaysShow = buf.readBoolean(); + double x = buf.readDouble(); + double y = buf.readDouble(); + double z = buf.readDouble(); + float xDist = buf.readFloat(); + float yDist = buf.readFloat(); + float zDist = buf.readFloat(); + float maxSpeed = buf.readFloat(); + int count = buf.readInt(); + Object option = FastNMS.INSTANCE.method$StreamCodec$decode(NetworkReflections.instance$ParticleTypes$STREAM_CODEC, buf); + if (option == null) return; + if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return; + Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option); + int id = BlockStateUtils.blockStateToId(blockState); + int remapped = user.clientModEnabled() ? modBlockStateMapper[id] : blockStateMapper[id]; + if (remapped == id) return; + Object type = FastNMS.INSTANCE.method$BlockParticleOption$getType(option); + Object remappedOption = FastNMS.INSTANCE.constructor$BlockParticleOption(type, BlockStateUtils.idToBlockState(remapped)); + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeBoolean(overrideLimiter); + buf.writeBoolean(alwaysShow); + buf.writeDouble(x); + buf.writeDouble(y); + buf.writeDouble(z); + buf.writeFloat(xDist); + buf.writeFloat(yDist); + buf.writeFloat(zDist); + buf.writeFloat(maxSpeed); + buf.writeInt(count); + FastNMS.INSTANCE.method$StreamCodec$encode(NetworkReflections.instance$ParticleTypes$STREAM_CODEC, buf, remappedOption); + } + } + + public static class LevelParticleListener1_20_5 implements ByteBufferPacketListener { + private final int[] blockStateMapper; + private final int[] modBlockStateMapper; + + public LevelParticleListener1_20_5(int[] blockStateMapper, int[] modBlockStateMapper) { + this.blockStateMapper = blockStateMapper; + this.modBlockStateMapper = modBlockStateMapper; + } + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + FriendlyByteBuf buf = event.getBuffer(); + boolean overrideLimiter = buf.readBoolean(); + double x = buf.readDouble(); + double y = buf.readDouble(); + double z = buf.readDouble(); + float xDist = buf.readFloat(); + float yDist = buf.readFloat(); + float zDist = buf.readFloat(); + float maxSpeed = buf.readFloat(); + int count = buf.readInt(); + Object option = FastNMS.INSTANCE.method$StreamCodec$decode(NetworkReflections.instance$ParticleTypes$STREAM_CODEC, buf); + if (option == null) return; + if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return; + Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option); + int id = BlockStateUtils.blockStateToId(blockState); + int remapped = user.clientModEnabled() ? modBlockStateMapper[id] : blockStateMapper[id]; + if (remapped == id) return; + Object type = FastNMS.INSTANCE.method$BlockParticleOption$getType(option); + Object remappedOption = FastNMS.INSTANCE.constructor$BlockParticleOption(type, BlockStateUtils.idToBlockState(remapped)); + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeBoolean(overrideLimiter); + buf.writeDouble(x); + buf.writeDouble(y); + buf.writeDouble(z); + buf.writeFloat(xDist); + buf.writeFloat(yDist); + buf.writeFloat(zDist); + buf.writeFloat(maxSpeed); + buf.writeInt(count); + FastNMS.INSTANCE.method$StreamCodec$encode(NetworkReflections.instance$ParticleTypes$STREAM_CODEC, buf, remappedOption); + } + } + + public static class LevelParticleListener1_20 implements ByteBufferPacketListener { + private final int[] blockStateMapper; + private final int[] modBlockStateMapper; + + public LevelParticleListener1_20(int[] blockStateMapper, int[] modBlockStateMapper) { + this.blockStateMapper = blockStateMapper; + this.modBlockStateMapper = modBlockStateMapper; + } + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + FriendlyByteBuf buf = event.getBuffer(); + Object particleType = FastNMS.INSTANCE.method$FriendlyByteBuf$readById(buf, MBuiltInRegistries.PARTICLE_TYPE); + boolean overrideLimiter = buf.readBoolean(); + double x = buf.readDouble(); + double y = buf.readDouble(); + double z = buf.readDouble(); + float xDist = buf.readFloat(); + float yDist = buf.readFloat(); + float zDist = buf.readFloat(); + float maxSpeed = buf.readFloat(); + int count = buf.readInt(); + Object option = FastNMS.INSTANCE.method$ClientboundLevelParticlesPacket$readParticle(buf, particleType); + if (option == null) return; + if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return; + Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option); + int id = BlockStateUtils.blockStateToId(blockState); + int remapped = user.clientModEnabled() ? modBlockStateMapper[id] : blockStateMapper[id]; + if (remapped == id) return; + Object type = FastNMS.INSTANCE.method$BlockParticleOption$getType(option); + Object remappedOption = FastNMS.INSTANCE.constructor$BlockParticleOption(type, BlockStateUtils.idToBlockState(remapped)); + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + FastNMS.INSTANCE.method$FriendlyByteBuf$writeId(buf, remappedOption, MBuiltInRegistries.PARTICLE_TYPE); + buf.writeBoolean(overrideLimiter); + buf.writeDouble(x); + buf.writeDouble(y); + buf.writeDouble(z); + buf.writeFloat(xDist); + buf.writeFloat(yDist); + buf.writeFloat(zDist); + buf.writeFloat(maxSpeed); + buf.writeInt(count); + FastNMS.INSTANCE.method$ParticleOptions$writeToNetwork(remappedOption, buf); + } + } + + public static class LevelEventListener implements ByteBufferPacketListener { + private final int[] blockStateMapper; + private final int[] modBlockStateMapper; + + public LevelEventListener(int[] blockStateMapper, int[] modBlockStateMapper) { + this.blockStateMapper = blockStateMapper; + this.modBlockStateMapper = modBlockStateMapper; + } + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + FriendlyByteBuf buf = event.getBuffer(); + int eventId = buf.readInt(); + if (eventId != WorldEvents.BLOCK_BREAK_EFFECT) return; + BlockPos blockPos = buf.readBlockPos(); + int state = buf.readInt(); + boolean global = buf.readBoolean(); + int newState = user.clientModEnabled() ? modBlockStateMapper[state] : blockStateMapper[state]; + Object blockState = BlockStateUtils.idToBlockState(state); + Object soundType = FastNMS.INSTANCE.method$BlockBehaviour$BlockStateBase$getSoundType(blockState); + Object soundEvent = FastNMS.INSTANCE.field$SoundType$breakSound(soundType); + Object rawSoundId = FastNMS.INSTANCE.field$SoundEvent$location(soundEvent); + if (BlockStateUtils.isVanillaBlock(state)) { + if (BukkitBlockManager.instance().isBreakSoundMissing(rawSoundId)) { + Key mappedSoundId = BukkitBlockManager.instance().replaceSoundIfExist(KeyUtils.resourceLocationToKey(rawSoundId)); + if (mappedSoundId != null) { + Object packet = FastNMS.INSTANCE.constructor$ClientboundSoundPacket( + FastNMS.INSTANCE.method$Holder$direct(FastNMS.INSTANCE.constructor$SoundEvent(KeyUtils.toResourceLocation(mappedSoundId), Optional.empty())), + CoreReflections.instance$SoundSource$BLOCKS, + blockPos.x() + 0.5, blockPos.y() + 0.5, blockPos.z() + 0.5, 1f, 0.8F, + RandomUtils.generateRandomLong() + ); + user.sendPacket(packet, true); + } + } + } else { + Key soundId = KeyUtils.resourceLocationToKey(rawSoundId); + Key mappedSoundId = BukkitBlockManager.instance().replaceSoundIfExist(soundId); + Object finalSoundId = KeyUtils.toResourceLocation(mappedSoundId == null ? soundId : mappedSoundId); + Object packet = FastNMS.INSTANCE.constructor$ClientboundSoundPacket( + FastNMS.INSTANCE.method$Holder$direct(FastNMS.INSTANCE.constructor$SoundEvent(finalSoundId, Optional.empty())), + CoreReflections.instance$SoundSource$BLOCKS, + blockPos.x() + 0.5, blockPos.y() + 0.5, blockPos.z() + 0.5, 1f, 0.8F, + RandomUtils.generateRandomLong() + ); + user.sendPacket(packet, true); + } + if (newState == state) { + return; + } + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeInt(eventId); + buf.writeBlockPos(blockPos); + buf.writeInt(newState); + buf.writeBoolean(global); + } + } + + public static class OpenScreenListener1_20 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptContainer()) return; + FriendlyByteBuf buf = event.getBuffer(); + int containerId = buf.readVarInt(); + int type = buf.readVarInt(); + String json = buf.readUtf(); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); + if (tokens.isEmpty()) return; + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(containerId); + buf.writeVarInt(type); + buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); + } + } + + public static class OpenScreenListener1_20_3 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptContainer()) return; + FriendlyByteBuf buf = event.getBuffer(); + int containerId = buf.readVarInt(); + int type = buf.readVarInt(); + Tag nbt = buf.readNbt(false); + if (nbt == null) return; + Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + if (tokens.isEmpty()) return; + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(containerId); + buf.writeVarInt(type); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); + } + } + + public static class SystemChatListener1_20 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptSystemChat()) return; + FriendlyByteBuf buf = event.getBuffer(); + String jsonOrPlainString = buf.readUtf(); + Map tokens = CraftEngine.instance().fontManager().matchTags(jsonOrPlainString); + if (tokens.isEmpty()) return; + boolean overlay = buf.readBoolean(); + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(jsonOrPlainString), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); + buf.writeBoolean(overlay); + } + } + + public static class SystemChatListener1_20_3 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptSystemChat()) return; + FriendlyByteBuf buf = event.getBuffer(); + Tag nbt = buf.readNbt(false); + if (nbt == null) return; + Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + if (tokens.isEmpty()) return; + boolean overlay = buf.readBoolean(); + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); + buf.writeBoolean(overlay); + } + } + + public static class TabListListener1_20 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptTabList()) return; + FriendlyByteBuf buf = event.getBuffer(); + String json1 = buf.readUtf(); + String json2 = buf.readUtf(); + Map tokens1 = CraftEngine.instance().fontManager().matchTags(json1); + Map tokens2 = CraftEngine.instance().fontManager().matchTags(json2); + if (tokens1.isEmpty() && tokens2.isEmpty()) return; + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + NetworkTextReplaceContext context = NetworkTextReplaceContext.of((BukkitServerPlayer) user); + buf.writeUtf(tokens1.isEmpty() ? json1 : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json1), tokens1, context))); + buf.writeUtf(tokens2.isEmpty() ? json2 : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json2), tokens2, context))); + } + } + + public static class TabListListener1_20_3 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptTabList()) return; + FriendlyByteBuf buf = event.getBuffer(); + Tag nbt1 = buf.readNbt(false); + if (nbt1 == null) return; + Tag nbt2 = buf.readNbt(false); + if (nbt2 == null) return; + Map tokens1 = CraftEngine.instance().fontManager().matchTags(nbt1.getAsString()); + Map tokens2 = CraftEngine.instance().fontManager().matchTags(nbt2.getAsString()); + if (tokens1.isEmpty() && tokens2.isEmpty()) return; + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + NetworkTextReplaceContext context = NetworkTextReplaceContext.of((BukkitServerPlayer) user); + buf.writeNbt(tokens1.isEmpty() ? nbt1 : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt1), tokens1, context)), false); + buf.writeNbt(tokens2.isEmpty() ? nbt2 : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt2), tokens2, context)), false); + } + } + + public static class SetActionBarListener1_20 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptActionBar()) return; + FriendlyByteBuf buf = event.getBuffer(); + String json = buf.readUtf(); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); + if (tokens.isEmpty()) return; + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); + } + } + + public static class SetActionBarListener1_20_3 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptActionBar()) return; + FriendlyByteBuf buf = event.getBuffer(); + Tag nbt = buf.readNbt(false); + if (nbt == null) return; + Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + if (tokens.isEmpty()) return; + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); + } + } + + public static class SetTitleListener1_20 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptTitle()) return; + FriendlyByteBuf buf = event.getBuffer(); + String json = buf.readUtf(); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); + if (tokens.isEmpty()) return; + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); + } + } + + public static class SetTitleListener1_20_3 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptTitle()) return; + FriendlyByteBuf buf = event.getBuffer(); + Tag nbt = buf.readNbt(false); + if (nbt == null) return; + Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + if (tokens.isEmpty()) return; + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); + } + } + + public static class SetSubtitleListener1_20 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptTitle()) return; + FriendlyByteBuf buf = event.getBuffer(); + String json = buf.readUtf(); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); + if (tokens.isEmpty()) return; + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); + } + } + + public static class SetSubtitleListener1_20_3 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptTitle()) return; + FriendlyByteBuf buf = event.getBuffer(); + Tag nbt = buf.readNbt(false); + if (nbt == null) return; + Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + if (tokens.isEmpty()) return; + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); + } + } + + public static class BossEventListener1_20 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptBossBar()) return; + FriendlyByteBuf buf = event.getBuffer(); + UUID uuid = buf.readUUID(); + int actionType = buf.readVarInt(); + if (actionType == 0) { + String json = buf.readUtf(); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); + if (tokens.isEmpty()) return; + float health = buf.readFloat(); + int color = buf.readVarInt(); + int division = buf.readVarInt(); + byte flag = buf.readByte(); + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeUUID(uuid); + buf.writeVarInt(actionType); + buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); + buf.writeFloat(health); + buf.writeVarInt(color); + buf.writeVarInt(division); + buf.writeByte(flag); + } else if (actionType == 3) { + String json = buf.readUtf(); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); + if (tokens.isEmpty()) return; + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeUUID(uuid); + buf.writeVarInt(actionType); + buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); + } + } + } + + public static class BossEventListener1_20_3 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptBossBar()) return; + FriendlyByteBuf buf = event.getBuffer(); + UUID uuid = buf.readUUID(); + int actionType = buf.readVarInt(); + if (actionType == 0) { + Tag nbt = buf.readNbt(false); + if (nbt == null) return; + Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + if (tokens.isEmpty()) return; + float health = buf.readFloat(); + int color = buf.readVarInt(); + int division = buf.readVarInt(); + byte flag = buf.readByte(); + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeUUID(uuid); + buf.writeVarInt(actionType); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); + buf.writeFloat(health); + buf.writeVarInt(color); + buf.writeVarInt(division); + buf.writeByte(flag); + } else if (actionType == 3) { + Tag nbt = buf.readNbt(false); + if (nbt == null) return; + Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + if (tokens.isEmpty()) return; + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeUUID(uuid); + buf.writeVarInt(actionType); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); + } + } + } + + public static class TeamListener1_20 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptTeam()) return; + FriendlyByteBuf buf = event.getBuffer(); + String name = buf.readUtf(); + byte method = buf.readByte(); + if (method != 2 && method != 0) + return; + String displayName = buf.readUtf(); + byte friendlyFlags = buf.readByte(); + String nameTagVisibility = buf.readUtf(40); + String collisionRule = buf.readUtf(40); + int color = buf.readVarInt(); + String prefix = buf.readUtf(); + String suffix = buf.readUtf(); + + Map tokens1 = CraftEngine.instance().fontManager().matchTags(displayName); + Map tokens2 = CraftEngine.instance().fontManager().matchTags(prefix); + Map tokens3 = CraftEngine.instance().fontManager().matchTags(suffix); + if (tokens1.isEmpty() && tokens2.isEmpty() && tokens3.isEmpty()) return; + event.setChanged(true); + NetworkTextReplaceContext context = NetworkTextReplaceContext.of((BukkitServerPlayer) user); + + List entities = method == 0 ? buf.readStringList() : null; + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeUtf(name); + buf.writeByte(method); + buf.writeUtf(tokens1.isEmpty() ? displayName : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(displayName), tokens1, context))); + buf.writeByte(friendlyFlags); + buf.writeUtf(nameTagVisibility); + buf.writeUtf(collisionRule); + buf.writeVarInt(color); + buf.writeUtf(tokens2.isEmpty() ? prefix : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(prefix), tokens2, context))); + buf.writeUtf(tokens3.isEmpty() ? suffix : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(suffix), tokens3, context))); + if (entities != null) { + buf.writeStringList(entities); + } + } + } + + public static class TeamListener1_20_3 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptTeam()) return; + FriendlyByteBuf buf = event.getBuffer(); + String name = buf.readUtf(); + byte method = buf.readByte(); + if (method != 2 && method != 0) return; + Tag displayName = buf.readNbt(false); + if (displayName == null) return; + byte friendlyFlags = buf.readByte(); + Either eitherVisibility = VersionHelper.isOrAbove1_21_5() ? Either.right(buf.readVarInt()) : Either.left(buf.readUtf(40)); + Either eitherCollisionRule = VersionHelper.isOrAbove1_21_5() ? Either.right(buf.readVarInt()) : Either.left(buf.readUtf(40)); + int color = buf.readVarInt(); + Tag prefix = buf.readNbt(false); + if (prefix == null) return; + Tag suffix = buf.readNbt(false); + if (suffix == null) return; + Map tokens1 = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); + Map tokens2 = CraftEngine.instance().fontManager().matchTags(prefix.getAsString()); + Map tokens3 = CraftEngine.instance().fontManager().matchTags(suffix.getAsString()); + if (tokens1.isEmpty() && tokens2.isEmpty() && tokens3.isEmpty()) return; + NetworkTextReplaceContext context = NetworkTextReplaceContext.of((BukkitServerPlayer) user); + List entities = method == 0 ? buf.readStringList() : null; + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeUtf(name); + buf.writeByte(method); + buf.writeNbt(tokens1.isEmpty() ? displayName : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens1, context)), false); + buf.writeByte(friendlyFlags); + eitherVisibility.ifLeft(buf::writeUtf).ifRight(buf::writeVarInt); + eitherCollisionRule.ifLeft(buf::writeUtf).ifRight(buf::writeVarInt); + buf.writeVarInt(color); + buf.writeNbt(tokens2.isEmpty() ? prefix : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(prefix), tokens2, context)), false); + buf.writeNbt(tokens3.isEmpty() ? suffix : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(suffix), tokens3, context)), false); + if (entities != null) { + buf.writeStringList(entities); + } + } + } + + public static class SetObjectiveListener1_20 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptScoreboard()) return; + FriendlyByteBuf buf = event.getBuffer(); + String objective = buf.readUtf(); + byte mode = buf.readByte(); + if (mode != 0 && mode != 2) return; + String displayName = buf.readUtf(); + int renderType = buf.readVarInt(); + Map tokens = CraftEngine.instance().fontManager().matchTags(displayName); + if (tokens.isEmpty()) return; + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeUtf(objective); + buf.writeByte(mode); + buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(displayName), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); + buf.writeVarInt(renderType); + } + } + + public static class SetObjectiveListener1_20_3 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptScoreboard()) return; + FriendlyByteBuf buf = event.getBuffer(); + String objective = buf.readUtf(); + byte mode = buf.readByte(); + if (mode != 0 && mode != 2) return; + Tag displayName = buf.readNbt(false); + if (displayName == null) return; + int renderType = buf.readVarInt(); + boolean optionalNumberFormat = buf.readBoolean(); + if (optionalNumberFormat) { + int format = buf.readVarInt(); + if (format == 0) { + Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); + if (tokens.isEmpty()) return; + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeUtf(objective); + buf.writeByte(mode); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); + buf.writeVarInt(renderType); + buf.writeBoolean(true); + buf.writeVarInt(0); + } else if (format == 1) { + Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); + if (tokens.isEmpty()) return; + Tag style = buf.readNbt(false); + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeUtf(objective); + buf.writeByte(mode); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); + buf.writeVarInt(renderType); + buf.writeBoolean(true); + buf.writeVarInt(1); + buf.writeNbt(style, false); + } else if (format == 2) { + Tag fixed = buf.readNbt(false); + if (fixed == null) return; + Map tokens1 = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); + Map tokens2 = CraftEngine.instance().fontManager().matchTags(fixed.getAsString()); + if (tokens1.isEmpty() && tokens2.isEmpty()) return; + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeUtf(objective); + buf.writeByte(mode); + buf.writeNbt(tokens1.isEmpty() ? displayName : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens1, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); + buf.writeVarInt(renderType); + buf.writeBoolean(true); + buf.writeVarInt(2); + buf.writeNbt(tokens2.isEmpty() ? fixed : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(fixed), tokens2, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); + } + } else { + Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); + if (tokens.isEmpty()) return; + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeUtf(objective); + buf.writeByte(mode); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); + buf.writeVarInt(renderType); + buf.writeBoolean(false); + } + } + } + + public static class SetScoreListener1_20_3 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!Config.interceptSetScore()) return; + if (!(user instanceof BukkitServerPlayer serverPlayer)) return; + boolean isChanged = false; + FriendlyByteBuf buf = event.getBuffer(); + String owner = buf.readUtf(); + String objectiveName = buf.readUtf(); + int score = buf.readVarInt(); + boolean hasDisplay = buf.readBoolean(); + Tag displayName = null; + if (hasDisplay) { + displayName = buf.readNbt(false); + } + outside: + if (displayName != null) { + Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); + if (tokens.isEmpty()) break outside; + Component component = AdventureHelper.tagToComponent(displayName); + component = AdventureHelper.replaceText(component, tokens, NetworkTextReplaceContext.of(serverPlayer)); + displayName = AdventureHelper.componentToTag(component); + isChanged = true; + } + boolean hasNumberFormat = buf.readBoolean(); + int format = -1; + Tag style = null; + Tag fixed = null; + if (hasNumberFormat) { + format = buf.readVarInt(); + if (format == 0) { + if (displayName == null) return; + } else if (format == 1) { + if (displayName == null) return; + style = buf.readNbt(false); + } else if (format == 2) { + fixed = buf.readNbt(false); + if (fixed == null) return; + Map tokens = CraftEngine.instance().fontManager().matchTags(fixed.getAsString()); + if (tokens.isEmpty() && !isChanged) return; + if (!tokens.isEmpty()) { + Component component = AdventureHelper.tagToComponent(fixed); + component = AdventureHelper.replaceText(component, tokens, NetworkTextReplaceContext.of(serverPlayer)); + fixed = AdventureHelper.componentToTag(component); + isChanged = true; + } + } + } + if (isChanged) { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeUtf(owner); + buf.writeUtf(objectiveName); + buf.writeVarInt(score); + if (hasDisplay) { + buf.writeBoolean(true); + buf.writeNbt(displayName, false); + } else { + buf.writeBoolean(false); + } + if (hasNumberFormat) { + buf.writeBoolean(true); + buf.writeVarInt(format); + if (format == 1) { + buf.writeNbt(style, false); + } else if (format == 2) { + buf.writeNbt(fixed, false); + } + } else { + buf.writeBoolean(false); + } + } + } + } + + public static class AddRecipeBookListener implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + FriendlyByteBuf buf = event.getBuffer(); + List entries = buf.readCollection(ArrayList::new, byteBuf -> { + RecipeBookEntry entry = RecipeBookEntry.read(byteBuf); + entry.applyClientboundData((BukkitServerPlayer) user); + return entry; + }); + boolean replace = buf.readBoolean(); + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeCollection(entries, ((byteBuf, recipeBookEntry) -> recipeBookEntry.write(byteBuf))); + buf.writeBoolean(replace); + } + } + + public static class PlaceGhostRecipeListener implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!VersionHelper.isOrAbove1_21_2()) return; + FriendlyByteBuf buf = event.getBuffer(); + int containerId = buf.readContainerId(); + RecipeDisplay display = RecipeDisplay.read(buf); + display.applyClientboundData((BukkitServerPlayer) user); + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeContainerId(containerId); + display.write(buf); + } + } + + public static class UpdateRecipesListener implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (VersionHelper.isOrAbove1_21_2()) return; + FriendlyByteBuf buf = event.getBuffer(); + List holders = buf.readCollection(ArrayList::new, byteBuf -> { + LegacyRecipeHolder holder = LegacyRecipeHolder.read(byteBuf); + holder.recipe().applyClientboundData((BukkitServerPlayer) user); + return holder; + }); + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeCollection(holders, ((byteBuf, recipeHolder) -> recipeHolder.write(byteBuf))); + } + } + + public static class UpdateAdvancementsListener implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!(user instanceof BukkitServerPlayer serverPlayer)) return; + FriendlyByteBuf buf = event.getBuffer(); + boolean reset = buf.readBoolean(); + List added = buf.readCollection(ArrayList::new, byteBuf -> { + AdvancementHolder holder = AdvancementHolder.read(byteBuf); + holder.applyClientboundData(serverPlayer); + return holder; + }); + Set removed = buf.readCollection(Sets::newLinkedHashSetWithExpectedSize, FriendlyByteBuf::readKey); + Map progress = buf.readMap(FriendlyByteBuf::readKey, AdvancementProgress::read); + + boolean showAdvancement = false; + if (VersionHelper.isOrAbove1_21_5()) { + showAdvancement = buf.readBoolean(); + } + + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + + buf.writeBoolean(reset); + buf.writeCollection(added, (byteBuf, advancementHolder) -> advancementHolder.write(byteBuf)); + buf.writeCollection(removed, FriendlyByteBuf::writeKey); + buf.writeMap(progress, FriendlyByteBuf::writeKey, (byteBuf, advancementProgress) -> advancementProgress.write(byteBuf)); + if (VersionHelper.isOrAbove1_21_5()) { + buf.writeBoolean(showAdvancement); + } + } + } + + public static class RemoveEntityListener implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + FriendlyByteBuf buf = event.getBuffer(); + boolean isChange = false; + IntList intList = buf.readIntIdList(); + for (int i = 0, size = intList.size(); i < size; i++) { + int entityId = intList.getInt(i); + EntityPacketHandler handler = user.entityPacketHandlers().remove(entityId); + if (handler != null && handler.handleEntitiesRemove(intList)) { + isChange = true; + } + } + if (isChange) { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeIntIdList(intList); + } + } + } + + public static class SoundListener implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + FriendlyByteBuf buf = event.getBuffer(); + int id = buf.readVarInt(); + if (id == 0) { + Key soundId = buf.readKey(); + Float range = null; + if (buf.readBoolean()) { + range = buf.readFloat(); + } + int source = buf.readVarInt(); + int x = buf.readInt(); + int y = buf.readInt(); + int z = buf.readInt(); + float volume = buf.readFloat(); + float pitch = buf.readFloat(); + long seed = buf.readLong(); + Key mapped = BukkitBlockManager.instance().replaceSoundIfExist(soundId); + if (mapped != null) { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(0); + buf.writeKey(mapped); + if (range != null) { + buf.writeBoolean(true); + buf.writeFloat(range); + } else { + buf.writeBoolean(false); + } + buf.writeVarInt(source); + buf.writeInt(x); + buf.writeInt(y); + buf.writeInt(z); + buf.writeFloat(volume); + buf.writeFloat(pitch); + buf.writeLong(seed); + } + } else { + Optional optionalSound = FastNMS.INSTANCE.method$IdMap$byId(MBuiltInRegistries.SOUND_EVENT, id - 1); + if (optionalSound.isEmpty()) return; + Object soundEvent = optionalSound.get(); + Key soundId = KeyUtils.resourceLocationToKey(FastNMS.INSTANCE.method$SoundEvent$location(soundEvent)); + int source = buf.readVarInt(); + int x = buf.readInt(); + int y = buf.readInt(); + int z = buf.readInt(); + float volume = buf.readFloat(); + float pitch = buf.readFloat(); + long seed = buf.readLong(); + Key mapped = BukkitBlockManager.instance().replaceSoundIfExist(soundId); + if (mapped != null) { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(0); + Object newId = KeyUtils.toResourceLocation(mapped); + Object newSoundEvent = FastNMS.INSTANCE.constructor$SoundEvent(newId, FastNMS.INSTANCE.method$SoundEvent$fixedRange(soundEvent)); + FastNMS.INSTANCE.method$SoundEvent$directEncode(buf, newSoundEvent); + buf.writeVarInt(source); + buf.writeInt(x); + buf.writeInt(y); + buf.writeInt(z); + buf.writeFloat(volume); + buf.writeFloat(pitch); + buf.writeLong(seed); + } + } + } + } + + public static class ContainerSetContentListener implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!(user instanceof BukkitServerPlayer serverPlayer)) return; + FriendlyByteBuf buf = event.getBuffer(); + int containerId = buf.readContainerId(); + int stateId = buf.readVarInt(); + int listSize = buf.readVarInt(); + List items = new ArrayList<>(listSize); + boolean changed = false; + Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); + for (int i = 0; i < listSize; i++) { + ItemStack itemStack = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); + Optional optional = BukkitItemManager.instance().s2c(itemStack, serverPlayer); + if (optional.isPresent()) { + items.add(optional.get()); + changed = true; + } else { + items.add(itemStack); + } + } + ItemStack carriedItem = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); + ItemStack newCarriedItem = carriedItem; + Optional optional = BukkitItemManager.instance().s2c(carriedItem, serverPlayer); + if (optional.isPresent()) { + changed = true; + newCarriedItem = optional.get(); + } + if (!changed) return; + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeContainerId(containerId); + buf.writeVarInt(stateId); + buf.writeVarInt(listSize); + Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); + for (ItemStack itemStack : items) { + FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, itemStack); + } + FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, newCarriedItem); + } + } + + public static class ContainerSetSlotListener implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!(user instanceof BukkitServerPlayer serverPlayer)) return; + FriendlyByteBuf buf = event.getBuffer(); + int containerId = buf.readContainerId(); + int stateId = buf.readVarInt(); + int slot = buf.readShort(); + Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); + ItemStack itemStack; + try { + itemStack = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); + } catch (Exception e) { + // 其他插件干的,比如某ty*****er,不要赖到ce头上 + return; + } + BukkitItemManager.instance().s2c(itemStack, serverPlayer).ifPresent((newItemStack) -> { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeContainerId(containerId); + buf.writeVarInt(stateId); + buf.writeShort(slot); + Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); + FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, newItemStack); + }); + } + } + + public static class SetCursorItemListener implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!(user instanceof BukkitServerPlayer serverPlayer)) return; + FriendlyByteBuf buf = event.getBuffer(); + Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); + ItemStack itemStack = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); + BukkitItemManager.instance().s2c(itemStack, serverPlayer).ifPresent((newItemStack) -> { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); + FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, newItemStack); + }); + } + } + + public static class SetEquipmentListener implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!(user instanceof BukkitServerPlayer serverPlayer)) return; + FriendlyByteBuf buf = event.getBuffer(); + boolean changed = false; + Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); + int entity = buf.readVarInt(); + List> slots = Lists.newArrayList(); + int slotMask; + do { + slotMask = buf.readByte(); + Object equipmentSlot = CoreReflections.instance$EquipmentSlot$values[slotMask & 127]; + ItemStack itemStack = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); + Optional optional = BukkitItemManager.instance().s2c(itemStack, serverPlayer); + if (optional.isPresent()) { + changed = true; + itemStack = optional.get(); + } + slots.add(com.mojang.datafixers.util.Pair.of(equipmentSlot, itemStack)); + } while ((slotMask & -128) != 0); + if (changed) { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(entity); + int i = slots.size(); + Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); + for (int j = 0; j < i; ++j) { + com.mojang.datafixers.util.Pair pair = slots.get(j); + Enum equipmentSlot = (Enum) pair.getFirst(); + boolean bl = j != i - 1; + int k = equipmentSlot.ordinal(); + buf.writeByte(bl ? k | -128 : k); + FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, pair.getSecond()); + } + } + } + } + + public static class SetPlayerInventoryListener1_21_2 implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!(user instanceof BukkitServerPlayer serverPlayer)) return; + FriendlyByteBuf buf = event.getBuffer(); + int slot = buf.readVarInt(); + Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); + ItemStack itemStack = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); + BukkitItemManager.instance().s2c(itemStack, serverPlayer).ifPresent((newItemStack) -> { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(slot); + Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); + FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, newItemStack); + }); + } + } + + public static class SetCreativeModeSlotListener implements ByteBufferPacketListener { + + @Override + public void onPacketReceive(NetWorkUser user, ByteBufPacketEvent event) { + if (!(user instanceof BukkitServerPlayer serverPlayer)) return; + if (!serverPlayer.isCreativeMode()) return; + FriendlyByteBuf buf = event.getBuffer(); + Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); + short slotNum = buf.readShort(); + ItemStack itemStack; + try { + itemStack = VersionHelper.isOrAbove1_20_5() ? + FastNMS.INSTANCE.method$FriendlyByteBuf$readUntrustedItem(friendlyBuf) : FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); + } catch (Exception e) { + return; + } + BukkitItemManager.instance().c2s(itemStack).ifPresent((newItemStack) -> { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeShort(slotNum); + Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); + if (VersionHelper.isOrAbove1_20_5()) { + FastNMS.INSTANCE.method$FriendlyByteBuf$writeUntrustedItem(newFriendlyBuf, newItemStack); + } else { + FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, newItemStack); + } + }); + } + } + + public static class ContainerClick1_20 implements ByteBufferPacketListener { + + @Override + public void onPacketReceive(NetWorkUser user, ByteBufPacketEvent event) { + FriendlyByteBuf buf = event.getBuffer(); + boolean changed = false; + Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); + int containerId = buf.readContainerId(); + int stateId = buf.readVarInt(); + short slotNum = buf.readShort(); + byte buttonNum = buf.readByte(); + int clickType = buf.readVarInt(); + int i = buf.readVarInt(); + Int2ObjectMap changedSlots = new Int2ObjectOpenHashMap<>(i); + for (int j = 0; j < i; ++j) { + int k = buf.readShort(); + ItemStack itemStack = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); + Optional optional = BukkitItemManager.instance().c2s(itemStack); + if (optional.isPresent()) { + changed = true; + itemStack = optional.get(); + } + changedSlots.put(k, itemStack); + } + ItemStack carriedItem = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); + Optional optional = BukkitItemManager.instance().c2s(carriedItem); + if (optional.isPresent()) { + changed = true; + carriedItem = optional.get(); + } + if (changed) { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeContainerId(containerId); + buf.writeVarInt(stateId); + buf.writeShort(slotNum); + buf.writeByte(buttonNum); + buf.writeVarInt(clickType); + buf.writeVarInt(changedSlots.size()); + Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); + changedSlots.forEach((k, v) -> { + buf.writeShort(k); + FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, v); + }); + FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, carriedItem); + } + } + } + + public class InteractEntityListener implements ByteBufferPacketListener { + + @Override + public void onPacketReceive(NetWorkUser user, ByteBufPacketEvent event) { + FriendlyByteBuf buf = event.getBuffer(); + int entityId = hasModelEngine() ? plugin.compatibilityManager().interactionToBaseEntity(buf.readVarInt()) : buf.readVarInt(); + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId); + if (furniture == null) return; + int actionType = buf.readVarInt(); + BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user; + if (serverPlayer.isSpectatorMode()) return; + Player platformPlayer = serverPlayer.platformPlayer(); + Location location = furniture.baseEntity().getLocation(); + + Runnable mainThreadTask; + if (actionType == 1) { + // ATTACK + boolean usingSecondaryAction = buf.readBoolean(); + if (entityId != furniture.baseEntityId()) { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(furniture.baseEntityId()); + buf.writeVarInt(actionType); + buf.writeBoolean(usingSecondaryAction); + } + + mainThreadTask = () -> { + // todo 冒险模式破坏工具白名单 + if (serverPlayer.isAdventureMode() || + !furniture.isValid()) return; + + // todo 重构家具时候注意,需要准备加载好的hitbox类,以获取hitbox坐标 + if (!serverPlayer.canInteractPoint(new Vec3d(location.getX(), location.getY(), location.getZ()), 16d)) { + return; + } + + FurnitureAttemptBreakEvent preBreakEvent = new FurnitureAttemptBreakEvent(serverPlayer.platformPlayer(), furniture); + if (EventUtils.fireAndCheckCancel(preBreakEvent)) + return; + + if (!BukkitCraftEngine.instance().antiGriefProvider().canBreak(platformPlayer, location)) + return; + + FurnitureBreakEvent breakEvent = new FurnitureBreakEvent(serverPlayer.platformPlayer(), furniture); + if (EventUtils.fireAndCheckCancel(breakEvent)) + return; + + Cancellable cancellable = Cancellable.of(breakEvent::isCancelled, breakEvent::setCancelled); + // execute functions + PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() + .withParameter(DirectContextParameters.FURNITURE, furniture) + .withParameter(DirectContextParameters.EVENT, cancellable) + .withParameter(DirectContextParameters.HAND, InteractionHand.MAIN_HAND) + .withParameter(DirectContextParameters.ITEM_IN_HAND, serverPlayer.getItemInHand(InteractionHand.MAIN_HAND)) + .withParameter(DirectContextParameters.POSITION, furniture.position()) + ); + furniture.config().execute(context, EventTrigger.LEFT_CLICK); + furniture.config().execute(context, EventTrigger.BREAK); + if (cancellable.isCancelled()) { + return; + } + + CraftEngineFurniture.remove(furniture, serverPlayer, !serverPlayer.isCreativeMode(), true); + }; + } else if (actionType == 2) { + // INTERACT_AT + float x = buf.readFloat(); + float y = buf.readFloat(); + float z = buf.readFloat(); + // todo 这个是错误的,这是实体的相对位置而非绝对位置 + Location interactionPoint = new Location(platformPlayer.getWorld(), x, y, z); + InteractionHand hand = buf.readVarInt() == 0 ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND; + boolean usingSecondaryAction = buf.readBoolean(); + if (entityId != furniture.baseEntityId()) { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(furniture.baseEntityId()); + buf.writeVarInt(actionType); + buf.writeFloat(x).writeFloat(y).writeFloat(z); + buf.writeVarInt(hand == InteractionHand.MAIN_HAND ? 0 : 1); + buf.writeBoolean(usingSecondaryAction); + } + + mainThreadTask = () -> { + if (!furniture.isValid()) { + return; + } + + // todo 重构家具时候注意,需要准备加载好的hitbox类,以获取hitbox坐标 + if (!serverPlayer.canInteractPoint(new Vec3d(location.getX(), location.getY(), location.getZ()), 16d)) { + return; + } + + FurnitureInteractEvent interactEvent = new FurnitureInteractEvent(serverPlayer.platformPlayer(), furniture, hand, interactionPoint); + if (EventUtils.fireAndCheckCancel(interactEvent)) { + return; + } + + Item itemInHand = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND); + Cancellable cancellable = Cancellable.of(interactEvent::isCancelled, interactEvent::setCancelled); + // execute functions + PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() + .withParameter(DirectContextParameters.EVENT, cancellable) + .withParameter(DirectContextParameters.FURNITURE, furniture) + .withParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand) + .withParameter(DirectContextParameters.HAND, hand) + .withParameter(DirectContextParameters.POSITION, furniture.position()) + ); + furniture.config().execute(context, EventTrigger.RIGHT_CLICK); + if (cancellable.isCancelled()) { + return; + } + + // 必须从网络包层面处理,否则无法获取交互的具体实体 + if (serverPlayer.isSecondaryUseActive() && !itemInHand.isEmpty()) { + // try placing another furniture above it + AABB hitBox = furniture.aabbByEntityId(entityId); + if (hitBox == null) return; + Optional> optionalCustomItem = itemInHand.getCustomItem(); + Location eyeLocation = platformPlayer.getEyeLocation(); + Vector direction = eyeLocation.getDirection(); + Location endLocation = eyeLocation.clone(); + endLocation.add(direction.multiply(serverPlayer.getCachedInteractionRange())); + Optional result = hitBox.clip(LocationUtils.toVec3d(eyeLocation), LocationUtils.toVec3d(endLocation)); + if (result.isEmpty()) { + return; + } + EntityHitResult hitResult = result.get(); + if (optionalCustomItem.isPresent() && !optionalCustomItem.get().behaviors().isEmpty()) { + for (ItemBehavior behavior : optionalCustomItem.get().behaviors()) { + if (behavior instanceof FurnitureItemBehavior) { + behavior.useOnBlock(new UseOnContext(serverPlayer, InteractionHand.MAIN_HAND, new BlockHitResult(hitResult.hitLocation(), hitResult.direction(), BlockPos.fromVec3d(hitResult.hitLocation()), false))); + return; + } + } + } + // now simulate vanilla item behavior + serverPlayer.setResendSound(); + FastNMS.INSTANCE.simulateInteraction( + serverPlayer.serverPlayer(), + DirectionUtils.toNMSDirection(hitResult.direction()), + hitResult.hitLocation().x, hitResult.hitLocation().y, hitResult.hitLocation().z, + LocationUtils.toBlockPos(hitResult.blockPos()) + ); + } else { + if (!serverPlayer.isSecondaryUseActive()) { + furniture.findFirstAvailableSeat(entityId).ifPresent(seatPos -> { + if (furniture.tryOccupySeat(seatPos)) { + furniture.spawnSeatEntityForPlayer(serverPlayer, seatPos); + } + }); + } + } + }; + } else if (actionType == 0) { + int hand = buf.readVarInt(); + boolean usingSecondaryAction = buf.readBoolean(); + if (entityId != furniture.baseEntityId()) { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(furniture.baseEntityId()); + buf.writeVarInt(actionType); + buf.writeVarInt(hand); + buf.writeBoolean(usingSecondaryAction); + } + return; + } else { + return; + } + + if (VersionHelper.isFolia()) { + platformPlayer.getScheduler().run(BukkitCraftEngine.instance().javaPlugin(), t -> mainThreadTask.run(), () -> {}); + } else { + BukkitCraftEngine.instance().scheduler().executeSync(mainThreadTask); + } + } + } + + public static class CustomPayloadListener1_20 implements ByteBufferPacketListener { + + @Override + public void onPacketReceive(NetWorkUser user, ByteBufPacketEvent event) { + if (VersionHelper.isOrAbove1_20_2()) return; + FriendlyByteBuf byteBuf = event.getBuffer(); + Key key = byteBuf.readKey(); + if (!key.equals(NetworkManager.MOD_CHANNEL_KEY)) return; + PayloadHelper.handleReceiver(new UnknownPayload(key, byteBuf.readBytes(byteBuf.readableBytes())), user); + } + } + + public class AddEntityListener implements ByteBufferPacketListener { + private final EntityTypeHandler[] handlers; + + public AddEntityListener(int entityTypes) { + this.handlers = new EntityTypeHandler[entityTypes]; + Arrays.fill(this.handlers, EntityTypeHandler.DoNothing.INSTANCE); + this.handlers[MEntityTypes.BLOCK_DISPLAY$registryId] = simpleAddEntityHandler(BlockDisplayPacketHandler.INSTANCE); + this.handlers[MEntityTypes.TEXT_DISPLAY$registryId] = simpleAddEntityHandler(TextDisplayPacketHandler.INSTANCE); + this.handlers[MEntityTypes.ARMOR_STAND$registryId] = simpleAddEntityHandler(ArmorStandPacketHandler.INSTANCE); + this.handlers[MEntityTypes.ITEM$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); + this.handlers[MEntityTypes.ITEM_FRAME$registryId] = simpleAddEntityHandler(ItemFramePacketHandler.INSTANCE); + this.handlers[MEntityTypes.GLOW_ITEM_FRAME$registryId] = simpleAddEntityHandler(ItemFramePacketHandler.INSTANCE); + this.handlers[MEntityTypes.ENDERMAN$registryId] = simpleAddEntityHandler(EndermanPacketHandler.INSTANCE); + this.handlers[MEntityTypes.CHEST_MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); + this.handlers[MEntityTypes.COMMAND_BLOCK_MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); + this.handlers[MEntityTypes.FURNACE_MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); + this.handlers[MEntityTypes.HOPPER_MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); + this.handlers[MEntityTypes.MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); + this.handlers[MEntityTypes.SPAWNER_MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); + this.handlers[MEntityTypes.TNT_MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); + this.handlers[MEntityTypes.FIREBALL$registryId] = createOptionalCustomProjectileEntityHandler(true); + this.handlers[MEntityTypes.EYE_OF_ENDER$registryId] = createOptionalCustomProjectileEntityHandler(true); + this.handlers[MEntityTypes.FIREWORK_ROCKET$registryId] = createOptionalCustomProjectileEntityHandler(true); + this.handlers[MEntityTypes.SMALL_FIREBALL$registryId] = createOptionalCustomProjectileEntityHandler(true); + this.handlers[MEntityTypes.EGG$registryId] = createOptionalCustomProjectileEntityHandler(true); + this.handlers[MEntityTypes.ENDER_PEARL$registryId] = createOptionalCustomProjectileEntityHandler(true); + this.handlers[MEntityTypes.EXPERIENCE_BOTTLE$registryId] = createOptionalCustomProjectileEntityHandler(true); + this.handlers[MEntityTypes.SNOWBALL$registryId] = createOptionalCustomProjectileEntityHandler(true); + this.handlers[MEntityTypes.POTION$registryId] = createOptionalCustomProjectileEntityHandler(true); + this.handlers[MEntityTypes.TRIDENT$registryId] = createOptionalCustomProjectileEntityHandler(false); + this.handlers[MEntityTypes.ARROW$registryId] = createOptionalCustomProjectileEntityHandler(false); + this.handlers[MEntityTypes.SPECTRAL_ARROW$registryId] = createOptionalCustomProjectileEntityHandler(false); + if (VersionHelper.isOrAbove1_20_3()) { + this.handlers[MEntityTypes.TNT$registryId] = simpleAddEntityHandler(PrimedTNTPacketHandler.INSTANCE); + } + if (VersionHelper.isOrAbove1_20_5()) { + this.handlers[MEntityTypes.OMINOUS_ITEM_SPAWNER$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); + } + this.handlers[MEntityTypes.FALLING_BLOCK$registryId] = (user, event) -> { + FriendlyByteBuf buf = event.getBuffer(); + int id = buf.readVarInt(); + UUID uuid = buf.readUUID(); + int type = buf.readVarInt(); + double x = buf.readDouble(); + double y = buf.readDouble(); + double z = buf.readDouble(); + Vec3d movement = VersionHelper.isOrAbove1_21_9() ? buf.readLpVec3() : null; + byte xRot = buf.readByte(); + byte yRot = buf.readByte(); + byte yHeadRot = buf.readByte(); + int data = buf.readVarInt(); + // Falling blocks + int remapped = remapBlockState(data, user.clientModEnabled()); + if (remapped != data) { + int xa = VersionHelper.isOrAbove1_21_9() ? -1 : buf.readShort(); + int ya = VersionHelper.isOrAbove1_21_9() ? -1 : buf.readShort(); + int za = VersionHelper.isOrAbove1_21_9() ? -1 : buf.readShort(); + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(id); + buf.writeUUID(uuid); + buf.writeVarInt(type); + buf.writeDouble(x); + buf.writeDouble(y); + buf.writeDouble(z); + if (VersionHelper.isOrAbove1_21_9()) buf.writeLpVec3(movement); + buf.writeByte(xRot); + buf.writeByte(yRot); + buf.writeByte(yHeadRot); + buf.writeVarInt(remapped); + if (!VersionHelper.isOrAbove1_21_9()) buf.writeShort(xa); + if (!VersionHelper.isOrAbove1_21_9()) buf.writeShort(ya); + if (!VersionHelper.isOrAbove1_21_9()) buf.writeShort(za); + } }; + this.handlers[MEntityTypes.ITEM_DISPLAY$registryId] = (user, event) -> { + FriendlyByteBuf buf = event.getBuffer(); + int id = buf.readVarInt(); + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(id); + if (furniture != null) { + user.entityPacketHandlers().put(id, new FurniturePacketHandler(furniture.fakeEntityIds())); + user.sendPacket(furniture.spawnPacket((Player) user.platformPlayer()), false); + if (Config.hideBaseEntity() && !furniture.hasExternalModel()) { + event.setCancelled(true); + } + } else { + user.entityPacketHandlers().put(id, ItemDisplayPacketHandler.INSTANCE); + } + }; + this.handlers[MEntityTypes.INTERACTION$registryId] = (user, event) -> { + if (BukkitFurnitureManager.NMS_COLLISION_ENTITY_TYPE != MEntityTypes.INTERACTION) return; + FriendlyByteBuf buf = event.getBuffer(); + int id = buf.readVarInt(); + // Cancel collider entity packet + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(id); + if (furniture != null) { + event.setCancelled(true); + user.entityPacketHandlers().put(id, FurnitureCollisionPacketHandler.INSTANCE); + } + }; + this.handlers[MEntityTypes.OAK_BOAT$registryId] = (user, event) -> { + if (BukkitFurnitureManager.NMS_COLLISION_ENTITY_TYPE != MEntityTypes.OAK_BOAT) return; + FriendlyByteBuf buf = event.getBuffer(); + int id = buf.readVarInt(); + // Cancel collider entity packet + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(id); + if (furniture != null) { + event.setCancelled(true); + user.entityPacketHandlers().put(id, FurnitureCollisionPacketHandler.INSTANCE); + } + }; + } + + private static EntityTypeHandler simpleAddEntityHandler(EntityPacketHandler handler) { + return (user, event) -> { + FriendlyByteBuf buf = event.getBuffer(); + user.entityPacketHandlers().put(buf.readVarInt(), handler); + }; + } + + private static EntityTypeHandler createOptionalCustomProjectileEntityHandler(boolean fallback) { + return (user, event) -> { + FriendlyByteBuf buf = event.getBuffer(); + int id = buf.readVarInt(); + BukkitProjectileManager.instance().projectileByEntityId(id).ifPresentOrElse(customProjectile -> { + ProjectilePacketHandler handler = new ProjectilePacketHandler(customProjectile, id); + handler.convertAddCustomProjectilePacket(buf, event); + user.entityPacketHandlers().put(id, handler); + }, () -> { + if (fallback) { + user.entityPacketHandlers().put(id, CommonItemPacketHandler.INSTANCE); + } + }); + }; + } + + public interface EntityTypeHandler { + + void handle(NetWorkUser user, ByteBufPacketEvent event); + + class DoNothing implements EntityTypeHandler { + public static final DoNothing INSTANCE = new DoNothing(); + + @Override + public void handle(NetWorkUser user, ByteBufPacketEvent event) { + } + } + } + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + FriendlyByteBuf buf = event.getBuffer(); + buf.readVarInt(); + buf.readUUID(); + int type = buf.readVarInt(); + this.handlers[type].handle(user, event); + } + } + + public static class SetEntityDataListener implements ByteBufferPacketListener { + + @Override + public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + if (!(user instanceof BukkitServerPlayer serverPlayer)) return; + FriendlyByteBuf buf = event.getBuffer(); + int id = buf.readVarInt(); + EntityPacketHandler handler = user.entityPacketHandlers().get(id); + if (handler != null) { + handler.handleSetEntityData(serverPlayer, event); + return; + } + if (Config.interceptEntityName()) { + boolean isChanged = false; + List packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf); + for (int i = 0; i < packedItems.size(); i++) { + Object packedItem = packedItems.get(i); + int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); + if (entityDataId != BaseEntityData.CustomName.id()) continue; + @SuppressWarnings("unchecked") + Optional optionalTextComponent = (Optional) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); + if (optionalTextComponent.isEmpty()) continue; + Object textComponent = optionalTextComponent.get(); + String json = ComponentUtils.minecraftToJson(textComponent); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); + if (tokens.isEmpty()) continue; + Component component = AdventureHelper.jsonToComponent(json); + component = AdventureHelper.replaceText(component, tokens, NetworkTextReplaceContext.of(serverPlayer)); + Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); + packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component)))); + isChanged = true; + break; + } + if (isChanged) { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(id); + FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf); + } + } } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java deleted file mode 100644 index f3897953b..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ /dev/null @@ -1,2631 +0,0 @@ -package net.momirealms.craftengine.bukkit.plugin.network; - -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import com.mojang.authlib.GameProfile; -import com.mojang.datafixers.util.Either; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.ints.IntList; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.TranslationArgument; -import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks; -import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; -import net.momirealms.craftengine.bukkit.api.event.FurnitureAttemptBreakEvent; -import net.momirealms.craftengine.bukkit.api.event.FurnitureBreakEvent; -import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent; -import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; -import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; -import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; -import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; -import net.momirealms.craftengine.bukkit.entity.projectile.BukkitProjectileManager; -import net.momirealms.craftengine.bukkit.item.BukkitItemManager; -import net.momirealms.craftengine.bukkit.item.behavior.FurnitureItemBehavior; -import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.plugin.injector.ProtectedFieldVisitor; -import net.momirealms.craftengine.bukkit.plugin.network.handler.*; -import net.momirealms.craftengine.bukkit.plugin.network.payload.DiscardedPayload; -import net.momirealms.craftengine.bukkit.plugin.network.payload.NetWorkDataTypes; -import net.momirealms.craftengine.bukkit.plugin.network.payload.Payload; -import net.momirealms.craftengine.bukkit.plugin.network.payload.UnknownPayload; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; -import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; -import net.momirealms.craftengine.bukkit.util.*; -import net.momirealms.craftengine.core.advancement.network.AdvancementHolder; -import net.momirealms.craftengine.core.advancement.network.AdvancementProgress; -import net.momirealms.craftengine.core.block.ImmutableBlockState; -import net.momirealms.craftengine.core.entity.player.InteractionHand; -import net.momirealms.craftengine.core.font.FontManager; -import net.momirealms.craftengine.core.font.IllegalCharacterProcessResult; -import net.momirealms.craftengine.core.item.CustomItem; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.behavior.ItemBehavior; -import net.momirealms.craftengine.core.item.context.UseOnContext; -import net.momirealms.craftengine.core.item.recipe.network.legacy.LegacyRecipeHolder; -import net.momirealms.craftengine.core.item.recipe.network.modern.RecipeBookEntry; -import net.momirealms.craftengine.core.item.recipe.network.modern.display.RecipeDisplay; -import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData; -import net.momirealms.craftengine.core.pack.host.ResourcePackHost; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; -import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; -import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; -import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; -import net.momirealms.craftengine.core.plugin.logger.Debugger; -import net.momirealms.craftengine.core.plugin.network.*; -import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; -import net.momirealms.craftengine.core.util.*; -import net.momirealms.craftengine.core.world.*; -import net.momirealms.craftengine.core.world.chunk.Palette; -import net.momirealms.craftengine.core.world.chunk.PalettedContainer; -import net.momirealms.craftengine.core.world.chunk.packet.BlockEntityData; -import net.momirealms.craftengine.core.world.chunk.packet.MCSection; -import net.momirealms.craftengine.core.world.collision.AABB; -import net.momirealms.sparrow.nbt.Tag; -import org.bukkit.*; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.PlayerInventory; -import org.bukkit.util.RayTraceResult; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.Nullable; - -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.function.BiConsumer; - -public class PacketConsumers { - private static BukkitNetworkManager.Handlers[] ADD_ENTITY_HANDLERS; - private static int[] BLOCK_STATE_MAPPINGS; - private static int[] MOD_BLOCK_STATE_MAPPINGS; - private static IntIdentityList SERVER_BLOCK_LIST; - private static IntIdentityList CLIENT_BLOCK_LIST; - private static IntIdentityList BIOME_LIST; - - public static void initEntities(int registrySize) { - ADD_ENTITY_HANDLERS = new BukkitNetworkManager.Handlers[registrySize]; - Arrays.fill(ADD_ENTITY_HANDLERS, BukkitNetworkManager.Handlers.DO_NOTHING); - ADD_ENTITY_HANDLERS[MEntityTypes.FALLING_BLOCK$registryId] = (user, event) -> { - FriendlyByteBuf buf = event.getBuffer(); - int id = buf.readVarInt(); - UUID uuid = buf.readUUID(); - int type = buf.readVarInt(); - double x = buf.readDouble(); - double y = buf.readDouble(); - double z = buf.readDouble(); - byte xRot = buf.readByte(); - byte yRot = buf.readByte(); - byte yHeadRot = buf.readByte(); - int data = buf.readVarInt(); - // Falling blocks - int remapped = user.clientModEnabled() ? remapMOD(data) : remap(data); - if (remapped != data) { - int xa = buf.readShort(); - int ya = buf.readShort(); - int za = buf.readShort(); - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeVarInt(id); - buf.writeUUID(uuid); - buf.writeVarInt(type); - buf.writeDouble(x); - buf.writeDouble(y); - buf.writeDouble(z); - buf.writeByte(xRot); - buf.writeByte(yRot); - buf.writeByte(yHeadRot); - buf.writeVarInt(remapped); - buf.writeShort(xa); - buf.writeShort(ya); - buf.writeShort(za); - } - }; - - ADD_ENTITY_HANDLERS[MEntityTypes.BLOCK_DISPLAY$registryId] = simpleAddEntityHandler(BlockDisplayPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.TEXT_DISPLAY$registryId] = simpleAddEntityHandler(TextDisplayPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.ARMOR_STAND$registryId] = simpleAddEntityHandler(ArmorStandPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.ITEM$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.ITEM_FRAME$registryId] = simpleAddEntityHandler(ItemFramePacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.GLOW_ITEM_FRAME$registryId] = simpleAddEntityHandler(ItemFramePacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.ENDERMAN$registryId] = simpleAddEntityHandler(EndermanPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.CHEST_MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.COMMAND_BLOCK_MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.FURNACE_MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.HOPPER_MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.SPAWNER_MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.TNT_MINECART$registryId] = simpleAddEntityHandler(MinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.FIREBALL$registryId] = createOptionalCustomProjectileEntityHandler(true); - ADD_ENTITY_HANDLERS[MEntityTypes.EYE_OF_ENDER$registryId] = createOptionalCustomProjectileEntityHandler(true); - ADD_ENTITY_HANDLERS[MEntityTypes.FIREWORK_ROCKET$registryId] = createOptionalCustomProjectileEntityHandler(true); - ADD_ENTITY_HANDLERS[MEntityTypes.SMALL_FIREBALL$registryId] = createOptionalCustomProjectileEntityHandler(true); - ADD_ENTITY_HANDLERS[MEntityTypes.EGG$registryId] = createOptionalCustomProjectileEntityHandler(true); - ADD_ENTITY_HANDLERS[MEntityTypes.ENDER_PEARL$registryId] = createOptionalCustomProjectileEntityHandler(true); - ADD_ENTITY_HANDLERS[MEntityTypes.EXPERIENCE_BOTTLE$registryId] = createOptionalCustomProjectileEntityHandler(true); - ADD_ENTITY_HANDLERS[MEntityTypes.SNOWBALL$registryId] = createOptionalCustomProjectileEntityHandler(true); - ADD_ENTITY_HANDLERS[MEntityTypes.POTION$registryId] = createOptionalCustomProjectileEntityHandler(true); - ADD_ENTITY_HANDLERS[MEntityTypes.TRIDENT$registryId] = createOptionalCustomProjectileEntityHandler(false); - ADD_ENTITY_HANDLERS[MEntityTypes.ARROW$registryId] = createOptionalCustomProjectileEntityHandler(false); - ADD_ENTITY_HANDLERS[MEntityTypes.SPECTRAL_ARROW$registryId] = createOptionalCustomProjectileEntityHandler(false); - if (VersionHelper.isOrAbove1_20_3()) { - ADD_ENTITY_HANDLERS[MEntityTypes.TNT$registryId] = simpleAddEntityHandler(PrimedTNTPacketHandler.INSTANCE); - } - if (VersionHelper.isOrAbove1_20_5()) { - ADD_ENTITY_HANDLERS[MEntityTypes.OMINOUS_ITEM_SPAWNER$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); - } - - ADD_ENTITY_HANDLERS[MEntityTypes.ITEM_DISPLAY$registryId] = (user, event) -> { - FriendlyByteBuf buf = event.getBuffer(); - int id = buf.readVarInt(); - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(id); - if (furniture != null) { - user.entityPacketHandlers().put(id, new FurniturePacketHandler(furniture.fakeEntityIds())); - user.sendPacket(furniture.spawnPacket((Player) user.platformPlayer()), false); - if (Config.hideBaseEntity() && !furniture.hasExternalModel()) { - event.setCancelled(true); - } - } else { - user.entityPacketHandlers().put(id, ItemDisplayPacketHandler.INSTANCE); - } - }; - ADD_ENTITY_HANDLERS[MEntityTypes.INTERACTION$registryId] = (user, event) -> { - if (BukkitFurnitureManager.NMS_COLLISION_ENTITY_TYPE != MEntityTypes.INTERACTION) return; - FriendlyByteBuf buf = event.getBuffer(); - int id = buf.readVarInt(); - // Cancel collider entity packet - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(id); - if (furniture != null) { - event.setCancelled(true); - user.entityPacketHandlers().put(id, FurnitureCollisionPacketHandler.INSTANCE); - } - }; - ADD_ENTITY_HANDLERS[MEntityTypes.OAK_BOAT$registryId] = (user, event) -> { - if (BukkitFurnitureManager.NMS_COLLISION_ENTITY_TYPE != MEntityTypes.OAK_BOAT) return; - FriendlyByteBuf buf = event.getBuffer(); - int id = buf.readVarInt(); - // Cancel collider entity packet - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(id); - if (furniture != null) { - event.setCancelled(true); - user.entityPacketHandlers().put(id, FurnitureCollisionPacketHandler.INSTANCE); - } - }; - } - - private static BukkitNetworkManager.Handlers simpleAddEntityHandler(EntityPacketHandler handler) { - return (user, event) -> { - FriendlyByteBuf buf = event.getBuffer(); - user.entityPacketHandlers().put(buf.readVarInt(), handler); - }; - } - - private static BukkitNetworkManager.Handlers createOptionalCustomProjectileEntityHandler(boolean fallback) { - return (user, event) -> { - FriendlyByteBuf buf = event.getBuffer(); - int id = buf.readVarInt(); - BukkitProjectileManager.instance().projectileByEntityId(id).ifPresentOrElse(customProjectile -> { - ProjectilePacketHandler handler = new ProjectilePacketHandler(customProjectile, id); - handler.convertAddCustomProjectilePacket(buf, event); - user.entityPacketHandlers().put(id, handler); - }, () -> { - if (fallback) { - user.entityPacketHandlers().put(id, CommonItemPacketHandler.INSTANCE); - } - }); - }; - } - - public static void initBlocks(Map map, int registrySize) { - int[] newMappings = new int[registrySize]; - for (int i = 0; i < registrySize; i++) { - newMappings[i] = i; - } - int[] newMappingsMOD = Arrays.copyOf(newMappings, registrySize); - for (Map.Entry entry : map.entrySet()) { - newMappings[entry.getKey()] = entry.getValue(); - if (BlockStateUtils.isVanillaBlock(entry.getKey())) { - newMappingsMOD[entry.getKey()] = entry.getValue(); - } - } - for (int i = 0; i < newMappingsMOD.length; i++) { - if (BlockStateUtils.isVanillaBlock(i)) { - newMappingsMOD[i] = newMappings[i]; - } - } - BLOCK_STATE_MAPPINGS = newMappings; - MOD_BLOCK_STATE_MAPPINGS = newMappingsMOD; - SERVER_BLOCK_LIST = new IntIdentityList(registrySize); - CLIENT_BLOCK_LIST = new IntIdentityList(BlockStateUtils.vanillaStateSize()); - BIOME_LIST = new IntIdentityList(RegistryUtils.currentBiomeRegistrySize()); - } - - public static int remap(int stateId) { - return BLOCK_STATE_MAPPINGS[stateId]; - } - - public static int remapMOD(int stateId) { - return MOD_BLOCK_STATE_MAPPINGS[stateId]; - } - - public static final BiConsumer FORGET_LEVEL_CHUNK = (user, event) -> { - try { - FriendlyByteBuf buf = event.getBuffer(); - if (VersionHelper.isOrAbove1_20_2()) { - long chunkPos = buf.readLong(); - user.setChunkTrackStatus(new ChunkPos(chunkPos), false); - } else { - int x = buf.readInt(); - int y = buf.readInt(); - user.setChunkTrackStatus(ChunkPos.of(x, y), false); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundForgetLevelChunkPacket", e); - } - }; - - public static final BiConsumer LEVEL_CHUNK_WITH_LIGHT = (user, event) -> { - try { - BukkitServerPlayer player = (BukkitServerPlayer) user; - FriendlyByteBuf buf = event.getBuffer(); - int chunkX = buf.readInt(); - int chunkZ = buf.readInt(); - player.setChunkTrackStatus(ChunkPos.of(chunkX, chunkZ), true); - boolean named = !VersionHelper.isOrAbove1_20_2(); - // ClientboundLevelChunkPacketData - int heightmapsCount = 0; - Map heightmapsMap = null; - Tag heightmaps = null; - if (VersionHelper.isOrAbove1_21_5()) { - heightmapsMap = new HashMap<>(); - heightmapsCount = buf.readVarInt(); - for (int i = 0; i < heightmapsCount; i++) { - int key = buf.readVarInt(); - long[] value = buf.readLongArray(); - heightmapsMap.put(key, value); - } - } else { - heightmaps = buf.readNbt(named); - } - - int varInt = buf.readVarInt(); - byte[] buffer = new byte[varInt]; - buf.readBytes(buffer); - int blockEntitiesDataCount = buf.readVarInt(); - List blockEntitiesData = new ArrayList<>(); - for (int i = 0; i < blockEntitiesDataCount; i++) { - byte packedXZ = buf.readByte(); - short y = buf.readShort(); - int type = buf.readVarInt(); - Tag tag = buf.readNbt(named); - BlockEntityData blockEntityData = new BlockEntityData(packedXZ, y, type, tag); - blockEntitiesData.add(blockEntityData); - } - // ClientboundLightUpdatePacketData - BitSet skyYMask = buf.readBitSet(); - BitSet blockYMask = buf.readBitSet(); - BitSet emptySkyYMask = buf.readBitSet(); - BitSet emptyBlockYMask = buf.readBitSet(); - List skyUpdates = buf.readByteArrayList(2048); - List blockUpdates = buf.readByteArrayList(2048); - // 开始处理 - if (user.clientModEnabled()) { - ByteBuf byteBuf = Unpooled.copiedBuffer(buffer); - FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(byteBuf); - FriendlyByteBuf newBuf = new FriendlyByteBuf(Unpooled.buffer()); - for (int i = 0, count = player.clientSideSectionCount(); i < count; i++) { - MCSection mcSection = new MCSection(SERVER_BLOCK_LIST, SERVER_BLOCK_LIST, BIOME_LIST); - mcSection.readPacket(friendlyByteBuf); - PalettedContainer container = mcSection.blockStateContainer(); - Palette palette = container.data().palette(); - if (palette.canRemap()) { - palette.remap(PacketConsumers::remapMOD); - } else { - for (int j = 0; j < 4096; j++) { - int state = container.get(j); - int newState = remapMOD(state); - if (newState != state) { - container.set(j, newState); - } - } - } - mcSection.writePacket(newBuf); - } - buffer = newBuf.array(); - } else { - ByteBuf byteBuf = Unpooled.copiedBuffer(buffer); - FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(byteBuf); - FriendlyByteBuf newBuf = new FriendlyByteBuf(Unpooled.buffer()); - for (int i = 0, count = player.clientSideSectionCount(); i < count; i++) { - MCSection mcSection = new MCSection(CLIENT_BLOCK_LIST, SERVER_BLOCK_LIST, BIOME_LIST); - mcSection.readPacket(friendlyByteBuf); - PalettedContainer container = mcSection.blockStateContainer(); - Palette palette = container.data().palette(); - if (palette.canRemap()) { - palette.remap(PacketConsumers::remap); - } else { - for (int j = 0; j < 4096; j++) { - int state = container.get(j); - int newState = remap(state); - if (newState != state) { - container.set(j, newState); - } - } - } - mcSection.writePacket(newBuf); - } - buffer = newBuf.array(); - } - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeInt(chunkX); - buf.writeInt(chunkZ); - if (VersionHelper.isOrAbove1_21_5()) { - buf.writeVarInt(heightmapsCount); - for (Map.Entry entry : heightmapsMap.entrySet()) { - buf.writeVarInt(entry.getKey()); - buf.writeLongArray(entry.getValue()); - } - } else { - buf.writeNbt(heightmaps, named); - } - buf.writeVarInt(buffer.length); - buf.writeBytes(buffer); - buf.writeVarInt(blockEntitiesDataCount); - for (BlockEntityData blockEntityData : blockEntitiesData) { - buf.writeByte(blockEntityData.packedXZ()); - buf.writeShort(blockEntityData.y()); - buf.writeVarInt(blockEntityData.type()); - buf.writeNbt(blockEntityData.tag(), named); - } - buf.writeBitSet(skyYMask); - buf.writeBitSet(blockYMask); - buf.writeBitSet(emptySkyYMask); - buf.writeBitSet(emptyBlockYMask); - buf.writeByteArrayList(skyUpdates); - buf.writeByteArrayList(blockUpdates); - event.setChanged(true); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundLevelChunkWithLightPacket", e); - } - }; - - public static final BiConsumer SECTION_BLOCK_UPDATE = (user, event) -> { - try { - if (user.clientModEnabled()) { - FriendlyByteBuf buf = event.getBuffer(); - long pos = buf.readLong(); - int blocks = buf.readVarInt(); - short[] positions = new short[blocks]; - int[] states = new int[blocks]; - for (int i = 0; i < blocks; i++) { - long k = buf.readVarLong(); - positions[i] = (short) ((int) (k & 4095L)); - states[i] = remapMOD((int) (k >>> 12)); - } - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeLong(pos); - buf.writeVarInt(blocks); - for (int i = 0; i < blocks; i++) { - buf.writeVarLong((long) states[i] << 12 | positions[i]); - } - event.setChanged(true); - } else { - FriendlyByteBuf buf = event.getBuffer(); - long pos = buf.readLong(); - int blocks = buf.readVarInt(); - short[] positions = new short[blocks]; - int[] states = new int[blocks]; - for (int i = 0; i < blocks; i++) { - long k = buf.readVarLong(); - positions[i] = (short) ((int) (k & 4095L)); - states[i] = remap((int) (k >>> 12)); - } - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeLong(pos); - buf.writeVarInt(blocks); - for (int i = 0; i < blocks; i++) { - buf.writeVarLong((long) states[i] << 12 | positions[i]); - } - event.setChanged(true); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSectionBlocksUpdatePacket", e); - } - }; - - public static final BiConsumer BLOCK_UPDATE = (user, event) -> { - try { - FriendlyByteBuf buf = event.getBuffer(); - BlockPos pos = buf.readBlockPos(); - int before = buf.readVarInt(); - if (user.clientModEnabled() && !BlockStateUtils.isVanillaBlock(before)) { - return; - } - int state = user.clientModEnabled() ? remapMOD(before) : remap(before); - if (state == before) { - return; - } - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeBlockPos(pos); - buf.writeVarInt(state); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundBlockUpdatePacket", e); - } - }; - - public static final BiConsumer LEVEL_EVENT = (user, event) -> { - try { - FriendlyByteBuf buf = event.getBuffer(); - int eventId = buf.readInt(); - if (eventId != WorldEvents.BLOCK_BREAK_EFFECT) return; - BlockPos blockPos = buf.readBlockPos(); - int state = buf.readInt(); - boolean global = buf.readBoolean(); - int newState = user.clientModEnabled() ? remapMOD(state) : remap(state); - if (newState == state) { - return; - } - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeInt(eventId); - buf.writeBlockPos(blockPos); - buf.writeInt(newState); - buf.writeBoolean(global); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundLevelEventPacket", e); - } - }; - - public static final BiConsumer TEAM_1_20_3 = (user, event) -> { - if (!Config.interceptTeam()) return; - try { - FriendlyByteBuf buf = event.getBuffer(); - String name = buf.readUtf(); - byte method = buf.readByte(); - if (method != 2 && method != 0) return; - Tag displayName = buf.readNbt(false); - if (displayName == null) return; - byte friendlyFlags = buf.readByte(); - Either eitherVisibility = VersionHelper.isOrAbove1_21_5() ? Either.right(buf.readVarInt()) : Either.left(buf.readUtf(40)); - Either eitherCollisionRule = VersionHelper.isOrAbove1_21_5() ? Either.right(buf.readVarInt()) : Either.left(buf.readUtf(40)); - int color = buf.readVarInt(); - Tag prefix = buf.readNbt(false); - if (prefix == null) return; - Tag suffix = buf.readNbt(false); - if (suffix == null) return; - Map tokens1 = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); - Map tokens2 = CraftEngine.instance().fontManager().matchTags(prefix.getAsString()); - Map tokens3 = CraftEngine.instance().fontManager().matchTags(suffix.getAsString()); - if (tokens1.isEmpty() && tokens2.isEmpty() && tokens3.isEmpty()) return; - NetworkTextReplaceContext context = NetworkTextReplaceContext.of((BukkitServerPlayer) user); - List entities = method == 0 ? buf.readStringList() : null; - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeUtf(name); - buf.writeByte(method); - buf.writeNbt(tokens1.isEmpty() ? displayName : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens1, context)), false); - buf.writeByte(friendlyFlags); - eitherVisibility.ifLeft(buf::writeUtf).ifRight(buf::writeVarInt); - eitherCollisionRule.ifLeft(buf::writeUtf).ifRight(buf::writeVarInt); - buf.writeVarInt(color); - buf.writeNbt(tokens2.isEmpty() ? prefix : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(prefix), tokens2, context)), false); - buf.writeNbt(tokens3.isEmpty() ? suffix : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(suffix), tokens3, context)), false); - if (entities != null) { - buf.writeStringList(entities); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSetPlayerTeamPacket", e); - } - }; - - public static final TriConsumer PLAYER_INFO_UPDATE = (user, event, packet) -> { - try { - if (!user.isOnline()) return; - if (!Config.interceptPlayerInfo()) return; - List entries = FastNMS.INSTANCE.field$ClientboundPlayerInfoUpdatePacket$entries(packet); - if (entries instanceof MarkedArrayList) { - return; - } - EnumSet> enums = FastNMS.INSTANCE.field$ClientboundPlayerInfoUpdatePacket$actions(packet); - outer: { - for (Object entry : enums) { - if (entry == NetworkReflections.instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_DISPLAY_NAME) { - break outer; - } - } - return; - } - boolean isChanged = false; - List newEntries = new MarkedArrayList<>(); - for (Object entry : entries) { - Object mcComponent = FastNMS.INSTANCE.field$ClientboundPlayerInfoUpdatePacket$Entry$displayName(entry); - if (mcComponent == null) { - newEntries.add(entry); - } else { - String json = ComponentUtils.minecraftToJson(mcComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); - if (tokens.isEmpty()) { - newEntries.add(entry); - } else { - Object newEntry = FastNMS.INSTANCE.constructor$ClientboundPlayerInfoUpdatePacket$Entry(entry, - ComponentUtils.adventureToMinecraft(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); - newEntries.add(newEntry); - isChanged = true; - } - } - } - if (isChanged) { - event.replacePacket(FastNMS.INSTANCE.constructor$ClientboundPlayerInfoUpdatePacket(enums, newEntries)); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundPlayerInfoUpdatePacket", e); - } - }; - - public static final BiConsumer TEAM_1_20 = (user, event) -> { - if (!Config.interceptTeam()) return; - try { - FriendlyByteBuf buf = event.getBuffer(); - String name = buf.readUtf(); - byte method = buf.readByte(); - if (method != 2 && method != 0) - return; - String displayName = buf.readUtf(); - byte friendlyFlags = buf.readByte(); - String nameTagVisibility = buf.readUtf(40); - String collisionRule = buf.readUtf(40); - int color = buf.readVarInt(); - String prefix = buf.readUtf(); - String suffix = buf.readUtf(); - - Map tokens1 = CraftEngine.instance().fontManager().matchTags(displayName); - Map tokens2 = CraftEngine.instance().fontManager().matchTags(prefix); - Map tokens3 = CraftEngine.instance().fontManager().matchTags(suffix); - if (tokens1.isEmpty() && tokens2.isEmpty() && tokens3.isEmpty()) return; - event.setChanged(true); - NetworkTextReplaceContext context = NetworkTextReplaceContext.of((BukkitServerPlayer) user); - - List entities = method == 0 ? buf.readStringList() : null; - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeUtf(name); - buf.writeByte(method); - buf.writeUtf(tokens1.isEmpty() ? displayName : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(displayName), tokens1, context))); - buf.writeByte(friendlyFlags); - buf.writeUtf(nameTagVisibility); - buf.writeUtf(collisionRule); - buf.writeVarInt(color); - buf.writeUtf(tokens2.isEmpty() ? prefix : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(prefix), tokens2, context))); - buf.writeUtf(tokens3.isEmpty() ? suffix : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(suffix), tokens3, context))); - if (entities != null) { - buf.writeStringList(entities); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSetPlayerTeamPacket", e); - } - }; - - public static final BiConsumer BOSS_EVENT_1_20 = (user, event) -> { - if (!Config.interceptBossBar()) return; - try { - FriendlyByteBuf buf = event.getBuffer(); - UUID uuid = buf.readUUID(); - int actionType = buf.readVarInt(); - if (actionType == 0) { - String json = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); - if (tokens.isEmpty()) return; - float health = buf.readFloat(); - int color = buf.readVarInt(); - int division = buf.readVarInt(); - byte flag = buf.readByte(); - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeUUID(uuid); - buf.writeVarInt(actionType); - buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); - buf.writeFloat(health); - buf.writeVarInt(color); - buf.writeVarInt(division); - buf.writeByte(flag); - } else if (actionType == 3) { - String json = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); - if (tokens.isEmpty()) return; - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeUUID(uuid); - buf.writeVarInt(actionType); - buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundBossEventPacket", e); - } - }; - - public static final BiConsumer BOSS_EVENT_1_20_3 = (user, event) -> { - if (!Config.interceptBossBar()) return; - try { - FriendlyByteBuf buf = event.getBuffer(); - UUID uuid = buf.readUUID(); - int actionType = buf.readVarInt(); - if (actionType == 0) { - Tag nbt = buf.readNbt(false); - if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); - if (tokens.isEmpty()) return; - float health = buf.readFloat(); - int color = buf.readVarInt(); - int division = buf.readVarInt(); - byte flag = buf.readByte(); - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeUUID(uuid); - buf.writeVarInt(actionType); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); - buf.writeFloat(health); - buf.writeVarInt(color); - buf.writeVarInt(division); - buf.writeByte(flag); - } else if (actionType == 3) { - Tag nbt = buf.readNbt(false); - if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); - if (tokens.isEmpty()) return; - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeUUID(uuid); - buf.writeVarInt(actionType); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundBossEventPacket", e); - } - }; - - public static final BiConsumer SET_OBJECTIVE_1_20 = (user, event) -> { - if (!Config.interceptScoreboard()) return; - try { - FriendlyByteBuf buf = event.getBuffer(); - String objective = buf.readUtf(); - byte mode = buf.readByte(); - if (mode != 0 && mode != 2) return; - String displayName = buf.readUtf(); - int renderType = buf.readVarInt(); - Map tokens = CraftEngine.instance().fontManager().matchTags(displayName); - if (tokens.isEmpty()) return; - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeUtf(objective); - buf.writeByte(mode); - buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(displayName), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); - buf.writeVarInt(renderType); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSetObjectivePacket", e); - } - }; - - public static final BiConsumer SET_OBJECTIVE_1_20_3 = (user, event) -> { - if (!Config.interceptScoreboard()) return; - try { - FriendlyByteBuf buf = event.getBuffer(); - String objective = buf.readUtf(); - byte mode = buf.readByte(); - if (mode != 0 && mode != 2) return; - Tag displayName = buf.readNbt(false); - if (displayName == null) return; - int renderType = buf.readVarInt(); - boolean optionalNumberFormat = buf.readBoolean(); - if (optionalNumberFormat) { - int format = buf.readVarInt(); - if (format == 0) { - Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); - if (tokens.isEmpty()) return; - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeUtf(objective); - buf.writeByte(mode); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); - buf.writeVarInt(renderType); - buf.writeBoolean(true); - buf.writeVarInt(0); - } else if (format == 1) { - Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); - if (tokens.isEmpty()) return; - Tag style = buf.readNbt(false); - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeUtf(objective); - buf.writeByte(mode); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); - buf.writeVarInt(renderType); - buf.writeBoolean(true); - buf.writeVarInt(1); - buf.writeNbt(style, false); - } else if (format == 2) { - Tag fixed = buf.readNbt(false); - if (fixed == null) return; - Map tokens1 = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); - Map tokens2 = CraftEngine.instance().fontManager().matchTags(fixed.getAsString()); - if (tokens1.isEmpty() && tokens2.isEmpty()) return; - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeUtf(objective); - buf.writeByte(mode); - buf.writeNbt(tokens1.isEmpty() ? displayName : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens1, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); - buf.writeVarInt(renderType); - buf.writeBoolean(true); - buf.writeVarInt(2); - buf.writeNbt(tokens2.isEmpty() ? fixed : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(fixed), tokens2, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); - } - } else { - Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); - if (tokens.isEmpty()) return; - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeUtf(objective); - buf.writeByte(mode); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); - buf.writeVarInt(renderType); - buf.writeBoolean(false); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSetObjectivePacket", e); - } - }; - - public static final BiConsumer SYSTEM_CHAT_1_20 = (user, event) -> { - if (!Config.interceptSystemChat()) return; - try { - FriendlyByteBuf buf = event.getBuffer(); - String jsonOrPlainString = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(jsonOrPlainString); - if (tokens.isEmpty()) return; - boolean overlay = buf.readBoolean(); - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(jsonOrPlainString), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); - buf.writeBoolean(overlay); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSystemChatPacket", e); - } - }; - - public static final BiConsumer SYSTEM_CHAT_1_20_3 = (user, event) -> { - if (!Config.interceptSystemChat()) return; - try { - FriendlyByteBuf buf = event.getBuffer(); - Tag nbt = buf.readNbt(false); - if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); - if (tokens.isEmpty()) return; - boolean overlay = buf.readBoolean(); - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); - buf.writeBoolean(overlay); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSystemChatPacket", e); - } - }; - - public static final BiConsumer SET_SUBTITLE_TEXT_1_20 = (user, event) -> { - if (!Config.interceptTitle()) return; - try { - FriendlyByteBuf buf = event.getBuffer(); - String json = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); - if (tokens.isEmpty()) return; - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSetSubtitleTextPacket", e); - } - }; - - public static final BiConsumer SET_SUBTITLE_TEXT_1_20_3 = (user, event) -> { - if (!Config.interceptTitle()) return; - try { - FriendlyByteBuf buf = event.getBuffer(); - Tag nbt = buf.readNbt(false); - if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); - if (tokens.isEmpty()) return; - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSetSubtitleTextPacket", e); - } - }; - - public static final BiConsumer SET_TITLE_TEXT_1_20 = (user, event) -> { - if (!Config.interceptTitle()) return; - try { - FriendlyByteBuf buf = event.getBuffer(); - String json = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); - if (tokens.isEmpty()) return; - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSetTitleTextPacket", e); - } - }; - - public static final BiConsumer SET_TITLE_TEXT_1_20_3 = (user, event) -> { - if (!Config.interceptTitle()) return; - try { - FriendlyByteBuf buf = event.getBuffer(); - Tag nbt = buf.readNbt(false); - if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); - if (tokens.isEmpty()) return; - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSetTitleTextPacket", e); - } - }; - - public static final BiConsumer SET_ACTIONBAR_TEXT_1_20 = (user, event) -> { - if (!Config.interceptActionBar()) return; - try { - FriendlyByteBuf buf = event.getBuffer(); - String json = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); - if (tokens.isEmpty()) return; - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSetActionBarTextPacket", e); - } - }; - - public static final BiConsumer SET_ACTIONBAR_TEXT_1_20_3 = (user, event) -> { - if (!Config.interceptActionBar()) return; - try { - FriendlyByteBuf buf = event.getBuffer(); - Tag nbt = buf.readNbt(false); - if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); - if (tokens.isEmpty()) return; - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSetActionBarTextPacket", e); - } - }; - - public static final BiConsumer TAB_LIST_1_20 = (user, event) -> { - if (!Config.interceptTabList()) return; - try { - FriendlyByteBuf buf = event.getBuffer(); - String json1 = buf.readUtf(); - String json2 = buf.readUtf(); - Map tokens1 = CraftEngine.instance().fontManager().matchTags(json1); - Map tokens2 = CraftEngine.instance().fontManager().matchTags(json2); - if (tokens1.isEmpty() && tokens2.isEmpty()) return; - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - NetworkTextReplaceContext context = NetworkTextReplaceContext.of((BukkitServerPlayer) user); - buf.writeUtf(tokens1.isEmpty() ? json1 : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json1), tokens1, context))); - buf.writeUtf(tokens2.isEmpty() ? json2 : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json2), tokens2, context))); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundTabListPacket", e); - } - }; - - public static final BiConsumer TAB_LIST_1_20_3 = (user, event) -> { - if (!Config.interceptTabList()) return; - try { - FriendlyByteBuf buf = event.getBuffer(); - Tag nbt1 = buf.readNbt(false); - if (nbt1 == null) return; - Tag nbt2 = buf.readNbt(false); - if (nbt2 == null) return; - Map tokens1 = CraftEngine.instance().fontManager().matchTags(nbt1.getAsString()); - Map tokens2 = CraftEngine.instance().fontManager().matchTags(nbt2.getAsString()); - if (tokens1.isEmpty() && tokens2.isEmpty()) return; - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - NetworkTextReplaceContext context = NetworkTextReplaceContext.of((BukkitServerPlayer) user); - buf.writeNbt(tokens1.isEmpty() ? nbt1 : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt1), tokens1, context)), false); - buf.writeNbt(tokens2.isEmpty() ? nbt2 : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt2), tokens2, context)), false); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundTabListPacket", e); - } - }; - - public static final BiConsumer OPEN_SCREEN_1_20 = (user, event) -> { - if (!Config.interceptContainer()) return; - try { - FriendlyByteBuf buf = event.getBuffer(); - int containerId = buf.readVarInt(); - int type = buf.readVarInt(); - String json = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); - if (tokens.isEmpty()) return; - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeVarInt(containerId); - buf.writeVarInt(type); - buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user)))); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundOpenScreenPacket", e); - } - }; - - public static final BiConsumer OPEN_SCREEN_1_20_3 = (user, event) -> { - if (!Config.interceptContainer()) return; - try { - FriendlyByteBuf buf = event.getBuffer(); - int containerId = buf.readVarInt(); - int type = buf.readVarInt(); - Tag nbt = buf.readNbt(false); - if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); - if (tokens.isEmpty()) return; - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeVarInt(containerId); - buf.writeVarInt(type); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundOpenScreenPacket", e); - } - }; - - public static final BiConsumer LEVEL_PARTICLE_1_21_4 = (user, event) -> { - try { - FriendlyByteBuf buf = event.getBuffer(); - boolean overrideLimiter = buf.readBoolean(); - boolean alwaysShow = buf.readBoolean(); - double x = buf.readDouble(); - double y = buf.readDouble(); - double z = buf.readDouble(); - float xDist = buf.readFloat(); - float yDist = buf.readFloat(); - float zDist = buf.readFloat(); - float maxSpeed = buf.readFloat(); - int count = buf.readInt(); - Object option = FastNMS.INSTANCE.method$StreamCodec$decode(NetworkReflections.instance$ParticleTypes$STREAM_CODEC, buf); - if (option == null) return; - if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return; - Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option); - int id = BlockStateUtils.blockStateToId(blockState); - int remapped = user.clientModEnabled() ? remapMOD(id) : remap(id); - if (remapped == id) return; - Object type = FastNMS.INSTANCE.method$BlockParticleOption$getType(option); - Object remappedOption = FastNMS.INSTANCE.constructor$BlockParticleOption(type, BlockStateUtils.idToBlockState(remapped)); - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeBoolean(overrideLimiter); - buf.writeBoolean(alwaysShow); - buf.writeDouble(x); - buf.writeDouble(y); - buf.writeDouble(z); - buf.writeFloat(xDist); - buf.writeFloat(yDist); - buf.writeFloat(zDist); - buf.writeFloat(maxSpeed); - buf.writeInt(count); - FastNMS.INSTANCE.method$StreamCodec$encode(NetworkReflections.instance$ParticleTypes$STREAM_CODEC, buf, remappedOption); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundLevelParticlesPacket", e); - } - }; - - public static final BiConsumer LEVEL_PARTICLE_1_20_5 = (user, event) -> { - try { - FriendlyByteBuf buf = event.getBuffer(); - boolean overrideLimiter = buf.readBoolean(); - double x = buf.readDouble(); - double y = buf.readDouble(); - double z = buf.readDouble(); - float xDist = buf.readFloat(); - float yDist = buf.readFloat(); - float zDist = buf.readFloat(); - float maxSpeed = buf.readFloat(); - int count = buf.readInt(); - Object option = FastNMS.INSTANCE.method$StreamCodec$decode(NetworkReflections.instance$ParticleTypes$STREAM_CODEC, buf); - if (option == null) return; - if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return; - Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option); - int id = BlockStateUtils.blockStateToId(blockState); - int remapped = user.clientModEnabled() ? remapMOD(id) : remap(id); - if (remapped == id) return; - Object type = FastNMS.INSTANCE.method$BlockParticleOption$getType(option); - Object remappedOption = FastNMS.INSTANCE.constructor$BlockParticleOption(type, BlockStateUtils.idToBlockState(remapped)); - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeBoolean(overrideLimiter); - buf.writeDouble(x); - buf.writeDouble(y); - buf.writeDouble(z); - buf.writeFloat(xDist); - buf.writeFloat(yDist); - buf.writeFloat(zDist); - buf.writeFloat(maxSpeed); - buf.writeInt(count); - FastNMS.INSTANCE.method$StreamCodec$encode(NetworkReflections.instance$ParticleTypes$STREAM_CODEC, buf, remappedOption); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundLevelParticlesPacket", e); - } - }; - - public static final BiConsumer LEVEL_PARTICLE_1_20 = (user, event) -> { - try { - FriendlyByteBuf buf = event.getBuffer(); - Object particleType = FastNMS.INSTANCE.method$FriendlyByteBuf$readById(buf, MBuiltInRegistries.PARTICLE_TYPE); - boolean overrideLimiter = buf.readBoolean(); - double x = buf.readDouble(); - double y = buf.readDouble(); - double z = buf.readDouble(); - float xDist = buf.readFloat(); - float yDist = buf.readFloat(); - float zDist = buf.readFloat(); - float maxSpeed = buf.readFloat(); - int count = buf.readInt(); - Object option = FastNMS.INSTANCE.method$ClientboundLevelParticlesPacket$readParticle(buf, particleType); - if (option == null) return; - if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return; - Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option); - int id = BlockStateUtils.blockStateToId(blockState); - int remapped = user.clientModEnabled() ? remapMOD(id) : remap(id); - if (remapped == id) return; - Object type = FastNMS.INSTANCE.method$BlockParticleOption$getType(option); - Object remappedOption = FastNMS.INSTANCE.constructor$BlockParticleOption(type, BlockStateUtils.idToBlockState(remapped)); - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - FastNMS.INSTANCE.method$FriendlyByteBuf$writeId(buf, remappedOption, MBuiltInRegistries.PARTICLE_TYPE); - buf.writeBoolean(overrideLimiter); - buf.writeDouble(x); - buf.writeDouble(y); - buf.writeDouble(z); - buf.writeFloat(xDist); - buf.writeFloat(yDist); - buf.writeFloat(zDist); - buf.writeFloat(maxSpeed); - buf.writeInt(count); - FastNMS.INSTANCE.method$ParticleOptions$writeToNetwork(remappedOption, buf); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundLevelParticlesPacket", e); - } - }; - - public static final TriConsumer PLAYER_ACTION = (user, event, packet) -> { - try { - if (!user.isOnline()) return; - BukkitServerPlayer player = (BukkitServerPlayer) user; - Player platformPlayer = player.platformPlayer(); - World world = platformPlayer.getWorld(); - Object blockPos = FastNMS.INSTANCE.field$ServerboundPlayerActionPacket$pos(packet); - BlockPos pos = LocationUtils.fromBlockPos(blockPos); - if (VersionHelper.isFolia()) { - platformPlayer.getScheduler().run(BukkitCraftEngine.instance().javaPlugin(), (t) -> { - try { - handlePlayerActionPacketOnMainThread(player, world, pos, packet); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundPlayerActionPacket", e); - } - }, () -> {}); - } else { - handlePlayerActionPacketOnMainThread(player, world, pos, packet); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundPlayerActionPacket", e); - } - }; - - private static void handlePlayerActionPacketOnMainThread(BukkitServerPlayer player, World world, BlockPos pos, Object packet) { - Object action = FastNMS.INSTANCE.field$ServerboundPlayerActionPacket$action(packet); - if (action == NetworkReflections.instance$ServerboundPlayerActionPacket$Action$START_DESTROY_BLOCK) { - Object serverLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(world); - Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(serverLevel, LocationUtils.toBlockPos(pos)); - int stateId = BlockStateUtils.blockStateToId(blockState); - // not a custom block - if (BlockStateUtils.isVanillaBlock(stateId)) { - if (Config.enableSoundSystem()) { - Object blockOwner = FastNMS.INSTANCE.method$BlockState$getBlock(blockState); - if (BukkitBlockManager.instance().isBlockSoundRemoved(blockOwner)) { - player.startMiningBlock(pos, blockState, null); - return; - } - } - if (player.isMiningBlock()) { - player.stopMiningBlock(); - } else { - player.setClientSideCanBreakBlock(true); - } - return; - } - if (player.isAdventureMode()) { - if (Config.simplifyAdventureBreakCheck()) { - ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId); - if (!player.canBreak(pos, state.vanillaBlockState().literalObject())) { - player.preventMiningBlock(); - return; - } - } else { - if (!player.canBreak(pos, null)) { - player.preventMiningBlock(); - return; - } - } - } - player.startMiningBlock(pos, blockState, BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId)); - } else if (action == NetworkReflections.instance$ServerboundPlayerActionPacket$Action$ABORT_DESTROY_BLOCK) { - if (player.isMiningBlock()) { - player.abortMiningBlock(); - } - } else if (action == NetworkReflections.instance$ServerboundPlayerActionPacket$Action$STOP_DESTROY_BLOCK) { - if (player.isMiningBlock()) { - player.stopMiningBlock(); - } - } - } - - public static final TriConsumer HELLO_C2S = (user, event, packet) -> { - try { - BukkitServerPlayer player = (BukkitServerPlayer) user; - String name = (String) NetworkReflections.methodHandle$ServerboundHelloPacket$nameGetter.invokeExact(packet); - player.setName(name); - if (VersionHelper.isOrAbove1_20_2()) { - UUID uuid = (UUID) NetworkReflections.methodHandle$ServerboundHelloPacket$uuidGetter.invokeExact(packet); - player.setUUID(uuid); - } else { - @SuppressWarnings("unchecked") - Optional uuid = (Optional) NetworkReflections.methodHandle$ServerboundHelloPacket$uuidGetter.invokeExact(packet); - if (uuid.isPresent()) { - player.setUUID(uuid.get()); - } else { - player.setUUID(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8))); - } - } - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundHelloPacket", e); - } - }; - - public static final TriConsumer SWING_HAND = (user, event, packet) -> { - try { - if (!user.isOnline()) return; - BukkitServerPlayer player = (BukkitServerPlayer) user; - if (!player.isMiningBlock()) return; - Object hand = FastNMS.INSTANCE.field$ServerboundSwingPacket$hand(packet); - if (hand == CoreReflections.instance$InteractionHand$MAIN_HAND) { - player.onSwingHand(); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundSwingPacket", e); - } - }; - - public static final TriConsumer USE_ITEM_ON = (user, event, packet) -> { - try { - if (!user.isOnline()) return; - BukkitServerPlayer player = (BukkitServerPlayer) user; - if (player.isMiningBlock()) { - player.stopMiningBlock(); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundUseItemOnPacket", e); - } - }; - - public static final TriConsumer RESPAWN = (user, event, packet) -> { - try { - BukkitServerPlayer player = (BukkitServerPlayer) user; - player.clearView(); - Object dimensionKey; - if (!VersionHelper.isOrAbove1_20_2()) { - dimensionKey = NetworkReflections.methodHandle$ClientboundRespawnPacket$dimensionGetter.invokeExact(packet); - } else { - Object commonInfo = NetworkReflections.methodHandle$ClientboundRespawnPacket$commonPlayerSpawnInfoGetter.invokeExact(packet); - dimensionKey = NetworkReflections.methodHandle$CommonPlayerSpawnInfo$dimensionGetter.invokeExact(commonInfo); - } - Object location = FastNMS.INSTANCE.field$ResourceKey$location(dimensionKey); - World world = Bukkit.getWorld(Objects.requireNonNull(NamespacedKey.fromString(location.toString()))); - if (world != null) { - int sectionCount = (world.getMaxHeight() - world.getMinHeight()) / 16; - player.setClientSideSectionCount(sectionCount); - player.setClientSideDimension(Key.of(location.toString())); - player.clearTrackedChunks(); - } else { - CraftEngine.instance().logger().warn("Failed to handle ClientboundRespawnPacket: World " + location + " does not exist"); - } - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundRespawnPacket", e); - } - }; - - public static final TriConsumer LOGIN = (user, event, packet) -> { - try { - BukkitServerPlayer player = (BukkitServerPlayer) user; - player.setConnectionState(ConnectionState.PLAY); - Object dimensionKey; - if (!VersionHelper.isOrAbove1_20_2()) { - dimensionKey = NetworkReflections.methodHandle$ClientboundLoginPacket$dimensionGetter.invokeExact(packet); - } else { - Object commonInfo = NetworkReflections.methodHandle$ClientboundLoginPacket$commonPlayerSpawnInfoGetter.invokeExact(packet); - dimensionKey = NetworkReflections.methodHandle$CommonPlayerSpawnInfo$dimensionGetter.invokeExact(commonInfo); - } - Object location = FastNMS.INSTANCE.field$ResourceKey$location(dimensionKey); - World world = Bukkit.getWorld(Objects.requireNonNull(NamespacedKey.fromString(location.toString()))); - if (world != null) { - int sectionCount = (world.getMaxHeight() - world.getMinHeight()) / 16; - player.setClientSideSectionCount(sectionCount); - player.setClientSideDimension(Key.of(location.toString())); - } else { - CraftEngine.instance().logger().warn("Failed to handle ClientboundLoginPacket: World " + location + " does not exist"); - } - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundLoginPacket", e); - } - }; - - // 1.21.4- - // We can't find the best solution, we can only keep the feel as good as possible - // When the hotbar is full, the latest creative mode inventory can only be accessed when the player opens the inventory screen. Currently, it is not worth further handling this issue. - public static final TriConsumer SET_CREATIVE_SLOT = (user, event, packet) -> { - try { - if (VersionHelper.isOrAbove1_21_4()) return; - if (!user.isOnline()) return; - BukkitServerPlayer player = (BukkitServerPlayer) user; - if (VersionHelper.isFolia()) { - player.platformPlayer().getScheduler().run(BukkitCraftEngine.instance().javaPlugin(), (t) -> { - try { - handleSetCreativeSlotPacketOnMainThread(player, packet); - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundSetCreativeModeSlotPacket", e); - } - }, () -> {}); - } else { - handleSetCreativeSlotPacketOnMainThread(player, packet); - } - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundSetCreativeModeSlotPacket", e); - } - }; - - private static void handleSetCreativeSlotPacketOnMainThread(BukkitServerPlayer player, Object packet) throws Throwable { - Player bukkitPlayer = player.platformPlayer(); - if (bukkitPlayer == null) return; - if (bukkitPlayer.getGameMode() != GameMode.CREATIVE) return; - int slot = VersionHelper.isOrAbove1_20_5() ? (short) NetworkReflections.methodHandle$ServerboundSetCreativeModeSlotPacket$slotNumGetter.invokeExact(packet) : (int) NetworkReflections.methodHandle$ServerboundSetCreativeModeSlotPacket$slotNumGetter.invokeExact(packet); - if (slot < 36 || slot > 44) return; - ItemStack item = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(NetworkReflections.methodHandle$ServerboundSetCreativeModeSlotPacket$itemStackGetter.invokeExact(packet)); - if (ItemStackUtils.isEmpty(item)) return; - if (slot - 36 != bukkitPlayer.getInventory().getHeldItemSlot()) { - return; - } - double interactionRange = player.getCachedInteractionRange(); - // do ray trace to get current block - RayTraceResult result = bukkitPlayer.rayTraceBlocks(interactionRange, FluidCollisionMode.NEVER); - if (result == null) return; - Block hitBlock = result.getHitBlock(); - if (hitBlock == null) return; - ImmutableBlockState state = CraftEngineBlocks.getCustomBlockState(hitBlock); - // not a custom block - if (state == null || state.isEmpty()) return; - Key itemId = state.settings().itemId(); - // no item available - if (itemId == null) return; - Object vanillaBlock = FastNMS.INSTANCE.method$BlockState$getBlock(state.vanillaBlockState().literalObject()); - Object vanillaBlockItem = FastNMS.INSTANCE.method$Block$asItem(vanillaBlock); - if (vanillaBlockItem == null) return; - Key addItemId = KeyUtils.namespacedKey2Key(item.getType().getKey()); - Key blockItemId = KeyUtils.resourceLocationToKey(FastNMS.INSTANCE.method$Registry$getKey(MBuiltInRegistries.ITEM, vanillaBlockItem)); - if (!addItemId.equals(blockItemId)) return; - ItemStack itemStack = BukkitCraftEngine.instance().itemManager().buildCustomItemStack(itemId, player); - if (ItemStackUtils.isEmpty(itemStack)) { - CraftEngine.instance().logger().warn("Item: " + itemId + " is not a valid item"); - return; - } - PlayerInventory inventory = bukkitPlayer.getInventory(); - int sameItemSlot = -1; - int emptySlot = -1; - for (int i = 0; i < 9 + 27; i++) { - ItemStack invItem = inventory.getItem(i); - if (ItemStackUtils.isEmpty(invItem)) { - if (emptySlot == -1 && i < 9) emptySlot = i; - continue; - } - if (invItem.getType().equals(itemStack.getType()) && invItem.getItemMeta().equals(itemStack.getItemMeta())) { - if (sameItemSlot == -1) sameItemSlot = i; - } - } - if (sameItemSlot != -1) { - if (sameItemSlot < 9) { - inventory.setHeldItemSlot(sameItemSlot); - ItemStack previousItem = inventory.getItem(slot - 36); - BukkitCraftEngine.instance().scheduler().sync().runDelayed(() -> inventory.setItem(slot - 36, previousItem)); - } else { - ItemStack sameItem = inventory.getItem(sameItemSlot); - int finalSameItemSlot = sameItemSlot; - BukkitCraftEngine.instance().scheduler().sync().runDelayed(() -> { - inventory.setItem(finalSameItemSlot, new ItemStack(Material.AIR)); - inventory.setItem(slot - 36, sameItem); - }); - } - } else { - if (item.getAmount() == 1) { - if (ItemStackUtils.isEmpty(inventory.getItem(slot - 36))) { - BukkitCraftEngine.instance().scheduler().sync().runDelayed(() -> inventory.setItem(slot - 36, itemStack)); - return; - } - if (emptySlot != -1) { - inventory.setHeldItemSlot(emptySlot); - inventory.setItem(emptySlot, itemStack); - } else { - BukkitCraftEngine.instance().scheduler().sync().runDelayed(() -> inventory.setItem(slot - 36, itemStack)); - } - } - } - } - - // 1.21.4+ - public static final TriConsumer PICK_ITEM_FROM_BLOCK = (user, event, packet) -> { - try { - if (!user.isOnline()) return; - Player player = (Player) user.platformPlayer(); - if (player == null) return; - Object pos = NetworkReflections.methodHandle$ServerboundPickItemFromBlockPacket$posGetter.invokeExact(packet); - if (VersionHelper.isFolia()) { - int x = FastNMS.INSTANCE.field$Vec3i$x(pos); - int z = FastNMS.INSTANCE.field$Vec3i$z(pos); - BukkitCraftEngine.instance().scheduler().sync().run(() -> { - try { - handlePickItemFromBlockPacketOnMainThread(player, pos); - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromBlockPacket", e); - } - }, player.getWorld(), x >> 4, z >> 4); - } else { - BukkitCraftEngine.instance().scheduler().sync().run(() -> { - try { - handlePickItemFromBlockPacketOnMainThread(player, pos); - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromBlockPacket", e); - } - }); - } - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromBlockPacket", e); - } - }; - - private static void handlePickItemFromBlockPacketOnMainThread(Player player, Object pos) throws Throwable { - Object serverLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld()); - Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(serverLevel, pos); - ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState)); - if (state == null) return; - Key itemId = state.settings().itemId(); - if (itemId == null) return; - pickItem(player, itemId, pos, null); - } - - // 1.21.4+ - public static final TriConsumer PICK_ITEM_FROM_ENTITY = (user, event, packet) -> { - try { - int entityId = (int) NetworkReflections.methodHandle$ServerboundPickItemFromEntityPacket$idGetter.invokeExact(packet); - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId); - if (furniture == null) return; - Player player = (Player) user.platformPlayer(); - if (player == null) return; - if (VersionHelper.isFolia()) { - player.getScheduler().run(BukkitCraftEngine.instance().javaPlugin(), (t) -> { - try { - handlePickItemFromEntityOnMainThread(player, furniture); - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromEntityPacket", e); - } - }, () -> {}); - } else { - BukkitCraftEngine.instance().scheduler().sync().run(() -> { - try { - handlePickItemFromEntityOnMainThread(player, furniture); - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromEntityPacket", e); - } - }); - } - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundPickItemFromEntityPacket", e); - } - }; - - private static void handlePickItemFromEntityOnMainThread(Player player, BukkitFurniture furniture) throws Throwable { - Key itemId = furniture.config().settings().itemId(); - if (itemId == null) return; - pickItem(player, itemId, null, FastNMS.INSTANCE.method$CraftEntity$getHandle(furniture.baseEntity())); - } - - private static void pickItem(Player player, Key itemId, @Nullable Object blockPos, @Nullable Object entity) throws Throwable { - ItemStack itemStack = BukkitCraftEngine.instance().itemManager().buildCustomItemStack(itemId, BukkitCraftEngine.instance().adapt(player)); - if (itemStack == null) { - CraftEngine.instance().logger().warn("Item: " + itemId + " is not a valid item"); - return; - } - assert CoreReflections.method$ServerGamePacketListenerImpl$tryPickItem != null; - if (VersionHelper.isOrAbove1_21_5()) { - CoreReflections.method$ServerGamePacketListenerImpl$tryPickItem.invoke( - CoreReflections.methodHandle$ServerPlayer$connectionGetter.invokeExact(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)), - FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack), blockPos, entity, true); - } else { - CoreReflections.method$ServerGamePacketListenerImpl$tryPickItem.invoke( - CoreReflections.methodHandle$ServerPlayer$connectionGetter.invokeExact(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)), FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack)); - } - } - - public static final BiConsumer ADD_ENTITY = (user, event) -> { - try { - FriendlyByteBuf buf = event.getBuffer(); - buf.readVarInt(); - buf.readUUID(); - int type = buf.readVarInt(); - ADD_ENTITY_HANDLERS[type].accept(user, event); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundAddEntityPacket", e); - } - }; - - // 1.21.2+ - public static final TriConsumer SYNC_ENTITY_POSITION = (user, event, packet) -> { - try { - int entityId = FastNMS.INSTANCE.method$ClientboundEntityPositionSyncPacket$id(packet); - EntityPacketHandler handler = user.entityPacketHandlers().get(entityId); - if (handler != null) { - handler.handleSyncEntityPosition((BukkitServerPlayer) user, event, packet); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundEntityPositionSyncPacket", e); - } - }; - - public static final BiConsumer REMOVE_ENTITY = (user, event) -> { - try { - FriendlyByteBuf buf = event.getBuffer(); - boolean isChange = false; - IntList intList = buf.readIntIdList(); - for (int i = 0, size = intList.size(); i < size; i++) { - int entityId = intList.getInt(i); - EntityPacketHandler handler = user.entityPacketHandlers().remove(entityId); - if (handler != null && handler.handleEntitiesRemove(intList)) { - isChange = true; - } - } - if (isChange) { - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeIntIdList(intList); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundRemoveEntitiesPacket", e); - } - }; - - public static final BiConsumer INTERACT_ENTITY = (user, event) -> { - try { - FriendlyByteBuf buf = event.getBuffer(); - int entityId = BukkitNetworkManager.hasModelEngine() ? - CraftEngine.instance().compatibilityManager().interactionToBaseEntity(buf.readVarInt()) : - buf.readVarInt(); - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId); - if (furniture == null) return; - int actionType = buf.readVarInt(); - BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user; - if (serverPlayer.isSpectatorMode()) return; - Player platformPlayer = serverPlayer.platformPlayer(); - Location location = furniture.baseEntity().getLocation(); - - Runnable mainThreadTask; - if (actionType == 1) { - // ATTACK - boolean usingSecondaryAction = buf.readBoolean(); - if (entityId != furniture.baseEntityId()) { - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeVarInt(furniture.baseEntityId()); - buf.writeVarInt(actionType); - buf.writeBoolean(usingSecondaryAction); - } - - mainThreadTask = () -> { - // todo 冒险模式破坏工具白名单 - if (serverPlayer.isAdventureMode() || - !furniture.isValid()) return; - - FurnitureAttemptBreakEvent preBreakEvent = new FurnitureAttemptBreakEvent(serverPlayer.platformPlayer(), furniture); - if (EventUtils.fireAndCheckCancel(preBreakEvent)) - return; - - if (!BukkitCraftEngine.instance().antiGriefProvider().canBreak(platformPlayer, location)) - return; - - FurnitureBreakEvent breakEvent = new FurnitureBreakEvent(serverPlayer.platformPlayer(), furniture); - if (EventUtils.fireAndCheckCancel(breakEvent)) - return; - - Cancellable cancellable = Cancellable.of(breakEvent::isCancelled, breakEvent::setCancelled); - // execute functions - PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() - .withParameter(DirectContextParameters.FURNITURE, furniture) - .withParameter(DirectContextParameters.EVENT, cancellable) - .withParameter(DirectContextParameters.HAND, InteractionHand.MAIN_HAND) - .withParameter(DirectContextParameters.ITEM_IN_HAND, serverPlayer.getItemInHand(InteractionHand.MAIN_HAND)) - .withParameter(DirectContextParameters.POSITION, furniture.position()) - ); - furniture.config().execute(context, EventTrigger.LEFT_CLICK); - furniture.config().execute(context, EventTrigger.BREAK); - if (cancellable.isCancelled()) { - return; - } - - CraftEngineFurniture.remove(furniture, serverPlayer, !serverPlayer.isCreativeMode(), true); - }; - } else if (actionType == 2) { - // INTERACT_AT - float x = buf.readFloat(); - float y = buf.readFloat(); - float z = buf.readFloat(); - Location interactionPoint = new Location(platformPlayer.getWorld(), x, y, z); - InteractionHand hand = buf.readVarInt() == 0 ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND; - boolean usingSecondaryAction = buf.readBoolean(); - if (entityId != furniture.baseEntityId()) { - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeVarInt(furniture.baseEntityId()); - buf.writeVarInt(actionType); - buf.writeFloat(x).writeFloat(y).writeFloat(z); - buf.writeVarInt(hand == InteractionHand.MAIN_HAND ? 0 : 1); - buf.writeBoolean(usingSecondaryAction); - } - - mainThreadTask = () -> { - if (!furniture.isValid()) { - return; - } - - FurnitureInteractEvent interactEvent = new FurnitureInteractEvent(serverPlayer.platformPlayer(), furniture, hand, interactionPoint); - if (EventUtils.fireAndCheckCancel(interactEvent)) { - return; - } - - Item itemInHand = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND); - Cancellable cancellable = Cancellable.of(interactEvent::isCancelled, interactEvent::setCancelled); - // execute functions - PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() - .withParameter(DirectContextParameters.EVENT, cancellable) - .withParameter(DirectContextParameters.FURNITURE, furniture) - .withParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand) - .withParameter(DirectContextParameters.HAND, hand) - .withParameter(DirectContextParameters.POSITION, furniture.position()) - ); - furniture.config().execute(context, EventTrigger.RIGHT_CLICK); - if (cancellable.isCancelled()) { - return; - } - - // 必须从网络包层面处理,否则无法获取交互的具体实体 - if (serverPlayer.isSecondaryUseActive() && !itemInHand.isEmpty()) { - // try placing another furniture above it - AABB hitBox = furniture.aabbByEntityId(entityId); - if (hitBox == null) return; - Optional> optionalCustomItem = itemInHand.getCustomItem(); - Location eyeLocation = platformPlayer.getEyeLocation(); - Vector direction = eyeLocation.getDirection(); - Location endLocation = eyeLocation.clone(); - endLocation.add(direction.multiply(serverPlayer.getCachedInteractionRange())); - Optional result = hitBox.clip(LocationUtils.toVec3d(eyeLocation), LocationUtils.toVec3d(endLocation)); - if (result.isEmpty()) { - return; - } - EntityHitResult hitResult = result.get(); - if (optionalCustomItem.isPresent() && !optionalCustomItem.get().behaviors().isEmpty()) { - for (ItemBehavior behavior : optionalCustomItem.get().behaviors()) { - if (behavior instanceof FurnitureItemBehavior) { - behavior.useOnBlock(new UseOnContext(serverPlayer, InteractionHand.MAIN_HAND, new BlockHitResult(hitResult.hitLocation(), hitResult.direction(), BlockPos.fromVec3d(hitResult.hitLocation()), false))); - return; - } - } - } - // now simulate vanilla item behavior - serverPlayer.setResendSound(); - FastNMS.INSTANCE.simulateInteraction( - serverPlayer.serverPlayer(), - DirectionUtils.toNMSDirection(hitResult.direction()), - hitResult.hitLocation().x, hitResult.hitLocation().y, hitResult.hitLocation().z, - LocationUtils.toBlockPos(hitResult.blockPos()) - ); - } else { - if (!serverPlayer.isSecondaryUseActive()) { - furniture.findFirstAvailableSeat(entityId).ifPresent(seatPos -> { - if (furniture.tryOccupySeat(seatPos)) { - furniture.spawnSeatEntityForPlayer(serverPlayer, seatPos); - } - }); - } - } - }; - } else if (actionType == 0) { - int hand = buf.readVarInt(); - boolean usingSecondaryAction = buf.readBoolean(); - if (entityId != furniture.baseEntityId()) { - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeVarInt(furniture.baseEntityId()); - buf.writeVarInt(actionType); - buf.writeVarInt(hand); - buf.writeBoolean(usingSecondaryAction); - } - return; - } else { - return; - } - - if (VersionHelper.isFolia()) { - platformPlayer.getScheduler().run(BukkitCraftEngine.instance().javaPlugin(), t -> mainThreadTask.run(), () -> {}); - } else { - BukkitCraftEngine.instance().scheduler().executeSync(mainThreadTask); - } - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundInteractPacket", e); - } - }; - - public static final BiConsumer SOUND = (user, event) -> { - try { - FriendlyByteBuf buf = event.getBuffer(); - int id = buf.readVarInt(); - if (id == 0) { - Key soundId = buf.readKey(); - Float range = null; - if (buf.readBoolean()) { - range = buf.readFloat(); - } - int source = buf.readVarInt(); - int x = buf.readInt(); - int y = buf.readInt(); - int z = buf.readInt(); - float volume = buf.readFloat(); - float pitch = buf.readFloat(); - long seed = buf.readLong(); - Key mapped = BukkitBlockManager.instance().replaceSoundIfExist(soundId); - if (mapped != null) { - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeVarInt(0); - buf.writeKey(mapped); - if (range != null) { - buf.writeBoolean(true); - buf.writeFloat(range); - } else { - buf.writeBoolean(false); - } - buf.writeVarInt(source); - buf.writeInt(x); - buf.writeInt(y); - buf.writeInt(z); - buf.writeFloat(volume); - buf.writeFloat(pitch); - buf.writeLong(seed); - } - } else { - Optional optionalSound = FastNMS.INSTANCE.method$IdMap$byId(MBuiltInRegistries.SOUND_EVENT, id - 1); - if (optionalSound.isEmpty()) return; - Object soundEvent = optionalSound.get(); - Key soundId = KeyUtils.resourceLocationToKey(FastNMS.INSTANCE.method$SoundEvent$location(soundEvent)); - int source = buf.readVarInt(); - int x = buf.readInt(); - int y = buf.readInt(); - int z = buf.readInt(); - float volume = buf.readFloat(); - float pitch = buf.readFloat(); - long seed = buf.readLong(); - Key mapped = BukkitBlockManager.instance().replaceSoundIfExist(soundId); - if (mapped != null) { - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeVarInt(0); - Object newId = KeyUtils.toResourceLocation(mapped); - Object newSoundEvent = FastNMS.INSTANCE.constructor$SoundEvent(newId, FastNMS.INSTANCE.method$SoundEvent$fixedRange(soundEvent)); - FastNMS.INSTANCE.method$SoundEvent$directEncode(buf, newSoundEvent); - buf.writeVarInt(source); - buf.writeInt(x); - buf.writeInt(y); - buf.writeInt(z); - buf.writeFloat(volume); - buf.writeFloat(pitch); - buf.writeLong(seed); - } - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSoundPacket", e); - } - }; - - // we handle it on packet level to prevent it from being captured by plugins - public static final TriConsumer RENAME_ITEM = (user, event, packet) -> { - try { - if (!Config.filterAnvil()) return; - if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_ANVIL)) { - return; - } - String message = (String) NetworkReflections.methodHandle$ServerboundRenameItemPacket$nameGetter.invokeExact(packet); - if (message != null && !message.isEmpty()) { - // check bypass - FontManager manager = CraftEngine.instance().fontManager(); - IllegalCharacterProcessResult result = manager.processIllegalCharacters(message); - if (result.has()) { - try { - NetworkReflections.methodHandle$ServerboundRenameItemPacket$nameSetter.invokeExact(packet, result.text()); - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to replace chat", e); - } - } - } - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundRenameItemPacket", e); - } - }; - - // we handle it on packet level to prevent it from being captured by plugins - public static final TriConsumer SIGN_UPDATE = (user, event, packet) -> { - try { - if (!Config.filterSign()) return; - // check bypass - if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_SIGN)) { - return; - } - String[] lines = (String[]) NetworkReflections.methodHandle$ServerboundSignUpdatePacket$linesGetter.invokeExact(packet); - FontManager manager = CraftEngine.instance().fontManager(); - if (!manager.isDefaultFontInUse()) return; - for (int i = 0; i < lines.length; i++) { - String line = lines[i]; - if (line != null && !line.isEmpty()) { - IllegalCharacterProcessResult result = manager.processIllegalCharacters(line); - if (result.has()) { - lines[i] = result.text(); - } - } - } - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundSignUpdatePacket", e); - } - }; - - // we handle it on packet level to prevent it from being captured by plugins - @SuppressWarnings("unchecked") - public static final TriConsumer EDIT_BOOK = (user, event, packet) -> { - try { - if (!Config.filterBook()) return; - FontManager manager = CraftEngine.instance().fontManager(); - if (!manager.isDefaultFontInUse()) return; - // check bypass - if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_BOOK)) { - return; - } - - boolean changed = false; - - List pages = (List) NetworkReflections.methodHandle$ServerboundEditBookPacket$pagesGetter.invokeExact(packet); - List newPages = new ArrayList<>(pages.size()); - Optional title = (Optional) NetworkReflections.methodHandle$ServerboundEditBookPacket$titleGetter.invokeExact(packet); - Optional newTitle; - - if (title.isPresent()) { - String titleStr = title.get(); - Pair result = processClientString(titleStr, manager); - newTitle = Optional.of(result.right()); - if (result.left()) { - changed = true; - } - } else { - newTitle = Optional.empty(); - } - - for (String page : pages) { - Pair result = processClientString(page, manager); - newPages.add(result.right()); - if (result.left()) { - changed = true; - } - } - - if (changed) { - Object newPacket = NetworkReflections.constructor$ServerboundEditBookPacket.newInstance( - (int) NetworkReflections.methodHandle$ServerboundEditBookPacket$slotGetter.invokeExact(packet), - newPages, - newTitle - ); - event.replacePacket(newPacket); - } - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundEditBookPacket", e); - } - }; - - private static Pair processClientString(String original, FontManager manager) { - if (original.isEmpty()) { - return Pair.of(false, original); - } - int[] codepoints = CharacterUtils.charsToCodePoints(original.toCharArray()); - int[] newCodepoints = new int[codepoints.length]; - boolean hasIllegal = false; - for (int i = 0; i < codepoints.length; i++) { - int codepoint = codepoints[i]; - if (manager.isIllegalCodepoint(codepoint)) { - newCodepoints[i] = '*'; - hasIllegal = true; - } else { - newCodepoints[i] = codepoint; - } - } - return hasIllegal ? Pair.of(true, new String(newCodepoints, 0, newCodepoints.length)) : Pair.of(false, original); - } - - public static final TriConsumer CUSTOM_PAYLOAD = (user, event, packet) -> { - try { - if (!VersionHelper.isOrAbove1_20_2()) return; - Object payload = NetworkReflections.methodHandle$ServerboundCustomPayloadPacket$payloadGetter.invokeExact(packet); - Payload clientPayload; - if (NetworkReflections.clazz$DiscardedPayload.isInstance(payload)) { - clientPayload = DiscardedPayload.from(payload); - } else if (!VersionHelper.isOrAbove1_20_5() && NetworkReflections.clazz$UnknownPayload.isInstance(payload)) { - clientPayload = UnknownPayload.from(payload); - } else { - return; - } - if (clientPayload == null || !clientPayload.channel().equals(NetworkManager.MOD_CHANNEL_KEY)) - return; - FriendlyByteBuf buf = clientPayload.toBuffer(); - NetWorkDataTypes dataType = buf.readEnumConstant(NetWorkDataTypes.class); - if (dataType == NetWorkDataTypes.CLIENT_CUSTOM_BLOCK) { - int clientBlockRegistrySize = dataType.decode(buf); - int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize(); - if (clientBlockRegistrySize != serverBlockRegistrySize) { - user.kick(Component.translatable( - "disconnect.craftengine.block_registry_mismatch", - TranslationArgument.numeric(clientBlockRegistrySize), - TranslationArgument.numeric(serverBlockRegistrySize) - )); - return; - } - user.setClientModState(true); - } else if (dataType == NetWorkDataTypes.CANCEL_BLOCK_UPDATE) { - if (dataType.decode(buf)) { - FriendlyByteBuf bufPayload = new FriendlyByteBuf(Unpooled.buffer()); - bufPayload.writeEnumConstant(dataType); - dataType.encode(bufPayload, true); - user.sendCustomPayload(NetworkManager.MOD_CHANNEL_KEY, bufPayload.array()); - } - } - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundCustomPayloadPacket", e); - } - }; - - @SuppressWarnings("unchecked") - public static final BiConsumer SET_ENTITY_DATA = (user, event) -> { - try { - if (!(user instanceof BukkitServerPlayer serverPlayer)) return; - FriendlyByteBuf buf = event.getBuffer(); - int id = buf.readVarInt(); - EntityPacketHandler handler = user.entityPacketHandlers().get(id); - if (handler != null) { - handler.handleSetEntityData(serverPlayer, event); - return; - } - if (Config.interceptEntityName()) { - boolean isChanged = false; - List packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf); - for (int i = 0; i < packedItems.size(); i++) { - Object packedItem = packedItems.get(i); - int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); - if (entityDataId != BaseEntityData.CustomName.id()) continue; - Optional optionalTextComponent = (Optional) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); - if (optionalTextComponent.isEmpty()) continue; - Object textComponent = optionalTextComponent.get(); - String json = ComponentUtils.minecraftToJson(textComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); - if (tokens.isEmpty()) continue; - Component component = AdventureHelper.jsonToComponent(json); - component = AdventureHelper.replaceText(component, tokens, NetworkTextReplaceContext.of(serverPlayer)); - Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); - packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component)))); - isChanged = true; - break; - } - if (isChanged) { - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeVarInt(id); - FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf); - } - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSetEntityDataPacket", e); - } - }; - - public static final BiConsumer SET_SCORE_1_20_3 = (user, event) -> { - try { - if (!Config.interceptSetScore()) return; - if (!(user instanceof BukkitServerPlayer serverPlayer)) return; - boolean isChanged = false; - FriendlyByteBuf buf = event.getBuffer(); - String owner = buf.readUtf(); - String objectiveName = buf.readUtf(); - int score = buf.readVarInt(); - boolean hasDisplay = buf.readBoolean(); - Tag displayName = null; - if (hasDisplay) { - displayName = buf.readNbt(false); - } - outside: - if (displayName != null) { - Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); - if (tokens.isEmpty()) break outside; - Component component = AdventureHelper.tagToComponent(displayName); - component = AdventureHelper.replaceText(component, tokens, NetworkTextReplaceContext.of(serverPlayer)); - displayName = AdventureHelper.componentToTag(component); - isChanged = true; - } - boolean hasNumberFormat = buf.readBoolean(); - int format = -1; - Tag style = null; - Tag fixed = null; - if (hasNumberFormat) { - format = buf.readVarInt(); - if (format == 0) { - if (displayName == null) return; - } else if (format == 1) { - if (displayName == null) return; - style = buf.readNbt(false); - } else if (format == 2) { - fixed = buf.readNbt(false); - if (fixed == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(fixed.getAsString()); - if (tokens.isEmpty() && !isChanged) return; - if (!tokens.isEmpty()) { - Component component = AdventureHelper.tagToComponent(fixed); - component = AdventureHelper.replaceText(component, tokens, NetworkTextReplaceContext.of(serverPlayer)); - fixed = AdventureHelper.componentToTag(component); - isChanged = true; - } - } - } - if (isChanged) { - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeUtf(owner); - buf.writeUtf(objectiveName); - buf.writeVarInt(score); - if (hasDisplay) { - buf.writeBoolean(true); - buf.writeNbt(displayName, false); - } else { - buf.writeBoolean(false); - } - if (hasNumberFormat) { - buf.writeBoolean(true); - buf.writeVarInt(format); - if (format == 1) { - buf.writeNbt(style, false); - } else if (format == 2) { - buf.writeNbt(fixed, false); - } - } else { - buf.writeBoolean(false); - } - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSetScorePacket", e); - } - }; - - public static final BiConsumer CONTAINER_SET_CONTENT = (user, event) -> { - try { - if (!(user instanceof BukkitServerPlayer serverPlayer)) return; - FriendlyByteBuf buf = event.getBuffer(); - int containerId = buf.readContainerId(); - int stateId = buf.readVarInt(); - int listSize = buf.readVarInt(); - List items = new ArrayList<>(listSize); - boolean changed = false; - Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); - for (int i = 0; i < listSize; i++) { - ItemStack itemStack = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); - Optional optional = BukkitItemManager.instance().s2c(itemStack, serverPlayer); - if (optional.isPresent()) { - items.add(optional.get()); - changed = true; - } else { - items.add(itemStack); - } - } - ItemStack carriedItem = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); - ItemStack newCarriedItem = carriedItem; - Optional optional = BukkitItemManager.instance().s2c(carriedItem, serverPlayer); - if (optional.isPresent()) { - changed = true; - newCarriedItem = optional.get(); - } - if (!changed) return; - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeContainerId(containerId); - buf.writeVarInt(stateId); - buf.writeVarInt(listSize); - Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); - for (ItemStack itemStack : items) { - FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, itemStack); - } - FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, newCarriedItem); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundContainerSetContentPacket", e); - } - }; - - public static final BiConsumer CONTAINER_SET_SLOT = (user, event) -> { - try { - if (!(user instanceof BukkitServerPlayer serverPlayer)) return; - FriendlyByteBuf buf = event.getBuffer(); - int containerId = buf.readContainerId(); - int stateId = buf.readVarInt(); - int slot = buf.readShort(); - Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); - ItemStack itemStack; - try { - itemStack = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); - } catch (Exception e) { - // 其他插件干的,比如某ty*****er,不要赖到ce头上 - return; - } - BukkitItemManager.instance().s2c(itemStack, serverPlayer).ifPresent((newItemStack) -> { - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeContainerId(containerId); - buf.writeVarInt(stateId); - buf.writeShort(slot); - Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); - FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, newItemStack); - }); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundContainerSetSlotPacket", e); - } - }; - - public static final BiConsumer SET_CURSOR_ITEM = (user, event) -> { - try { - if (!(user instanceof BukkitServerPlayer serverPlayer)) return; - FriendlyByteBuf buf = event.getBuffer(); - Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); - ItemStack itemStack = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); - BukkitItemManager.instance().s2c(itemStack, serverPlayer).ifPresent((newItemStack) -> { - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); - FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, newItemStack); - }); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSetCursorItemPacket", e); - } - }; - - public static final BiConsumer SET_EQUIPMENT = (user, event) -> { - try { - if (!(user instanceof BukkitServerPlayer serverPlayer)) return; - FriendlyByteBuf buf = event.getBuffer(); - boolean changed = false; - Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); - int entity = buf.readVarInt(); - List> slots = Lists.newArrayList(); - int slotMask; - do { - slotMask = buf.readByte(); - Object equipmentSlot = CoreReflections.instance$EquipmentSlot$values[slotMask & 127]; - ItemStack itemStack = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); - Optional optional = BukkitItemManager.instance().s2c(itemStack, serverPlayer); - if (optional.isPresent()) { - changed = true; - itemStack = optional.get(); - } - slots.add(com.mojang.datafixers.util.Pair.of(equipmentSlot, itemStack)); - } while ((slotMask & -128) != 0); - if (changed) { - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeVarInt(entity); - int i = slots.size(); - Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); - for (int j = 0; j < i; ++j) { - com.mojang.datafixers.util.Pair pair = slots.get(j); - Enum equipmentSlot = (Enum) pair.getFirst(); - boolean bl = j != i - 1; - int k = equipmentSlot.ordinal(); - buf.writeByte(bl ? k | -128 : k); - FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, pair.getSecond()); - } - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSetEquipmentPacket", e); - } - }; - - public static final BiConsumer SET_PLAYER_INVENTORY_1_21_2 = (user, event) -> { - try { - if (!(user instanceof BukkitServerPlayer serverPlayer)) return; - FriendlyByteBuf buf = event.getBuffer(); - int slot = buf.readVarInt(); - Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); - ItemStack itemStack = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); - BukkitItemManager.instance().s2c(itemStack, serverPlayer).ifPresent((newItemStack) -> { - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeVarInt(slot); - Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); - FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, newItemStack); - }); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSetPlayerInventoryPacket", e); - } - }; - - public static final BiConsumer SET_CREATIVE_MODE_SLOT = (user, event) -> { - try { - if (!(user instanceof BukkitServerPlayer serverPlayer)) return; - if (!serverPlayer.isCreativeMode()) return; - FriendlyByteBuf buf = event.getBuffer(); - Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); - short slotNum = buf.readShort(); - ItemStack itemStack; - try { - itemStack = VersionHelper.isOrAbove1_20_5() ? - FastNMS.INSTANCE.method$FriendlyByteBuf$readUntrustedItem(friendlyBuf) : FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); - } catch (Exception e) { - return; - } - BukkitItemManager.instance().c2s(itemStack).ifPresent((newItemStack) -> { - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeShort(slotNum); - Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); - if (VersionHelper.isOrAbove1_20_5()) { - FastNMS.INSTANCE.method$FriendlyByteBuf$writeUntrustedItem(newFriendlyBuf, newItemStack); - } else { - FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, newItemStack); - } - }); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundSetCreativeModeSlotPacket", e); - } - }; - - // 因为不能走编码器只能替换对象 - public static final TriConsumer CONTAINER_CLICK_1_21_5 = (user, event, packet) -> { - try { - BukkitServerPlayer player = (BukkitServerPlayer) user; - int containerId = FastNMS.INSTANCE.field$ServerboundContainerClickPacket$containerId(packet); - int stateId = FastNMS.INSTANCE.field$ServerboundContainerClickPacket$stateId(packet); - short slotNum = FastNMS.INSTANCE.field$ServerboundContainerClickPacket$slotNum(packet); - byte buttonNum = FastNMS.INSTANCE.field$ServerboundContainerClickPacket$buttonNum(packet); - Object clickType = FastNMS.INSTANCE.field$ServerboundContainerClickPacket$clickType(packet); - @SuppressWarnings("unchecked") - Int2ObjectMap changedSlots = FastNMS.INSTANCE.field$ServerboundContainerClickPacket$changedSlots(packet); - Int2ObjectMap newChangedSlots = new Int2ObjectOpenHashMap<>(changedSlots.size()); - for (Int2ObjectMap.Entry entry : changedSlots.int2ObjectEntrySet()) { - newChangedSlots.put(entry.getIntKey(), FastNMS.INSTANCE.constructor$InjectedHashedStack(entry.getValue(), player)); - } - Object carriedItem = FastNMS.INSTANCE.constructor$InjectedHashedStack(FastNMS.INSTANCE.field$ServerboundContainerClickPacket$carriedItem(packet), player); - event.replacePacket(FastNMS.INSTANCE.constructor$ServerboundContainerClickPacket(containerId, stateId, slotNum, buttonNum, clickType, Int2ObjectMaps.unmodifiable(newChangedSlots), carriedItem)); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundContainerClickPacket", e); - } - }; - - public static final BiConsumer CONTAINER_CLICK_1_20 = (user, event) -> { - try { - FriendlyByteBuf buf = event.getBuffer(); - boolean changed = false; - Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); - int containerId = buf.readContainerId(); - int stateId = buf.readVarInt(); - short slotNum = buf.readShort(); - byte buttonNum = buf.readByte(); - int clickType = buf.readVarInt(); - int i = buf.readVarInt(); - Int2ObjectMap changedSlots = new Int2ObjectOpenHashMap<>(i); - for (int j = 0; j < i; ++j) { - int k = buf.readShort(); - ItemStack itemStack = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); - Optional optional = BukkitItemManager.instance().c2s(itemStack); - if (optional.isPresent()) { - changed = true; - itemStack = optional.get(); - } - changedSlots.put(k, itemStack); - } - ItemStack carriedItem = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); - Optional optional = BukkitItemManager.instance().c2s(carriedItem); - if (optional.isPresent()) { - changed = true; - carriedItem = optional.get(); - } - if (changed) { - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeContainerId(containerId); - buf.writeVarInt(stateId); - buf.writeShort(slotNum); - buf.writeByte(buttonNum); - buf.writeVarInt(clickType); - buf.writeVarInt(changedSlots.size()); - Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); - changedSlots.forEach((k, v) -> { - buf.writeShort(k); - FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, v); - }); - FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, carriedItem); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundContainerClickPacket", e); - } - }; - - public static final TriConsumer RESOURCE_PACK_RESPONSE = (user, event, packet) -> { - try { - Object action = FastNMS.INSTANCE.field$ServerboundResourcePackPacket$action(packet); - - if (VersionHelper.isOrAbove1_20_3()) { - UUID uuid = FastNMS.INSTANCE.field$ServerboundResourcePackPacket$id(packet); - if (!user.isResourcePackLoading(uuid)) { - // 不是CraftEngine发送的资源包,不管 - return; - } - } - - if (action == null) { - user.kick(Component.text("Corrupted ResourcePackResponse Packet")); - return; - } - - // 检查是否是拒绝 - if (Config.kickOnDeclined()) { - if (action == NetworkReflections.instance$ServerboundResourcePackPacket$Action$DECLINED || action == NetworkReflections.instance$ServerboundResourcePackPacket$Action$DISCARDED) { - user.kick(Component.translatable("multiplayer.requiredTexturePrompt.disconnect")); - return; - } - } - - // 检查是否失败 - if (Config.kickOnFailedApply()) { - if (action == NetworkReflections.instance$ServerboundResourcePackPacket$Action$FAILED_DOWNLOAD - || (VersionHelper.isOrAbove1_20_3() && action == NetworkReflections.instance$ServerboundResourcePackPacket$Action$INVALID_URL)) { - user.kick(Component.translatable("multiplayer.requiredTexturePrompt.disconnect")); - return; - } - } - - boolean isTerminal = action != NetworkReflections.instance$ServerboundResourcePackPacket$Action$ACCEPTED && action != NetworkReflections.instance$ServerboundResourcePackPacket$Action$DOWNLOADED; - if (isTerminal && VersionHelper.isOrAbove1_20_2()) { - event.setCancelled(true); - Object packetListener = FastNMS.INSTANCE.method$Connection$getPacketListener(user.connection()); - if (!CoreReflections.clazz$ServerConfigurationPacketListenerImpl.isInstance(packetListener)) return; - // 主线程上处理这个包 - CraftEngine.instance().scheduler().executeSync(() -> { - try { - // 当客户端发出多次成功包的时候,finish会报错,我们忽略他 - NetworkReflections.methodHandle$ServerCommonPacketListener$handleResourcePackResponse.invokeExact(packetListener, packet); - CoreReflections.methodHandle$ServerConfigurationPacketListenerImpl$finishCurrentTask.invokeExact(packetListener, CoreReflections.instance$ServerResourcePackConfigurationTask$TYPE); - } catch (Throwable e) { - Debugger.RESOURCE_PACK.warn(() -> "Cannot finish current task", e); - } - }); - } - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ServerboundResourcePackPacket", e); - } - }; - - public static final TriConsumer ENTITY_EVENT = (user, event, packet) -> { - try { - Object player = user.serverPlayer(); - if (player == null) return; - int entityId = (int) NetworkReflections.methodHandle$ClientboundEntityEventPacket$entityIdGetter.invokeExact(packet); - if (entityId != FastNMS.INSTANCE.method$Entity$getId(player)) return; - byte eventId = (byte) NetworkReflections.methodHandle$ClientboundEntityEventPacket$eventIdGetter.invokeExact(packet); - if (eventId >= 24 && eventId <= 28) { - CraftEngine.instance().fontManager().refreshEmojiSuggestions(user.uuid()); - } - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundEntityEventPacket", e); - } - }; - - public static final TriConsumer MOVE_POS_AND_ROTATE_ENTITY = (user, event, packet) -> { - try { - int entityId = ProtectedFieldVisitor.get().field$ClientboundMoveEntityPacket$entityId(packet); - if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) { - event.setCancelled(true); - } - EntityPacketHandler handler = user.entityPacketHandlers().get(entityId); - if (handler != null) { - handler.handleMoveAndRotate(user, event, packet); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundMoveEntityPacket$PosRot", e); - } - }; - - public static final TriConsumer MOVE_POS_ENTITY = (user, event, packet) -> { - try { - int entityId = ProtectedFieldVisitor.get().field$ClientboundMoveEntityPacket$entityId(packet); - EntityPacketHandler handler = user.entityPacketHandlers().get(entityId); - if (handler != null) { - handler.handleMove(user, event, packet); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundMoveEntityPacket", e); - } - }; - - public static final TriConsumer ROTATE_HEAD = (user, event, packet) -> { - try { - int entityId = (int) NetworkReflections.methodHandle$ClientboundRotateHeadPacket$entityIdGetter.invokeExact(packet); - if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) { - event.setCancelled(true); - } - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundRotateHeadPacket", e); - } - }; - - public static final TriConsumer SET_ENTITY_MOTION = (user, event, packet) -> { - try { - if (!VersionHelper.isOrAbove1_21_6()) return; - int entityId = (int) NetworkReflections.methodHandle$ClientboundSetEntityMotionPacket$idGetter.invokeExact(packet); - if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) { - event.setCancelled(true); - } - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSetEntityMotionPacket", e); - } - }; - - // 这个包是由 JoinWorldTask 发出的,客户端收到后会返回 ServerboundFinishConfigurationPacket - @SuppressWarnings("unchecked") - public static final TriConsumer FINISH_CONFIGURATION = (user, event, packet) -> { - try { - if (!VersionHelper.isOrAbove1_20_2() || !Config.sendPackOnJoin()) { - // 防止后期调试进配置阶段造成问题 - user.setShouldProcessFinishConfiguration(false); - return; - } - - if (!user.shouldProcessFinishConfiguration()) return; - Object packetListener = FastNMS.INSTANCE.method$Connection$getPacketListener(user.connection()); - if (!CoreReflections.clazz$ServerConfigurationPacketListenerImpl.isInstance(packetListener)) { - return; - } - - // 防止后续加入的JoinWorldTask再次处理 - user.setShouldProcessFinishConfiguration(false); - - // 取消 ClientboundFinishConfigurationPacket,让客户端发呆,并结束掉当前的进入世界任务 - event.setCancelled(true); - try { - CoreReflections.methodHandle$ServerConfigurationPacketListenerImpl$finishCurrentTask.invokeExact(packetListener, CoreReflections.instance$JoinWorldTask$TYPE); - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to finish current task for " + user.name(), e); - } - - if (VersionHelper.isOrAbove1_20_5()) { - // 1.20.5+开始会检查是否结束需要重新设置回去,不然不会发keepAlive包 - CoreReflections.methodHandle$ServerCommonPacketListenerImpl$closedSetter.invokeExact(packetListener, false); - } - - // 请求资源包 - ResourcePackHost host = CraftEngine.instance().packManager().resourcePackHost(); - host.requestResourcePackDownloadLink(user.uuid()).whenComplete((dataList, t) -> { - if (t != null) { - CraftEngine.instance().logger().warn("Failed to get pack data for player " + user.name(), t); - FastNMS.INSTANCE.method$ServerConfigurationPacketListenerImpl$returnToWorld(packetListener); - return; - } - if (dataList.isEmpty()) { - FastNMS.INSTANCE.method$ServerConfigurationPacketListenerImpl$returnToWorld(packetListener); - return; - } - Queue configurationTasks; - try { - configurationTasks = (Queue) CoreReflections.methodHandle$ServerConfigurationPacketListenerImpl$configurationTasksGetter.invokeExact(packetListener); - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to get configuration tasks for player " + user.name(), e); - FastNMS.INSTANCE.method$ServerConfigurationPacketListenerImpl$returnToWorld(packetListener); - return; - } - // 向配置阶段连接的任务重加入资源包的任务 - for (ResourcePackDownloadData data : dataList) { - configurationTasks.add(FastNMS.INSTANCE.constructor$ServerResourcePackConfigurationTask(ResourcePackUtils.createServerResourcePackInfo(data.uuid(), data.url(), data.sha1()))); - user.addResourcePackUUID(data.uuid()); - } - // 最后再加入一个 JoinWorldTask 并开始资源包任务 - FastNMS.INSTANCE.method$ServerConfigurationPacketListenerImpl$returnToWorld(packetListener); - }); - } catch (Throwable e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundFinishConfigurationPacket", e); - } - }; - - public static final TriConsumer LOGIN_FINISHED = (user, event, packet) -> { - try { - GameProfile gameProfile = FastNMS.INSTANCE.field$ClientboundLoginFinishedPacket$gameProfile(packet); - user.setName(gameProfile.getName()); - user.setUUID(gameProfile.getId()); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundLoginFinishedPacket", e); - } - }; - - public static final BiConsumer ADD_RECIPE_BOOK = (user, event) -> { - try { - FriendlyByteBuf buf = event.getBuffer(); - List entries = buf.readCollection(ArrayList::new, byteBuf -> { - RecipeBookEntry entry = RecipeBookEntry.read(byteBuf); - entry.applyClientboundData((BukkitServerPlayer) user); - return entry; - }); - boolean replace = buf.readBoolean(); - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeCollection(entries, ((byteBuf, recipeBookEntry) -> recipeBookEntry.write(byteBuf))); - buf.writeBoolean(replace); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundRecipeBookAddPacket", e); - } - }; - - public static final BiConsumer PLACE_GHOST_RECIPE = (user, event) -> { - try { - if (!VersionHelper.isOrAbove1_21_2()) return; - FriendlyByteBuf buf = event.getBuffer(); - int containerId = buf.readContainerId(); - RecipeDisplay display = RecipeDisplay.read(buf); - display.applyClientboundData((BukkitServerPlayer) user); - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeContainerId(containerId); - display.write(buf); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundPlaceGhostRecipePacket", e); - } - }; - - public static final BiConsumer UPDATE_RECIPES = (user, event) -> { - try { - if (VersionHelper.isOrAbove1_21_2()) return; - FriendlyByteBuf buf = event.getBuffer(); - List holders = buf.readCollection(ArrayList::new, byteBuf -> { - LegacyRecipeHolder holder = LegacyRecipeHolder.read(byteBuf); - holder.recipe().applyClientboundData((BukkitServerPlayer) user); - return holder; - }); - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeCollection(holders, ((byteBuf, recipeHolder) -> recipeHolder.write(byteBuf))); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundUpdateRecipesPacket", e); - } - }; - - public static final BiConsumer UPDATE_ADVANCEMENTS = (user, event) -> { - try { - if (!(user instanceof BukkitServerPlayer serverPlayer)) return; - FriendlyByteBuf buf = event.getBuffer(); - boolean reset = buf.readBoolean(); - List added = buf.readCollection(ArrayList::new, byteBuf -> { - AdvancementHolder holder = AdvancementHolder.read(byteBuf); - holder.applyClientboundData(serverPlayer); - return holder; - }); - Set removed = buf.readCollection(Sets::newLinkedHashSetWithExpectedSize, FriendlyByteBuf::readKey); - Map progress = buf.readMap(FriendlyByteBuf::readKey, AdvancementProgress::read); - - boolean showAdvancement = false; - if (VersionHelper.isOrAbove1_21_5()) { - showAdvancement = buf.readBoolean(); - } - - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - - buf.writeBoolean(reset); - buf.writeCollection(added, (byteBuf, advancementHolder) -> advancementHolder.write(byteBuf)); - buf.writeCollection(removed, FriendlyByteBuf::writeKey); - buf.writeMap(progress, FriendlyByteBuf::writeKey, (byteBuf, advancementProgress) -> advancementProgress.write(byteBuf)); - if (VersionHelper.isOrAbove1_21_5()) { - buf.writeBoolean(showAdvancement); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundUpdateAdvancementsPacket", e); - } - }; - - public static final TriConsumer UPDATE_TAGS = (user, event, packet) -> { - try { - Object modifiedPacket = BukkitBlockManager.instance().cachedUpdateTagsPacket(); - if (packet.equals(modifiedPacket) || modifiedPacket == null) return; - event.replacePacket(modifiedPacket); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundUpdateTagsPacket", e); - } - }; -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketIds.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketIds.java index dd41cb280..b8b0e9cc8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketIds.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketIds.java @@ -69,4 +69,6 @@ public interface PacketIds { int clientboundUpdateRecipesPacket(); int clientboundForgetLevelChunkPacket(); + + int serverboundCustomPayloadPacket(); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/BlockDisplayPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/BlockDisplayPacketHandler.java index 953f45bf5..fa86d8d8f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/BlockDisplayPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/BlockDisplayPacketHandler.java @@ -4,7 +4,7 @@ import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; import net.momirealms.craftengine.bukkit.entity.data.BlockDisplayEntityData; import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; +import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.core.entity.player.Player; @@ -36,12 +36,7 @@ public class BlockDisplayPacketHandler implements EntityPacketHandler { if (entityDataId == BlockDisplayEntityData.DisplayedBlock.id()) { Object blockState = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); int stateId = BlockStateUtils.blockStateToId(blockState); - int newStateId; - if (!user.clientModEnabled()) { - newStateId = PacketConsumers.remap(stateId); - } else { - newStateId = PacketConsumers.remapMOD(stateId); - } + int newStateId= BukkitNetworkManager.instance().remapBlockState(stateId, user.clientModEnabled()); if (newStateId == stateId) continue; Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue( diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/CommonItemPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/CommonItemPacketHandler.java index 80a4c20cd..ecc0a03d5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/CommonItemPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/CommonItemPacketHandler.java @@ -41,7 +41,7 @@ public class CommonItemPacketHandler implements EntityPacketHandler { continue; } ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsItemStack); - Optional optional = BukkitItemManager.instance().s2c(itemStack, (BukkitServerPlayer) user); + Optional optional = BukkitItemManager.instance().s2c(itemStack, user); if (optional.isEmpty()) continue; isChanged = true; itemStack = optional.get(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/EndermanPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/EndermanPacketHandler.java index 3d5561c01..760820024 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/EndermanPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/EndermanPacketHandler.java @@ -4,7 +4,7 @@ import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; import net.momirealms.craftengine.bukkit.entity.data.EnderManData; import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; +import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.core.entity.player.Player; @@ -38,12 +38,7 @@ public class EndermanPacketHandler implements EntityPacketHandler { Optional blockState = (Optional) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); if (blockState.isEmpty()) continue; int stateId = BlockStateUtils.blockStateToId(blockState.get()); - int newStateId; - if (!user.clientModEnabled()) { - newStateId = PacketConsumers.remap(stateId); - } else { - newStateId = PacketConsumers.remapMOD(stateId); - } + int newStateId = BukkitNetworkManager.instance().remapBlockState(stateId, user.clientModEnabled()); if (newStateId == stateId) continue; Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue( diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/MinecartPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/MinecartPacketHandler.java index 653b1f18a..ff4c81023 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/MinecartPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/MinecartPacketHandler.java @@ -4,7 +4,7 @@ import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.entity.data.AbstractMinecartData; import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; +import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.core.entity.player.Player; @@ -25,7 +25,7 @@ import java.util.Optional; public class MinecartPacketHandler implements EntityPacketHandler { public static final MinecartPacketHandler INSTANCE = new MinecartPacketHandler(); - private static final BlockStateHandler BLOCK_STATE_HANDLER = VersionHelper.isOrAbove1_21_3() ? BlockStateHandler_1_21_3.INSTANCE : BlockStateHandler_1_20.INSTANCE; + private static final BlockStateHandler BLOCK_STATE_HANDLER = VersionHelper.isOrAbove1_21_5() ? BlockStateHandler_1_21_5.INSTANCE : BlockStateHandler_1_20.INSTANCE; @Override public void handleSetEntityData(Player user, ByteBufPacketEvent event) { @@ -69,8 +69,8 @@ public class MinecartPacketHandler implements EntityPacketHandler { Object handle(NetWorkUser user, Object packedItem, int entityDataId); } - static class BlockStateHandler_1_21_3 implements BlockStateHandler { - protected static final BlockStateHandler INSTANCE = new BlockStateHandler_1_21_3(); + static class BlockStateHandler_1_21_5 implements BlockStateHandler { + protected static final BlockStateHandler INSTANCE = new BlockStateHandler_1_21_5(); @Override public Object handle(NetWorkUser user, Object packedItem, int entityDataId) { @@ -79,12 +79,7 @@ public class MinecartPacketHandler implements EntityPacketHandler { Optional blockState = (Optional) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); if (blockState.isEmpty()) return null; int stateId = BlockStateUtils.blockStateToId(blockState.get()); - int newStateId; - if (!user.clientModEnabled()) { - newStateId = PacketConsumers.remap(stateId); - } else { - newStateId = PacketConsumers.remapMOD(stateId); - } + int newStateId = BukkitNetworkManager.instance().remapBlockState(stateId, user.clientModEnabled()); if (newStateId == stateId) return null; Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); return FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue( @@ -100,12 +95,7 @@ public class MinecartPacketHandler implements EntityPacketHandler { public Object handle(NetWorkUser user, Object packedItem, int entityDataId) { if (entityDataId != AbstractMinecartData.DisplayBlock.id()) return null; int stateId = (int) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); - int newStateId; - if (!user.clientModEnabled()) { - newStateId = PacketConsumers.remap(stateId); - } else { - newStateId = PacketConsumers.remapMOD(stateId); - } + int newStateId = BukkitNetworkManager.instance().remapBlockState(stateId, user.clientModEnabled()); if (newStateId == stateId) return null; Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); return FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, newStateId); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/PrimedTNTPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/PrimedTNTPacketHandler.java index ef3178e21..6fab87f02 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/PrimedTNTPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/PrimedTNTPacketHandler.java @@ -4,7 +4,7 @@ import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; import net.momirealms.craftengine.bukkit.entity.data.PrimedTntData; import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; +import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.core.entity.player.Player; @@ -36,12 +36,7 @@ public class PrimedTNTPacketHandler implements EntityPacketHandler { if (entityDataId == PrimedTntData.BlockState.id()) { Object blockState = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); int stateId = BlockStateUtils.blockStateToId(blockState); - int newStateId; - if (!user.clientModEnabled()) { - newStateId = PacketConsumers.remap(stateId); - } else { - newStateId = PacketConsumers.remapMOD(stateId); - } + int newStateId = BukkitNetworkManager.instance().remapBlockState(stateId, user.clientModEnabled()); if (newStateId == stateId) continue; Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue( diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java index 5c7d29b86..a85925165 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ProjectilePacketHandler.java @@ -16,8 +16,9 @@ import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; import net.momirealms.craftengine.core.plugin.network.NMSPacketEvent; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; import net.momirealms.craftengine.core.util.FriendlyByteBuf; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.Vec3d; import org.bukkit.inventory.ItemStack; import java.util.ArrayList; @@ -70,13 +71,14 @@ public class ProjectilePacketHandler implements EntityPacketHandler { double x = buf.readDouble(); double y = buf.readDouble(); double z = buf.readDouble(); + Vec3d movement = VersionHelper.isOrAbove1_21_9() ? buf.readLpVec3() : null; byte xRot = buf.readByte(); byte yRot = buf.readByte(); byte yHeadRot = buf.readByte(); int data = buf.readVarInt(); - int xa = buf.readShort(); - int ya = buf.readShort(); - int za = buf.readShort(); + int xa = VersionHelper.isOrAbove1_21_9() ? -1 : buf.readShort(); + int ya = VersionHelper.isOrAbove1_21_9() ? -1 : buf.readShort(); + int za = VersionHelper.isOrAbove1_21_9() ? -1 : buf.readShort(); event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); @@ -86,13 +88,14 @@ public class ProjectilePacketHandler implements EntityPacketHandler { buf.writeDouble(x); buf.writeDouble(y); buf.writeDouble(z); - buf.writeByte(MCUtils.packDegrees(MCUtils.clamp(-MCUtils.unpackDegrees(xRot), -90.0F, 90.0F))); - buf.writeByte(MCUtils.packDegrees(-MCUtils.unpackDegrees(yRot))); + if (VersionHelper.isOrAbove1_21_9()) buf.writeLpVec3(movement); + buf.writeByte(MiscUtils.packDegrees(MiscUtils.clamp(-MiscUtils.unpackDegrees(xRot), -90.0F, 90.0F))); + buf.writeByte(MiscUtils.packDegrees(-MiscUtils.unpackDegrees(yRot))); buf.writeByte(yHeadRot); buf.writeVarInt(data); - buf.writeShort(xa); - buf.writeShort(ya); - buf.writeShort(za); + if (!VersionHelper.isOrAbove1_21_9()) buf.writeShort(xa); + if (!VersionHelper.isOrAbove1_21_9()) buf.writeShort(ya); + if (!VersionHelper.isOrAbove1_21_9()) buf.writeShort(za); } private Object convertCustomProjectilePositionSyncPacket(Object packet) { @@ -112,7 +115,7 @@ public class ProjectilePacketHandler implements EntityPacketHandler { Optional> customItem = BukkitItemManager.instance().getCustomItem(this.projectile.metadata().item()); if (customItem.isEmpty()) return itemDisplayValues; ProjectileMeta meta = this.projectile.metadata(); - Item displayedItem = customItem.get().buildItem(ItemBuildContext.EMPTY); + Item displayedItem = customItem.get().buildItem(ItemBuildContext.empty()); // 我们应当使用新的展示物品的组件覆盖原物品的组件,以完成附魔,附魔光效等组件的继承 displayedItem = this.projectile.item().mergeCopy(displayedItem); ItemDisplayEntityData.InterpolationDelay.addEntityDataIfNotDefaultValue(-1, itemDisplayValues); @@ -139,12 +142,12 @@ public class ProjectilePacketHandler implements EntityPacketHandler { short xa = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$xa(packet); short ya = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$ya(packet); short za = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$za(packet); - float xRot = MCUtils.unpackDegrees(FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$xRot(packet)); - float yRot = MCUtils.unpackDegrees(FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$yRot(packet)); + float xRot = MiscUtils.unpackDegrees(FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$xRot(packet)); + float yRot = MiscUtils.unpackDegrees(FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$yRot(packet)); boolean onGround = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$onGround(packet); return FastNMS.INSTANCE.constructor$ClientboundMoveEntityPacket$PosRot( entityId, xa, ya, za, - MCUtils.packDegrees(-yRot), MCUtils.packDegrees(MCUtils.clamp(-xRot, -90.0F, 90.0F)), + MiscUtils.packDegrees(-yRot), MiscUtils.packDegrees(MiscUtils.clamp(-xRot, -90.0F, 90.0F)), onGround ); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIdFinder.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIdFinder.java deleted file mode 100644 index e8d383aec..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIdFinder.java +++ /dev/null @@ -1,75 +0,0 @@ -package net.momirealms.craftengine.bukkit.plugin.network.id; - -import com.google.gson.JsonElement; -import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.util.VersionHelper; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -public class PacketIdFinder { - private static final Map> gamePacketIdsByName = new HashMap<>(); - private static final Map, Integer>> gamePacketIdsByClazz = new HashMap<>(); - private static final int maxC2SPacketId; - private static final int maxS2CPacketId; - - static { - try { - if (VersionHelper.isOrAbove1_21()) { - Object packetReport = CoreReflections.constructor$PacketReport.newInstance((Object) null); - JsonElement jsonElement = (JsonElement) CoreReflections.method$PacketReport$serializePackets.invoke(packetReport); - JsonElement play = jsonElement.getAsJsonObject().get("play"); - for (Map.Entry entry : play.getAsJsonObject().entrySet()) { - Map ids = new HashMap<>(); - gamePacketIdsByName.put(entry.getKey(), ids); - for (var entry2 : entry.getValue().getAsJsonObject().entrySet()) { - ids.put(entry2.getKey(), entry2.getValue().getAsJsonObject().get("protocol_id").getAsInt()); - } - } - } else if (VersionHelper.isOrAbove1_20_5()) { - gamePacketIdsByName.putAll(FastNMS.INSTANCE.gamePacketIdsByName()); - } else { - gamePacketIdsByClazz.putAll(FastNMS.INSTANCE.gamePacketIdsByClazz()); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to get packets", e); - } - maxS2CPacketId = calculateMaxId("clientbound"); - maxC2SPacketId = calculateMaxId("serverbound"); - } - - private static int calculateMaxId(String direction) { - if (VersionHelper.isOrAbove1_20_5()) { - return gamePacketIdsByName.getOrDefault(direction, Collections.emptyMap()).size(); - } else { - return gamePacketIdsByClazz.getOrDefault(direction, Collections.emptyMap()).size(); - } - } - - public static int c2sGamePackets() { - return maxC2SPacketId; - } - - public static int s2cGamePackets() { - return maxS2CPacketId; - } - - public static int clientboundByName(String packetName) { - return gamePacketIdsByName.get("clientbound").getOrDefault(packetName, -1); - } - - public static int clientboundByClazz(Class clazz) { - return gamePacketIdsByClazz.get("clientbound").getOrDefault(clazz, -1); - } - - public static int serverboundByName(String packetName) { - return gamePacketIdsByName.get("serverbound").getOrDefault(packetName, -1); - } - - public static int serverboundByClazz(Class clazz) { - return gamePacketIdsByClazz.get("serverbound").getOrDefault(clazz, -1); - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20.java index 7a3b81947..8b33c8fef 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20.java @@ -2,176 +2,183 @@ package net.momirealms.craftengine.bukkit.plugin.network.id; import net.momirealms.craftengine.bukkit.plugin.network.PacketIds; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; +import net.momirealms.craftengine.core.plugin.network.PacketFlow; public class PacketIds1_20 implements PacketIds { @Override public int clientboundBlockUpdatePacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundBlockUpdatePacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundBlockUpdatePacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundSectionBlocksUpdatePacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundSectionBlocksUpdatePacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundSectionBlocksUpdatePacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundLevelParticlesPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundLevelParticlesPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundLevelParticlesPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundLevelEventPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundLevelEventPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundLevelEventPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundAddEntityPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundAddEntityPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundAddEntityPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundOpenScreenPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundOpenScreenPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundOpenScreenPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundSoundPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundSoundPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundSoundPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundRemoveEntitiesPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundRemoveEntitiesPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundRemoveEntitiesPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundSetEntityDataPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundSetEntityDataPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundSetEntityDataPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundSetTitleTextPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundSetTitleTextPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundSetTitleTextPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundSetSubtitleTextPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundSetSubtitleTextPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundSetSubtitleTextPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundSetActionBarTextPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundSetActionBarTextPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundSetActionBarTextPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundBossEventPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundBossEventPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundBossEventPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundSystemChatPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundSystemChatPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundSystemChatPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundTabListPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundTabListPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundTabListPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundSetPlayerTeamPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundSetPlayerTeamPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundSetPlayerTeamPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundSetObjectivePacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundSetObjectivePacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundSetObjectivePacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundLevelChunkWithLightPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundLevelChunkWithLightPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundLevelChunkWithLightPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundPlayerInfoUpdatePacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundPlayerInfoUpdatePacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundPlayerInfoUpdatePacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundSetScorePacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundSetScorePacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundSetScorePacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundContainerSetContentPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundContainerSetContentPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundContainerSetContentPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundContainerSetSlotPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundContainerSetSlotPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundContainerSetSlotPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundSetCursorItemPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundSetCursorItemPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundSetCursorItemPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundSetEquipmentPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundSetEquipmentPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundSetEquipmentPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundSetPlayerInventoryPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundSetPlayerInventoryPacket); - } - - @Override - public int serverboundContainerClickPacket() { - return PacketIdFinder.serverboundByClazz(NetworkReflections.clazz$ServerboundContainerClickPacket); - } - - @Override - public int serverboundSetCreativeModeSlotPacket() { - return PacketIdFinder.serverboundByClazz(NetworkReflections.clazz$ServerboundSetCreativeModeSlotPacket); - } - - @Override - public int clientboundBlockEventPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundBlockEventPacket); - } - - @Override - public int serverboundInteractPacket() { - return PacketIdFinder.serverboundByClazz(NetworkReflections.clazz$ServerboundInteractPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundSetPlayerInventoryPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundRecipeBookAddPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundRecipeBookAddPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundRecipeBookAddPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundPlaceGhostRecipePacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundPlaceGhostRecipePacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundPlaceGhostRecipePacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundUpdateRecipesPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundUpdateRecipesPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundUpdateRecipesPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundUpdateAdvancementsPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundUpdateAdvancementsPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundUpdateAdvancementsPacket, PacketFlow.CLIENTBOUND); } @Override public int clientboundForgetLevelChunkPacket() { - return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundForgetLevelChunkPacket); + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundForgetLevelChunkPacket, PacketFlow.CLIENTBOUND); + } + + @Override + public int clientboundBlockEventPacket() { + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundBlockEventPacket, PacketFlow.CLIENTBOUND); + } + + @Override + public int serverboundContainerClickPacket() { + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ServerboundContainerClickPacket, PacketFlow.SERVERBOUND); + } + + @Override + public int serverboundSetCreativeModeSlotPacket() { + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ServerboundSetCreativeModeSlotPacket, PacketFlow.SERVERBOUND); + } + + + @Override + public int serverboundInteractPacket() { + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ServerboundInteractPacket, PacketFlow.SERVERBOUND); + } + + @Override + public int serverboundCustomPayloadPacket() { + return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ServerboundCustomPayloadPacket, PacketFlow.SERVERBOUND); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20_5.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20_5.java index b3a9b624a..9aea4a3df 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20_5.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20_5.java @@ -1,176 +1,182 @@ package net.momirealms.craftengine.bukkit.plugin.network.id; import net.momirealms.craftengine.bukkit.plugin.network.PacketIds; +import net.momirealms.craftengine.core.plugin.network.PacketFlow; public class PacketIds1_20_5 implements PacketIds { @Override public int clientboundBlockUpdatePacket() { - return PacketIdFinder.clientboundByName("minecraft:block_update"); + return PlayPacketIdHelper.byName("minecraft:block_update", PacketFlow.CLIENTBOUND); } @Override public int clientboundSectionBlocksUpdatePacket() { - return PacketIdFinder.clientboundByName("minecraft:section_blocks_update"); + return PlayPacketIdHelper.byName("minecraft:section_blocks_update", PacketFlow.CLIENTBOUND); } @Override public int clientboundLevelParticlesPacket() { - return PacketIdFinder.clientboundByName("minecraft:level_particles"); + return PlayPacketIdHelper.byName("minecraft:level_particles", PacketFlow.CLIENTBOUND); } @Override public int clientboundLevelEventPacket() { - return PacketIdFinder.clientboundByName("minecraft:level_event"); + return PlayPacketIdHelper.byName("minecraft:level_event", PacketFlow.CLIENTBOUND); } @Override public int clientboundAddEntityPacket() { - return PacketIdFinder.clientboundByName("minecraft:add_entity"); + return PlayPacketIdHelper.byName("minecraft:add_entity", PacketFlow.CLIENTBOUND); } @Override public int clientboundOpenScreenPacket() { - return PacketIdFinder.clientboundByName("minecraft:open_screen"); + return PlayPacketIdHelper.byName("minecraft:open_screen", PacketFlow.CLIENTBOUND); } @Override public int clientboundSoundPacket() { - return PacketIdFinder.clientboundByName("minecraft:sound"); + return PlayPacketIdHelper.byName("minecraft:sound", PacketFlow.CLIENTBOUND); } @Override public int clientboundRemoveEntitiesPacket() { - return PacketIdFinder.clientboundByName("minecraft:remove_entities"); + return PlayPacketIdHelper.byName("minecraft:remove_entities", PacketFlow.CLIENTBOUND); } @Override public int clientboundSetEntityDataPacket() { - return PacketIdFinder.clientboundByName("minecraft:set_entity_data"); + return PlayPacketIdHelper.byName("minecraft:set_entity_data", PacketFlow.CLIENTBOUND); } @Override public int clientboundSetTitleTextPacket() { - return PacketIdFinder.clientboundByName("minecraft:set_title_text"); + return PlayPacketIdHelper.byName("minecraft:set_title_text", PacketFlow.CLIENTBOUND); } @Override public int clientboundSetSubtitleTextPacket() { - return PacketIdFinder.clientboundByName("minecraft:set_subtitle_text"); + return PlayPacketIdHelper.byName("minecraft:set_subtitle_text", PacketFlow.CLIENTBOUND); } @Override public int clientboundSetActionBarTextPacket() { - return PacketIdFinder.clientboundByName("minecraft:set_action_bar_text"); + return PlayPacketIdHelper.byName("minecraft:set_action_bar_text", PacketFlow.CLIENTBOUND); } @Override public int clientboundBossEventPacket() { - return PacketIdFinder.clientboundByName("minecraft:boss_event"); + return PlayPacketIdHelper.byName("minecraft:boss_event", PacketFlow.CLIENTBOUND); } @Override public int clientboundSystemChatPacket() { - return PacketIdFinder.clientboundByName("minecraft:system_chat"); + return PlayPacketIdHelper.byName("minecraft:system_chat", PacketFlow.CLIENTBOUND); } @Override public int clientboundTabListPacket() { - return PacketIdFinder.clientboundByName("minecraft:tab_list"); + return PlayPacketIdHelper.byName("minecraft:tab_list", PacketFlow.CLIENTBOUND); } @Override public int clientboundSetPlayerTeamPacket() { - return PacketIdFinder.clientboundByName("minecraft:set_player_team"); + return PlayPacketIdHelper.byName("minecraft:set_player_team", PacketFlow.CLIENTBOUND); } @Override public int clientboundSetObjectivePacket() { - return PacketIdFinder.clientboundByName("minecraft:set_objective"); + return PlayPacketIdHelper.byName("minecraft:set_objective", PacketFlow.CLIENTBOUND); } @Override public int clientboundLevelChunkWithLightPacket() { - return PacketIdFinder.clientboundByName("minecraft:level_chunk_with_light"); + return PlayPacketIdHelper.byName("minecraft:level_chunk_with_light", PacketFlow.CLIENTBOUND); } @Override public int clientboundPlayerInfoUpdatePacket() { - return PacketIdFinder.clientboundByName("minecraft:player_info_update"); + return PlayPacketIdHelper.byName("minecraft:player_info_update", PacketFlow.CLIENTBOUND); } @Override public int clientboundSetScorePacket() { - return PacketIdFinder.clientboundByName("minecraft:set_score"); + return PlayPacketIdHelper.byName("minecraft:set_score", PacketFlow.CLIENTBOUND); } @Override public int clientboundContainerSetContentPacket() { - return PacketIdFinder.clientboundByName("minecraft:container_set_content"); + return PlayPacketIdHelper.byName("minecraft:container_set_content", PacketFlow.CLIENTBOUND); } @Override public int clientboundContainerSetSlotPacket() { - return PacketIdFinder.clientboundByName("minecraft:container_set_slot"); + return PlayPacketIdHelper.byName("minecraft:container_set_slot", PacketFlow.CLIENTBOUND); } @Override public int clientboundSetCursorItemPacket() { - return PacketIdFinder.clientboundByName("minecraft:set_cursor_item"); + return PlayPacketIdHelper.byName("minecraft:set_cursor_item", PacketFlow.CLIENTBOUND); } @Override public int clientboundSetEquipmentPacket() { - return PacketIdFinder.clientboundByName("minecraft:set_equipment"); + return PlayPacketIdHelper.byName("minecraft:set_equipment", PacketFlow.CLIENTBOUND); } @Override public int clientboundSetPlayerInventoryPacket() { - return PacketIdFinder.clientboundByName("minecraft:set_player_inventory"); + return PlayPacketIdHelper.byName("minecraft:set_player_inventory", PacketFlow.CLIENTBOUND); } @Override public int clientboundBlockEventPacket() { - return PacketIdFinder.clientboundByName("minecraft:block_event"); + return PlayPacketIdHelper.byName("minecraft:block_event", PacketFlow.CLIENTBOUND); } @Override public int clientboundRecipeBookAddPacket() { - return PacketIdFinder.clientboundByName("minecraft:recipe_book_add"); + return PlayPacketIdHelper.byName("minecraft:recipe_book_add", PacketFlow.CLIENTBOUND); } @Override public int clientboundPlaceGhostRecipePacket() { - return PacketIdFinder.clientboundByName("minecraft:place_ghost_recipe"); + return PlayPacketIdHelper.byName("minecraft:place_ghost_recipe", PacketFlow.CLIENTBOUND); } @Override public int clientboundUpdateRecipesPacket() { - return PacketIdFinder.clientboundByName("minecraft:update_recipes"); + return PlayPacketIdHelper.byName("minecraft:update_recipes", PacketFlow.CLIENTBOUND); } @Override public int clientboundUpdateAdvancementsPacket() { - return PacketIdFinder.clientboundByName("minecraft:update_advancements"); - } - - @Override - public int serverboundContainerClickPacket() { - return PacketIdFinder.serverboundByName("minecraft:container_click"); - } - - @Override - public int serverboundSetCreativeModeSlotPacket() { - return PacketIdFinder.serverboundByName("minecraft:set_creative_mode_slot"); - } - - @Override - public int serverboundInteractPacket() { - return PacketIdFinder.serverboundByName("minecraft:interact"); + return PlayPacketIdHelper.byName("minecraft:update_advancements", PacketFlow.CLIENTBOUND); } @Override public int clientboundForgetLevelChunkPacket() { - return PacketIdFinder.clientboundByName("minecraft:forget_level_chunk"); + return PlayPacketIdHelper.byName("minecraft:forget_level_chunk", PacketFlow.CLIENTBOUND); + } + + @Override + public int serverboundContainerClickPacket() { + return PlayPacketIdHelper.byName("minecraft:container_click", PacketFlow.SERVERBOUND); + } + + @Override + public int serverboundSetCreativeModeSlotPacket() { + return PlayPacketIdHelper.byName("minecraft:set_creative_mode_slot", PacketFlow.SERVERBOUND); + } + + @Override + public int serverboundInteractPacket() { + return PlayPacketIdHelper.byName("minecraft:interact", PacketFlow.SERVERBOUND); + } + + @Override + public int serverboundCustomPayloadPacket() { + return PlayPacketIdHelper.byName("custom_payload", PacketFlow.SERVERBOUND); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PlayPacketIdHelper.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PlayPacketIdHelper.java new file mode 100644 index 000000000..19ac2a8d1 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PlayPacketIdHelper.java @@ -0,0 +1,75 @@ +package net.momirealms.craftengine.bukkit.plugin.network.id; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.network.PacketFlow; +import net.momirealms.craftengine.core.util.VersionHelper; + +import java.util.*; + +public class PlayPacketIdHelper { + // 1.20.5-latest + private static final Map> byName = new EnumMap<>(PacketFlow.class); + private static final Map byId = new EnumMap<>(PacketFlow.class); + // 1.20-1.20.4 + private static final Map, Integer>> byClazz = new EnumMap<>(PacketFlow.class); + + static { + try { + if (VersionHelper.isOrAbove1_21()) { + Object packetReport = CoreReflections.constructor$PacketReport.newInstance((Object) null); + JsonObject packetReportData = ((JsonElement) CoreReflections.method$PacketReport$serializePackets.invoke(packetReport)).getAsJsonObject(); + JsonObject playData = packetReportData.get("play").getAsJsonObject(); + for (Map.Entry entry : playData.entrySet()) { + Map ids = new HashMap<>(); + byName.put(PacketFlow.valueOf(entry.getKey().toUpperCase(Locale.ROOT)), ids); + for (var entry2 : entry.getValue().getAsJsonObject().entrySet()) { + ids.put(entry2.getKey(), entry2.getValue().getAsJsonObject().get("protocol_id").getAsInt()); + } + } + } else if (VersionHelper.isOrAbove1_20_5()) { + for (Map.Entry> entry : FastNMS.INSTANCE.gamePacketIdsByName().entrySet()) { + byName.put(PacketFlow.valueOf(entry.getKey().toUpperCase(Locale.ROOT)), entry.getValue()); + } + } else { + for (Map.Entry, Integer>> entry : FastNMS.INSTANCE.gamePacketIdsByClazz().entrySet()) { + byClazz.put(PacketFlow.valueOf(entry.getKey().toUpperCase(Locale.ROOT)), entry.getValue()); + } + } + if (!byName.isEmpty()) { + for (Map.Entry> entry : byName.entrySet()) { + String[] ids = new String[entry.getValue().size()]; + for (Map.Entry nameIdEntry : entry.getValue().entrySet()) { + ids[nameIdEntry.getValue()] = nameIdEntry.getKey(); + } + byId.put(entry.getKey(), ids); + } + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to init packet registry", e); + } + } + + public static int count(PacketFlow direction) { + if (VersionHelper.isOrAbove1_20_5()) { + return byName.getOrDefault(direction, Collections.emptyMap()).size(); + } else { + return byClazz.getOrDefault(direction, Collections.emptyMap()).size(); + } + } + + public static String byId(int id, PacketFlow direction) { + return byId.get(direction)[id]; + } + + public static int byName(String packetName, PacketFlow direction) { + return byName.get(direction).getOrDefault(packetName, -1); + } + + public static int byClazz(Class clazz, PacketFlow direction) { + return byClazz.get(direction).getOrDefault(clazz, -1); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/listener/ByteBufferPacketListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/listener/ByteBufferPacketListener.java new file mode 100644 index 000000000..ebc7dbe34 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/listener/ByteBufferPacketListener.java @@ -0,0 +1,13 @@ +package net.momirealms.craftengine.bukkit.plugin.network.listener; + +import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; + +public interface ByteBufferPacketListener { + + default void onPacketReceive(NetWorkUser user, ByteBufPacketEvent event) { + } + + default void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/listener/ByteBufferPacketListenerHolder.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/listener/ByteBufferPacketListenerHolder.java new file mode 100644 index 000000000..d59614303 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/listener/ByteBufferPacketListenerHolder.java @@ -0,0 +1,4 @@ +package net.momirealms.craftengine.bukkit.plugin.network.listener; + +public record ByteBufferPacketListenerHolder(String id, ByteBufferPacketListener listener) { +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/listener/NMSPacketListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/listener/NMSPacketListener.java new file mode 100644 index 000000000..b7471726b --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/listener/NMSPacketListener.java @@ -0,0 +1,13 @@ +package net.momirealms.craftengine.bukkit.plugin.network.listener; + +import net.momirealms.craftengine.core.plugin.network.NMSPacketEvent; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; + +public interface NMSPacketListener { + + default void onPacketReceive(NetWorkUser user, NMSPacketEvent event, Object packet) { + } + + default void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodec.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodec.java deleted file mode 100644 index 7fa1d4131..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodec.java +++ /dev/null @@ -1,22 +0,0 @@ -package net.momirealms.craftengine.bukkit.plugin.network.payload; - -import io.netty.buffer.ByteBuf; - -import java.util.function.Function; - -public interface NetWorkCodec extends NetWorkEncoder, NetWorkDecoder { - - default NetWorkCodec map(Function factory, Function getter) { - return new NetWorkCodec<>() { - @Override - public O decode(ByteBuf in) { - return factory.apply(NetWorkCodec.this.decode(in)); - } - - @Override - public void encode(ByteBuf out, O value) { - NetWorkCodec.this.encode(out, getter.apply(value)); - } - }; - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDataTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDataTypes.java deleted file mode 100644 index 37da1618d..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDataTypes.java +++ /dev/null @@ -1,28 +0,0 @@ -package net.momirealms.craftengine.bukkit.plugin.network.payload; - -import io.netty.buffer.ByteBuf; - -public enum NetWorkDataTypes { - CLIENT_CUSTOM_BLOCK(NetWorkCodecs.INTEGER), - CANCEL_BLOCK_UPDATE(NetWorkCodecs.BOOLEAN); - - private final NetWorkCodec codec; - - NetWorkDataTypes(NetWorkCodec codec) { - this.codec = codec; - } - - public NetWorkCodec codec() { - return codec; - } - - @SuppressWarnings("unchecked") - public V decode(ByteBuf buf) { - return (V) codec.decode(buf); - } - - @SuppressWarnings("unchecked") - public void encode(ByteBuf buf, V value) { - ((NetWorkCodec) codec).encode(buf, value); - } -} \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDecoder.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDecoder.java deleted file mode 100644 index fc3024db0..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDecoder.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.momirealms.craftengine.bukkit.plugin.network.payload; - -import io.netty.buffer.ByteBuf; - -public interface NetWorkDecoder { - T decode(ByteBuf in); -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkEncoder.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkEncoder.java deleted file mode 100644 index 3c346115a..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkEncoder.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.momirealms.craftengine.bukkit.plugin.network.payload; - -import io.netty.buffer.ByteBuf; - -public interface NetWorkEncoder { - void encode(ByteBuf out, T value); -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/PayloadHelper.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/PayloadHelper.java new file mode 100644 index 000000000..0fee4fb84 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/PayloadHelper.java @@ -0,0 +1,55 @@ +package net.momirealms.craftengine.bukkit.plugin.network.payload; + +import io.netty.buffer.Unpooled; +import net.momirealms.craftengine.bukkit.plugin.network.payload.protocol.CancelBlockUpdatePacket; +import net.momirealms.craftengine.bukkit.plugin.network.payload.protocol.ClientBlockStateSizePacket; +import net.momirealms.craftengine.bukkit.plugin.network.payload.protocol.ClientCustomBlockPacket; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.network.ModPacket; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.network.NetworkManager; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.registry.WritableRegistry; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; +import net.momirealms.craftengine.core.util.ResourceKey; + +public class PayloadHelper { + + public static void registerDataTypes() { + registerDataType(ClientCustomBlockPacket.TYPE, ClientCustomBlockPacket.CODEC); + registerDataType(CancelBlockUpdatePacket.TYPE, CancelBlockUpdatePacket.CODEC); + registerDataType(ClientBlockStateSizePacket.TYPE, ClientBlockStateSizePacket.CODEC); + } + + public static void registerDataType(ResourceKey> key, NetworkCodec codec) { + ((WritableRegistry>) BuiltInRegistries.MOD_PACKET).register(key, codec); + } + + public static void sendData(NetWorkUser user, ModPacket data) { + @SuppressWarnings("unchecked") + NetworkCodec codec = (NetworkCodec) BuiltInRegistries.MOD_PACKET.getValue(data.type()); + if (codec == null) { + CraftEngine.instance().logger().warn("Unknown data type class: " + data.getClass().getName()); + return; + } + FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); + buf.writeByte(BuiltInRegistries.MOD_PACKET.getId(codec)); + codec.encode(buf, data); + user.sendCustomPayload(NetworkManager.MOD_CHANNEL_KEY, buf.array()); + } + + public static void handleReceiver(Payload payload, NetWorkUser user) { + FriendlyByteBuf buf = payload.toBuffer(); + byte type = buf.readByte(); + @SuppressWarnings("unchecked") + NetworkCodec codec = (NetworkCodec) BuiltInRegistries.MOD_PACKET.getValue(type); + if (codec == null) { + CraftEngine.instance().logger().warn("Unknown data type received: " + type); + return; + } + + ModPacket networkData = codec.decode(buf); + networkData.handle(user); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/UnknownPayload.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/UnknownPayload.java index 4143eea27..7eece0691 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/UnknownPayload.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/UnknownPayload.java @@ -11,8 +11,8 @@ public record UnknownPayload(Key channel, ByteBuf rawPayload) implements Payload public static UnknownPayload from(Object payload) { try { - Object id = NetworkReflections.field$UnknownPayload$id.get(payload); - ByteBuf data = (ByteBuf) NetworkReflections.field$UnknownPayload$data.get(payload); + Object id = NetworkReflections.field$ServerboundCustomPayloadPacket$UnknownPayload$id.get(payload); + ByteBuf data = (ByteBuf) NetworkReflections.field$ServerboundCustomPayloadPacket$UnknownPayload$data.get(payload); Key channel = KeyUtils.resourceLocationToKey(id); return new UnknownPayload(channel, data); } catch (Exception e) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/CancelBlockUpdatePacket.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/CancelBlockUpdatePacket.java new file mode 100644 index 000000000..215046487 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/CancelBlockUpdatePacket.java @@ -0,0 +1,39 @@ +package net.momirealms.craftengine.bukkit.plugin.network.payload.protocol; + +import net.momirealms.craftengine.bukkit.plugin.network.payload.PayloadHelper; +import net.momirealms.craftengine.core.plugin.network.ModPacket; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceKey; + +public record CancelBlockUpdatePacket(boolean enabled) implements ModPacket { + public static final ResourceKey> TYPE = ResourceKey.create( + BuiltInRegistries.MOD_PACKET.key().location(), Key.of("craftengine", "cancel_block_update") + ); + public static final NetworkCodec CODEC = ModPacket.codec( + CancelBlockUpdatePacket::encode, + CancelBlockUpdatePacket::new + ); + + private CancelBlockUpdatePacket(FriendlyByteBuf buf) { + this(buf.readBoolean()); + } + + private void encode(FriendlyByteBuf buf) { + buf.writeBoolean(this.enabled); + } + + @Override + public ResourceKey> type() { + return TYPE; + } + + @Override + public void handle(NetWorkUser user) { + if (!this.enabled) return; + PayloadHelper.sendData(user, this); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientBlockStateSizePacket.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientBlockStateSizePacket.java new file mode 100644 index 000000000..41adce8c7 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientBlockStateSizePacket.java @@ -0,0 +1,39 @@ +package net.momirealms.craftengine.bukkit.plugin.network.payload.protocol; + + +import net.momirealms.craftengine.core.plugin.network.ModPacket; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; +import net.momirealms.craftengine.core.util.IntIdentityList; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceKey; + +public record ClientBlockStateSizePacket(int blockStateSize) implements ModPacket { + public static final ResourceKey> TYPE = ResourceKey.create( + BuiltInRegistries.MOD_PACKET.key().location(), Key.of("craftengine", "client_block_state_size") + ); + public static final NetworkCodec CODEC = ModPacket.codec( + ClientBlockStateSizePacket::encode, + ClientBlockStateSizePacket::new + ); + + private ClientBlockStateSizePacket(FriendlyByteBuf buf) { + this(buf.readInt()); + } + + private void encode(FriendlyByteBuf buf) { + buf.writeInt(this.blockStateSize); + } + + @Override + public ResourceKey> type() { + return TYPE; + } + + @Override + public void handle(NetWorkUser user) { + user.setClientBlockList(new IntIdentityList(this.blockStateSize)); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientCustomBlockPacket.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientCustomBlockPacket.java new file mode 100644 index 000000000..257399aae --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientCustomBlockPacket.java @@ -0,0 +1,77 @@ +package net.momirealms.craftengine.bukkit.plugin.network.payload.protocol; + + +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslationArgument; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.paper.PaperReflections; +import net.momirealms.craftengine.bukkit.util.RegistryUtils; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.network.ModPacket; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.util.*; +import org.bukkit.entity.Player; + +public record ClientCustomBlockPacket(int size) implements ModPacket { + public static final ResourceKey> TYPE = ResourceKey.create( + BuiltInRegistries.MOD_PACKET.key().location(), Key.of("craftengine", "client_custom_block") + ); + public static final NetworkCodec CODEC = ModPacket.codec( + ClientCustomBlockPacket::encode, + ClientCustomBlockPacket::new + ); + + private ClientCustomBlockPacket(FriendlyByteBuf buf) { + this(buf.readInt()); + } + + private void encode(FriendlyByteBuf buf) { + buf.writeInt(this.size); + } + + @Override + public ResourceKey> type() { + return TYPE; + } + + @Override + public void handle(NetWorkUser user) { + if (user.clientModEnabled()) return; // 防止滥用 + int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize(); + if (this.size != serverBlockRegistrySize) { + user.kick(Component.translatable( + "disconnect.craftengine.block_registry_mismatch", + TranslationArgument.numeric(this.size), + TranslationArgument.numeric(serverBlockRegistrySize) + )); + return; + } + user.setClientModState(true); + user.setClientBlockList(new IntIdentityList(this.size)); + if (!VersionHelper.isOrAbove1_20_2()) { + // 因为旧版本没有配置阶段需要重新发送区块 + try { + Object chunkLoader = PaperReflections.field$ServerPlayer$chunkLoader.get(user.serverPlayer()); + LongOpenHashSet sentChunks = (LongOpenHashSet) PaperReflections.field$RegionizedPlayerChunkLoader$PlayerChunkLoaderData$sentChunks.get(chunkLoader); + Object serverLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(((Player) user.platformPlayer()).getWorld()); + Object lightEngine = CoreReflections.method$BlockAndTintGetter$getLightEngine.invoke(serverLevel); + Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel); + for (long chunkPos : sentChunks) { + int chunkX = (int) chunkPos; + int chunkZ = (int) (chunkPos >> 32); + Object levelChunk = FastNMS.INSTANCE.method$ServerChunkCache$getChunk(chunkSource, chunkX, chunkZ, false); + Object packet = NetworkReflections.constructor$ClientboundLevelChunkWithLightPacket.newInstance(levelChunk, lightEngine, null, null); + user.sendPacket(packet, true); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to refresh chunk for player " + user.name(), e); + } + } + } + +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java index 55ae08a45..0d4bdd343 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.gson.JsonElement; import com.mojang.serialization.Codec; import com.mojang.serialization.DynamicOps; @@ -469,17 +470,17 @@ public final class CoreReflections { public static final Object instance$Direction$SOUTH; public static final Object instance$Direction$WEST; public static final Object instance$Direction$EAST; - public static final Object[] instance$Directions; + public static final Object[] instance$Direction$values; static { try { - instance$Directions = (Object[]) method$Direction$values.invoke(null); - instance$Direction$DOWN = instance$Directions[0]; - instance$Direction$UP = instance$Directions[1]; - instance$Direction$NORTH = instance$Directions[2]; - instance$Direction$SOUTH = instance$Directions[3]; - instance$Direction$WEST = instance$Directions[4]; - instance$Direction$EAST = instance$Directions[5]; + instance$Direction$values = (Object[]) method$Direction$values.invoke(null); + instance$Direction$DOWN = instance$Direction$values[0]; + instance$Direction$UP = instance$Direction$values[1]; + instance$Direction$NORTH = instance$Direction$values[2]; + instance$Direction$SOUTH = instance$Direction$values[3]; + instance$Direction$WEST = instance$Direction$values[4]; + instance$Direction$EAST = instance$Direction$values[5]; } catch (ReflectiveOperationException e) { throw new ReflectionInitException("Failed to init Direction", e); } @@ -667,9 +668,9 @@ public final class CoreReflections { ) ); - public static final Method method$SynchedEntityData$get = requireNonNull( - ReflectionUtils.getMethod(clazz$SynchedEntityData, Object.class, clazz$EntityDataAccessor) - ); + // public static final Method method$SynchedEntityData$get = requireNonNull( + // ReflectionUtils.getDeclaredMethod(clazz$SynchedEntityData, Object.class, new String[]{"get", VersionHelper.isOrAbove1_20_5() ? "a" : "b"}, clazz$EntityDataAccessor) + // ); public static final Class clazz$SynchedEntityData$DataValue = requireNonNull( BukkitReflectionUtils.findReobfOrMojmapClass( @@ -917,7 +918,7 @@ public final class CoreReflections { ); public static final Method method$IdMapper$add = requireNonNull( - ReflectionUtils.getMethod(clazz$IdMapper, void.class, Object.class) + ReflectionUtils.getMethod(clazz$IdMapper, void.class, new String[] {"add", "b"}, Object.class) ); public static final Object instance$Block$BLOCK_STATE_REGISTRY; @@ -974,9 +975,9 @@ public final class CoreReflections { ReflectionUtils.getDeclaredField(clazz$PalettedContainer$Data, clazz$Palette, 0) ); - public static final Method method$Palette$write = requireNonNull( - ReflectionUtils.getMethod(clazz$Palette, void.class, clazz$FriendlyByteBuf) - ); + // public static final Method method$Palette$write = requireNonNull( + // ReflectionUtils.getMethod(clazz$Palette, void.class, clazz$FriendlyByteBuf) + // ); public static final Class clazz$ChunkAccess = requireNonNull( BukkitReflectionUtils.findReobfOrMojmapClass( @@ -1219,6 +1220,10 @@ public final class CoreReflections { ReflectionUtils.getStaticMethod(clazz$MapColor, clazz$MapColor, int.class) ); + public static final Field field$MapColor$id = requireNonNull( + ReflectionUtils.getDeclaredField(clazz$MapColor, int.class, 1) + ); + public static final Class clazz$PushReaction = requireNonNull( BukkitReflectionUtils.findReobfOrMojmapClass( "world.level.material.EnumPistonReaction", @@ -1230,6 +1235,16 @@ public final class CoreReflections { ReflectionUtils.getMethod(clazz$PushReaction, new String[] { "values" }) ); + public static final Object[] instance$PushReaction$values; + + static { + try { + instance$PushReaction$values = (Object[]) method$PushReaction$values.invoke(null); + } catch (ReflectiveOperationException e) { + throw new ReflectionInitException("Failed to init PushReaction", e); + } + } + public static final Class clazz$NoteBlockInstrument = requireNonNull( BukkitReflectionUtils.findReobfOrMojmapClass( "world.level.block.state.properties.BlockPropertyInstrument", @@ -1241,6 +1256,16 @@ public final class CoreReflections { ReflectionUtils.getMethod(clazz$NoteBlockInstrument, new String[] { "values" }) ); + public static final Object[] instance$NoteBlockInstrument$values; + + static { + try { + instance$NoteBlockInstrument$values = (Object[]) method$NoteBlockInstrument$values.invoke(null); + } catch (ReflectiveOperationException e) { + throw new ReflectionInitException("Failed to init NoteBlockInstrument", e); + } + } + public static final Class clazz$BlockStateBase = requireNonNull( BukkitReflectionUtils.findReobfOrMojmapClass( "world.level.block.state.BlockBase$BlockData", @@ -1291,7 +1316,6 @@ public final class CoreReflections { ReflectionUtils.getDeclaredField(clazz$BlockStateBase, clazz$MapColor, 0) ); - public static final Field field$BlockStateBase$instrument = requireNonNull( ReflectionUtils.getDeclaredField(clazz$BlockStateBase, clazz$NoteBlockInstrument, 0) ); @@ -1570,6 +1594,17 @@ public final class CoreReflections { ) ); + public static final Method method$BlockBehaviour$hasAnalogOutputSignal = requireNonNull( + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, boolean.class, new String[]{"hasAnalogOutputSignal", + VersionHelper.isOrAbove1_20_5() ? "c_" : "d_"}, clazz$BlockState) + ); + + public static final Method method$BlockBehaviour$getAnalogOutputSignal = requireNonNull( + VersionHelper.isOrAbove1_21_9() + ? ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, int.class, new String[]{"getAnalogOutputSignal", "a"}, clazz$BlockState, clazz$Level, clazz$BlockPos, clazz$Direction) + : ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, int.class, new String[]{"getAnalogOutputSignal", "a"}, clazz$BlockState, clazz$Level, clazz$BlockPos) + ); + public static final Method method$Entity$level = requireNonNull( ReflectionUtils.getMethod(clazz$Entity, clazz$Level) ); @@ -1678,6 +1713,10 @@ public final class CoreReflections { ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, boolean.class, clazz$BlockState, clazz$LevelReader, clazz$BlockPos) ); + public static final Method method$Block$stepOn = requireNonNull( + ReflectionUtils.getMethod(clazz$Block, void.class, new String[] {"stepOn", "a"}, clazz$Level, clazz$BlockPos, clazz$BlockState, clazz$Entity) + ); + public static final Method method$BlockBehaviour$onExplosionHit = MiscUtils.requireNonNullIf( ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, clazz$BlockState, VersionHelper.isOrAbove1_21_2() ? clazz$ServerLevel : clazz$Level, clazz$BlockPos, clazz$Explosion, BiConsumer.class), VersionHelper.isOrAbove1_21() @@ -2314,12 +2353,30 @@ public final class CoreReflections { ) ); + public static final Class clazz$WorldlyContainerHolder = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.IInventoryHolder", + "world.WorldlyContainerHolder" + ) + ); + public static final Method method$BonemealableBlock$isValidBonemealTarget = requireNonNull( VersionHelper.isOrAbove1_20_2() ? ReflectionUtils.getInstanceMethod(clazz$BonemealableBlock, boolean.class, clazz$LevelReader, clazz$BlockPos, clazz$BlockState) : ReflectionUtils.getInstanceMethod(clazz$BonemealableBlock, boolean.class, clazz$LevelReader, clazz$BlockPos, clazz$BlockState, boolean.class) ); + public static final Class clazz$WorldlyContainer = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.IWorldInventory", + "world.WorldlyContainer" + ) + ); + + + public static final Method method$WorldlyContainerHolder$getContainer = requireNonNull( + ReflectionUtils.getMethod(clazz$WorldlyContainerHolder, clazz$WorldlyContainer, clazz$BlockState, clazz$LevelAccessor, clazz$BlockPos) + ); public static final Method method$BonemealableBlock$isBonemealSuccess = requireNonNull( ReflectionUtils.getMethod(clazz$BonemealableBlock, boolean.class, clazz$Level, clazz$RandomSource, clazz$BlockPos, clazz$BlockState) ); @@ -2526,19 +2583,19 @@ public final class CoreReflections { ReflectionUtils.getMethod(clazz$BlockHitResult, clazz$BlockHitResult, clazz$BlockPos) ); - public static final Field field$BlockHitResul$blockPos = requireNonNull( + public static final Field field$BlockHitResult$blockPos = requireNonNull( ReflectionUtils.getDeclaredField(clazz$BlockHitResult, clazz$BlockPos, 0) ); - public static final Field field$BlockHitResul$direction = requireNonNull( + public static final Field field$BlockHitResult$direction = requireNonNull( ReflectionUtils.getDeclaredField(clazz$BlockHitResult, clazz$Direction, 0) ); - public static final Field field$BlockHitResul$miss = requireNonNull( + public static final Field field$BlockHitResult$miss = requireNonNull( ReflectionUtils.getDeclaredField(clazz$BlockHitResult, boolean.class, 0) ); - public static final Field field$BlockHitResul$inside = requireNonNull( + public static final Field field$BlockHitResult$inside = requireNonNull( ReflectionUtils.getDeclaredField(clazz$BlockHitResult, boolean.class, 1) ); @@ -3578,23 +3635,23 @@ public final class CoreReflections { clazz$Registry, clazz$HolderLookup$RegistryLookup, new String[]{"asLookup", "p"} ); - public static final Field field$ServerEntity$broadcast = requireNonNull( - ReflectionUtils.getDeclaredField( - clazz$ServerEntity, Consumer.class, 0 - ) - ); + // public static final Field field$ServerEntity$broadcast = requireNonNull( + // ReflectionUtils.getDeclaredField( + // clazz$ServerEntity, Consumer.class, 0 + // ) + // ); - public static final MethodHandle methodHandle$ServerEntity$broadcastSetter; + // public static final MethodHandle methodHandle$ServerEntity$broadcastSetter; public static final MethodHandle methodHandle$ServerEntity$updateIntervalSetter; public static final MethodHandle methodHandle$ServerPlayer$connectionGetter; public static final MethodHandle methodHandle$ServerPlayer$getAttributeMethod; static { try { - methodHandle$ServerEntity$broadcastSetter = requireNonNull( - ReflectionUtils.unreflectSetter(field$ServerEntity$broadcast) - .asType(MethodType.methodType(void.class, Object.class, Consumer.class)) - ); + // methodHandle$ServerEntity$broadcastSetter = requireNonNull( + // ReflectionUtils.unreflectSetter(field$ServerEntity$broadcast) + // .asType(MethodType.methodType(void.class, Object.class, Consumer.class)) + // ); methodHandle$ServerEntity$updateIntervalSetter = requireNonNull( ReflectionUtils.unreflectSetter(field$ServerEntity$updateInterval) .asType(MethodType.methodType(void.class, Object.class, int.class)) @@ -3627,6 +3684,10 @@ public final class CoreReflections { ReflectionUtils.getDeclaredField(clazz$FireBlock, Object2IntMap.class, 0) ); + public static final Field field$FireBlock$burnOdds = requireNonNull( + ReflectionUtils.getDeclaredField(clazz$FireBlock, Object2IntMap.class, 1) + ); + public static final Class clazz$EnchantmentMenu = requireNonNull( BukkitReflectionUtils.findReobfOrMojmapClass( "world.inventory.ContainerEnchantTable", @@ -3644,11 +3705,12 @@ public final class CoreReflections { // 1.20.5+ public static final Field field$ItemStack$CODEC = ReflectionUtils.getDeclaredField(clazz$ItemStack, "CODEC", "b"); - public static final Codec instance$ItemStack$CODEC; + public static final Codec instance$ItemStack$CODEC = getItemStack$CODEC(); - static { + @SuppressWarnings("unchecked") + private static Codec getItemStack$CODEC() { try { - instance$ItemStack$CODEC = VersionHelper.isOrAbove1_20_5() ? (Codec) field$ItemStack$CODEC.get(null) : null; + return VersionHelper.isOrAbove1_20_5() ? (Codec) field$ItemStack$CODEC.get(null) : null; } catch (ReflectiveOperationException e) { throw new ReflectionInitException("Failed to init ItemStack$CODEC", e); } @@ -4177,4 +4239,226 @@ public final class CoreReflections { "world.level.storage.loot.entries.LootPoolEntryType" ) ); + + public static final Method method$BlockAndTintGetter$getLightEngine = requireNonNull( + ReflectionUtils.getDeclaredMethod(clazz$BlockAndTintGetter, clazz$LevelLightEngine) + ); + + public static final Method method$Block$fallOn = requireNonNull( + ReflectionUtils.getDeclaredMethod(clazz$Block, void.class, clazz$Level, clazz$BlockState, clazz$BlockPos, clazz$Entity, VersionHelper.isOrAbove1_21_5() ? double.class : float.class) + ); + + public static final Method method$Block$updateEntityMovementAfterFallOn = requireNonNull( + ReflectionUtils.getDeclaredMethod(clazz$Block, void.class, clazz$BlockGetter, clazz$Entity) + ); + + public static final Class clazz$AdvancementRewards = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "advancements.AdvancementRewards", + "advancements.AdvancementRewards" + ) + ); + + public static final Field field$AdvancementRewards$EMPTY = requireNonNull( + ReflectionUtils.getStaticDeclaredField(clazz$AdvancementRewards, clazz$AdvancementRewards, 0) + ); + + public static final Object instance$AdvancementRewards$EMPTY; + + static { + try { + instance$AdvancementRewards$EMPTY = field$AdvancementRewards$EMPTY.get(null); + } catch (ReflectiveOperationException e) { + throw new ReflectionInitException("Failed to initialize AdvancementRewards$EMPTY", e); + } + } + + public static final Class clazz$AdvancementRequirements = MiscUtils.requireNonNullIf( + BukkitReflectionUtils.findReobfOrMojmapClass( + "advancements.AdvancementRequirements", + "advancements.AdvancementRequirements" + ), VersionHelper.isOrAbove1_20_2() + ); + + public static final Constructor constructor$AdvancementRequirements = Optional.ofNullable(clazz$AdvancementRequirements) + .map(it -> { + if (VersionHelper.isOrAbove1_20_3()) { + return ReflectionUtils.getConstructor(it, List.class); + } else { + return ReflectionUtils.getConstructor(it, String[][].class); + } + }).orElse(null); + + public static final Class clazz$AdvancementProgress = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "advancements.AdvancementProgress", + "advancements.AdvancementProgress" + ) + ); + + public static final Constructor constructor$AdvancementProgress = requireNonNull( + ReflectionUtils.getConstructor(clazz$AdvancementProgress) + ); + + public static final Method method$AdvancementProgress$update = requireNonNull( + VersionHelper.isOrAbove1_20_2() ? + ReflectionUtils.getMethod(clazz$AdvancementProgress, void.class, clazz$AdvancementRequirements) : + ReflectionUtils.getMethod(clazz$AdvancementProgress, void.class, Map.class, String[][].class) + ); + + public static final Class clazz$AdvancementType = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "advancements.AdvancementFrameType", + VersionHelper.isOrAbove1_20_3() ? "advancements.AdvancementType" : "advancements.FrameType" + ) + ); + + public static final Method method$AdvancementType$values = requireNonNull( + ReflectionUtils.getStaticMethod(clazz$AdvancementType, clazz$AdvancementType.arrayType()) + ); + + public static final Object[] instance$AdvancementType$values; + + static { + try { + instance$AdvancementType$values = (Object[]) method$AdvancementType$values.invoke(null); + } catch (ReflectiveOperationException e) { + throw new ReflectionInitException("Failed to initialize AdvancementTypes", e); + } + } + + public static final Class clazz$DisplayInfo = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "advancements.AdvancementDisplay", + "advancements.DisplayInfo" + ) + ); + + public static final Constructor constructor$DisplayInfo = requireNonNull( + VersionHelper.isOrAbove1_20_3() ? + ReflectionUtils.getConstructor(clazz$DisplayInfo, clazz$ItemStack, clazz$Component, clazz$Component, Optional.class, clazz$AdvancementType, boolean.class, boolean.class, boolean.class) : + ReflectionUtils.getConstructor(clazz$DisplayInfo, clazz$ItemStack, clazz$Component, clazz$Component, clazz$ResourceLocation, clazz$AdvancementType, boolean.class, boolean.class, boolean.class) + ); + + public static final Class clazz$Criterion = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "advancements.Criterion", + "advancements.Criterion" + ) + ); + + public static final Class clazz$CriterionTrigger = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "advancements.CriterionTrigger", + "advancements.CriterionTrigger" + ) + ); + + public static final Class clazz$CriterionTriggerInstance = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "advancements.CriterionInstance", + "advancements.CriterionTriggerInstance" + ) + ); + + public static final Class clazz$ImpossibleTrigger = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "advancements.critereon.CriterionTriggerImpossible", + "advancements.critereon.ImpossibleTrigger" + ) + ); + + public static final Constructor constructor$ImpossibleTrigger = requireNonNull( + ReflectionUtils.getConstructor(clazz$ImpossibleTrigger) + ); + + public static final Class clazz$ImpossibleTrigger$TriggerInstance = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "advancements.critereon.CriterionTriggerImpossible$a", + "advancements.critereon.ImpossibleTrigger$TriggerInstance" + ) + ); + + public static final Constructor constructor$Criterion = requireNonNull( + VersionHelper.isOrAbove1_20_2() ? + ReflectionUtils.getConstructor(clazz$Criterion, clazz$CriterionTrigger, clazz$CriterionTriggerInstance) : + ReflectionUtils.getConstructor(clazz$Criterion, clazz$CriterionTriggerInstance) + ); + + public static final Constructor constructor$ImpossibleTrigger$TriggerInstance = requireNonNull( + ReflectionUtils.getConstructor(clazz$ImpossibleTrigger$TriggerInstance) + ); + + public static final Class clazz$Advancement = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "advancements.Advancement", + "advancements.Advancement" + ) + ); + + public static final Constructor constructor$Advancement = requireNonNull( + VersionHelper.isOrAbove1_20_2() ? + ReflectionUtils.getConstructor(clazz$Advancement, Optional.class, Optional.class, clazz$AdvancementRewards, Map.class, clazz$AdvancementRequirements, boolean.class) : + ReflectionUtils.getConstructor(clazz$Advancement, clazz$ResourceLocation, clazz$Advancement, clazz$DisplayInfo, clazz$AdvancementRewards, Map.class, String[][].class, boolean.class) + ); + + public static final Class clazz$CriterionProgress = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "advancements.CriterionProgress", + "advancements.CriterionProgress" + ) + ); + + public static final Method method$AdvancementProgress$grantProgress = requireNonNull( + ReflectionUtils.getMethod(clazz$AdvancementProgress, boolean.class, new String[]{"grantProgress", "a"}, String.class) + ); + + public static final Class clazz$AdvancementHolder = MiscUtils.requireNonNullIf( + BukkitReflectionUtils.findReobfOrMojmapClass( + "advancements.AdvancementHolder", + "advancements.AdvancementHolder" + ), VersionHelper.isOrAbove1_20_2() + ); + + public static final Constructor constructor$AdvancementHolder = Optional.ofNullable(clazz$AdvancementHolder) + .map(it -> ReflectionUtils.getConstructor(it, clazz$ResourceLocation, clazz$Advancement)) + .orElse(null); + + public static final Class clazz$FenceGateBlock = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.level.block.BlockFenceGate", + "world.level.block.FenceGateBlock" + ) + ); + + public static final Class clazz$GameEvent = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("world.level.gameevent.GameEvent") + ) + ); + + public static final Class clazz$HolderSet = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("core.HolderSet") + ) + ); + + public static final Method method$BlockStateBase$isBlock = requireNonNull( + ReflectionUtils.getDeclaredMethod(clazz$BlockStateBase, boolean.class, new String[]{"is", "a"}, clazz$Block) + ); + + public static final Class clazz$Projectile = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.entity.projectile.IProjectile", + "world.entity.projectile.Projectile" + ) + ); + + public static final Method method$BlockBehaviour$onProjectileHit = requireNonNull( + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, new String[]{"onProjectileHit", "a"}, clazz$Level, clazz$BlockState, clazz$BlockHitResult, clazz$Projectile) + ); + + public static final Field field$EnumProperty$values = requireNonNull( + ReflectionUtils.getDeclaredField(clazz$EnumProperty, VersionHelper.isOrAbove1_21_2() ? List.class : ImmutableSet.class, 0) + ); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlockStateProperties.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlockStateProperties.java index 0bec500a3..452f4efb3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlockStateProperties.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlockStateProperties.java @@ -4,24 +4,34 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.ReflectionInitExcepti import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.Collection; import static java.util.Objects.requireNonNull; public final class MBlockStateProperties { public static final Object WATERLOGGED; + public static final Object FACING; static { try { Object waterlogged = null; + Object facing = null; for (Field field : CoreReflections.clazz$BlockStateProperties.getDeclaredFields()) { if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) { Object instance = field.get(null); if (CoreReflections.clazz$Property.isInstance(instance) && CoreReflections.field$Property$name.get(instance).equals("waterlogged")) { waterlogged = instance; + } else if (CoreReflections.clazz$EnumProperty.isInstance(instance) && CoreReflections.field$Property$name.get(instance).equals("facing")) { + @SuppressWarnings("unchecked") + Collection values = (Collection) CoreReflections.field$EnumProperty$values.get(instance); + if (values.size() == CoreReflections.instance$Direction$values.length) { + facing = instance; + } } } } WATERLOGGED = requireNonNull(waterlogged); + FACING = requireNonNull(facing); } catch (ReflectiveOperationException e) { throw new ReflectionInitException("Failed to init MBlockStateProperties", e); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlocks.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlocks.java index ccdb91f69..0bb390b3b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlocks.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlocks.java @@ -6,52 +6,35 @@ import net.momirealms.craftengine.core.util.VersionHelper; public final class MBlocks { private MBlocks() {} - public static final Object AIR; - public static final Object AIR$defaultState; - public static final Object STONE; - public static final Object STONE$defaultState; - public static final Object BEDROCK; - public static final Object OBSIDIAN; - public static final Object FIRE; - public static final Object SOUL_FIRE; - public static final Object ICE; - public static final Object SHORT_GRASS; - public static final Object SHORT_GRASS$defaultState; - public static final Object SHULKER_BOX; - public static final Object COMPOSTER; - public static final Object SNOW; - public static final Object WATER; - public static final Object WATER$defaultState; - public static final Object TNT; - public static final Object TNT$defaultState; - public static final Object LODESTONE; - public static final Object END_PORTAL_FRAME; + public static final Object AIR = getById("air"); + public static final Object AIR$defaultState = FastNMS.INSTANCE.method$Block$defaultState(AIR); + public static final Object STONE = getById("stone"); + public static final Object STONE$defaultState = FastNMS.INSTANCE.method$Block$defaultState(STONE); + public static final Object FIRE = getById("fire"); + public static final Object SOUL_FIRE = getById("soul_fire"); + public static final Object ICE = getById("ice"); + public static final Object SHORT_GRASS = getById(VersionHelper.isOrAbove1_20_3() ? "short_grass" : "grass"); + public static final Object SHORT_GRASS$defaultState = FastNMS.INSTANCE.method$Block$defaultState(SHORT_GRASS); + public static final Object SHULKER_BOX = getById("shulker_box"); + public static final Object COMPOSTER = getById("composter"); + public static final Object SNOW = getById("snow"); + public static final Object WATER = getById("water"); + public static final Object WATER$defaultState = FastNMS.INSTANCE.method$Block$defaultState(WATER); + public static final Object TNT = getById("tnt"); + public static final Object TNT$defaultState = FastNMS.INSTANCE.method$Block$defaultState(TNT); + public static final Object BARRIER = getById("barrier"); + public static final Object CARVED_PUMPKIN = getById("carved_pumpkin"); + public static final Object JACK_O_LANTERN = getById("jack_o_lantern"); + public static final Object MELON = getById("melon"); + public static final Object PUMPKIN = getById("pumpkin"); + public static final Object FARMLAND = getById("farmland"); + public static final Object LODESTONE = getById("lodestone"); + public static final Object BEDROCK = getById("bedrock"); + public static final Object OBSIDIAN = getById("obsidian"); + public static final Object END_PORTAL_FRAME = getById("end_portal_frame"); private static Object getById(String id) { Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id); return FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, rl); } - - static { - AIR = getById("air"); - AIR$defaultState = FastNMS.INSTANCE.method$Block$defaultState(AIR); - FIRE = getById("fire"); - SOUL_FIRE = getById("soul_fire"); - STONE = getById("stone"); - STONE$defaultState = FastNMS.INSTANCE.method$Block$defaultState(STONE); - BEDROCK = getById("bedrock"); - OBSIDIAN = getById("obsidian"); - ICE = getById("ice"); - SHORT_GRASS = getById(VersionHelper.isOrAbove1_20_3() ? "short_grass" : "grass"); - SHORT_GRASS$defaultState = FastNMS.INSTANCE.method$Block$defaultState(SHORT_GRASS); - SHULKER_BOX = getById("shulker_box"); - COMPOSTER = getById("composter"); - SNOW = getById("snow"); - WATER = getById("water"); - WATER$defaultState = FastNMS.INSTANCE.method$Block$defaultState(WATER); - TNT = getById("tnt"); - TNT$defaultState = FastNMS.INSTANCE.method$Block$defaultState(TNT); - LODESTONE = getById("lodestone"); - END_PORTAL_FRAME = getById("end_portal_frame"); - } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBuiltInRegistries.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBuiltInRegistries.java index ef6d7a8b1..565ae69c3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBuiltInRegistries.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBuiltInRegistries.java @@ -22,6 +22,7 @@ public final class MBuiltInRegistries { public static final Object PARTICLE_TYPE; public static final Object DATA_COMPONENT_TYPE; public static final Object LOOT_POOL_ENTRY_TYPE; + public static final Object GAME_EVENT; static { Field[] fields = CoreReflections.clazz$BuiltInRegistries.getDeclaredFields(); @@ -37,6 +38,7 @@ public final class MBuiltInRegistries { Object registries$RecipeType = null; Object registries$DataComponentType = null; Object registries$LootPoolEntryType = null; + Object registries$GameEvent = null; for (Field field : fields) { Type fieldType = field.getGenericType(); if (fieldType instanceof ParameterizedType paramType) { @@ -67,6 +69,8 @@ public final class MBuiltInRegistries { registries$Fluid = field.get(null); } else if (type == CoreReflections.clazz$LootPoolEntryType) { registries$LootPoolEntryType = field.get(null); + } else if (type == CoreReflections.clazz$GameEvent) { + registries$GameEvent = field.get(null); } } } @@ -82,6 +86,7 @@ public final class MBuiltInRegistries { RECIPE_TYPE = requireNonNull(registries$RecipeType); LOOT_POOL_ENTRY_TYPE = requireNonNull(registries$LootPoolEntryType); DATA_COMPONENT_TYPE = registries$DataComponentType; + GAME_EVENT = requireNonNull(registries$GameEvent); } catch (ReflectiveOperationException e) { throw new ReflectionInitException("Failed to init BuiltInRegistries", e); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MEntitySelectors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MEntitySelectors.java new file mode 100644 index 000000000..76bbaf19b --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MEntitySelectors.java @@ -0,0 +1,12 @@ +package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft; + +import net.momirealms.craftengine.bukkit.nms.FastNMS; + +import java.util.function.Predicate; + +public final class MEntitySelectors { + private MEntitySelectors() {} + + public static final Predicate NO_SPECTATORS = entity -> !FastNMS.INSTANCE.method$Entity$isSpectator(entity); + +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MGameEvents.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MGameEvents.java new file mode 100644 index 000000000..fad931574 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MGameEvents.java @@ -0,0 +1,15 @@ +package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft; + +import net.momirealms.craftengine.bukkit.nms.FastNMS; + +public final class MGameEvents { + private MGameEvents() {} + + public static final Object BLOCK_ACTIVATE = getById("block_activate"); + public static final Object BLOCK_DEACTIVATE = getById("block_deactivate"); + + private static Object getById(String id) { + Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id); + return FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.GAME_EVENT, rl); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MTagKeys.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MTagKeys.java index bc3a11172..3f444599e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MTagKeys.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MTagKeys.java @@ -9,6 +9,10 @@ public final class MTagKeys { public static final Object Item$WOOL = create(MRegistries.ITEM, "wool"); public static final Object Block$WALLS = create(MRegistries.BLOCK, "walls"); + public static final Object Block$SHULKER_BOXES = create(MRegistries.BLOCK, "shulker_boxes"); + public static final Object Block$FENCES = create(MRegistries.BLOCK, "fences"); + public static final Object Block$WOODEN_FENCES = create(MRegistries.BLOCK, "wooden_fences"); + public static final Object Block$DIRT = create(MRegistries.BLOCK, "dirt"); private static Object create(Object registry, String location) { Object resourceLocation = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", location); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java index 4e421cff8..efbcc589d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java @@ -1057,7 +1057,6 @@ public final class NetworkReflections { ) ); - public static final Constructor constructor$ClientboundMoveEntityPacket$PosRot = requireNonNull( ReflectionUtils.getTheOnlyConstructor(clazz$ClientboundMoveEntityPacket$PosRot) ); @@ -1635,7 +1634,7 @@ public final class NetworkReflections { } // 1.20.2~1.20.4 - public static final Class clazz$UnknownPayload = MiscUtils.requireNonNullIf( + public static final Class clazz$ServerboundCustomPayloadPacket$UnknownPayload = MiscUtils.requireNonNullIf( ReflectionUtils.getClazz( BukkitReflectionUtils.assembleMCClass("network.protocol.common.ServerboundCustomPayloadPacket$UnknownPayload") ), @@ -1643,17 +1642,17 @@ public final class NetworkReflections { ); // 1.20.2~1.20.4 - public static final Field field$UnknownPayload$id = Optional.ofNullable(clazz$UnknownPayload) + public static final Field field$ServerboundCustomPayloadPacket$UnknownPayload$id = Optional.ofNullable(clazz$ServerboundCustomPayloadPacket$UnknownPayload) .map(it -> ReflectionUtils.getDeclaredField(it, CoreReflections.clazz$ResourceLocation, 0)) .orElse(null); // 1.20.2~1.20.4 - public static final Field field$UnknownPayload$data = Optional.ofNullable(clazz$UnknownPayload) + public static final Field field$ServerboundCustomPayloadPacket$UnknownPayload$data = Optional.ofNullable(clazz$ServerboundCustomPayloadPacket$UnknownPayload) .map(it -> ReflectionUtils.getDeclaredField(it, ByteBuf.class, 0)) .orElse(null); // 1.20.2~1.20.4 - public static final Constructor constructor$UnknownPayload = Optional.ofNullable(clazz$UnknownPayload) + public static final Constructor constructor$ServerboundCustomPayloadPacket$UnknownPayload = Optional.ofNullable(clazz$ServerboundCustomPayloadPacket$UnknownPayload) .map(it -> ReflectionUtils.getConstructor(it, CoreReflections.clazz$ResourceLocation, ByteBuf.class)) .orElse(null); @@ -1672,4 +1671,10 @@ public final class NetworkReflections { "network.protocol.game.ClientboundForgetLevelChunkPacket" ) ); + + public static final Constructor constructor$ClientboundUpdateAdvancementsPacket = requireNonNull( + VersionHelper.isOrAbove1_21_5() ? + ReflectionUtils.getConstructor(clazz$ClientboundUpdateAdvancementsPacket, boolean.class, Collection.class, Set.class, Map.class, boolean.class) : + ReflectionUtils.getConstructor(clazz$ClientboundUpdateAdvancementsPacket, boolean.class, Collection.class, Set.class, Map.class) + ); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/paper/PaperReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/paper/PaperReflections.java index 1210342a2..d6d4095bd 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/paper/PaperReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/paper/PaperReflections.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.plugin.reflection.paper; import com.google.gson.Gson; import io.papermc.paper.event.player.AsyncChatDecorateEvent; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.core.util.ReflectionUtils; @@ -97,4 +98,23 @@ public final class PaperReflections { public static final Method method$BookMeta$page = requireNonNull( ReflectionUtils.getMethod(CraftBukkitReflections.clazz$BookMeta, void.class, int.class, clazz$AdventureComponent) ); + + public static final Class clazz$RegionizedPlayerChunkLoader$PlayerChunkLoaderData = requireNonNull( + ReflectionUtils.getClazz( + "ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader$PlayerChunkLoaderData", + "io.papermc.paper.chunk.system.RegionizedPlayerChunkLoader$PlayerChunkLoaderData" + ) + ); + + public static final Field field$ServerPlayer$chunkLoader = requireNonNull( + ReflectionUtils.getDeclaredField( + CoreReflections.clazz$ServerPlayer, PaperReflections.clazz$RegionizedPlayerChunkLoader$PlayerChunkLoaderData, 0 + ) + ); + + public static final Field field$RegionizedPlayerChunkLoader$PlayerChunkLoaderData$sentChunks = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$RegionizedPlayerChunkLoader$PlayerChunkLoaderData, LongOpenHashSet.class, 0 + ) + ); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index 9d4ebc8ee..2b578908c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -1,25 +1,30 @@ package net.momirealms.craftengine.bukkit.plugin.user; +import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable; import com.google.common.collect.Lists; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; +import io.papermc.paper.registry.RegistryAccess; +import io.papermc.paper.registry.RegistryKey; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks; +import net.momirealms.craftengine.bukkit.block.entity.BlockEntityHolder; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.plugin.gui.CraftEngineInventoryHolder; +import net.momirealms.craftengine.bukkit.plugin.gui.CraftEngineGUIHolder; import net.momirealms.craftengine.bukkit.plugin.network.payload.DiscardedPayload; -import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MMobEffects; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.bukkit.util.*; import net.momirealms.craftengine.bukkit.world.BukkitWorld; +import net.momirealms.craftengine.core.advancement.AdvancementType; import net.momirealms.craftengine.core.block.BlockStateWrapper; import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; import net.momirealms.craftengine.core.entity.player.GameMode; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.player.Player; @@ -31,17 +36,21 @@ import net.momirealms.craftengine.core.plugin.network.ConnectionState; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; import net.momirealms.craftengine.core.sound.SoundSource; import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.IntIdentityList; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.craftengine.core.world.BlockPos; -import net.momirealms.craftengine.core.world.ChunkPos; +import net.momirealms.craftengine.core.world.*; import net.momirealms.craftengine.core.world.World; -import net.momirealms.craftengine.core.world.WorldEvents; +import net.momirealms.craftengine.core.world.chunk.ChunkStatus; +import net.momirealms.craftengine.core.world.collision.AABB; import org.bukkit.*; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeInstance; import org.bukkit.block.Block; +import org.bukkit.damage.DamageSource; +import org.bukkit.damage.DamageType; import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; @@ -66,6 +75,8 @@ public class BukkitServerPlayer extends Player { private ChannelHandler connection; private String name; private UUID uuid; + private boolean isNameVerified; + private boolean isUUIDVerified; private ConnectionState decoderState; private ConnectionState encoderState; private boolean shouldProcessFinishConfiguration = true; @@ -97,6 +108,7 @@ public class BukkitServerPlayer extends Player { private int resentSwingTick; // has fabric client mod or not private boolean hasClientMod = false; + private IntIdentityList blockList = new IntIdentityList(BlockStateUtils.vanillaBlockStateCount()); // cache if player can break blocks private boolean clientSideCanBreak = true; // prevent AFK players from consuming too much CPU resource on predicting @@ -111,11 +123,9 @@ public class BukkitServerPlayer extends Player { // cooldown data private CooldownData cooldownData; // tracked chunks - private final Set trackedChunks = Collections.synchronizedSet(new HashSet<>()); - // relighted chunks - private final Set relightedChunks = Collections.synchronizedSet(new HashSet<>()); + private ConcurrentLong2ReferenceChainedHashTable trackedChunks; // entity view - private final Map entityTypeView = new ConcurrentHashMap<>(); + private Map entityTypeView; public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) { this.channel = channel; @@ -135,8 +145,12 @@ public class BukkitServerPlayer extends Player { this.playerRef = new WeakReference<>(player); this.serverPlayerRef = new WeakReference<>(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)); this.uuid = player.getUniqueId(); + this.isUUIDVerified = true; this.name = player.getName(); + this.isNameVerified = true; byte[] bytes = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(CooldownData.COOLDOWN_KEY), PersistentDataType.BYTE_ARRAY); + this.trackedChunks = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(768, 0.5f); + this.entityTypeView = new ConcurrentHashMap<>(256); try { this.cooldownData = CooldownData.fromBytes(bytes); } catch (IOException e) { @@ -229,6 +243,11 @@ public class BukkitServerPlayer extends Player { return AdventureModeUtils.canPlace(platformPlayer().getInventory().getItemInMainHand(), new Location(platformPlayer().getWorld(), pos.x(), pos.y(), pos.z()), state); } + @Override + public void sendToast(Component text, Item icon, AdvancementType type) { + this.plugin.advancementManager().sendToast(this, icon, text, type); + } + @Override public void sendActionBar(Component text) { Object packet = FastNMS.INSTANCE.constructor$ClientboundActionBarPacket(ComponentUtils.adventureToMinecraft(text)); @@ -309,30 +328,54 @@ public class BukkitServerPlayer extends Player { } @Override - public void setName(String name) { - if (this.name != null) return; + public boolean isNameVerified() { + return this.isNameVerified; + } + + @Override + public void setUnverifiedName(String name) { + if (this.isNameVerified) return; this.name = name; } + @Override + public void setVerifiedName(String name) { + if (this.isNameVerified) return; + this.name = name; + this.isNameVerified = true; + } + @Override public UUID uuid() { return this.uuid; } @Override - public void setUUID(UUID uuid) { - if (this.uuid != null) return; + public boolean isUUIDVerified() { + return this.isUUIDVerified; + } + + @Override + public void setUnverifiedUUID(UUID uuid) { + if (this.isUUIDVerified) return; this.uuid = uuid; } + @Override + public void setVerifiedUUID(UUID uuid) { + if (this.isUUIDVerified) return; + this.uuid = uuid; + this.isUUIDVerified = true; + } + @Override public void playSound(Key sound, SoundSource source, float volume, float pitch) { platformPlayer().playSound(platformPlayer(), sound.toString(), SoundUtils.toBukkit(source), volume, pitch); } @Override - public void playSound(Key sound, BlockPos blockPos, SoundSource source, float volume, float pitch) { - platformPlayer().playSound(new Location(null, blockPos.x() + 0.5, blockPos.y() + 0.5, blockPos.z() + 0.5), sound.toString(), SoundUtils.toBukkit(source), volume, pitch); + public void playSound(Position pos, Key sound, SoundSource source, float volume, float pitch) { + platformPlayer().playSound(new Location(null, pos.x(), pos.y(), pos.z()), sound.toString(), SoundUtils.toBukkit(source), volume, pitch); } @Override @@ -378,7 +421,7 @@ public class BukkitServerPlayer extends Player { if (VersionHelper.isOrAbove1_20_2()) { Object dataPayload; if (!VersionHelper.isOrAbove1_20_5()) { - dataPayload = NetworkReflections.constructor$UnknownPayload.newInstance(channelResourceLocation, Unpooled.wrappedBuffer(data)); + dataPayload = NetworkReflections.constructor$ServerboundCustomPayloadPacket$UnknownPayload.newInstance(channelResourceLocation, Unpooled.wrappedBuffer(data)); } else if (DiscardedPayload.useNewMethod) { dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelResourceLocation, data); } else { @@ -464,10 +507,10 @@ public class BukkitServerPlayer extends Player { } else { this.gameTicks = FastNMS.INSTANCE.field$MinecraftServer$currentTick(); } - if (this.gameTicks % 30 == 0) { + if (this.gameTicks % 20 == 0) { this.updateGUI(); } - if (this.isDestroyingBlock) { + if (this.isDestroyingBlock) { this.tickBlockDestroy(); } if (Config.predictBreaking() && !this.isDestroyingCustomBlock) { @@ -485,14 +528,33 @@ public class BukkitServerPlayer extends Player { private void updateGUI() { org.bukkit.inventory.Inventory top = !VersionHelper.isOrAbove1_21() ? LegacyInventoryUtils.getTopInventory(platformPlayer()) : platformPlayer().getOpenInventory().getTopInventory(); - if (!CraftBukkitReflections.clazz$MinecraftInventory.isInstance(FastNMS.INSTANCE.method$CraftInventory$getInventory(top))) { - return; - } - if (top.getHolder() instanceof CraftEngineInventoryHolder holder) { + if (!InventoryUtils.isCustomContainer(top)) return; + if (top.getHolder() instanceof CraftEngineGUIHolder holder) { holder.gui().onTimer(); + } else if (top.getHolder() instanceof BlockEntityHolder holder) { + BlockEntity blockEntity = holder.blockEntity(); + BlockPos blockPos = blockEntity.pos(); + if (!canInteractWithBlock(blockPos, 4d)) { + platformPlayer().closeInventory(); + } } } + public boolean canInteractWithBlock(BlockPos pos, double distance) { + double d = this.getCachedInteractionRange() + distance; + return (new AABB(pos)).distanceToSqr(this.getEyePosition()) < d * d; + } + + public boolean canInteractPoint(Vec3d pos, double distance) { + double d = this.getCachedInteractionRange() + distance; + return Vec3d.distanceToSqr(this.getEyePosition(), pos) < d * d; + } + + public final Vec3d getEyePosition() { + Location eyeLocation = this.platformPlayer().getEyeLocation(); + return new Vec3d(eyeLocation.getX(), eyeLocation.getY(), eyeLocation.getZ()); + } + @Override public float getDestroyProgress(Object blockState, BlockPos pos) { Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState); @@ -500,7 +562,6 @@ public class BukkitServerPlayer extends Player { if (optionalCustomState.isPresent()) { ImmutableBlockState customState = optionalCustomState.get(); Item tool = getItemInHand(InteractionHand.MAIN_HAND); - boolean isCorrectTool = FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(tool.getLiteralObject(), blockState); // 如果自定义方块在服务端侧未使用正确的工具,那么需要还原挖掘速度 if (!BlockStateUtils.isCorrectTool(customState, tool)) { progress *= customState.settings().incorrectToolSpeed(); @@ -660,9 +721,8 @@ public class BukkitServerPlayer extends Player { // send hit sound if the sound is removed if (currentTick - this.lastHitBlockTime > 3) { - Object blockOwner = FastNMS.INSTANCE.method$BlockState$getBlock(destroyedState); - Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(blockOwner); - Object soundEvent = CoreReflections.field$SoundType$hitSound.get(soundType); + Object soundType = FastNMS.INSTANCE.method$BlockBehaviour$BlockStateBase$getSoundType(destroyedState); + Object soundEvent = FastNMS.INSTANCE.field$SoundType$hitSound(soundType); Object soundId = FastNMS.INSTANCE.field$SoundEvent$location(soundEvent); player.playSound(location, soundId.toString(), SoundCategory.BLOCKS, 0.5F, 0.5F); this.lastHitBlockTime = currentTick; @@ -909,14 +969,26 @@ public class BukkitServerPlayer extends Player { return resentSwingTick == gameTicks(); } + @Override public boolean clientModEnabled() { return this.hasClientMod; } + @Override public void setClientModState(boolean enable) { this.hasClientMod = enable; } + @Override + public void setClientBlockList(IntIdentityList blockList) { + this.blockList = blockList; + } + + @Override + public IntIdentityList clientBlockList() { + return this.blockList; + } + @Override public void addResourcePackUUID(UUID uuid) { if (VersionHelper.isOrAbove1_20_3()) { @@ -1036,21 +1108,40 @@ public class BukkitServerPlayer extends Player { } @Override - public boolean isChunkTracked(ChunkPos chunkPos) { - return this.trackedChunks.contains(chunkPos); + public boolean isChunkTracked(long chunkPos) { + return this.trackedChunks.containsKey(chunkPos); } @Override - public void setChunkTrackStatus(ChunkPos chunkPos, boolean tracked) { - if (tracked) { - this.trackedChunks.add(chunkPos); - } else { - this.trackedChunks.remove(chunkPos); - } + public ChunkStatus getTrackedChunk(long chunkPos) { + return this.trackedChunks.get(chunkPos); + } + + @Override + public void addTrackedChunk(long chunkPos, ChunkStatus chunkStatus) { + this.trackedChunks.put(chunkPos, chunkStatus); + } + + @Override + public void removeTrackedChunk(long chunkPos) { + this.trackedChunks.remove(chunkPos); } @Override public void clearTrackedChunks() { this.trackedChunks.clear(); } + + @Override + public void teleport(WorldPosition worldPosition) { + Location location = new Location((org.bukkit.World) worldPosition.world().platformWorld(), worldPosition.x(), worldPosition.y(), worldPosition.z(), worldPosition.yRot(), worldPosition.xRot()); + this.platformPlayer().teleportAsync(location, PlayerTeleportEvent.TeleportCause.PLUGIN); + } + + @Override + public void damage(double amount, Key damageType) { + @SuppressWarnings("deprecation") + DamageType type = Registry.DAMAGE_TYPE.get(KeyUtils.toNamespacedKey(damageType)); + this.platformPlayer().damage(amount, DamageSource.builder(type != null ? type : DamageType.GENERIC).build()); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java index 3d3a592f7..8c1f4c3d3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java @@ -1,15 +1,10 @@ package net.momirealms.craftengine.bukkit.util; -import net.momirealms.craftengine.bukkit.block.BukkitBlockStateWrapper; +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.reflection.ReflectionInitException; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries; -import net.momirealms.craftengine.core.block.BlockSettings; -import net.momirealms.craftengine.core.block.BlockStateWrapper; -import net.momirealms.craftengine.core.block.DelegatingBlockState; -import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.*; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.util.Key; import org.bukkit.block.Block; @@ -18,35 +13,20 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.IdentityHashMap; import java.util.List; -import java.util.Map; import java.util.Optional; public final class BlockStateUtils { - public static final IdentityHashMap CLIENT_SIDE_NOTE_BLOCKS = new IdentityHashMap<>(); - private static int vanillaStateSize; - private static boolean hasInit; - public static Map IGNITE_ODDS; - - @SuppressWarnings("unchecked") - public static void init(int size) { - if (hasInit) { - throw new IllegalStateException("BlockStateUtils has already been initialized"); - } - vanillaStateSize = size; - try { - IGNITE_ODDS = (Map) CoreReflections.field$FireBlock$igniteOdds.get(MBlocks.FIRE); - } catch (ReflectiveOperationException e) { - throw new ReflectionInitException("Failed to initialize instance$FireBlock$igniteOdds", e); - } - hasInit = true; - } + private BlockStateUtils() {} public static BlockStateWrapper toBlockStateWrapper(BlockData blockData) { Object state = blockDataToBlockState(blockData); - int id = blockStateToId(state); - return new BukkitBlockStateWrapper(state, id); + return toBlockStateWrapper(state); + } + + public static BlockStateWrapper toBlockStateWrapper(Object blockState) { + int id = blockStateToId(blockState); + return BlockRegistryMirror.byId(id); } public static boolean isCorrectTool(@NotNull ImmutableBlockState state, @Nullable Item itemInHand) { @@ -60,13 +40,13 @@ public final class BlockStateUtils { } @SuppressWarnings("unchecked") - public static List getAllVanillaBlockStates(Key block) { + public static List getPossibleBlockStates(Key block) { try { Object blockIns = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(block)); Object definition = CoreReflections.field$Block$StateDefinition.get(blockIns); return (List) CoreReflections.field$StateDefinition$states.get(definition); } catch (Exception e) { - throw new RuntimeException("Failed to get all block states for " + block, e); + throw new RuntimeException("Failed to get possible block states for " + block, e); } } @@ -112,10 +92,6 @@ public final class BlockStateUtils { return FastNMS.INSTANCE.method$BlockStateBase$isReplaceable(state); } - public static boolean isClientSideNoteBlock(Object state) { - return CLIENT_SIDE_NOTE_BLOCKS.containsKey(state); - } - public static boolean isVanillaBlock(Object state) { return !(state instanceof DelegatingBlockState); } @@ -125,11 +101,11 @@ public final class BlockStateUtils { } public static boolean isVanillaBlock(int id) { - return id >= 0 && id < vanillaStateSize; + return BukkitBlockManager.instance().isVanillaBlockState(id); } - public static int vanillaStateSize() { - return vanillaStateSize; + public static int vanillaBlockStateCount() { + return BukkitBlockManager.instance().vanillaBlockStateCount(); } public static Optional getOptionalCustomBlockState(Object state) { @@ -140,12 +116,11 @@ public final class BlockStateUtils { } } - public static boolean isBurnable(Object state) { - Object blockOwner = getBlockOwner(state); - return IGNITE_ODDS.getOrDefault(blockOwner, 0) > 0; - } - public static Object getBlockState(Block block) { return FastNMS.INSTANCE.method$BlockGetter$getBlockState(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(block.getWorld()), LocationUtils.toBlockPos(block.getX(), block.getY(), block.getZ())); } + + public static boolean isBurnable(Object blockState) { + return BukkitBlockManager.instance().isBurnable(blockState); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockUtils.java new file mode 100644 index 000000000..08546729e --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockUtils.java @@ -0,0 +1,22 @@ +package net.momirealms.craftengine.bukkit.util; + +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MTagKeys; +import net.momirealms.craftengine.core.block.BlockStateWrapper; + +public final class BlockUtils { + private BlockUtils() {} + + public static boolean isExceptionForConnection(BlockStateWrapper state) { + Object blockState = state.literalObject(); + return CoreReflections.clazz$LeavesBlock.isInstance(BlockStateUtils.getBlockOwner(blockState)) + || FastNMS.INSTANCE.method$BlockStateBase$isBlock(blockState, MBlocks.BARRIER) + || FastNMS.INSTANCE.method$BlockStateBase$isBlock(blockState, MBlocks.CARVED_PUMPKIN) + || FastNMS.INSTANCE.method$BlockStateBase$isBlock(blockState, MBlocks.JACK_O_LANTERN) + || FastNMS.INSTANCE.method$BlockStateBase$isBlock(blockState, MBlocks.MELON) + || FastNMS.INSTANCE.method$BlockStateBase$isBlock(blockState, MBlocks.PUMPKIN) + || FastNMS.INSTANCE.method$BlockStateBase$is(blockState, MTagKeys.Block$SHULKER_BOXES); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/DirectionUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/DirectionUtils.java index cf0ef5d0c..41ce992b8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/DirectionUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/DirectionUtils.java @@ -43,12 +43,9 @@ public final class DirectionUtils { } public static Direction fromNMSDirection(Object direction) { - try { - int index = (int) CoreReflections.method$Direction$ordinal.invoke(direction); - return Direction.values()[index]; - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } + Enum directionEnum = (Enum) direction; + int index = directionEnum.ordinal(); + return Direction.values()[index]; } public static boolean isYAxis(Object nmsDirection) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EnchantmentUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EnchantmentUtils.java index 6424b21f3..2a6bd2117 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EnchantmentUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EnchantmentUtils.java @@ -11,6 +11,7 @@ public final class EnchantmentUtils { @SuppressWarnings("unchecked") public static Map toMap(Object itemEnchantments) throws ReflectiveOperationException { + if (itemEnchantments == null) return Map.of(); Map map = new HashMap<>(); Map enchantments = (Map) CoreReflections.field$ItemEnchantments$enchantments.get(itemEnchantments); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InventoryUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InventoryUtils.java index 089f0b315..7b1fed616 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InventoryUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InventoryUtils.java @@ -1,8 +1,11 @@ package net.momirealms.craftengine.bukkit.util; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.nms.StorageContainer; import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryEvent; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; @@ -49,4 +52,11 @@ public final class InventoryUtils { } return -1; } + + public static boolean isCustomContainer(Inventory inventory) { + if (inventory == null) return false; + Object container = FastNMS.INSTANCE.method$CraftInventory$getInventory(inventory); + if (container == null) return false; + return container instanceof StorageContainer; + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LightUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LightUtils.java index 636c5c1e3..298ca5786 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LightUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LightUtils.java @@ -1,7 +1,6 @@ package net.momirealms.craftengine.bukkit.util; import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.core.plugin.CraftEngine; import org.bukkit.World; @@ -23,7 +22,7 @@ public final class LightUtils { if (chunkHolder == null) continue; List players = FastNMS.INSTANCE.method$ChunkHolder$getPlayers(chunkHolder); if (players.isEmpty()) continue; - Object lightEngine = CoreReflections.field$ChunkHolder$lightEngine.get(chunkHolder); + Object lightEngine = FastNMS.INSTANCE.method$ChunkSource$getLightEngine(chunkSource); Object chunkPos = FastNMS.INSTANCE.constructor$ChunkPos((int) chunkKey, (int) (chunkKey >> 32)); Object lightPacket = FastNMS.INSTANCE.constructor$ClientboundLightUpdatePacket(chunkPos, lightEngine, entry.getValue(), entry.getValue()); for (Object player : players) { @@ -36,4 +35,42 @@ public final class LightUtils { CraftEngine.instance().logger().warn("Could not update light for world " + world.getName(), e); } } +// +// public static void relightChunk(BukkitServerPlayer player, ChunkPos pos) { +// long chunkKey = pos.longKey; +// ChunkStatus status = player.getTrackedChunk(chunkKey); +// // 不处理未加载区块 +// if (status == null || status.relighted()) return; +// for (ChunkPos anotherPos : pos.adjacentChunkPos()) { +// // 要求周围区块必须都加载 +// if (player.getTrackedChunk(anotherPos.longKey) == null) { +// return; +// } +// } +// status.setRelighted(true); +// net.momirealms.craftengine.core.world.World world = player.world(); +// Object serverLevel = world.serverWorld(); +// Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel); +// Object chunkHolder = FastNMS.INSTANCE.method$ServerChunkCache$getVisibleChunkIfPresent(chunkSource, chunkKey); +// if (chunkHolder == null) return; +// CEWorld ceWorld = BukkitWorldManager.instance().getWorld(world.uuid()); +// CEChunk ceChunk = ceWorld.getChunkAtIfLoaded(chunkKey); +// if (ceChunk == null) return; +// CESection[] sections = ceChunk.sections(); +// BitSet bitSet = new BitSet(); +// for (int i = 0; i < sections.length; i++) { +// if (!sections[i].statesContainer().isEmpty()) { +// bitSet.set(i); +// } +// } +// if (bitSet.isEmpty()) return; +// try { +// Object lightEngine = CoreReflections.field$ChunkHolder$lightEngine.get(chunkHolder); +// Object chunkPos = FastNMS.INSTANCE.constructor$ChunkPos((int) chunkKey, (int) (chunkKey >> 32)); +// Object lightPacket = FastNMS.INSTANCE.constructor$ClientboundLightUpdatePacket(chunkPos, lightEngine, bitSet, bitSet); +// player.sendPacket(lightPacket, false); +// } catch (Throwable t) { +// CraftEngine.instance().logger().warn("Could not send relight packet for " + player.name() + " at " + player.world().name() + " " + pos.x + "," + pos.z, t); +// } +// } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LocationUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LocationUtils.java index 0d0688e1c..9b572b357 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LocationUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LocationUtils.java @@ -29,7 +29,7 @@ public final class LocationUtils { return new Vec3d( FastNMS.INSTANCE.field$Vec3$x(vec), FastNMS.INSTANCE.field$Vec3$y(vec), - FastNMS.INSTANCE.field$Vec3$y(vec) + FastNMS.INSTANCE.field$Vec3$z(vec) ); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/NoteBlockChainUpdateUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/NoteBlockChainUpdateUtils.java index b77fea1ba..1f00cd1dc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/NoteBlockChainUpdateUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/NoteBlockChainUpdateUtils.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.util; +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; public final class NoteBlockChainUpdateUtils { @@ -11,7 +12,7 @@ public final class NoteBlockChainUpdateUtils { if (times-- < 0) return; Object relativePos = FastNMS.INSTANCE.method$BlockPos$relative(blockPos, direction); Object state = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, relativePos); - if (BlockStateUtils.isClientSideNoteBlock(state)) { + if (BukkitBlockManager.CLIENT_SIDE_NOTE_BLOCKS.contains(state)) { FastNMS.INSTANCE.method$ServerChunkCache$blockChanged(chunkSource, relativePos); noteBlockChainUpdate(level, chunkSource, direction, relativePos, times); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/StairsShapeUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/StairsShapeUtils.java index dde8b33f7..bd6175bcd 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/StairsShapeUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/StairsShapeUtils.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.bukkit.util; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.core.block.state.properties.StairsShape; +import net.momirealms.craftengine.core.block.properties.type.StairsShape; public final class StairsShapeUtils { private StairsShapeUtils() {} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitCEWorld.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitCEWorld.java index aadb65e6f..988f8e787 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitCEWorld.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitCEWorld.java @@ -9,7 +9,8 @@ import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor; import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; -import java.util.HashSet; +import java.util.ArrayList; +import java.util.List; public class BukkitCEWorld extends CEWorld { @@ -22,20 +23,21 @@ public class BukkitCEWorld extends CEWorld { } @Override - public void tick() { - HashSet poses; - synchronized (super.updatedSectionSet) { - poses = new HashSet<>(super.updatedSectionSet); - super.updatedSectionSet.clear(); - } + public void updateLight() { if (Config.enableLightSystem()) { + super.isUpdatingLights = true; LightUtils.updateChunkLight( - (org.bukkit.World) world.platformWorld(), - SectionPosUtils.toMap(poses, - world.worldHeight().getMinSection() - 1, - world.worldHeight().getMaxSection() + 1 + (org.bukkit.World) this.world.platformWorld(), + SectionPosUtils.toMap(super.lightSections, + this.world.worldHeight().getMinSection() - 1, + this.world.worldHeight().getMaxSection() + 1 ) ); + super.lightSections.clear(); + super.isUpdatingLights = false; + List pendingLightSections = super.pendingLightSections; + super.pendingLightSections = new ArrayList<>(Math.max(pendingLightSections.size() / 2, 8)); + super.lightSections.addAll(pendingLightSections); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitExistingBlock.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitExistingBlock.java index 38dd8db86..22402fadc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitExistingBlock.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitExistingBlock.java @@ -7,11 +7,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.core.block.BlockRegistryMirror; -import net.momirealms.craftengine.core.block.BlockStateWrapper; -import net.momirealms.craftengine.core.block.CustomBlock; -import net.momirealms.craftengine.core.block.ImmutableBlockState; -import net.momirealms.craftengine.core.block.state.StatePropertyAccessor; +import net.momirealms.craftengine.core.block.*; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.world.ExistingBlock; @@ -64,7 +60,7 @@ public class BukkitExistingBlock implements ExistingBlock { @Override public @NotNull BlockStateWrapper blockState() { Object blockState = BlockStateUtils.getBlockState(this.block); - return BlockRegistryMirror.stateByRegistryId(BlockStateUtils.blockStateToId(blockState)); + return BlockRegistryMirror.byId(BlockStateUtils.blockStateToId(blockState)); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorld.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorld.java index bfb04f654..4a3b36f80 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorld.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorld.java @@ -1,8 +1,10 @@ package net.momirealms.craftengine.bukkit.world; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.*; import net.momirealms.craftengine.core.block.BlockStateWrapper; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.sound.SoundSource; @@ -10,6 +12,7 @@ import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.*; import net.momirealms.craftengine.core.world.particle.ParticleData; +import net.momirealms.craftengine.core.world.particle.ParticleType; import org.bukkit.Location; import org.bukkit.Particle; import org.bukkit.SoundCategory; @@ -21,6 +24,9 @@ import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; import java.lang.ref.WeakReference; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.UUID; public class BukkitWorld implements World { @@ -44,7 +50,8 @@ public class BukkitWorld implements World { @Override public WorldHeight worldHeight() { if (this.worldHeight == null) { - this.worldHeight = WorldHeight.create(platformWorld().getMinHeight(), platformWorld().getMaxHeight() - platformWorld().getMinHeight()); + org.bukkit.World bWorld = platformWorld(); + this.worldHeight = WorldHeight.create(bWorld.getMinHeight(), bWorld.getMaxHeight() - bWorld.getMinHeight()); } return this.worldHeight; } @@ -100,8 +107,8 @@ public class BukkitWorld implements World { } @Override - public void spawnParticle(Position location, Key particle, int count, double xOffset, double yOffset, double zOffset, double speed, @Nullable ParticleData extraData, @NotNull Context context) { - Particle particleType = ParticleUtils.getParticle(particle); + public void spawnParticle(Position location, ParticleType particle, int count, double xOffset, double yOffset, double zOffset, double speed, @Nullable ParticleData extraData, @NotNull Context context) { + Particle particleType = (Particle) particle.platformParticle(); if (particleType == null) return; org.bukkit.World platformWorld = platformWorld(); platformWorld.spawnParticle(particleType, location.x(), location.y(), location.z(), count, xOffset, yOffset, zOffset, speed, extraData == null ? null : ParticleUtils.toBukkitParticleData(extraData, context, platformWorld, location.x(), location.y(), location.z())); @@ -123,4 +130,24 @@ public class BukkitWorld implements World { public void levelEvent(int id, BlockPos pos, int data) { FastNMS.INSTANCE.method$LevelAccessor$levelEvent(serverWorld(), id, LocationUtils.toBlockPos(pos), data); } + + @Override + public CEWorld storageWorld() { + return BukkitWorldManager.instance().getWorld(uuid()); + } + + @Override + public List getTrackedBy(ChunkPos pos) { + Object serverLevel = serverWorld(); + Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel); + Object chunkHolder = FastNMS.INSTANCE.method$ServerChunkCache$getVisibleChunkIfPresent(chunkSource, pos.longKey); + if (chunkHolder == null) return Collections.emptyList(); + List players = FastNMS.INSTANCE.method$ChunkHolder$getPlayers(chunkHolder); + if (players.isEmpty()) return Collections.emptyList(); + List tracked = new ArrayList<>(players.size()); + for (Object player : players) { + tracked.add(BukkitAdaptors.adapt(FastNMS.INSTANCE.method$ServerPlayer$getBukkitEntity(player))); + } + return tracked; + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java index 134e2ee7f..9be7ba113 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java @@ -1,15 +1,15 @@ package net.momirealms.craftengine.bukkit.world; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.injector.WorldStorageInjector; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask; +import net.momirealms.craftengine.core.util.ConcurrentUUID2ReferenceChainedHashTable; import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.SectionPos; @@ -30,30 +30,23 @@ import org.bukkit.event.world.*; import org.jetbrains.annotations.NotNull; import java.io.IOException; -import java.util.Map; import java.util.Optional; import java.util.UUID; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantReadWriteLock; public class BukkitWorldManager implements WorldManager, Listener { private static BukkitWorldManager instance; private final BukkitCraftEngine plugin; - private final Map worlds; - private CEWorld[] worldArray = new CEWorld[0]; - private final ReentrantReadWriteLock worldMapLock = new ReentrantReadWriteLock(); - private SchedulerTask tickTask; - // cache - private UUID lastVisitedUUID; - private CEWorld lastVisitedWorld; + private final ConcurrentUUID2ReferenceChainedHashTable worlds; + private CEWorld[] worldArray; private StorageAdaptor storageAdaptor; - private boolean isTicking = false; private boolean initialized = false; + private UUID lastWorldUUID = null; + private CEWorld lastWorld = null; public BukkitWorldManager(BukkitCraftEngine plugin) { instance = this; this.plugin = plugin; - this.worlds = new Object2ObjectOpenHashMap<>(32, 0.5f); + this.worlds = ConcurrentUUID2ReferenceChainedHashTable.createWithCapacity(10, 0.5f); this.storageAdaptor = new DefaultStorageAdaptor(); for (World world : Bukkit.getWorlds()) { this.worlds.put(world.getUID(), new BukkitCEWorld(new BukkitWorld(world), this.storageAdaptor)); @@ -75,20 +68,15 @@ public class BukkitWorldManager implements WorldManager, Listener { @Override public CEWorld getWorld(UUID uuid) { - if (uuid.equals(this.lastVisitedUUID)) { - return this.lastVisitedWorld; + if (uuid == this.lastWorldUUID || uuid.equals(this.lastWorldUUID)) { + return this.lastWorld; } - this.worldMapLock.readLock().lock(); - try { - CEWorld world = worlds.get(uuid); - if (world != null) { - this.lastVisitedUUID = uuid; - this.lastVisitedWorld = world; - } - return world; - } finally { - this.worldMapLock.readLock().unlock(); + CEWorld world = this.worlds.get(uuid); + if (world != null) { + this.lastWorldUUID = uuid; + this.lastWorld = world; } + return world; } @Override @@ -101,37 +89,19 @@ public class BukkitWorldManager implements WorldManager, Listener { } public void delayedInit() { - // events and tasks - this.tickTask = this.plugin.scheduler().asyncRepeating(() -> { - try { - if (this.isTicking) { - return; - } - this.isTicking = true; - for (CEWorld world : this.worldArray) { - world.tick(); - } - } finally { - this.isTicking = false; - } - }, 50, 50, TimeUnit.MILLISECONDS); // load loaded chunks - this.worldMapLock.writeLock().lock(); - try { - for (World world : Bukkit.getWorlds()) { - try { - CEWorld ceWorld = this.worlds.computeIfAbsent(world.getUID(), k -> new BukkitCEWorld(new BukkitWorld(world), this.storageAdaptor)); - for (Chunk chunk : world.getLoadedChunks()) { - handleChunkLoad(ceWorld, chunk); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Error loading world: " + world.getName(), e); + for (World world : Bukkit.getWorlds()) { + try { + CEWorld ceWorld = this.worlds.computeIfAbsent(world.getUID(), k -> new BukkitCEWorld(new BukkitWorld(world), this.storageAdaptor)); + for (Chunk chunk : world.getLoadedChunks()) { + handleChunkLoad(ceWorld, chunk); } + ceWorld.setTicking(true); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Error loading world: " + world.getName(), e); } - this.resetWorldArray(); - } finally { - this.worldMapLock.writeLock().unlock(); } + this.resetWorldArray(); Bukkit.getPluginManager().registerEvents(this, this.plugin.javaPlugin()); this.initialized = true; } @@ -142,11 +112,9 @@ public class BukkitWorldManager implements WorldManager, Listener { if (this.storageAdaptor instanceof Listener listener) { HandlerList.unregisterAll(listener); } - if (this.tickTask != null && !this.tickTask.cancelled()) { - this.tickTask.cancel(); - } for (World world : Bukkit.getWorlds()) { CEWorld ceWorld = getWorld(world.getUID()); + ceWorld.setTicking(false); for (Chunk chunk : world.getLoadedChunks()) { handleChunkUnload(ceWorld, chunk); } @@ -157,6 +125,8 @@ public class BukkitWorldManager implements WorldManager, Listener { } } this.worlds.clear(); + this.lastWorld = null; + this.lastWorldUUID = null; } @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) @@ -166,33 +136,27 @@ public class BukkitWorldManager implements WorldManager, Listener { @Override public void loadWorld(net.momirealms.craftengine.core.world.World world) { - this.worldMapLock.writeLock().lock(); - try { - if (this.worlds.containsKey(world.uuid())) return; - CEWorld ceWorld = new BukkitCEWorld(world, this.storageAdaptor); - this.worlds.put(world.uuid(), ceWorld); - this.resetWorldArray(); - for (Chunk chunk : ((World) world.platformWorld()).getLoadedChunks()) { - handleChunkLoad(ceWorld, chunk); - } - } finally { - this.worldMapLock.writeLock().unlock(); + UUID uuid = world.uuid(); + if (this.worlds.containsKey(uuid)) return; + CEWorld ceWorld = new BukkitCEWorld(world, this.storageAdaptor); + this.worlds.put(uuid, ceWorld); + this.resetWorldArray(); + for (Chunk chunk : ((World) world.platformWorld()).getLoadedChunks()) { + handleChunkLoad(ceWorld, chunk); } + ceWorld.setTicking(true); } @Override public void loadWorld(CEWorld world) { - this.worldMapLock.writeLock().lock(); - try { - if (this.worlds.containsKey(world.world().uuid())) return; - this.worlds.put(world.world().uuid(), world); - this.resetWorldArray(); - for (Chunk chunk : ((World) world.world().platformWorld()).getLoadedChunks()) { - handleChunkLoad(world, chunk); - } - } finally { - this.worldMapLock.writeLock().unlock(); + UUID uuid = world.world().uuid(); + if (this.worlds.containsKey(uuid)) return; + this.worlds.put(uuid, world); + this.resetWorldArray(); + for (Chunk chunk : ((World) world.world().platformWorld()).getLoadedChunks()) { + handleChunkLoad(world, chunk); } + world.setTicking(true); } @Override @@ -214,24 +178,20 @@ public class BukkitWorldManager implements WorldManager, Listener { @Override public void unloadWorld(net.momirealms.craftengine.core.world.World world) { - CEWorld ceWorld; - this.worldMapLock.writeLock().lock(); - try { - ceWorld = this.worlds.remove(world.uuid()); - if (ceWorld == null) { - return; - } - if (ceWorld == this.lastVisitedWorld) { - this.lastVisitedWorld = null; - this.lastVisitedUUID = null; - } - this.resetWorldArray(); - } finally { - this.worldMapLock.writeLock().unlock(); + UUID uuid = world.uuid(); + CEWorld ceWorld = this.worlds.remove(uuid); + if (ceWorld == null) { + return; } + this.resetWorldArray(); + ceWorld.setTicking(false); for (Chunk chunk : ((World) world.platformWorld()).getLoadedChunks()) { handleChunkUnload(ceWorld, chunk); } + if (uuid.equals(this.lastWorldUUID)) { + this.lastWorld = null; + this.lastWorldUUID = null; + } try { ceWorld.worldDataStorage().close(); } catch (IOException e) { @@ -254,30 +214,18 @@ public class BukkitWorldManager implements WorldManager, Listener { @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onChunkLoad(ChunkLoadEvent event) { - this.worldMapLock.readLock().lock(); - CEWorld world; - try { - world = worlds.get(event.getWorld().getUID()); - if (world == null) { - return; - } - } finally { - this.worldMapLock.readLock().unlock(); + CEWorld world = worlds.get(event.getWorld().getUID()); + if (world == null) { + return; } handleChunkLoad(world, event.getChunk()); } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onChunkUnload(ChunkUnloadEvent event) { - CEWorld world; - this.worldMapLock.readLock().lock(); - try { - world = worlds.get(event.getWorld().getUID()); - if (world == null) { - return; - } - } finally { - this.worldMapLock.readLock().unlock(); + CEWorld world = worlds.get(event.getWorld().getUID()); + if (world == null) { + return; } handleChunkUnload(world, event.getChunk()); } @@ -328,16 +276,19 @@ public class BukkitWorldManager implements WorldManager, Listener { } private void handleChunkLoad(CEWorld ceWorld, Chunk chunk) { - ChunkPos pos = new ChunkPos(chunk.getX(), chunk.getZ()); - if (ceWorld.isChunkLoaded(pos.longKey)) return; + int chunkX = chunk.getX(); + int chunkZ = chunk.getZ(); + ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); + if (ceWorld.isChunkLoaded(chunkPos.longKey)) return; CEChunk ceChunk; try { - ceChunk = ceWorld.worldDataStorage().readChunkAt(ceWorld, pos); + ceChunk = ceWorld.worldDataStorage().readChunkAt(ceWorld, chunkPos); try { CESection[] ceSections = ceChunk.sections(); Object worldServer = FastNMS.INSTANCE.field$CraftChunk$worldServer(chunk); Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer); - Object levelChunk = FastNMS.INSTANCE.method$ServerChunkCache$getChunkAtIfLoadedMainThread(chunkSource, chunk.getX(), chunk.getZ()); + Object lightEngine = FastNMS.INSTANCE.method$ChunkSource$getLightEngine(chunkSource); + Object levelChunk = FastNMS.INSTANCE.method$ServerChunkCache$getChunkAtIfLoadedMainThread(chunkSource, chunkX, chunkZ); Object[] sections = FastNMS.INSTANCE.method$ChunkAccess$getSections(levelChunk); synchronized (sections) { for (int i = 0; i < ceSections.length; i++) { @@ -392,13 +343,22 @@ public class BukkitWorldManager implements WorldManager, Listener { } } if (Config.restoreCustomBlocks()) { + boolean isEmptyBefore = FastNMS.INSTANCE.method$LevelSection$hasOnlyAir(section); + int sectionY = ceSection.sectionY; + if (isEmptyBefore) { + FastNMS.INSTANCE.method$LightEventListener$updateSectionStatus(lightEngine, FastNMS.INSTANCE.method$SectionPos$of(chunkX, sectionY, chunkZ), false); + } if (!ceSection.statesContainer().isEmpty()) { for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { for (int y = 0; y < 16; y++) { ImmutableBlockState customState = ceSection.getBlockState(x, y, z); if (!customState.isEmpty() && customState.customBlockState() != null) { - FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, customState.customBlockState().literalObject(), false); + Object newState = customState.customBlockState().literalObject(); + Object previous = FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, newState, false); + if (newState != previous && FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(newState, previous)) { + FastNMS.INSTANCE.method$ThreadedLevelLightEngine$checkBlock(lightEngine, LocationUtils.toBlockPos(chunkX * 16 + x, sectionY * 16 + y, chunkZ * 16 + z)); + } } } } @@ -406,7 +366,7 @@ public class BukkitWorldManager implements WorldManager, Listener { } } int finalI = i; - WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z), + WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(chunkPos.x, ceChunk.sectionY(i), chunkPos.z), (injected) -> sections[finalI] = injected); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/particle/BukkitParticleType.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/particle/BukkitParticleType.java new file mode 100644 index 000000000..0bba8abb3 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/particle/BukkitParticleType.java @@ -0,0 +1,25 @@ +package net.momirealms.craftengine.bukkit.world.particle; + +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.world.particle.ParticleType; +import org.bukkit.Particle; + +public class BukkitParticleType implements ParticleType { + private final Particle particle; + private final Key type; + + public BukkitParticleType(Particle particle, Key type) { + this.particle = particle; + this.type = type; + } + + @Override + public Key type() { + return this.type; + } + + @Override + public Particle platformParticle() { + return particle; + } +} diff --git a/common-files/src/main/resources/additional-real-blocks.yml b/common-files/src/main/resources/additional-real-blocks.yml deleted file mode 100644 index 1573d2fbf..000000000 --- a/common-files/src/main/resources/additional-real-blocks.yml +++ /dev/null @@ -1,83 +0,0 @@ -# This file will register an additional number of block states to the server, based on the mappings defined in mappings.yml. -# If you're unsure what this means, you can read the following explanation below. - -# Suppose you create a new type of leaf, but its appearance has only two states (waterlogged and normal). -# However, because of the defined properties such as distance, persistent, and waterlogged, it requires at least 2x2x7 = 28 different block states. -# By default, the plugin only registers the same number of block states as those defined in the mappings.yml file. -# Therefore, during actual configuration, you will notice that the internal IDs are insufficient -# (without configuring additional-real-block, one type of leaf can only provide 26 states, whereas creating a new leaf requires 28 states). -# The purpose of this file is to register additional block states with the server when starting it, ensuring the correct mapping between real blocks and the visual appearance of fake blocks on the server. - -# Some common questions: -# Q: Do I need to restart the server for the changes to take effect? -# A: Yes! Modifying the block registry while the server is running is extremely risky. -# Q: When do I need to configure this file? -# A: When the number of real block IDs is insufficient, but there are still available appearances. - -minecraft:oak_leaves: 112 -minecraft:oak_sapling: 1 -minecraft:birch_sapling: 1 -minecraft:spruce_sapling: 1 -minecraft:jungle_sapling: 1 -minecraft:dark_oak_sapling: 1 -minecraft:pale_oak_sapling: 1 -minecraft:acacia_sapling: 1 -minecraft:cherry_sapling: 1 -minecraft:anvil: 2 -minecraft:chipped_anvil: 2 -minecraft:damaged_anvil: 2 -minecraft:sugarcane: 14 -minecraft:iron_trapdoor: 32 -minecraft:acacia_trapdoor: 32 -minecraft:oak_trapdoor: 32 -minecraft:spruce_trapdoor: 32 -minecraft:birch_trapdoor: 32 -minecraft:jungle_trapdoor: 32 -minecraft:dark_oak_trapdoor: 32 -minecraft:pale_oak_trapdoor: 32 -minecraft:mangrove_trapdoor: 32 -minecraft:cherry_trapdoor: 32 -minecraft:bamboo_trapdoor: 32 -minecraft:crimson_trapdoor: 32 -minecraft:warped_trapdoor: 32 -minecraft:copper_trapdoor: 32 -minecraft:exposed_copper_trapdoor: 32 -minecraft:weathered_copper_trapdoor: 32 -minecraft:oxidized_copper_trapdoor: 32 -minecraft:waxed_copper_trapdoor: 32 -minecraft:waxed_exposed_copper_trapdoor: 32 -minecraft:waxed_weathered_copper_trapdoor: 32 -minecraft:waxed_oxidized_copper_trapdoor: 32 -minecraft:iron_door: 32 -minecraft:acacia_door: 32 -minecraft:oak_door: 32 -minecraft:spruce_door: 32 -minecraft:birch_door: 32 -minecraft:jungle_door: 32 -minecraft:dark_oak_door: 32 -minecraft:pale_oak_door: 32 -minecraft:mangrove_door: 32 -minecraft:cherry_door: 32 -minecraft:bamboo_door: 32 -minecraft:crimson_door: 32 -minecraft:warped_door: 32 -minecraft:copper_door: 32 -minecraft:exposed_copper_door: 32 -minecraft:weathered_copper_door: 32 -minecraft:oxidized_copper_door: 32 -minecraft:waxed_copper_door: 32 -minecraft:waxed_exposed_copper_door: 32 -minecraft:waxed_weathered_copper_door: 32 -minecraft:waxed_oxidized_copper_door: 32 -minecraft:oak_fence_gate: 16 -minecraft:acacia_fence_gate: 16 -minecraft:spruce_fence_gate: 16 -minecraft:birch_fence_gate: 16 -minecraft:jungle_fence_gate: 16 -minecraft:dark_oak_fence_gate: 16 -minecraft:pale_oak_fence_gate: 16 -minecraft:mangrove_fence_gate: 16 -minecraft:cherry_fence_gate: 16 -minecraft:bamboo_fence_gate: 16 -minecraft:crimson_fence_gate: 16 -minecraft:warped_fence_gate: 16 \ No newline at end of file diff --git a/common-files/src/main/resources/commands.yml b/common-files/src/main/resources/commands.yml index 3ac0d8c5c..a96b802e0 100644 --- a/common-files/src/main/resources/commands.yml +++ b/common-files/src/main/resources/commands.yml @@ -152,7 +152,9 @@ debug_real_state_usage: permission: ce.command.debug.state_usage usage: - /craftengine debug real-state-usage + - /craftengine debug serverside-state-usage - /ce debug real-state-usage + - /ce debug serverside-state-usage debug_item_data: enable: true @@ -198,11 +200,25 @@ debug_is_chunk_persistent_loaded: debug_entity_id: enable: true - permission: ce.command.debug.debug_entity_id + permission: ce.command.debug.entity_id usage: - /craftengine debug entity-id - /ce debug entity-id +debug_save_default_resources: + enable: true + permission: ce.command.debug.save_default_resources + usage: + - /craftengine debug save-default-resources + - /ce debug save-default-resources + +debug_clean_cache: + enable: true + permission: ce.command.debug.clean_cache + usage: + - /craftengine debug clean-cache + - /ce debug clean-cache + debug_test: enable: true permission: ce.command.debug.test diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index b29463033..ee7b4e559 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -23,6 +23,7 @@ resource-pack: method-1: false method-2: false method-3: false # Enable this would increase the resource pack size by 0.67MB + # [Premium Exclusive] # Obfuscate your resource pack obfuscation: enable: false @@ -50,9 +51,14 @@ resource-pack: - "@vanilla_models" bypass-sounds: [] bypass-equipments: [] - # Validate if there are any errors in the resource pack, such as missing textures or models - validate: + # Validate if there is any error in the resource pack, such as missing textures or models + # If your resource pack is compliant with the standard, you can disable validation to improve the resource pack generation speed. + validation: enable: true + # [Premium Exclusive] + # Fix images that are not within the texture atlas. It is unreasonable to always rely on plugins to fix your mistakes. + # You should strive to make your resource pack more standardized after gaining some experience with resource packs. + fix-atlas: true # Define the name of the overlay folders overlay-format: "ce_overlay_{version}" # Allowed values: @@ -65,10 +71,10 @@ resource-pack: # Remove 1.21.5+ tinted_leaves particles remove-tinted-leaves-particle: true merge-external-folders: - - ModelEngine/resource pack - - BetterModel/build + - "ModelEngine/resource pack" merge-external-zip-files: - - CustomNameplates/resourcepack.zip + - "CustomNameplates/resourcepack.zip" + - "BetterModel/build.zip" exclude-file-extensions: ["md", "psd", "bbmodel", "db", "ini"] # Exclude the shaders when generating the resource pack exclude-core-shaders: false @@ -98,6 +104,8 @@ resource-pack: file-to-upload: "./generated/resource_pack.zip" # Resend the resource pack to players upon successful upload resend-on-upload: true + # Whether a verified player UUID is required to get the resource pack + strict-player-uuid-validation: true duplicated-files-handler: - term: type: any_of @@ -136,18 +144,46 @@ resource-pack: type: merge_atlas item: - # Make custom-model-data and item-model clientside by default - client-bound-model: false - # Add a tag on custom name and lore + # [Premium Exclusive] + # Makes custom-model-data and item-model client-side by default. + # + # This provides several benefits. For example, you can update model values + # dynamically without causing inconsistencies for players' existing items. + # + # The main drawback is that plugins relying on custom-model-data for item + # identification will not work correctly, as this data is not present in + # the server-side item stack. + # + # You can override this global setting per item using the + # client-bound-model option. + client-bound-model: true + # When enabled (recommended), this option adds both custom-model-data + # and an item-model to optimize client-side rendering. + # + # If disabled, the system falls back to using only custom-model-data. + # You can override this behavior by setting the item-model option + # on a per-item basis. + # + # This option only works if your resource pack supports 1.21.1 or below + always-use-item-model: true + # Since Minecraft renders lore text in italics by default, you can + # optionally prefix any lore with to remove the italic formatting. non-italic-tag: false - # Determines when to trigger the item updater - # This feature may incur some performance overhead. Please do not enable it unless necessary. - # Correct use case: When you designed incorrect weapon attributes and need to update the values for items already held by players. - # Wrong use case: When you want to update an item's name and lore to a newer version (In this case you should use client-bound-data instead of the item updater) + # Defines the trigger condition for the item updater. + # + # Warning: This operation is performance-intensive. Enable only if needed. + # + # Purpose: Reserved for correcting faults on existing player items. + # Not intended for updating names/lore; use 'client-bound-data' for those changes. update-triggers: click-in-inventory: false # this option won't work for players in creative mode drop: false pick-up: false + # Decided the starting value for automatic custom model data assignment. + custom-model-data-starting-value: + default: 10000 + overrides: + paper: 20000 equipment: # The sacrificed-vanilla-armor argument determines which vanilla armor gets completely removed (loses all its trims) @@ -161,27 +197,31 @@ equipment: humanoid-leggings: minecraft:trims/entity/humanoid_leggings/chainmail block: + # This decides the amount of real blocks on serverside. You should only consider increasing this value when your server state is insufficient. + # It is recommended to increase it by 500 each time. This option requires a restart to apply. + serverside-blocks: 2000 # Enables the sound system, which prevents the client from hearing some non-custom block sounds and improves the client experience. sound-system: enable: true - # In Adventure Mode, players need the correct tool to break custom blocks. - # Vanilla clients DO NOT recognize custom block IDs (e.g., craftengine:note_block_0). + # Adventure mode requires correct tools to break custom blocks. + # Vanilla clients cannot recognize custom block IDs (e.g., craftengine:custom_100). # - # - When ENABLED: - # - Players can break custom blocks if their tools can mine their VANILLA EQUIVALENTS. - # Example: A tool for "note_block" can break "craftengine:note_block_0". + # ENABLED: + # - Tools that can break vanilla equivalents also break custom variants. + # Example: A "note_block" tool breaks custom blocks based on note blocks # - # - When DISABLED: - # ⚠️ WARNING: - # - Server MUST list ACTUAL CUSTOM BLOCK IDs in item's `can_break` component. - # - Sending custom IDs (e.g., craftengine:note_block_0) to vanilla clients WILL CRASH THEM! - # ✅ Solution: - # - Use `client-bound-data` to safely sync custom block data to clients. + # DISABLED: + # ⚠️ Server MUST specify SERVERSIDE CUSTOM BLOCK IDs in item's `can_break`. + # ⚠️ Sending custom block IDs to vanilla clients WILL CAUSE CRASHES! + # ✅ Recommended: Use `client-bound-data` for safe client synchronization. simplify-adventure-break-check: false # Similar to the option above, but designed for block placement simplify-adventure-place-check: false - # Whether plugin should predict the next block to break - # This can help improve mining experience to some extent at the cost of performance + # Uses raycasting to predict the player's next block break, + # enabling pre-calculation of mining speed attributes. + + # Enables block break prediction. + # Enhances mining responsiveness with moderate performance cost. predict-breaking: enable: false interval: 10 @@ -217,29 +257,17 @@ image: chat: true command: true sign: true - # Allow and tags in third-party plugins via packet manipulation - # ⚠️ Disable unused handlers to reduce async thread workload - intercept-packets: - system-chat: true - tab-list: true # Tab list header and footer - player-info: true # Player list in tab - set-score: true - actionbar: true - title: true - bossbar: true - container: true # GUI - team: true # Team prefix, suffix and display name - scoreboard: true - entity-name: false - armor-stand: true # Legacy Holograms - text-display: true # Modern Holograms - item: true - advancement: true + # Decided the starting value for automatic codepoint assignment. + codepoint-starting-value: + default: 19968 + overrides: + minecraft:default: 57344 # 57344 ~ 63743 (U+E000 ~ U+F8FF) + # Defines Unicode characters used for positioning # - Must match the font defined in resource packs # - Do NOT modify unless you understand text rendering mechanics offset-characters: - font: minecraft:offset_chars + font: minecraft:default -1: '\uf800' -2: '\uf801' -3: '\uf802' @@ -285,6 +313,26 @@ image: 128: '\uf844' 256: '\uf845' +network: + # Allow tags in third-party plugins via packet manipulation + # ⚠️ Disable unused handlers to reduce async thread workload + intercept-packets: + system-chat: true + tab-list: true # Tab list header and footer + player-info: true # Player list in tab + set-score: true + actionbar: true + title: true + bossbar: true + container: true # GUI + team: true # Team prefix, suffix and display name + scoreboard: true + entity-name: false + armor-stand: true # Legacy Holograms + text-display: true # Modern Holograms + item: true + advancement: true + recipe: # Master switch for custom recipes # NOTE: When enabled, plugin recipes will OVERRIDE vanilla recipes @@ -361,6 +409,8 @@ gui: light-system: # Required for custom light-emitting blocks enable: true + # Async light update + async-update: true chunk-system: # With cache system, those frequently load/unload chunks would consume fewer resources on serialization diff --git a/common-files/src/main/resources/mappings.yml b/common-files/src/main/resources/mappings.yml deleted file mode 100644 index d3f9f58f2..000000000 --- a/common-files/src/main/resources/mappings.yml +++ /dev/null @@ -1,4452 +0,0 @@ -# This is one of the plugin's core settings - it basically controls how many block states you can use in your config files. -# Heads up: if you edit this, you'll need to restart the server for changes to take effect. -# Below is the default setup - feel free to add or remove stuff as needed. - -#### Anvil #### -# An anvil has four possible orientations, but the east-west and north-south orientations look exactly the same. -minecraft:anvil[facing=north]: minecraft:anvil[facing=south] -minecraft:anvil[facing=east]: minecraft:anvil[facing=west] -minecraft:chipped_anvil[facing=north]: minecraft:chipped_anvil[facing=south] -minecraft:chipped_anvil[facing=east]: minecraft:chipped_anvil[facing=west] -minecraft:damaged_anvil[facing=north]: minecraft:damaged_anvil[facing=south] -minecraft:damaged_anvil[facing=east]: minecraft:damaged_anvil[facing=west] - -#### Sapling #### -# Every sapling has two stages, 0 and 1, but they look exactly the same. -minecraft:oak_sapling[stage=1]: minecraft:oak_sapling[stage=0] -minecraft:birch_sapling[stage=1]: minecraft:birch_sapling[stage=0] -minecraft:spruce_sapling[stage=1]: minecraft:spruce_sapling[stage=0] -minecraft:jungle_sapling[stage=1]: minecraft:jungle_sapling[stage=0] -minecraft:dark_oak_sapling[stage=1]: minecraft:dark_oak_sapling[stage=0] -minecraft:acacia_sapling[stage=1]: minecraft:acacia_sapling[stage=0] -minecraft:cherry_sapling[stage=1]: minecraft:cherry_sapling[stage=0] -$$>=1.21.4#sapling: - minecraft:pale_oak_sapling[stage=1]: minecraft:pale_oak_sapling[stage=0] - -#### Sculk Sensor #### -# The Sculk Sensor's hitbox is exactly half a block. Plus, its appearance only changes based on the sculk_sensor_phase, -# not the power level. That means we can repurpose the extra states to make bottom-half slabs -minecraft:sculk_sensor[power=1,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:sculk_sensor[power=2,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:sculk_sensor[power=3,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:sculk_sensor[power=4,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:sculk_sensor[power=5,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:sculk_sensor[power=6,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:sculk_sensor[power=7,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:sculk_sensor[power=8,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:sculk_sensor[power=9,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:sculk_sensor[power=10,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:sculk_sensor[power=11,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:sculk_sensor[power=12,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:sculk_sensor[power=13,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:sculk_sensor[power=14,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:sculk_sensor[power=15,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:sculk_sensor[power=1,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:sculk_sensor[power=2,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:sculk_sensor[power=3,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:sculk_sensor[power=4,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:sculk_sensor[power=5,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:sculk_sensor[power=6,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:sculk_sensor[power=7,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:sculk_sensor[power=8,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:sculk_sensor[power=9,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:sculk_sensor[power=10,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:sculk_sensor[power=11,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:sculk_sensor[power=12,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:sculk_sensor[power=13,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:sculk_sensor[power=14,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:sculk_sensor[power=15,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:sculk_sensor[power=1,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:sculk_sensor[power=2,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:sculk_sensor[power=3,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:sculk_sensor[power=4,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:sculk_sensor[power=5,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:sculk_sensor[power=6,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:sculk_sensor[power=7,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:sculk_sensor[power=8,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:sculk_sensor[power=9,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:sculk_sensor[power=10,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:sculk_sensor[power=11,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:sculk_sensor[power=12,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:sculk_sensor[power=13,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:sculk_sensor[power=14,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:sculk_sensor[power=15,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:sculk_sensor[power=1,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:sculk_sensor[power=2,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:sculk_sensor[power=3,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:sculk_sensor[power=4,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:sculk_sensor[power=5,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:sculk_sensor[power=6,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:sculk_sensor[power=7,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:sculk_sensor[power=8,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:sculk_sensor[power=9,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:sculk_sensor[power=10,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:sculk_sensor[power=11,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:sculk_sensor[power=12,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:sculk_sensor[power=13,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:sculk_sensor[power=14,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:sculk_sensor[power=15,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:sculk_sensor[power=1,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:sculk_sensor[power=2,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:sculk_sensor[power=3,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:sculk_sensor[power=4,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:sculk_sensor[power=5,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:sculk_sensor[power=6,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:sculk_sensor[power=7,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:sculk_sensor[power=8,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:sculk_sensor[power=9,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:sculk_sensor[power=10,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:sculk_sensor[power=11,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:sculk_sensor[power=12,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:sculk_sensor[power=13,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:sculk_sensor[power=14,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:sculk_sensor[power=15,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:sculk_sensor[power=1,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:sculk_sensor[power=2,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:sculk_sensor[power=3,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:sculk_sensor[power=4,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:sculk_sensor[power=5,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:sculk_sensor[power=6,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:sculk_sensor[power=7,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:sculk_sensor[power=8,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:sculk_sensor[power=9,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:sculk_sensor[power=10,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:sculk_sensor[power=11,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:sculk_sensor[power=12,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:sculk_sensor[power=13,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:sculk_sensor[power=14,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:sculk_sensor[power=15,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] - -#### Calibrated Sculk Sensor #### -# Just like the regular Sculk Sensor, but the Calibrated Sculk Sensor has directional facing - which gives us way more usable states to play with -minecraft:calibrated_sculk_sensor[facing=north,power=1,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=2,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=3,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=4,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=5,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=6,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=7,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=8,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=9,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=10,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=11,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=12,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=13,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=14,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=15,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=1,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=2,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=3,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=4,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=5,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=6,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=7,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=8,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=9,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=10,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=11,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=12,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=13,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=14,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=15,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=1,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=2,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=3,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=4,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=5,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=6,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=7,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=8,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=9,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=10,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=11,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=12,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=13,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=14,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=15,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=north,power=1,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=2,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=3,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=4,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=5,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=6,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=7,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=8,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=9,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=10,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=11,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=12,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=13,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=14,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=15,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=1,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=2,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=3,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=4,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=5,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=6,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=7,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=8,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=9,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=10,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=11,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=12,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=13,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=14,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=15,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=1,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=2,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=3,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=4,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=5,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=6,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=7,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=8,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=9,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=10,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=11,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=12,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=13,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=14,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=north,power=15,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=1,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=2,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=3,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=4,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=5,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=6,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=7,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=8,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=9,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=10,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=11,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=12,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=13,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=14,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=15,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=1,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=2,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=3,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=4,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=5,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=6,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=7,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=8,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=9,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=10,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=11,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=12,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=13,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=14,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=15,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=1,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=2,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=3,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=4,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=5,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=6,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=7,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=8,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=9,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=10,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=11,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=12,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=13,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=14,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=15,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=west,power=1,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=2,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=3,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=4,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=5,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=6,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=7,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=8,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=9,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=10,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=11,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=12,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=13,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=14,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=15,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=1,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=2,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=3,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=4,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=5,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=6,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=7,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=8,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=9,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=10,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=11,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=12,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=13,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=14,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=15,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=1,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=2,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=3,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=4,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=5,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=6,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=7,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=8,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=9,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=10,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=11,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=12,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=13,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=14,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=west,power=15,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=1,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=2,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=3,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=4,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=5,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=6,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=7,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=8,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=9,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=10,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=11,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=12,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=13,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=14,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=15,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=1,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=2,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=3,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=4,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=5,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=6,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=7,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=8,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=9,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=10,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=11,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=12,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=13,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=14,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=15,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=1,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=2,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=3,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=4,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=5,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=6,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=7,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=8,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=9,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=10,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=11,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=12,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=13,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=14,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=15,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=east,power=1,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=2,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=3,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=4,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=5,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=6,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=7,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=8,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=9,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=10,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=11,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=12,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=13,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=14,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=15,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=1,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=2,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=3,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=4,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=5,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=6,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=7,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=8,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=9,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=10,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=11,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=12,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=13,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=14,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=15,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=1,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=2,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=3,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=4,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=5,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=6,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=7,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=8,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=9,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=10,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=11,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=12,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=13,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=14,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=east,power=15,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=1,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=2,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=3,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=4,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=5,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=6,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=7,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=8,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=9,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=10,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=11,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=12,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=13,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=14,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=15,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=1,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=2,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=3,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=4,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=5,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=6,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=7,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=8,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=9,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=10,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=11,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=12,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=13,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=14,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=15,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=1,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=2,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=3,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=4,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=5,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=6,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=7,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=8,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=9,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=10,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=11,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=12,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=13,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=14,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=15,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] -minecraft:calibrated_sculk_sensor[facing=south,power=1,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=2,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=3,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=4,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=5,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=6,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=7,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=8,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=9,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=10,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=11,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=12,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=13,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=14,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=15,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=1,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=2,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=3,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=4,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=5,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=6,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=7,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=8,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=9,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=10,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=11,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=12,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=13,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=14,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=15,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=1,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=2,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=3,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=4,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=5,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=6,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=7,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=8,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=9,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=10,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=11,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=12,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=13,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=14,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] -minecraft:calibrated_sculk_sensor[facing=south,power=15,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] - -#### Mushroom #### -# Most people probably don't mind that mushroom blocks look the same on all six sides. So that means each type can free up like, 63 different states we could use for other stuff -minecraft:mushroom_stem[down=false,east=false,north=false,south=false,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=false,north=false,south=false,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=true,north=false,south=false,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=true,north=false,south=false,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=false,north=true,south=false,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=false,north=true,south=false,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=true,north=true,south=false,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=true,north=true,south=false,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=false,north=false,south=true,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=false,north=false,south=true,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=true,north=false,south=true,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=true,north=false,south=true,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=false,north=true,south=true,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=false,north=true,south=true,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=true,north=true,south=true,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=false,north=false,south=false,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=false,north=false,south=false,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=true,north=false,south=false,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=true,north=false,south=false,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=false,north=true,south=false,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=false,north=true,south=false,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=true,north=true,south=false,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=true,north=true,south=false,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=false,north=false,south=true,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=false,north=false,south=true,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=true,north=false,south=true,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=true,north=false,south=true,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=false,north=true,south=true,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=false,north=true,south=true,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=true,north=true,south=true,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=false,north=false,south=false,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=false,north=false,south=false,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=true,north=false,south=false,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=true,north=false,south=false,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=false,north=true,south=false,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=false,north=true,south=false,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=true,north=true,south=false,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=true,north=true,south=false,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=false,north=false,south=true,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=false,north=false,south=true,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=true,north=false,south=true,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=true,north=false,south=true,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=false,north=true,south=true,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=false,north=true,south=true,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=true,north=true,south=true,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=false,north=false,south=false,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=false,north=false,south=false,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=true,north=false,south=false,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=true,north=false,south=false,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=false,north=true,south=false,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=false,north=true,south=false,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=true,north=true,south=false,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=true,north=true,south=false,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=false,north=false,south=true,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=false,north=false,south=true,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=true,north=false,south=true,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=true,north=false,south=true,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=false,north=true,south=true,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=true,east=false,north=true,south=true,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:mushroom_stem[down=false,east=true,north=true,south=true,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=false,north=false,south=false,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=false,north=false,south=false,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=true,north=false,south=false,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=true,north=false,south=false,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=false,north=true,south=false,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=false,north=true,south=false,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=true,north=true,south=false,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=true,north=true,south=false,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=false,north=false,south=true,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=false,north=false,south=true,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=true,north=false,south=true,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=true,north=false,south=true,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=false,north=true,south=true,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=false,north=true,south=true,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=true,north=true,south=true,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=false,north=false,south=false,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=false,north=false,south=false,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=true,north=false,south=false,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=true,north=false,south=false,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=false,north=true,south=false,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=false,north=true,south=false,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=true,north=true,south=false,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=true,north=true,south=false,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=false,north=false,south=true,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=false,north=false,south=true,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=true,north=false,south=true,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=true,north=false,south=true,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=false,north=true,south=true,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=false,north=true,south=true,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=true,north=true,south=true,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=false,north=false,south=false,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=false,north=false,south=false,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=true,north=false,south=false,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=true,north=false,south=false,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=false,north=true,south=false,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=false,north=true,south=false,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=true,north=true,south=false,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=true,north=true,south=false,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=false,north=false,south=true,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=false,north=false,south=true,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=true,north=false,south=true,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=true,north=false,south=true,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=false,north=true,south=true,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=false,north=true,south=true,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=true,north=true,south=true,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=false,north=false,south=false,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=false,north=false,south=false,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=true,north=false,south=false,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=true,north=false,south=false,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=false,north=true,south=false,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=false,north=true,south=false,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=true,north=true,south=false,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=true,north=true,south=false,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=false,north=false,south=true,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=false,north=false,south=true,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=true,north=false,south=true,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=true,north=false,south=true,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=false,north=true,south=true,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=true,east=false,north=true,south=true,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:brown_mushroom_block[down=false,east=true,north=true,south=true,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=false,north=false,south=false,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=false,north=false,south=false,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=true,north=false,south=false,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=true,north=false,south=false,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=false,north=true,south=false,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=false,north=true,south=false,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=true,north=true,south=false,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=true,north=true,south=false,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=false,north=false,south=true,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=false,north=false,south=true,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=true,north=false,south=true,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=true,north=false,south=true,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=false,north=true,south=true,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=false,north=true,south=true,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=true,north=true,south=true,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=false,north=false,south=false,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=false,north=false,south=false,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=true,north=false,south=false,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=true,north=false,south=false,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=false,north=true,south=false,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=false,north=true,south=false,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=true,north=true,south=false,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=true,north=true,south=false,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=false,north=false,south=true,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=false,north=false,south=true,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=true,north=false,south=true,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=true,north=false,south=true,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=false,north=true,south=true,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=false,north=true,south=true,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=true,north=true,south=true,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=false,north=false,south=false,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=false,north=false,south=false,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=true,north=false,south=false,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=true,north=false,south=false,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=false,north=true,south=false,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=false,north=true,south=false,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=true,north=true,south=false,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=true,north=true,south=false,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=false,north=false,south=true,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=false,north=false,south=true,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=true,north=false,south=true,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=true,north=false,south=true,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=false,north=true,south=true,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=false,north=true,south=true,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=true,north=true,south=true,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=false,north=false,south=false,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=false,north=false,south=false,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=true,north=false,south=false,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=true,north=false,south=false,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=false,north=true,south=false,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=false,north=true,south=false,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=true,north=true,south=false,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=true,north=true,south=false,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=false,north=false,south=true,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=false,north=false,south=true,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=true,north=false,south=true,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=true,north=false,south=true,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=false,north=true,south=true,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=true,east=false,north=true,south=true,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] -minecraft:red_mushroom_block[down=false,east=true,north=true,south=true,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] - -#### Kelp #### -# 'kelp' here means specifically the top block of the kelp plant. Great for making aquatic crops. -minecraft:kelp[age=1]: minecraft:kelp[age=0] -minecraft:kelp[age=2]: minecraft:kelp[age=0] -minecraft:kelp[age=3]: minecraft:kelp[age=0] -minecraft:kelp[age=4]: minecraft:kelp[age=0] -minecraft:kelp[age=5]: minecraft:kelp[age=0] -minecraft:kelp[age=6]: minecraft:kelp[age=0] -minecraft:kelp[age=7]: minecraft:kelp[age=0] -minecraft:kelp[age=8]: minecraft:kelp[age=0] -minecraft:kelp[age=9]: minecraft:kelp[age=0] -minecraft:kelp[age=10]: minecraft:kelp[age=0] -minecraft:kelp[age=11]: minecraft:kelp[age=0] -minecraft:kelp[age=12]: minecraft:kelp[age=0] -minecraft:kelp[age=13]: minecraft:kelp[age=0] -minecraft:kelp[age=14]: minecraft:kelp[age=0] -minecraft:kelp[age=15]: minecraft:kelp[age=0] -minecraft:kelp[age=16]: minecraft:kelp[age=0] -minecraft:kelp[age=17]: minecraft:kelp[age=0] -minecraft:kelp[age=18]: minecraft:kelp[age=0] -minecraft:kelp[age=19]: minecraft:kelp[age=0] -minecraft:kelp[age=20]: minecraft:kelp[age=0] -minecraft:kelp[age=21]: minecraft:kelp[age=0] -minecraft:kelp[age=22]: minecraft:kelp[age=0] -minecraft:kelp[age=23]: minecraft:kelp[age=0] -minecraft:kelp[age=24]: minecraft:kelp[age=0] -minecraft:kelp[age=25]: minecraft:kelp[age=0] - -#### Vines #### -# Unless you tweak the vine's block tag, the client will always think it's climbable. -# Since vines look identical at different growth stages, we can repurpose those extra states to make custom blocks like ropes. -minecraft:weeping_vines[age=1]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=2]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=3]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=4]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=5]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=6]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=7]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=8]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=9]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=10]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=11]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=12]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=13]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=14]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=15]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=16]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=17]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=18]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=19]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=20]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=21]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=22]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=23]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=24]: minecraft:weeping_vines[age=0] -minecraft:weeping_vines[age=25]: minecraft:weeping_vines[age=0] -minecraft:twisting_vines[age=1]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=2]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=3]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=4]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=5]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=6]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=7]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=8]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=9]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=10]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=11]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=12]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=13]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=14]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=15]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=16]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=17]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=18]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=19]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=20]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=21]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=22]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=23]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=24]: minecraft:twisting_vines[age=0] -minecraft:twisting_vines[age=25]: minecraft:twisting_vines[age=0] -minecraft:cave_vines[age=1,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=2,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=3,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=4,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=5,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=6,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=7,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=8,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=9,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=10,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=11,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=12,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=13,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=14,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=15,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=16,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=17,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=18,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=19,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=20,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=21,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=22,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=23,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=24,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=25,berries=false]: minecraft:cave_vines[age=0,berries=false] -minecraft:cave_vines[age=1,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=2,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=3,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=4,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=5,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=6,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=7,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=8,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=9,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=10,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=11,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=12,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=13,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=14,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=15,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=16,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=17,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=18,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=19,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=20,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=21,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=22,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=23,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=24,berries=true]: minecraft:cave_vines[age=0,berries=true] -minecraft:cave_vines[age=25,berries=true]: minecraft:cave_vines[age=0,berries=true] - -#### SugarCane #### -# Sugar cane looks exactly the same no matter its growth stage. Plus, it's got this perfect hitbox that makes it awesome for taller plants -minecraft:sugar_cane[age=1]: minecraft:sugar_cane[age=0] -minecraft:sugar_cane[age=2]: minecraft:sugar_cane[age=0] -minecraft:sugar_cane[age=3]: minecraft:sugar_cane[age=0] -minecraft:sugar_cane[age=4]: minecraft:sugar_cane[age=0] -minecraft:sugar_cane[age=5]: minecraft:sugar_cane[age=0] -minecraft:sugar_cane[age=6]: minecraft:sugar_cane[age=0] -minecraft:sugar_cane[age=7]: minecraft:sugar_cane[age=0] -minecraft:sugar_cane[age=8]: minecraft:sugar_cane[age=0] -minecraft:sugar_cane[age=9]: minecraft:sugar_cane[age=0] -minecraft:sugar_cane[age=10]: minecraft:sugar_cane[age=0] -minecraft:sugar_cane[age=11]: minecraft:sugar_cane[age=0] -minecraft:sugar_cane[age=12]: minecraft:sugar_cane[age=0] -minecraft:sugar_cane[age=13]: minecraft:sugar_cane[age=0] -minecraft:sugar_cane[age=14]: minecraft:sugar_cane[age=0] -minecraft:sugar_cane[age=15]: minecraft:sugar_cane[age=0] - -#### Cactus #### -# Cactus looks the same at all growth stages.Its hitbox is 14x14x15, making it perfect for creating blocks that are just slightly smaller than full-size -minecraft:cactus[age=1]: minecraft:cactus[age=0] -minecraft:cactus[age=2]: minecraft:cactus[age=0] -minecraft:cactus[age=3]: minecraft:cactus[age=0] -minecraft:cactus[age=4]: minecraft:cactus[age=0] -minecraft:cactus[age=5]: minecraft:cactus[age=0] -minecraft:cactus[age=6]: minecraft:cactus[age=0] -minecraft:cactus[age=7]: minecraft:cactus[age=0] -minecraft:cactus[age=8]: minecraft:cactus[age=0] -minecraft:cactus[age=9]: minecraft:cactus[age=0] -minecraft:cactus[age=10]: minecraft:cactus[age=0] -minecraft:cactus[age=11]: minecraft:cactus[age=0] -minecraft:cactus[age=12]: minecraft:cactus[age=0] -minecraft:cactus[age=13]: minecraft:cactus[age=0] -minecraft:cactus[age=14]: minecraft:cactus[age=0] -minecraft:cactus[age=15]: minecraft:cactus[age=0] - -#### Leaves #### -# The 'distance' and 'persistent' properties are used under the hood to optimize how leaves decay, but visually? They look exactly the same. -# These are some of the few block types that actually support transparent textures. -minecraft:oak_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:oak_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:oak_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:oak_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:oak_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:oak_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:oak_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:oak_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:oak_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:oak_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:oak_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:oak_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:oak_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:oak_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:oak_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:oak_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:oak_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:oak_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:oak_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:oak_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:oak_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:oak_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:oak_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:oak_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:oak_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:oak_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:acacia_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:acacia_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:acacia_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:acacia_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:acacia_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:acacia_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:acacia_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:acacia_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:acacia_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:acacia_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:acacia_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:acacia_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:acacia_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:acacia_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:acacia_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:acacia_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:acacia_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:acacia_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:acacia_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:acacia_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:acacia_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:acacia_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:acacia_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:acacia_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:acacia_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:acacia_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:jungle_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:jungle_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:jungle_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:jungle_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:jungle_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:jungle_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:jungle_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:jungle_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:jungle_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:jungle_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:jungle_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:jungle_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:jungle_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:jungle_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:jungle_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:jungle_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:jungle_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:jungle_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:jungle_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:jungle_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:jungle_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:jungle_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:jungle_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:jungle_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:jungle_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:jungle_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:birch_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:birch_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:birch_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:birch_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:birch_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:birch_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:birch_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:birch_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:birch_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:birch_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:birch_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:birch_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:birch_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:birch_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:birch_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:birch_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:birch_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:birch_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:birch_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:birch_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:birch_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:birch_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:birch_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:birch_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:birch_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:birch_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:mangrove_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:mangrove_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:mangrove_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:mangrove_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:mangrove_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:mangrove_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:mangrove_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:mangrove_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:mangrove_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:mangrove_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:mangrove_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:mangrove_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:mangrove_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:mangrove_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:mangrove_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:mangrove_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:mangrove_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:mangrove_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:mangrove_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:mangrove_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:mangrove_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:mangrove_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:mangrove_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:mangrove_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:mangrove_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:mangrove_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:cherry_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:cherry_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:cherry_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:cherry_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:cherry_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:cherry_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:cherry_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:cherry_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:cherry_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:cherry_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:cherry_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:cherry_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:cherry_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:cherry_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:cherry_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:cherry_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:cherry_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:cherry_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:cherry_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:cherry_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:cherry_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:cherry_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:cherry_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:cherry_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:cherry_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:cherry_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:dark_oak_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:dark_oak_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:dark_oak_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:dark_oak_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:dark_oak_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:dark_oak_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:dark_oak_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:dark_oak_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:dark_oak_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:dark_oak_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:dark_oak_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:dark_oak_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:dark_oak_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:dark_oak_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:dark_oak_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:dark_oak_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:dark_oak_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:dark_oak_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:dark_oak_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:dark_oak_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:dark_oak_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:dark_oak_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:dark_oak_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:dark_oak_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:dark_oak_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:dark_oak_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:azalea_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:azalea_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:azalea_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:azalea_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:azalea_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:azalea_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:azalea_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:azalea_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:azalea_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:azalea_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:azalea_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:azalea_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:azalea_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:azalea_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:azalea_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:azalea_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:azalea_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:azalea_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:azalea_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:azalea_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:azalea_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:azalea_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:azalea_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:azalea_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:azalea_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:azalea_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:flowering_azalea_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:flowering_azalea_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:flowering_azalea_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:flowering_azalea_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:flowering_azalea_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:flowering_azalea_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:flowering_azalea_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:flowering_azalea_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:flowering_azalea_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:flowering_azalea_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:flowering_azalea_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:flowering_azalea_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:flowering_azalea_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:flowering_azalea_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:flowering_azalea_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:flowering_azalea_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:flowering_azalea_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:flowering_azalea_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:flowering_azalea_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:flowering_azalea_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:flowering_azalea_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:flowering_azalea_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:flowering_azalea_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:flowering_azalea_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:flowering_azalea_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:flowering_azalea_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:spruce_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:spruce_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:spruce_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:spruce_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:spruce_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:spruce_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:spruce_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:spruce_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:spruce_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:spruce_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:spruce_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:spruce_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:spruce_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] -minecraft:spruce_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:spruce_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:spruce_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:spruce_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:spruce_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:spruce_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:spruce_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:spruce_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:spruce_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:spruce_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:spruce_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:spruce_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] -minecraft:spruce_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] - -$$>=1.21.4#leaves: - minecraft:pale_oak_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] - minecraft:pale_oak_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] - minecraft:pale_oak_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] - minecraft:pale_oak_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] - minecraft:pale_oak_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] - minecraft:pale_oak_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] - minecraft:pale_oak_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] - minecraft:pale_oak_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] - minecraft:pale_oak_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] - minecraft:pale_oak_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] - minecraft:pale_oak_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] - minecraft:pale_oak_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] - minecraft:pale_oak_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] - minecraft:pale_oak_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] - minecraft:pale_oak_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] - minecraft:pale_oak_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] - minecraft:pale_oak_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] - minecraft:pale_oak_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] - minecraft:pale_oak_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] - minecraft:pale_oak_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] - minecraft:pale_oak_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] - minecraft:pale_oak_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] - minecraft:pale_oak_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] - minecraft:pale_oak_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] - minecraft:pale_oak_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] - minecraft:pale_oak_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] - -#### Tripwire #### -# Tripwires actually have 128 different states, but we're keeping just two of them to match vanilla's visual styles. -# Honestly, as long as the tripwire works properly, most players won't even mind what it looks like. -# Tripwire hitboxes aren't all the same - the triggered ones are way shorter. -minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] -minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] - -#### Note Block #### -# This block has the most unused states in Minecraft, but the client always thinks it's interactive. -# Plus, there's some visual glitches when the client try predicting instrument changes. -# We've kept a full set of note settings by default - that way it plays nice with resource packs that show notes -minecraft:note_block[instrument=hat,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=hat,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=hat,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=hat,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=hat,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=hat,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=hat,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=hat,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=hat,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=hat,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=hat,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=hat,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=hat,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=hat,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=hat,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=hat,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=hat,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=hat,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=hat,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=hat,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=hat,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=hat,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=hat,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=hat,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=hat,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=hat,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=hat,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=hat,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=hat,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=hat,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=hat,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=hat,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=hat,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=hat,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=hat,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=hat,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=hat,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=hat,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=hat,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=hat,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=hat,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=hat,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=hat,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=hat,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=hat,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=hat,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=hat,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=hat,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=hat,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=hat,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=basedrum,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=basedrum,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=basedrum,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=basedrum,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=basedrum,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=basedrum,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=basedrum,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=basedrum,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=basedrum,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=basedrum,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=basedrum,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=basedrum,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=basedrum,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=basedrum,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=basedrum,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=basedrum,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=basedrum,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=basedrum,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=basedrum,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=basedrum,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=basedrum,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=basedrum,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=basedrum,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=basedrum,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=basedrum,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=basedrum,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=basedrum,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=basedrum,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=basedrum,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=basedrum,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=basedrum,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=basedrum,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=basedrum,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=basedrum,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=basedrum,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=basedrum,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=basedrum,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=basedrum,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=basedrum,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=basedrum,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=basedrum,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=basedrum,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=basedrum,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=basedrum,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=basedrum,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=basedrum,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=basedrum,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=basedrum,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=basedrum,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=basedrum,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=snare,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=snare,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=snare,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=snare,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=snare,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=snare,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=snare,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=snare,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=snare,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=snare,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=snare,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=snare,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=snare,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=snare,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=snare,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=snare,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=snare,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=snare,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=snare,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=snare,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=snare,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=snare,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=snare,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=snare,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=snare,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=snare,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=snare,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=snare,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=snare,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=snare,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=snare,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=snare,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=snare,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=snare,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=snare,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=snare,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=snare,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=snare,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=snare,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=snare,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=snare,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=snare,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=snare,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=snare,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=snare,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=snare,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=snare,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=snare,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=snare,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=snare,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=bass,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=bass,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=bass,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=bass,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=bass,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=bass,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=bass,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=bass,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=bass,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=bass,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=bass,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=bass,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=bass,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=bass,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=bass,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=bass,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=bass,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=bass,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=bass,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=bass,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=bass,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=bass,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=bass,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=bass,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=bass,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=bass,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=bass,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=bass,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=bass,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=bass,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=bass,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=bass,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=bass,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=bass,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=bass,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=bass,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=bass,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=bass,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=bass,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=bass,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=bass,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=bass,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=bass,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=bass,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=bass,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=bass,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=bass,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=bass,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=bass,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=bass,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=flute,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=flute,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=flute,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=flute,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=flute,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=flute,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=flute,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=flute,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=flute,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=flute,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=flute,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=flute,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=flute,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=flute,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=flute,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=flute,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=flute,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=flute,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=flute,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=flute,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=flute,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=flute,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=flute,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=flute,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=flute,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=flute,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=flute,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=flute,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=flute,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=flute,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=flute,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=flute,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=flute,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=flute,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=flute,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=flute,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=flute,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=flute,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=flute,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=flute,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=flute,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=flute,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=flute,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=flute,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=flute,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=flute,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=flute,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=flute,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=flute,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=flute,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=bell,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=bell,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=bell,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=bell,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=bell,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=bell,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=bell,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=bell,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=bell,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=bell,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=bell,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=bell,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=bell,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=bell,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=bell,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=bell,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=bell,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=bell,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=bell,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=bell,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=bell,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=bell,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=bell,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=bell,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=bell,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=bell,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=bell,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=bell,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=bell,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=bell,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=bell,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=bell,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=bell,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=bell,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=bell,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=bell,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=bell,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=bell,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=bell,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=bell,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=bell,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=bell,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=bell,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=bell,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=bell,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=bell,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=bell,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=bell,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=bell,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=bell,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=guitar,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=guitar,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=guitar,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=guitar,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=guitar,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=guitar,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=guitar,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=guitar,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=guitar,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=guitar,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=guitar,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=guitar,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=guitar,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=guitar,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=guitar,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=guitar,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=guitar,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=guitar,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=guitar,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=guitar,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=guitar,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=guitar,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=guitar,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=guitar,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=guitar,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=guitar,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=guitar,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=guitar,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=guitar,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=guitar,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=guitar,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=guitar,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=guitar,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=guitar,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=guitar,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=guitar,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=guitar,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=guitar,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=guitar,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=guitar,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=guitar,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=guitar,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=guitar,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=guitar,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=guitar,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=guitar,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=guitar,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=guitar,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=guitar,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=guitar,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=chime,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=chime,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=chime,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=chime,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=chime,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=chime,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=chime,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=chime,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=chime,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=chime,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=chime,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=chime,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=chime,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=chime,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=chime,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=chime,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=chime,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=chime,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=chime,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=chime,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=chime,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=chime,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=chime,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=chime,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=chime,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=chime,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=chime,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=chime,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=chime,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=chime,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=chime,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=chime,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=chime,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=chime,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=chime,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=chime,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=chime,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=chime,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=chime,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=chime,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=chime,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=chime,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=chime,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=chime,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=chime,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=chime,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=chime,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=chime,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=chime,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=chime,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=xylophone,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=xylophone,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=xylophone,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=xylophone,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=xylophone,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=xylophone,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=xylophone,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=xylophone,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=xylophone,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=xylophone,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=xylophone,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=xylophone,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=xylophone,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=xylophone,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=xylophone,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=xylophone,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=xylophone,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=xylophone,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=xylophone,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=xylophone,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=xylophone,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=xylophone,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=xylophone,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=xylophone,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=xylophone,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=xylophone,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=xylophone,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=xylophone,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=xylophone,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=xylophone,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=xylophone,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=xylophone,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=xylophone,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=xylophone,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=xylophone,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=xylophone,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=xylophone,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=xylophone,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=xylophone,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=xylophone,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=xylophone,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=xylophone,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=xylophone,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=xylophone,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=xylophone,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=xylophone,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=xylophone,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=xylophone,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=xylophone,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=xylophone,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=iron_xylophone,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=iron_xylophone,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=cow_bell,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=cow_bell,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=cow_bell,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=cow_bell,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=cow_bell,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=cow_bell,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=cow_bell,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=cow_bell,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=cow_bell,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=cow_bell,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=cow_bell,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=cow_bell,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=cow_bell,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=cow_bell,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=cow_bell,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=cow_bell,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=cow_bell,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=cow_bell,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=cow_bell,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=cow_bell,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=cow_bell,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=cow_bell,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=cow_bell,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=cow_bell,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=cow_bell,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=cow_bell,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=cow_bell,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=cow_bell,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=cow_bell,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=cow_bell,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=cow_bell,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=cow_bell,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=cow_bell,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=cow_bell,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=cow_bell,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=cow_bell,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=cow_bell,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=cow_bell,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=cow_bell,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=cow_bell,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=cow_bell,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=cow_bell,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=cow_bell,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=cow_bell,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=cow_bell,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=cow_bell,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=cow_bell,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=cow_bell,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=cow_bell,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=cow_bell,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=didgeridoo,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=didgeridoo,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=didgeridoo,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=didgeridoo,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=didgeridoo,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=didgeridoo,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=didgeridoo,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=didgeridoo,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=didgeridoo,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=didgeridoo,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=didgeridoo,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=didgeridoo,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=didgeridoo,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=didgeridoo,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=didgeridoo,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=didgeridoo,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=didgeridoo,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=didgeridoo,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=didgeridoo,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=didgeridoo,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=didgeridoo,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=didgeridoo,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=didgeridoo,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=didgeridoo,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=didgeridoo,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=didgeridoo,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=didgeridoo,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=didgeridoo,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=didgeridoo,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=didgeridoo,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=didgeridoo,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=didgeridoo,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=didgeridoo,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=didgeridoo,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=didgeridoo,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=didgeridoo,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=didgeridoo,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=didgeridoo,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=didgeridoo,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=didgeridoo,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=didgeridoo,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=didgeridoo,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=didgeridoo,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=didgeridoo,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=didgeridoo,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=didgeridoo,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=didgeridoo,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=didgeridoo,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=didgeridoo,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=didgeridoo,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=bit,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=bit,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=bit,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=bit,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=bit,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=bit,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=bit,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=bit,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=bit,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=bit,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=bit,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=bit,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=bit,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=bit,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=bit,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=bit,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=bit,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=bit,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=bit,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=bit,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=bit,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=bit,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=bit,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=bit,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=bit,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=bit,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=bit,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=bit,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=bit,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=bit,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=bit,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=bit,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=bit,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=bit,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=bit,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=bit,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=bit,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=bit,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=bit,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=bit,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=bit,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=bit,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=bit,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=bit,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=bit,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=bit,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=bit,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=bit,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=bit,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=bit,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=banjo,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=banjo,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=banjo,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=banjo,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=banjo,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=banjo,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=banjo,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=banjo,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=banjo,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=banjo,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=banjo,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=banjo,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=banjo,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=banjo,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=banjo,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=banjo,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=banjo,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=banjo,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=banjo,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=banjo,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=banjo,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=banjo,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=banjo,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=banjo,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=banjo,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=banjo,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=banjo,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=banjo,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=banjo,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=banjo,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=banjo,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=banjo,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=banjo,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=banjo,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=banjo,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=banjo,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=banjo,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=banjo,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=banjo,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=banjo,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=banjo,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=banjo,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=banjo,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=banjo,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=banjo,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=banjo,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=banjo,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=banjo,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=banjo,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=banjo,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=pling,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=pling,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=pling,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=pling,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=pling,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=pling,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=pling,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=pling,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=pling,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=pling,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=pling,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=pling,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=pling,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=pling,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=pling,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=pling,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=pling,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=pling,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=pling,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=pling,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=pling,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=pling,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=pling,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=pling,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=pling,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=pling,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=pling,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=pling,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=pling,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=pling,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=pling,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=pling,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=pling,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=pling,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=pling,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=pling,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=pling,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=pling,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=pling,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=pling,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=pling,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=pling,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=pling,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=pling,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=pling,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=pling,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=pling,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=pling,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=pling,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=pling,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=zombie,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=zombie,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=zombie,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=zombie,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=zombie,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=zombie,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=zombie,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=zombie,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=zombie,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=zombie,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=zombie,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=zombie,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=zombie,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=zombie,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=zombie,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=zombie,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=zombie,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=zombie,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=zombie,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=zombie,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=zombie,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=zombie,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=zombie,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=zombie,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=zombie,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=zombie,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=zombie,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=zombie,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=zombie,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=zombie,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=zombie,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=zombie,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=zombie,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=zombie,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=zombie,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=zombie,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=zombie,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=zombie,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=zombie,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=zombie,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=zombie,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=zombie,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=zombie,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=zombie,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=zombie,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=zombie,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=zombie,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=zombie,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=zombie,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=zombie,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=skeleton,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=skeleton,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=skeleton,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=skeleton,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=skeleton,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=skeleton,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=skeleton,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=skeleton,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=skeleton,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=skeleton,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=skeleton,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=skeleton,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=skeleton,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=skeleton,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=skeleton,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=skeleton,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=skeleton,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=skeleton,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=skeleton,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=skeleton,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=skeleton,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=skeleton,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=skeleton,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=skeleton,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=skeleton,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=skeleton,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=skeleton,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=skeleton,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=skeleton,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=skeleton,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=skeleton,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=skeleton,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=skeleton,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=skeleton,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=skeleton,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=skeleton,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=skeleton,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=skeleton,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=skeleton,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=skeleton,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=skeleton,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=skeleton,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=skeleton,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=skeleton,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=skeleton,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=skeleton,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=skeleton,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=skeleton,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=skeleton,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=skeleton,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=creeper,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=creeper,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=creeper,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=creeper,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=creeper,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=creeper,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=creeper,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=creeper,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=creeper,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=creeper,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=creeper,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=creeper,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=creeper,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=creeper,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=creeper,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=creeper,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=creeper,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=creeper,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=creeper,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=creeper,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=creeper,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=creeper,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=creeper,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=creeper,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=creeper,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=creeper,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=creeper,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=creeper,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=creeper,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=creeper,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=creeper,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=creeper,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=creeper,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=creeper,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=creeper,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=creeper,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=creeper,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=creeper,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=creeper,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=creeper,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=creeper,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=creeper,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=creeper,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=creeper,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=creeper,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=creeper,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=creeper,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=creeper,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=creeper,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=creeper,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=dragon,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=dragon,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=dragon,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=dragon,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=dragon,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=dragon,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=dragon,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=dragon,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=dragon,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=dragon,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=dragon,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=dragon,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=dragon,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=dragon,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=dragon,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=dragon,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=dragon,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=dragon,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=dragon,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=dragon,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=dragon,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=dragon,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=dragon,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=dragon,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=dragon,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=dragon,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=dragon,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=dragon,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=dragon,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=dragon,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=dragon,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=dragon,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=dragon,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=dragon,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=dragon,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=dragon,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=dragon,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=dragon,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=dragon,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=dragon,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=dragon,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=dragon,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=dragon,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=dragon,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=dragon,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=dragon,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=dragon,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=dragon,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=dragon,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=dragon,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=wither_skeleton,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=wither_skeleton,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=piglin,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=piglin,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=piglin,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=piglin,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=piglin,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=piglin,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=piglin,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=piglin,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=piglin,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=piglin,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=piglin,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=piglin,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=piglin,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=piglin,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=piglin,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=piglin,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=piglin,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=piglin,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=piglin,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=piglin,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=piglin,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=piglin,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=piglin,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=piglin,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=piglin,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=piglin,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=piglin,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=piglin,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=piglin,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=piglin,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=piglin,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=piglin,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=piglin,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=piglin,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=piglin,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=piglin,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=piglin,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=piglin,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=piglin,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=piglin,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=piglin,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=piglin,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=piglin,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=piglin,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=piglin,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=piglin,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=piglin,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=piglin,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=piglin,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=piglin,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] -minecraft:note_block[instrument=custom_head,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] -minecraft:note_block[instrument=custom_head,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] -minecraft:note_block[instrument=custom_head,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] -minecraft:note_block[instrument=custom_head,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] -minecraft:note_block[instrument=custom_head,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] -minecraft:note_block[instrument=custom_head,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] -minecraft:note_block[instrument=custom_head,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] -minecraft:note_block[instrument=custom_head,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] -minecraft:note_block[instrument=custom_head,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] -minecraft:note_block[instrument=custom_head,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] -minecraft:note_block[instrument=custom_head,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] -minecraft:note_block[instrument=custom_head,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] -minecraft:note_block[instrument=custom_head,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] -minecraft:note_block[instrument=custom_head,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] -minecraft:note_block[instrument=custom_head,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] -minecraft:note_block[instrument=custom_head,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] -minecraft:note_block[instrument=custom_head,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] -minecraft:note_block[instrument=custom_head,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] -minecraft:note_block[instrument=custom_head,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] -minecraft:note_block[instrument=custom_head,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] -minecraft:note_block[instrument=custom_head,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] -minecraft:note_block[instrument=custom_head,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] -minecraft:note_block[instrument=custom_head,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] -minecraft:note_block[instrument=custom_head,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] -minecraft:note_block[instrument=custom_head,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] -minecraft:note_block[instrument=custom_head,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] -minecraft:note_block[instrument=custom_head,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] -minecraft:note_block[instrument=custom_head,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] -minecraft:note_block[instrument=custom_head,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] -minecraft:note_block[instrument=custom_head,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] -minecraft:note_block[instrument=custom_head,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] -minecraft:note_block[instrument=custom_head,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] -minecraft:note_block[instrument=custom_head,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] -minecraft:note_block[instrument=custom_head,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] -minecraft:note_block[instrument=custom_head,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] -minecraft:note_block[instrument=custom_head,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] -minecraft:note_block[instrument=custom_head,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] -minecraft:note_block[instrument=custom_head,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] -minecraft:note_block[instrument=custom_head,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] -minecraft:note_block[instrument=custom_head,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] -minecraft:note_block[instrument=custom_head,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] -minecraft:note_block[instrument=custom_head,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] -minecraft:note_block[instrument=custom_head,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] -minecraft:note_block[instrument=custom_head,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] -minecraft:note_block[instrument=custom_head,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] -minecraft:note_block[instrument=custom_head,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] -minecraft:note_block[instrument=custom_head,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] -minecraft:note_block[instrument=custom_head,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] -minecraft:note_block[instrument=custom_head,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] -minecraft:note_block[instrument=custom_head,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] - -#### Trapdoor #### -# Trapdoors look identical whether they're powered or not - which means we can double our trapdoors by using both states -minecraft:iron_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] -minecraft:iron_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] -minecraft:iron_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] -minecraft:iron_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] -minecraft:iron_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] -minecraft:iron_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] -minecraft:iron_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] -minecraft:iron_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] -minecraft:iron_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] -minecraft:iron_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] -minecraft:iron_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] -minecraft:iron_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] -minecraft:iron_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] -minecraft:iron_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] -minecraft:iron_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] -minecraft:iron_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] -minecraft:iron_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:iron_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:iron_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:iron_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:iron_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:iron_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:iron_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:iron_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:iron_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:iron_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:iron_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:iron_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:iron_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:iron_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:iron_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:iron_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:acacia_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] -minecraft:acacia_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] -minecraft:acacia_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] -minecraft:acacia_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] -minecraft:acacia_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] -minecraft:acacia_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] -minecraft:acacia_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] -minecraft:acacia_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] -minecraft:acacia_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] -minecraft:acacia_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] -minecraft:acacia_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] -minecraft:acacia_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] -minecraft:acacia_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] -minecraft:acacia_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] -minecraft:acacia_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] -minecraft:acacia_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] -minecraft:acacia_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:acacia_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:acacia_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:acacia_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:acacia_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:acacia_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:acacia_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:acacia_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:acacia_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:acacia_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:acacia_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:acacia_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:acacia_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:acacia_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:acacia_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:acacia_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:oak_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] -minecraft:oak_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] -minecraft:oak_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] -minecraft:oak_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] -minecraft:oak_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] -minecraft:oak_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] -minecraft:oak_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] -minecraft:oak_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] -minecraft:oak_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] -minecraft:oak_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] -minecraft:oak_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] -minecraft:oak_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] -minecraft:oak_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] -minecraft:oak_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] -minecraft:oak_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] -minecraft:oak_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] -minecraft:oak_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:oak_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:oak_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:oak_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:oak_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:oak_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:oak_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:oak_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:oak_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:oak_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:oak_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:oak_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:oak_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:oak_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:oak_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:oak_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:spruce_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] -minecraft:spruce_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] -minecraft:spruce_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] -minecraft:spruce_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] -minecraft:spruce_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] -minecraft:spruce_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] -minecraft:spruce_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] -minecraft:spruce_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] -minecraft:spruce_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] -minecraft:spruce_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] -minecraft:spruce_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] -minecraft:spruce_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] -minecraft:spruce_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] -minecraft:spruce_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] -minecraft:spruce_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] -minecraft:spruce_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] -minecraft:spruce_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:spruce_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:spruce_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:spruce_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:spruce_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:spruce_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:spruce_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:spruce_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:spruce_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:spruce_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:spruce_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:spruce_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:spruce_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:spruce_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:spruce_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:spruce_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:birch_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] -minecraft:birch_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] -minecraft:birch_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] -minecraft:birch_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] -minecraft:birch_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] -minecraft:birch_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] -minecraft:birch_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] -minecraft:birch_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] -minecraft:birch_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] -minecraft:birch_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] -minecraft:birch_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] -minecraft:birch_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] -minecraft:birch_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] -minecraft:birch_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] -minecraft:birch_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] -minecraft:birch_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] -minecraft:birch_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:birch_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:birch_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:birch_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:birch_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:birch_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:birch_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:birch_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:birch_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:birch_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:birch_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:birch_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:birch_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:birch_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:birch_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:birch_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:jungle_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] -minecraft:jungle_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] -minecraft:jungle_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] -minecraft:jungle_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] -minecraft:jungle_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] -minecraft:jungle_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] -minecraft:jungle_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] -minecraft:jungle_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] -minecraft:jungle_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] -minecraft:jungle_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] -minecraft:jungle_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] -minecraft:jungle_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] -minecraft:jungle_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] -minecraft:jungle_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] -minecraft:jungle_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] -minecraft:jungle_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] -minecraft:jungle_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:jungle_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:jungle_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:jungle_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:jungle_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:jungle_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:jungle_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:jungle_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:jungle_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:jungle_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:jungle_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:jungle_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:jungle_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:jungle_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:jungle_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:jungle_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:dark_oak_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] -minecraft:dark_oak_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] -minecraft:dark_oak_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] -minecraft:dark_oak_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] -minecraft:dark_oak_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] -minecraft:dark_oak_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] -minecraft:dark_oak_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] -minecraft:dark_oak_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] -minecraft:dark_oak_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] -minecraft:dark_oak_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] -minecraft:dark_oak_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] -minecraft:dark_oak_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] -minecraft:dark_oak_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] -minecraft:dark_oak_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] -minecraft:dark_oak_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] -minecraft:dark_oak_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] -minecraft:dark_oak_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:dark_oak_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:dark_oak_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:dark_oak_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:dark_oak_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:dark_oak_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:dark_oak_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:dark_oak_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:dark_oak_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:dark_oak_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:dark_oak_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:dark_oak_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:dark_oak_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:dark_oak_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:dark_oak_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:dark_oak_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:mangrove_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] -minecraft:mangrove_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] -minecraft:mangrove_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] -minecraft:mangrove_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] -minecraft:mangrove_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] -minecraft:mangrove_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] -minecraft:mangrove_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] -minecraft:mangrove_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] -minecraft:mangrove_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] -minecraft:mangrove_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] -minecraft:mangrove_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] -minecraft:mangrove_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] -minecraft:mangrove_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] -minecraft:mangrove_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] -minecraft:mangrove_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] -minecraft:mangrove_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] -minecraft:mangrove_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:mangrove_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:mangrove_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:mangrove_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:mangrove_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:mangrove_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:mangrove_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:mangrove_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:mangrove_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:mangrove_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:mangrove_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:mangrove_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:mangrove_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:mangrove_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:mangrove_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:mangrove_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:cherry_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] -minecraft:cherry_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] -minecraft:cherry_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] -minecraft:cherry_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] -minecraft:cherry_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] -minecraft:cherry_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] -minecraft:cherry_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] -minecraft:cherry_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] -minecraft:cherry_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] -minecraft:cherry_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] -minecraft:cherry_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] -minecraft:cherry_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] -minecraft:cherry_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] -minecraft:cherry_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] -minecraft:cherry_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] -minecraft:cherry_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] -minecraft:cherry_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:cherry_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:cherry_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:cherry_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:cherry_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:cherry_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:cherry_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:cherry_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:cherry_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:cherry_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:cherry_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:cherry_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:cherry_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:cherry_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:cherry_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:cherry_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:bamboo_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] -minecraft:bamboo_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] -minecraft:bamboo_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] -minecraft:bamboo_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] -minecraft:bamboo_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] -minecraft:bamboo_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] -minecraft:bamboo_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] -minecraft:bamboo_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] -minecraft:bamboo_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] -minecraft:bamboo_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] -minecraft:bamboo_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] -minecraft:bamboo_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] -minecraft:bamboo_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] -minecraft:bamboo_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] -minecraft:bamboo_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] -minecraft:bamboo_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] -minecraft:bamboo_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:bamboo_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:bamboo_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:bamboo_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:bamboo_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:bamboo_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:bamboo_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:bamboo_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:bamboo_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:bamboo_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:bamboo_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:bamboo_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:bamboo_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:bamboo_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:bamboo_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:bamboo_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:crimson_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] -minecraft:crimson_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] -minecraft:crimson_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] -minecraft:crimson_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] -minecraft:crimson_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] -minecraft:crimson_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] -minecraft:crimson_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] -minecraft:crimson_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] -minecraft:crimson_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] -minecraft:crimson_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] -minecraft:crimson_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] -minecraft:crimson_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] -minecraft:crimson_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] -minecraft:crimson_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] -minecraft:crimson_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] -minecraft:crimson_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] -minecraft:crimson_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:crimson_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:crimson_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:crimson_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:crimson_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:crimson_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:crimson_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:crimson_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:crimson_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:crimson_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:crimson_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:crimson_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:crimson_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:crimson_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:crimson_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:crimson_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:warped_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] -minecraft:warped_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] -minecraft:warped_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] -minecraft:warped_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] -minecraft:warped_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] -minecraft:warped_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] -minecraft:warped_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] -minecraft:warped_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] -minecraft:warped_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] -minecraft:warped_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] -minecraft:warped_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] -minecraft:warped_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] -minecraft:warped_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] -minecraft:warped_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] -minecraft:warped_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] -minecraft:warped_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] -minecraft:warped_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:warped_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:warped_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:warped_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] -minecraft:warped_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:warped_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:warped_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:warped_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] -minecraft:warped_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:warped_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:warped_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:warped_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] -minecraft:warped_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:warped_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:warped_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] -minecraft:warped_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] - -$$>=1.20.3#trapdoor: - minecraft:copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] - minecraft:copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] - minecraft:copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] - minecraft:copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] - minecraft:copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] - minecraft:copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] - minecraft:copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] - minecraft:copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] - minecraft:copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] - minecraft:copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] - minecraft:copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] - minecraft:copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] - minecraft:copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] - minecraft:copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] - minecraft:copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] - minecraft:copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] - minecraft:copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:exposed_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] - minecraft:exposed_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] - minecraft:exposed_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] - minecraft:exposed_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] - minecraft:exposed_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] - minecraft:exposed_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] - minecraft:exposed_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] - minecraft:exposed_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] - minecraft:exposed_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] - minecraft:exposed_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] - minecraft:exposed_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] - minecraft:exposed_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] - minecraft:exposed_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] - minecraft:exposed_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] - minecraft:exposed_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] - minecraft:exposed_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] - minecraft:exposed_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:exposed_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:exposed_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:exposed_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:exposed_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:exposed_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:exposed_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:exposed_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:exposed_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:exposed_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:exposed_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:exposed_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:exposed_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:exposed_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:exposed_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:exposed_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:weathered_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] - minecraft:weathered_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] - minecraft:weathered_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] - minecraft:weathered_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] - minecraft:weathered_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] - minecraft:weathered_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] - minecraft:weathered_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] - minecraft:weathered_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] - minecraft:weathered_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] - minecraft:weathered_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] - minecraft:weathered_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] - minecraft:weathered_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] - minecraft:weathered_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] - minecraft:weathered_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] - minecraft:weathered_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] - minecraft:weathered_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] - minecraft:weathered_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:weathered_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:weathered_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:weathered_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:weathered_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:weathered_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:weathered_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:weathered_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:weathered_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:weathered_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:weathered_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:weathered_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:weathered_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:weathered_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:weathered_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:weathered_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:oxidized_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] - minecraft:oxidized_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] - minecraft:oxidized_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] - minecraft:oxidized_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] - minecraft:oxidized_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] - minecraft:oxidized_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] - minecraft:oxidized_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] - minecraft:oxidized_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] - minecraft:oxidized_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] - minecraft:oxidized_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] - minecraft:oxidized_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] - minecraft:oxidized_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] - minecraft:oxidized_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] - minecraft:oxidized_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] - minecraft:oxidized_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] - minecraft:oxidized_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] - minecraft:oxidized_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:oxidized_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:oxidized_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:oxidized_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:oxidized_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:oxidized_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:oxidized_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:oxidized_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:oxidized_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:oxidized_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:oxidized_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:oxidized_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:oxidized_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:oxidized_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:oxidized_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:oxidized_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] - # Fun fact: copper blocks look the same whether waxed or not. - # We're playing it safe with the default setup - keeping vanilla's waxed states recognizable. - # But you can always change it to convert waxed blocks back to regular ones. - minecraft:waxed_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] - minecraft:waxed_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] - minecraft:waxed_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] - minecraft:waxed_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] - minecraft:waxed_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] - minecraft:waxed_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] - minecraft:waxed_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] - minecraft:waxed_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] - minecraft:waxed_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] - minecraft:waxed_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] - minecraft:waxed_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] - minecraft:waxed_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] - minecraft:waxed_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] - minecraft:waxed_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] - minecraft:waxed_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] - minecraft:waxed_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] - minecraft:waxed_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:waxed_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:waxed_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:waxed_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:waxed_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:waxed_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:waxed_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:waxed_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:waxed_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:waxed_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:waxed_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:waxed_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:waxed_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:waxed_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:waxed_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:waxed_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:waxed_exposed_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] - minecraft:waxed_exposed_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] - minecraft:waxed_exposed_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] - minecraft:waxed_exposed_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] - minecraft:waxed_exposed_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] - minecraft:waxed_exposed_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] - minecraft:waxed_exposed_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] - minecraft:waxed_exposed_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] - minecraft:waxed_exposed_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] - minecraft:waxed_exposed_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] - minecraft:waxed_exposed_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] - minecraft:waxed_exposed_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] - minecraft:waxed_exposed_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] - minecraft:waxed_exposed_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] - minecraft:waxed_exposed_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] - minecraft:waxed_exposed_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] - minecraft:waxed_exposed_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:waxed_exposed_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:waxed_exposed_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:waxed_exposed_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:waxed_exposed_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:waxed_exposed_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:waxed_exposed_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:waxed_exposed_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:waxed_exposed_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:waxed_exposed_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:waxed_exposed_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:waxed_exposed_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:waxed_exposed_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:waxed_exposed_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:waxed_exposed_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:waxed_exposed_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:waxed_weathered_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] - minecraft:waxed_weathered_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] - minecraft:waxed_weathered_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] - minecraft:waxed_weathered_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] - minecraft:waxed_weathered_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] - minecraft:waxed_weathered_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] - minecraft:waxed_weathered_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] - minecraft:waxed_weathered_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] - minecraft:waxed_weathered_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] - minecraft:waxed_weathered_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] - minecraft:waxed_weathered_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] - minecraft:waxed_weathered_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] - minecraft:waxed_weathered_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] - minecraft:waxed_weathered_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] - minecraft:waxed_weathered_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] - minecraft:waxed_weathered_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] - minecraft:waxed_weathered_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:waxed_weathered_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:waxed_weathered_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:waxed_weathered_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:waxed_weathered_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:waxed_weathered_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:waxed_weathered_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:waxed_weathered_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:waxed_weathered_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:waxed_weathered_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:waxed_weathered_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:waxed_weathered_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:waxed_weathered_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:waxed_weathered_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:waxed_weathered_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:waxed_weathered_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] - minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] - minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] - minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] - minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] - minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] - minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] - minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] - minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] - minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] - minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] - minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] - minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] - minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] - minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] - minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] - minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] - -$$>=1.21.4#trapdoor: - minecraft:pale_oak_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] - minecraft:pale_oak_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] - minecraft:pale_oak_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] - minecraft:pale_oak_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] - minecraft:pale_oak_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] - minecraft:pale_oak_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] - minecraft:pale_oak_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] - minecraft:pale_oak_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] - minecraft:pale_oak_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] - minecraft:pale_oak_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] - minecraft:pale_oak_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] - minecraft:pale_oak_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] - minecraft:pale_oak_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] - minecraft:pale_oak_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] - minecraft:pale_oak_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] - minecraft:pale_oak_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] - minecraft:pale_oak_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:pale_oak_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:pale_oak_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:pale_oak_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] - minecraft:pale_oak_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:pale_oak_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:pale_oak_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:pale_oak_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] - minecraft:pale_oak_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:pale_oak_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:pale_oak_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:pale_oak_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] - minecraft:pale_oak_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:pale_oak_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:pale_oak_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] - minecraft:pale_oak_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] - -#### Door #### -# A door look exactly the same whether it's powered on or off, just like how a trapdoor works -minecraft:oak_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:oak_door[facing=east,half=lower,hinge=left,open=false,powered=false] -minecraft:oak_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:oak_door[facing=east,half=lower,hinge=right,open=false,powered=false] -minecraft:oak_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:oak_door[facing=east,half=upper,hinge=left,open=false,powered=false] -minecraft:oak_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:oak_door[facing=east,half=upper,hinge=right,open=false,powered=false] -minecraft:oak_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:oak_door[facing=east,half=lower,hinge=left,open=true,powered=false] -minecraft:oak_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:oak_door[facing=east,half=lower,hinge=right,open=true,powered=false] -minecraft:oak_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:oak_door[facing=east,half=upper,hinge=left,open=true,powered=false] -minecraft:oak_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:oak_door[facing=east,half=upper,hinge=right,open=true,powered=false] -minecraft:oak_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:oak_door[facing=north,half=lower,hinge=left,open=false,powered=false] -minecraft:oak_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:oak_door[facing=north,half=lower,hinge=right,open=false,powered=false] -minecraft:oak_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:oak_door[facing=north,half=upper,hinge=left,open=false,powered=false] -minecraft:oak_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:oak_door[facing=north,half=upper,hinge=right,open=false,powered=false] -minecraft:oak_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:oak_door[facing=north,half=lower,hinge=left,open=true,powered=false] -minecraft:oak_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:oak_door[facing=north,half=lower,hinge=right,open=true,powered=false] -minecraft:oak_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:oak_door[facing=north,half=upper,hinge=left,open=true,powered=false] -minecraft:oak_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:oak_door[facing=north,half=upper,hinge=right,open=true,powered=false] -minecraft:oak_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:oak_door[facing=south,half=lower,hinge=left,open=false,powered=false] -minecraft:oak_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:oak_door[facing=south,half=lower,hinge=right,open=false,powered=false] -minecraft:oak_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:oak_door[facing=south,half=upper,hinge=left,open=false,powered=false] -minecraft:oak_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:oak_door[facing=south,half=upper,hinge=right,open=false,powered=false] -minecraft:oak_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:oak_door[facing=south,half=lower,hinge=left,open=true,powered=false] -minecraft:oak_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:oak_door[facing=south,half=lower,hinge=right,open=true,powered=false] -minecraft:oak_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:oak_door[facing=south,half=upper,hinge=left,open=true,powered=false] -minecraft:oak_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:oak_door[facing=south,half=upper,hinge=right,open=true,powered=false] -minecraft:oak_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:oak_door[facing=west,half=lower,hinge=left,open=false,powered=false] -minecraft:oak_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:oak_door[facing=west,half=lower,hinge=right,open=false,powered=false] -minecraft:oak_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:oak_door[facing=west,half=upper,hinge=left,open=false,powered=false] -minecraft:oak_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:oak_door[facing=west,half=upper,hinge=right,open=false,powered=false] -minecraft:oak_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:oak_door[facing=west,half=lower,hinge=left,open=true,powered=false] -minecraft:oak_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:oak_door[facing=west,half=lower,hinge=right,open=true,powered=false] -minecraft:oak_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:oak_door[facing=west,half=upper,hinge=left,open=true,powered=false] -minecraft:oak_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:oak_door[facing=west,half=upper,hinge=right,open=true,powered=false] -minecraft:spruce_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:spruce_door[facing=east,half=lower,hinge=left,open=false,powered=false] -minecraft:spruce_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:spruce_door[facing=east,half=lower,hinge=right,open=false,powered=false] -minecraft:spruce_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:spruce_door[facing=east,half=upper,hinge=left,open=false,powered=false] -minecraft:spruce_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:spruce_door[facing=east,half=upper,hinge=right,open=false,powered=false] -minecraft:spruce_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:spruce_door[facing=east,half=lower,hinge=left,open=true,powered=false] -minecraft:spruce_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:spruce_door[facing=east,half=lower,hinge=right,open=true,powered=false] -minecraft:spruce_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:spruce_door[facing=east,half=upper,hinge=left,open=true,powered=false] -minecraft:spruce_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:spruce_door[facing=east,half=upper,hinge=right,open=true,powered=false] -minecraft:spruce_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:spruce_door[facing=north,half=lower,hinge=left,open=false,powered=false] -minecraft:spruce_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:spruce_door[facing=north,half=lower,hinge=right,open=false,powered=false] -minecraft:spruce_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:spruce_door[facing=north,half=upper,hinge=left,open=false,powered=false] -minecraft:spruce_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:spruce_door[facing=north,half=upper,hinge=right,open=false,powered=false] -minecraft:spruce_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:spruce_door[facing=north,half=lower,hinge=left,open=true,powered=false] -minecraft:spruce_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:spruce_door[facing=north,half=lower,hinge=right,open=true,powered=false] -minecraft:spruce_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:spruce_door[facing=north,half=upper,hinge=left,open=true,powered=false] -minecraft:spruce_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:spruce_door[facing=north,half=upper,hinge=right,open=true,powered=false] -minecraft:spruce_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:spruce_door[facing=south,half=lower,hinge=left,open=false,powered=false] -minecraft:spruce_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:spruce_door[facing=south,half=lower,hinge=right,open=false,powered=false] -minecraft:spruce_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:spruce_door[facing=south,half=upper,hinge=left,open=false,powered=false] -minecraft:spruce_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:spruce_door[facing=south,half=upper,hinge=right,open=false,powered=false] -minecraft:spruce_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:spruce_door[facing=south,half=lower,hinge=left,open=true,powered=false] -minecraft:spruce_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:spruce_door[facing=south,half=lower,hinge=right,open=true,powered=false] -minecraft:spruce_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:spruce_door[facing=south,half=upper,hinge=left,open=true,powered=false] -minecraft:spruce_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:spruce_door[facing=south,half=upper,hinge=right,open=true,powered=false] -minecraft:spruce_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:spruce_door[facing=west,half=lower,hinge=left,open=false,powered=false] -minecraft:spruce_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:spruce_door[facing=west,half=lower,hinge=right,open=false,powered=false] -minecraft:spruce_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:spruce_door[facing=west,half=upper,hinge=left,open=false,powered=false] -minecraft:spruce_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:spruce_door[facing=west,half=upper,hinge=right,open=false,powered=false] -minecraft:spruce_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:spruce_door[facing=west,half=lower,hinge=left,open=true,powered=false] -minecraft:spruce_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:spruce_door[facing=west,half=lower,hinge=right,open=true,powered=false] -minecraft:spruce_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:spruce_door[facing=west,half=upper,hinge=left,open=true,powered=false] -minecraft:spruce_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:spruce_door[facing=west,half=upper,hinge=right,open=true,powered=false] -minecraft:birch_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:birch_door[facing=east,half=lower,hinge=left,open=false,powered=false] -minecraft:birch_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:birch_door[facing=east,half=lower,hinge=right,open=false,powered=false] -minecraft:birch_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:birch_door[facing=east,half=upper,hinge=left,open=false,powered=false] -minecraft:birch_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:birch_door[facing=east,half=upper,hinge=right,open=false,powered=false] -minecraft:birch_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:birch_door[facing=east,half=lower,hinge=left,open=true,powered=false] -minecraft:birch_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:birch_door[facing=east,half=lower,hinge=right,open=true,powered=false] -minecraft:birch_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:birch_door[facing=east,half=upper,hinge=left,open=true,powered=false] -minecraft:birch_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:birch_door[facing=east,half=upper,hinge=right,open=true,powered=false] -minecraft:birch_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:birch_door[facing=north,half=lower,hinge=left,open=false,powered=false] -minecraft:birch_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:birch_door[facing=north,half=lower,hinge=right,open=false,powered=false] -minecraft:birch_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:birch_door[facing=north,half=upper,hinge=left,open=false,powered=false] -minecraft:birch_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:birch_door[facing=north,half=upper,hinge=right,open=false,powered=false] -minecraft:birch_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:birch_door[facing=north,half=lower,hinge=left,open=true,powered=false] -minecraft:birch_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:birch_door[facing=north,half=lower,hinge=right,open=true,powered=false] -minecraft:birch_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:birch_door[facing=north,half=upper,hinge=left,open=true,powered=false] -minecraft:birch_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:birch_door[facing=north,half=upper,hinge=right,open=true,powered=false] -minecraft:birch_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:birch_door[facing=south,half=lower,hinge=left,open=false,powered=false] -minecraft:birch_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:birch_door[facing=south,half=lower,hinge=right,open=false,powered=false] -minecraft:birch_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:birch_door[facing=south,half=upper,hinge=left,open=false,powered=false] -minecraft:birch_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:birch_door[facing=south,half=upper,hinge=right,open=false,powered=false] -minecraft:birch_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:birch_door[facing=south,half=lower,hinge=left,open=true,powered=false] -minecraft:birch_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:birch_door[facing=south,half=lower,hinge=right,open=true,powered=false] -minecraft:birch_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:birch_door[facing=south,half=upper,hinge=left,open=true,powered=false] -minecraft:birch_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:birch_door[facing=south,half=upper,hinge=right,open=true,powered=false] -minecraft:birch_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:birch_door[facing=west,half=lower,hinge=left,open=false,powered=false] -minecraft:birch_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:birch_door[facing=west,half=lower,hinge=right,open=false,powered=false] -minecraft:birch_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:birch_door[facing=west,half=upper,hinge=left,open=false,powered=false] -minecraft:birch_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:birch_door[facing=west,half=upper,hinge=right,open=false,powered=false] -minecraft:birch_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:birch_door[facing=west,half=lower,hinge=left,open=true,powered=false] -minecraft:birch_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:birch_door[facing=west,half=lower,hinge=right,open=true,powered=false] -minecraft:birch_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:birch_door[facing=west,half=upper,hinge=left,open=true,powered=false] -minecraft:birch_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:birch_door[facing=west,half=upper,hinge=right,open=true,powered=false] -minecraft:jungle_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:jungle_door[facing=east,half=lower,hinge=left,open=false,powered=false] -minecraft:jungle_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:jungle_door[facing=east,half=lower,hinge=right,open=false,powered=false] -minecraft:jungle_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:jungle_door[facing=east,half=upper,hinge=left,open=false,powered=false] -minecraft:jungle_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:jungle_door[facing=east,half=upper,hinge=right,open=false,powered=false] -minecraft:jungle_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:jungle_door[facing=east,half=lower,hinge=left,open=true,powered=false] -minecraft:jungle_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:jungle_door[facing=east,half=lower,hinge=right,open=true,powered=false] -minecraft:jungle_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:jungle_door[facing=east,half=upper,hinge=left,open=true,powered=false] -minecraft:jungle_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:jungle_door[facing=east,half=upper,hinge=right,open=true,powered=false] -minecraft:jungle_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:jungle_door[facing=north,half=lower,hinge=left,open=false,powered=false] -minecraft:jungle_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:jungle_door[facing=north,half=lower,hinge=right,open=false,powered=false] -minecraft:jungle_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:jungle_door[facing=north,half=upper,hinge=left,open=false,powered=false] -minecraft:jungle_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:jungle_door[facing=north,half=upper,hinge=right,open=false,powered=false] -minecraft:jungle_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:jungle_door[facing=north,half=lower,hinge=left,open=true,powered=false] -minecraft:jungle_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:jungle_door[facing=north,half=lower,hinge=right,open=true,powered=false] -minecraft:jungle_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:jungle_door[facing=north,half=upper,hinge=left,open=true,powered=false] -minecraft:jungle_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:jungle_door[facing=north,half=upper,hinge=right,open=true,powered=false] -minecraft:jungle_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:jungle_door[facing=south,half=lower,hinge=left,open=false,powered=false] -minecraft:jungle_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:jungle_door[facing=south,half=lower,hinge=right,open=false,powered=false] -minecraft:jungle_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:jungle_door[facing=south,half=upper,hinge=left,open=false,powered=false] -minecraft:jungle_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:jungle_door[facing=south,half=upper,hinge=right,open=false,powered=false] -minecraft:jungle_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:jungle_door[facing=south,half=lower,hinge=left,open=true,powered=false] -minecraft:jungle_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:jungle_door[facing=south,half=lower,hinge=right,open=true,powered=false] -minecraft:jungle_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:jungle_door[facing=south,half=upper,hinge=left,open=true,powered=false] -minecraft:jungle_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:jungle_door[facing=south,half=upper,hinge=right,open=true,powered=false] -minecraft:jungle_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:jungle_door[facing=west,half=lower,hinge=left,open=false,powered=false] -minecraft:jungle_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:jungle_door[facing=west,half=lower,hinge=right,open=false,powered=false] -minecraft:jungle_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:jungle_door[facing=west,half=upper,hinge=left,open=false,powered=false] -minecraft:jungle_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:jungle_door[facing=west,half=upper,hinge=right,open=false,powered=false] -minecraft:jungle_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:jungle_door[facing=west,half=lower,hinge=left,open=true,powered=false] -minecraft:jungle_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:jungle_door[facing=west,half=lower,hinge=right,open=true,powered=false] -minecraft:jungle_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:jungle_door[facing=west,half=upper,hinge=left,open=true,powered=false] -minecraft:jungle_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:jungle_door[facing=west,half=upper,hinge=right,open=true,powered=false] -minecraft:acacia_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:acacia_door[facing=east,half=lower,hinge=left,open=false,powered=false] -minecraft:acacia_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:acacia_door[facing=east,half=lower,hinge=right,open=false,powered=false] -minecraft:acacia_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:acacia_door[facing=east,half=upper,hinge=left,open=false,powered=false] -minecraft:acacia_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:acacia_door[facing=east,half=upper,hinge=right,open=false,powered=false] -minecraft:acacia_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:acacia_door[facing=east,half=lower,hinge=left,open=true,powered=false] -minecraft:acacia_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:acacia_door[facing=east,half=lower,hinge=right,open=true,powered=false] -minecraft:acacia_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:acacia_door[facing=east,half=upper,hinge=left,open=true,powered=false] -minecraft:acacia_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:acacia_door[facing=east,half=upper,hinge=right,open=true,powered=false] -minecraft:acacia_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:acacia_door[facing=north,half=lower,hinge=left,open=false,powered=false] -minecraft:acacia_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:acacia_door[facing=north,half=lower,hinge=right,open=false,powered=false] -minecraft:acacia_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:acacia_door[facing=north,half=upper,hinge=left,open=false,powered=false] -minecraft:acacia_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:acacia_door[facing=north,half=upper,hinge=right,open=false,powered=false] -minecraft:acacia_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:acacia_door[facing=north,half=lower,hinge=left,open=true,powered=false] -minecraft:acacia_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:acacia_door[facing=north,half=lower,hinge=right,open=true,powered=false] -minecraft:acacia_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:acacia_door[facing=north,half=upper,hinge=left,open=true,powered=false] -minecraft:acacia_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:acacia_door[facing=north,half=upper,hinge=right,open=true,powered=false] -minecraft:acacia_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:acacia_door[facing=south,half=lower,hinge=left,open=false,powered=false] -minecraft:acacia_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:acacia_door[facing=south,half=lower,hinge=right,open=false,powered=false] -minecraft:acacia_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:acacia_door[facing=south,half=upper,hinge=left,open=false,powered=false] -minecraft:acacia_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:acacia_door[facing=south,half=upper,hinge=right,open=false,powered=false] -minecraft:acacia_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:acacia_door[facing=south,half=lower,hinge=left,open=true,powered=false] -minecraft:acacia_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:acacia_door[facing=south,half=lower,hinge=right,open=true,powered=false] -minecraft:acacia_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:acacia_door[facing=south,half=upper,hinge=left,open=true,powered=false] -minecraft:acacia_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:acacia_door[facing=south,half=upper,hinge=right,open=true,powered=false] -minecraft:acacia_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:acacia_door[facing=west,half=lower,hinge=left,open=false,powered=false] -minecraft:acacia_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:acacia_door[facing=west,half=lower,hinge=right,open=false,powered=false] -minecraft:acacia_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:acacia_door[facing=west,half=upper,hinge=left,open=false,powered=false] -minecraft:acacia_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:acacia_door[facing=west,half=upper,hinge=right,open=false,powered=false] -minecraft:acacia_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:acacia_door[facing=west,half=lower,hinge=left,open=true,powered=false] -minecraft:acacia_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:acacia_door[facing=west,half=lower,hinge=right,open=true,powered=false] -minecraft:acacia_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:acacia_door[facing=west,half=upper,hinge=left,open=true,powered=false] -minecraft:acacia_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:acacia_door[facing=west,half=upper,hinge=right,open=true,powered=false] -minecraft:dark_oak_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:dark_oak_door[facing=east,half=lower,hinge=left,open=false,powered=false] -minecraft:dark_oak_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:dark_oak_door[facing=east,half=lower,hinge=right,open=false,powered=false] -minecraft:dark_oak_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:dark_oak_door[facing=east,half=upper,hinge=left,open=false,powered=false] -minecraft:dark_oak_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:dark_oak_door[facing=east,half=upper,hinge=right,open=false,powered=false] -minecraft:dark_oak_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:dark_oak_door[facing=east,half=lower,hinge=left,open=true,powered=false] -minecraft:dark_oak_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:dark_oak_door[facing=east,half=lower,hinge=right,open=true,powered=false] -minecraft:dark_oak_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:dark_oak_door[facing=east,half=upper,hinge=left,open=true,powered=false] -minecraft:dark_oak_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:dark_oak_door[facing=east,half=upper,hinge=right,open=true,powered=false] -minecraft:dark_oak_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:dark_oak_door[facing=north,half=lower,hinge=left,open=false,powered=false] -minecraft:dark_oak_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:dark_oak_door[facing=north,half=lower,hinge=right,open=false,powered=false] -minecraft:dark_oak_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:dark_oak_door[facing=north,half=upper,hinge=left,open=false,powered=false] -minecraft:dark_oak_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:dark_oak_door[facing=north,half=upper,hinge=right,open=false,powered=false] -minecraft:dark_oak_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:dark_oak_door[facing=north,half=lower,hinge=left,open=true,powered=false] -minecraft:dark_oak_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:dark_oak_door[facing=north,half=lower,hinge=right,open=true,powered=false] -minecraft:dark_oak_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:dark_oak_door[facing=north,half=upper,hinge=left,open=true,powered=false] -minecraft:dark_oak_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:dark_oak_door[facing=north,half=upper,hinge=right,open=true,powered=false] -minecraft:dark_oak_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:dark_oak_door[facing=south,half=lower,hinge=left,open=false,powered=false] -minecraft:dark_oak_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:dark_oak_door[facing=south,half=lower,hinge=right,open=false,powered=false] -minecraft:dark_oak_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:dark_oak_door[facing=south,half=upper,hinge=left,open=false,powered=false] -minecraft:dark_oak_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:dark_oak_door[facing=south,half=upper,hinge=right,open=false,powered=false] -minecraft:dark_oak_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:dark_oak_door[facing=south,half=lower,hinge=left,open=true,powered=false] -minecraft:dark_oak_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:dark_oak_door[facing=south,half=lower,hinge=right,open=true,powered=false] -minecraft:dark_oak_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:dark_oak_door[facing=south,half=upper,hinge=left,open=true,powered=false] -minecraft:dark_oak_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:dark_oak_door[facing=south,half=upper,hinge=right,open=true,powered=false] -minecraft:dark_oak_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:dark_oak_door[facing=west,half=lower,hinge=left,open=false,powered=false] -minecraft:dark_oak_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:dark_oak_door[facing=west,half=lower,hinge=right,open=false,powered=false] -minecraft:dark_oak_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:dark_oak_door[facing=west,half=upper,hinge=left,open=false,powered=false] -minecraft:dark_oak_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:dark_oak_door[facing=west,half=upper,hinge=right,open=false,powered=false] -minecraft:dark_oak_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:dark_oak_door[facing=west,half=lower,hinge=left,open=true,powered=false] -minecraft:dark_oak_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:dark_oak_door[facing=west,half=lower,hinge=right,open=true,powered=false] -minecraft:dark_oak_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:dark_oak_door[facing=west,half=upper,hinge=left,open=true,powered=false] -minecraft:dark_oak_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:dark_oak_door[facing=west,half=upper,hinge=right,open=true,powered=false] -minecraft:mangrove_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:mangrove_door[facing=east,half=lower,hinge=left,open=false,powered=false] -minecraft:mangrove_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:mangrove_door[facing=east,half=lower,hinge=right,open=false,powered=false] -minecraft:mangrove_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:mangrove_door[facing=east,half=upper,hinge=left,open=false,powered=false] -minecraft:mangrove_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:mangrove_door[facing=east,half=upper,hinge=right,open=false,powered=false] -minecraft:mangrove_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:mangrove_door[facing=east,half=lower,hinge=left,open=true,powered=false] -minecraft:mangrove_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:mangrove_door[facing=east,half=lower,hinge=right,open=true,powered=false] -minecraft:mangrove_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:mangrove_door[facing=east,half=upper,hinge=left,open=true,powered=false] -minecraft:mangrove_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:mangrove_door[facing=east,half=upper,hinge=right,open=true,powered=false] -minecraft:mangrove_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:mangrove_door[facing=north,half=lower,hinge=left,open=false,powered=false] -minecraft:mangrove_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:mangrove_door[facing=north,half=lower,hinge=right,open=false,powered=false] -minecraft:mangrove_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:mangrove_door[facing=north,half=upper,hinge=left,open=false,powered=false] -minecraft:mangrove_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:mangrove_door[facing=north,half=upper,hinge=right,open=false,powered=false] -minecraft:mangrove_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:mangrove_door[facing=north,half=lower,hinge=left,open=true,powered=false] -minecraft:mangrove_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:mangrove_door[facing=north,half=lower,hinge=right,open=true,powered=false] -minecraft:mangrove_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:mangrove_door[facing=north,half=upper,hinge=left,open=true,powered=false] -minecraft:mangrove_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:mangrove_door[facing=north,half=upper,hinge=right,open=true,powered=false] -minecraft:mangrove_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:mangrove_door[facing=south,half=lower,hinge=left,open=false,powered=false] -minecraft:mangrove_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:mangrove_door[facing=south,half=lower,hinge=right,open=false,powered=false] -minecraft:mangrove_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:mangrove_door[facing=south,half=upper,hinge=left,open=false,powered=false] -minecraft:mangrove_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:mangrove_door[facing=south,half=upper,hinge=right,open=false,powered=false] -minecraft:mangrove_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:mangrove_door[facing=south,half=lower,hinge=left,open=true,powered=false] -minecraft:mangrove_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:mangrove_door[facing=south,half=lower,hinge=right,open=true,powered=false] -minecraft:mangrove_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:mangrove_door[facing=south,half=upper,hinge=left,open=true,powered=false] -minecraft:mangrove_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:mangrove_door[facing=south,half=upper,hinge=right,open=true,powered=false] -minecraft:mangrove_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:mangrove_door[facing=west,half=lower,hinge=left,open=false,powered=false] -minecraft:mangrove_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:mangrove_door[facing=west,half=lower,hinge=right,open=false,powered=false] -minecraft:mangrove_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:mangrove_door[facing=west,half=upper,hinge=left,open=false,powered=false] -minecraft:mangrove_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:mangrove_door[facing=west,half=upper,hinge=right,open=false,powered=false] -minecraft:mangrove_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:mangrove_door[facing=west,half=lower,hinge=left,open=true,powered=false] -minecraft:mangrove_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:mangrove_door[facing=west,half=lower,hinge=right,open=true,powered=false] -minecraft:mangrove_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:mangrove_door[facing=west,half=upper,hinge=left,open=true,powered=false] -minecraft:mangrove_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:mangrove_door[facing=west,half=upper,hinge=right,open=true,powered=false] -minecraft:cherry_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:cherry_door[facing=east,half=lower,hinge=left,open=false,powered=false] -minecraft:cherry_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:cherry_door[facing=east,half=lower,hinge=right,open=false,powered=false] -minecraft:cherry_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:cherry_door[facing=east,half=upper,hinge=left,open=false,powered=false] -minecraft:cherry_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:cherry_door[facing=east,half=upper,hinge=right,open=false,powered=false] -minecraft:cherry_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:cherry_door[facing=east,half=lower,hinge=left,open=true,powered=false] -minecraft:cherry_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:cherry_door[facing=east,half=lower,hinge=right,open=true,powered=false] -minecraft:cherry_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:cherry_door[facing=east,half=upper,hinge=left,open=true,powered=false] -minecraft:cherry_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:cherry_door[facing=east,half=upper,hinge=right,open=true,powered=false] -minecraft:cherry_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:cherry_door[facing=north,half=lower,hinge=left,open=false,powered=false] -minecraft:cherry_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:cherry_door[facing=north,half=lower,hinge=right,open=false,powered=false] -minecraft:cherry_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:cherry_door[facing=north,half=upper,hinge=left,open=false,powered=false] -minecraft:cherry_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:cherry_door[facing=north,half=upper,hinge=right,open=false,powered=false] -minecraft:cherry_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:cherry_door[facing=north,half=lower,hinge=left,open=true,powered=false] -minecraft:cherry_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:cherry_door[facing=north,half=lower,hinge=right,open=true,powered=false] -minecraft:cherry_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:cherry_door[facing=north,half=upper,hinge=left,open=true,powered=false] -minecraft:cherry_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:cherry_door[facing=north,half=upper,hinge=right,open=true,powered=false] -minecraft:cherry_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:cherry_door[facing=south,half=lower,hinge=left,open=false,powered=false] -minecraft:cherry_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:cherry_door[facing=south,half=lower,hinge=right,open=false,powered=false] -minecraft:cherry_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:cherry_door[facing=south,half=upper,hinge=left,open=false,powered=false] -minecraft:cherry_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:cherry_door[facing=south,half=upper,hinge=right,open=false,powered=false] -minecraft:cherry_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:cherry_door[facing=south,half=lower,hinge=left,open=true,powered=false] -minecraft:cherry_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:cherry_door[facing=south,half=lower,hinge=right,open=true,powered=false] -minecraft:cherry_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:cherry_door[facing=south,half=upper,hinge=left,open=true,powered=false] -minecraft:cherry_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:cherry_door[facing=south,half=upper,hinge=right,open=true,powered=false] -minecraft:cherry_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:cherry_door[facing=west,half=lower,hinge=left,open=false,powered=false] -minecraft:cherry_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:cherry_door[facing=west,half=lower,hinge=right,open=false,powered=false] -minecraft:cherry_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:cherry_door[facing=west,half=upper,hinge=left,open=false,powered=false] -minecraft:cherry_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:cherry_door[facing=west,half=upper,hinge=right,open=false,powered=false] -minecraft:cherry_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:cherry_door[facing=west,half=lower,hinge=left,open=true,powered=false] -minecraft:cherry_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:cherry_door[facing=west,half=lower,hinge=right,open=true,powered=false] -minecraft:cherry_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:cherry_door[facing=west,half=upper,hinge=left,open=true,powered=false] -minecraft:cherry_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:cherry_door[facing=west,half=upper,hinge=right,open=true,powered=false] -minecraft:bamboo_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:bamboo_door[facing=east,half=lower,hinge=left,open=false,powered=false] -minecraft:bamboo_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:bamboo_door[facing=east,half=lower,hinge=right,open=false,powered=false] -minecraft:bamboo_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:bamboo_door[facing=east,half=upper,hinge=left,open=false,powered=false] -minecraft:bamboo_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:bamboo_door[facing=east,half=upper,hinge=right,open=false,powered=false] -minecraft:bamboo_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:bamboo_door[facing=east,half=lower,hinge=left,open=true,powered=false] -minecraft:bamboo_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:bamboo_door[facing=east,half=lower,hinge=right,open=true,powered=false] -minecraft:bamboo_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:bamboo_door[facing=east,half=upper,hinge=left,open=true,powered=false] -minecraft:bamboo_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:bamboo_door[facing=east,half=upper,hinge=right,open=true,powered=false] -minecraft:bamboo_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:bamboo_door[facing=north,half=lower,hinge=left,open=false,powered=false] -minecraft:bamboo_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:bamboo_door[facing=north,half=lower,hinge=right,open=false,powered=false] -minecraft:bamboo_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:bamboo_door[facing=north,half=upper,hinge=left,open=false,powered=false] -minecraft:bamboo_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:bamboo_door[facing=north,half=upper,hinge=right,open=false,powered=false] -minecraft:bamboo_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:bamboo_door[facing=north,half=lower,hinge=left,open=true,powered=false] -minecraft:bamboo_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:bamboo_door[facing=north,half=lower,hinge=right,open=true,powered=false] -minecraft:bamboo_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:bamboo_door[facing=north,half=upper,hinge=left,open=true,powered=false] -minecraft:bamboo_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:bamboo_door[facing=north,half=upper,hinge=right,open=true,powered=false] -minecraft:bamboo_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:bamboo_door[facing=south,half=lower,hinge=left,open=false,powered=false] -minecraft:bamboo_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:bamboo_door[facing=south,half=lower,hinge=right,open=false,powered=false] -minecraft:bamboo_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:bamboo_door[facing=south,half=upper,hinge=left,open=false,powered=false] -minecraft:bamboo_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:bamboo_door[facing=south,half=upper,hinge=right,open=false,powered=false] -minecraft:bamboo_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:bamboo_door[facing=south,half=lower,hinge=left,open=true,powered=false] -minecraft:bamboo_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:bamboo_door[facing=south,half=lower,hinge=right,open=true,powered=false] -minecraft:bamboo_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:bamboo_door[facing=south,half=upper,hinge=left,open=true,powered=false] -minecraft:bamboo_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:bamboo_door[facing=south,half=upper,hinge=right,open=true,powered=false] -minecraft:bamboo_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:bamboo_door[facing=west,half=lower,hinge=left,open=false,powered=false] -minecraft:bamboo_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:bamboo_door[facing=west,half=lower,hinge=right,open=false,powered=false] -minecraft:bamboo_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:bamboo_door[facing=west,half=upper,hinge=left,open=false,powered=false] -minecraft:bamboo_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:bamboo_door[facing=west,half=upper,hinge=right,open=false,powered=false] -minecraft:bamboo_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:bamboo_door[facing=west,half=lower,hinge=left,open=true,powered=false] -minecraft:bamboo_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:bamboo_door[facing=west,half=lower,hinge=right,open=true,powered=false] -minecraft:bamboo_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:bamboo_door[facing=west,half=upper,hinge=left,open=true,powered=false] -minecraft:bamboo_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:bamboo_door[facing=west,half=upper,hinge=right,open=true,powered=false] -minecraft:crimson_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:crimson_door[facing=east,half=lower,hinge=left,open=false,powered=false] -minecraft:crimson_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:crimson_door[facing=east,half=lower,hinge=right,open=false,powered=false] -minecraft:crimson_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:crimson_door[facing=east,half=upper,hinge=left,open=false,powered=false] -minecraft:crimson_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:crimson_door[facing=east,half=upper,hinge=right,open=false,powered=false] -minecraft:crimson_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:crimson_door[facing=east,half=lower,hinge=left,open=true,powered=false] -minecraft:crimson_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:crimson_door[facing=east,half=lower,hinge=right,open=true,powered=false] -minecraft:crimson_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:crimson_door[facing=east,half=upper,hinge=left,open=true,powered=false] -minecraft:crimson_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:crimson_door[facing=east,half=upper,hinge=right,open=true,powered=false] -minecraft:crimson_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:crimson_door[facing=north,half=lower,hinge=left,open=false,powered=false] -minecraft:crimson_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:crimson_door[facing=north,half=lower,hinge=right,open=false,powered=false] -minecraft:crimson_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:crimson_door[facing=north,half=upper,hinge=left,open=false,powered=false] -minecraft:crimson_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:crimson_door[facing=north,half=upper,hinge=right,open=false,powered=false] -minecraft:crimson_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:crimson_door[facing=north,half=lower,hinge=left,open=true,powered=false] -minecraft:crimson_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:crimson_door[facing=north,half=lower,hinge=right,open=true,powered=false] -minecraft:crimson_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:crimson_door[facing=north,half=upper,hinge=left,open=true,powered=false] -minecraft:crimson_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:crimson_door[facing=north,half=upper,hinge=right,open=true,powered=false] -minecraft:crimson_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:crimson_door[facing=south,half=lower,hinge=left,open=false,powered=false] -minecraft:crimson_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:crimson_door[facing=south,half=lower,hinge=right,open=false,powered=false] -minecraft:crimson_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:crimson_door[facing=south,half=upper,hinge=left,open=false,powered=false] -minecraft:crimson_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:crimson_door[facing=south,half=upper,hinge=right,open=false,powered=false] -minecraft:crimson_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:crimson_door[facing=south,half=lower,hinge=left,open=true,powered=false] -minecraft:crimson_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:crimson_door[facing=south,half=lower,hinge=right,open=true,powered=false] -minecraft:crimson_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:crimson_door[facing=south,half=upper,hinge=left,open=true,powered=false] -minecraft:crimson_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:crimson_door[facing=south,half=upper,hinge=right,open=true,powered=false] -minecraft:crimson_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:crimson_door[facing=west,half=lower,hinge=left,open=false,powered=false] -minecraft:crimson_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:crimson_door[facing=west,half=lower,hinge=right,open=false,powered=false] -minecraft:crimson_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:crimson_door[facing=west,half=upper,hinge=left,open=false,powered=false] -minecraft:crimson_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:crimson_door[facing=west,half=upper,hinge=right,open=false,powered=false] -minecraft:crimson_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:crimson_door[facing=west,half=lower,hinge=left,open=true,powered=false] -minecraft:crimson_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:crimson_door[facing=west,half=lower,hinge=right,open=true,powered=false] -minecraft:crimson_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:crimson_door[facing=west,half=upper,hinge=left,open=true,powered=false] -minecraft:crimson_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:crimson_door[facing=west,half=upper,hinge=right,open=true,powered=false] -minecraft:warped_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:warped_door[facing=east,half=lower,hinge=left,open=false,powered=false] -minecraft:warped_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:warped_door[facing=east,half=lower,hinge=right,open=false,powered=false] -minecraft:warped_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:warped_door[facing=east,half=upper,hinge=left,open=false,powered=false] -minecraft:warped_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:warped_door[facing=east,half=upper,hinge=right,open=false,powered=false] -minecraft:warped_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:warped_door[facing=east,half=lower,hinge=left,open=true,powered=false] -minecraft:warped_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:warped_door[facing=east,half=lower,hinge=right,open=true,powered=false] -minecraft:warped_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:warped_door[facing=east,half=upper,hinge=left,open=true,powered=false] -minecraft:warped_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:warped_door[facing=east,half=upper,hinge=right,open=true,powered=false] -minecraft:warped_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:warped_door[facing=north,half=lower,hinge=left,open=false,powered=false] -minecraft:warped_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:warped_door[facing=north,half=lower,hinge=right,open=false,powered=false] -minecraft:warped_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:warped_door[facing=north,half=upper,hinge=left,open=false,powered=false] -minecraft:warped_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:warped_door[facing=north,half=upper,hinge=right,open=false,powered=false] -minecraft:warped_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:warped_door[facing=north,half=lower,hinge=left,open=true,powered=false] -minecraft:warped_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:warped_door[facing=north,half=lower,hinge=right,open=true,powered=false] -minecraft:warped_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:warped_door[facing=north,half=upper,hinge=left,open=true,powered=false] -minecraft:warped_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:warped_door[facing=north,half=upper,hinge=right,open=true,powered=false] -minecraft:warped_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:warped_door[facing=south,half=lower,hinge=left,open=false,powered=false] -minecraft:warped_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:warped_door[facing=south,half=lower,hinge=right,open=false,powered=false] -minecraft:warped_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:warped_door[facing=south,half=upper,hinge=left,open=false,powered=false] -minecraft:warped_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:warped_door[facing=south,half=upper,hinge=right,open=false,powered=false] -minecraft:warped_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:warped_door[facing=south,half=lower,hinge=left,open=true,powered=false] -minecraft:warped_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:warped_door[facing=south,half=lower,hinge=right,open=true,powered=false] -minecraft:warped_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:warped_door[facing=south,half=upper,hinge=left,open=true,powered=false] -minecraft:warped_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:warped_door[facing=south,half=upper,hinge=right,open=true,powered=false] -minecraft:warped_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:warped_door[facing=west,half=lower,hinge=left,open=false,powered=false] -minecraft:warped_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:warped_door[facing=west,half=lower,hinge=right,open=false,powered=false] -minecraft:warped_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:warped_door[facing=west,half=upper,hinge=left,open=false,powered=false] -minecraft:warped_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:warped_door[facing=west,half=upper,hinge=right,open=false,powered=false] -minecraft:warped_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:warped_door[facing=west,half=lower,hinge=left,open=true,powered=false] -minecraft:warped_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:warped_door[facing=west,half=lower,hinge=right,open=true,powered=false] -minecraft:warped_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:warped_door[facing=west,half=upper,hinge=left,open=true,powered=false] -minecraft:warped_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:warped_door[facing=west,half=upper,hinge=right,open=true,powered=false] -minecraft:iron_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:iron_door[facing=east,half=lower,hinge=left,open=false,powered=false] -minecraft:iron_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:iron_door[facing=east,half=lower,hinge=right,open=false,powered=false] -minecraft:iron_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:iron_door[facing=east,half=upper,hinge=left,open=false,powered=false] -minecraft:iron_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:iron_door[facing=east,half=upper,hinge=right,open=false,powered=false] -minecraft:iron_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:iron_door[facing=east,half=lower,hinge=left,open=true,powered=false] -minecraft:iron_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:iron_door[facing=east,half=lower,hinge=right,open=true,powered=false] -minecraft:iron_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:iron_door[facing=east,half=upper,hinge=left,open=true,powered=false] -minecraft:iron_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:iron_door[facing=east,half=upper,hinge=right,open=true,powered=false] -minecraft:iron_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:iron_door[facing=north,half=lower,hinge=left,open=false,powered=false] -minecraft:iron_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:iron_door[facing=north,half=lower,hinge=right,open=false,powered=false] -minecraft:iron_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:iron_door[facing=north,half=upper,hinge=left,open=false,powered=false] -minecraft:iron_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:iron_door[facing=north,half=upper,hinge=right,open=false,powered=false] -minecraft:iron_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:iron_door[facing=north,half=lower,hinge=left,open=true,powered=false] -minecraft:iron_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:iron_door[facing=north,half=lower,hinge=right,open=true,powered=false] -minecraft:iron_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:iron_door[facing=north,half=upper,hinge=left,open=true,powered=false] -minecraft:iron_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:iron_door[facing=north,half=upper,hinge=right,open=true,powered=false] -minecraft:iron_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:iron_door[facing=south,half=lower,hinge=left,open=false,powered=false] -minecraft:iron_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:iron_door[facing=south,half=lower,hinge=right,open=false,powered=false] -minecraft:iron_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:iron_door[facing=south,half=upper,hinge=left,open=false,powered=false] -minecraft:iron_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:iron_door[facing=south,half=upper,hinge=right,open=false,powered=false] -minecraft:iron_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:iron_door[facing=south,half=lower,hinge=left,open=true,powered=false] -minecraft:iron_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:iron_door[facing=south,half=lower,hinge=right,open=true,powered=false] -minecraft:iron_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:iron_door[facing=south,half=upper,hinge=left,open=true,powered=false] -minecraft:iron_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:iron_door[facing=south,half=upper,hinge=right,open=true,powered=false] -minecraft:iron_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:iron_door[facing=west,half=lower,hinge=left,open=false,powered=false] -minecraft:iron_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:iron_door[facing=west,half=lower,hinge=right,open=false,powered=false] -minecraft:iron_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:iron_door[facing=west,half=upper,hinge=left,open=false,powered=false] -minecraft:iron_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:iron_door[facing=west,half=upper,hinge=right,open=false,powered=false] -minecraft:iron_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:iron_door[facing=west,half=lower,hinge=left,open=true,powered=false] -minecraft:iron_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:iron_door[facing=west,half=lower,hinge=right,open=true,powered=false] -minecraft:iron_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:iron_door[facing=west,half=upper,hinge=left,open=true,powered=false] -minecraft:iron_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:iron_door[facing=west,half=upper,hinge=right,open=true,powered=false] - -$$>=1.20.3#door: - minecraft:copper_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:copper_door[facing=east,half=lower,hinge=left,open=false,powered=false] - minecraft:copper_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:copper_door[facing=east,half=lower,hinge=right,open=false,powered=false] - minecraft:copper_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:copper_door[facing=east,half=upper,hinge=left,open=false,powered=false] - minecraft:copper_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:copper_door[facing=east,half=upper,hinge=right,open=false,powered=false] - minecraft:copper_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:copper_door[facing=east,half=lower,hinge=left,open=true,powered=false] - minecraft:copper_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:copper_door[facing=east,half=lower,hinge=right,open=true,powered=false] - minecraft:copper_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:copper_door[facing=east,half=upper,hinge=left,open=true,powered=false] - minecraft:copper_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:copper_door[facing=east,half=upper,hinge=right,open=true,powered=false] - minecraft:copper_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:copper_door[facing=north,half=lower,hinge=left,open=false,powered=false] - minecraft:copper_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:copper_door[facing=north,half=lower,hinge=right,open=false,powered=false] - minecraft:copper_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:copper_door[facing=north,half=upper,hinge=left,open=false,powered=false] - minecraft:copper_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:copper_door[facing=north,half=upper,hinge=right,open=false,powered=false] - minecraft:copper_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:copper_door[facing=north,half=lower,hinge=left,open=true,powered=false] - minecraft:copper_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:copper_door[facing=north,half=lower,hinge=right,open=true,powered=false] - minecraft:copper_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:copper_door[facing=north,half=upper,hinge=left,open=true,powered=false] - minecraft:copper_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:copper_door[facing=north,half=upper,hinge=right,open=true,powered=false] - minecraft:copper_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:copper_door[facing=south,half=lower,hinge=left,open=false,powered=false] - minecraft:copper_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:copper_door[facing=south,half=lower,hinge=right,open=false,powered=false] - minecraft:copper_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:copper_door[facing=south,half=upper,hinge=left,open=false,powered=false] - minecraft:copper_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:copper_door[facing=south,half=upper,hinge=right,open=false,powered=false] - minecraft:copper_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:copper_door[facing=south,half=lower,hinge=left,open=true,powered=false] - minecraft:copper_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:copper_door[facing=south,half=lower,hinge=right,open=true,powered=false] - minecraft:copper_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:copper_door[facing=south,half=upper,hinge=left,open=true,powered=false] - minecraft:copper_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:copper_door[facing=south,half=upper,hinge=right,open=true,powered=false] - minecraft:copper_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:copper_door[facing=west,half=lower,hinge=left,open=false,powered=false] - minecraft:copper_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:copper_door[facing=west,half=lower,hinge=right,open=false,powered=false] - minecraft:copper_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:copper_door[facing=west,half=upper,hinge=left,open=false,powered=false] - minecraft:copper_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:copper_door[facing=west,half=upper,hinge=right,open=false,powered=false] - minecraft:copper_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:copper_door[facing=west,half=lower,hinge=left,open=true,powered=false] - minecraft:copper_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:copper_door[facing=west,half=lower,hinge=right,open=true,powered=false] - minecraft:copper_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:copper_door[facing=west,half=upper,hinge=left,open=true,powered=false] - minecraft:copper_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:copper_door[facing=west,half=upper,hinge=right,open=true,powered=false] - minecraft:exposed_copper_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:exposed_copper_door[facing=east,half=lower,hinge=left,open=false,powered=false] - minecraft:exposed_copper_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:exposed_copper_door[facing=east,half=lower,hinge=right,open=false,powered=false] - minecraft:exposed_copper_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:exposed_copper_door[facing=east,half=upper,hinge=left,open=false,powered=false] - minecraft:exposed_copper_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:exposed_copper_door[facing=east,half=upper,hinge=right,open=false,powered=false] - minecraft:exposed_copper_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:exposed_copper_door[facing=east,half=lower,hinge=left,open=true,powered=false] - minecraft:exposed_copper_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:exposed_copper_door[facing=east,half=lower,hinge=right,open=true,powered=false] - minecraft:exposed_copper_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:exposed_copper_door[facing=east,half=upper,hinge=left,open=true,powered=false] - minecraft:exposed_copper_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:exposed_copper_door[facing=east,half=upper,hinge=right,open=true,powered=false] - minecraft:exposed_copper_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:exposed_copper_door[facing=north,half=lower,hinge=left,open=false,powered=false] - minecraft:exposed_copper_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:exposed_copper_door[facing=north,half=lower,hinge=right,open=false,powered=false] - minecraft:exposed_copper_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:exposed_copper_door[facing=north,half=upper,hinge=left,open=false,powered=false] - minecraft:exposed_copper_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:exposed_copper_door[facing=north,half=upper,hinge=right,open=false,powered=false] - minecraft:exposed_copper_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:exposed_copper_door[facing=north,half=lower,hinge=left,open=true,powered=false] - minecraft:exposed_copper_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:exposed_copper_door[facing=north,half=lower,hinge=right,open=true,powered=false] - minecraft:exposed_copper_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:exposed_copper_door[facing=north,half=upper,hinge=left,open=true,powered=false] - minecraft:exposed_copper_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:exposed_copper_door[facing=north,half=upper,hinge=right,open=true,powered=false] - minecraft:exposed_copper_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:exposed_copper_door[facing=south,half=lower,hinge=left,open=false,powered=false] - minecraft:exposed_copper_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:exposed_copper_door[facing=south,half=lower,hinge=right,open=false,powered=false] - minecraft:exposed_copper_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:exposed_copper_door[facing=south,half=upper,hinge=left,open=false,powered=false] - minecraft:exposed_copper_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:exposed_copper_door[facing=south,half=upper,hinge=right,open=false,powered=false] - minecraft:exposed_copper_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:exposed_copper_door[facing=south,half=lower,hinge=left,open=true,powered=false] - minecraft:exposed_copper_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:exposed_copper_door[facing=south,half=lower,hinge=right,open=true,powered=false] - minecraft:exposed_copper_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:exposed_copper_door[facing=south,half=upper,hinge=left,open=true,powered=false] - minecraft:exposed_copper_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:exposed_copper_door[facing=south,half=upper,hinge=right,open=true,powered=false] - minecraft:exposed_copper_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:exposed_copper_door[facing=west,half=lower,hinge=left,open=false,powered=false] - minecraft:exposed_copper_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:exposed_copper_door[facing=west,half=lower,hinge=right,open=false,powered=false] - minecraft:exposed_copper_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:exposed_copper_door[facing=west,half=upper,hinge=left,open=false,powered=false] - minecraft:exposed_copper_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:exposed_copper_door[facing=west,half=upper,hinge=right,open=false,powered=false] - minecraft:exposed_copper_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:exposed_copper_door[facing=west,half=lower,hinge=left,open=true,powered=false] - minecraft:exposed_copper_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:exposed_copper_door[facing=west,half=lower,hinge=right,open=true,powered=false] - minecraft:exposed_copper_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:exposed_copper_door[facing=west,half=upper,hinge=left,open=true,powered=false] - minecraft:exposed_copper_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:exposed_copper_door[facing=west,half=upper,hinge=right,open=true,powered=false] - minecraft:weathered_copper_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:weathered_copper_door[facing=east,half=lower,hinge=left,open=false,powered=false] - minecraft:weathered_copper_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:weathered_copper_door[facing=east,half=lower,hinge=right,open=false,powered=false] - minecraft:weathered_copper_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:weathered_copper_door[facing=east,half=upper,hinge=left,open=false,powered=false] - minecraft:weathered_copper_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:weathered_copper_door[facing=east,half=upper,hinge=right,open=false,powered=false] - minecraft:weathered_copper_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:weathered_copper_door[facing=east,half=lower,hinge=left,open=true,powered=false] - minecraft:weathered_copper_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:weathered_copper_door[facing=east,half=lower,hinge=right,open=true,powered=false] - minecraft:weathered_copper_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:weathered_copper_door[facing=east,half=upper,hinge=left,open=true,powered=false] - minecraft:weathered_copper_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:weathered_copper_door[facing=east,half=upper,hinge=right,open=true,powered=false] - minecraft:weathered_copper_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:weathered_copper_door[facing=north,half=lower,hinge=left,open=false,powered=false] - minecraft:weathered_copper_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:weathered_copper_door[facing=north,half=lower,hinge=right,open=false,powered=false] - minecraft:weathered_copper_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:weathered_copper_door[facing=north,half=upper,hinge=left,open=false,powered=false] - minecraft:weathered_copper_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:weathered_copper_door[facing=north,half=upper,hinge=right,open=false,powered=false] - minecraft:weathered_copper_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:weathered_copper_door[facing=north,half=lower,hinge=left,open=true,powered=false] - minecraft:weathered_copper_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:weathered_copper_door[facing=north,half=lower,hinge=right,open=true,powered=false] - minecraft:weathered_copper_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:weathered_copper_door[facing=north,half=upper,hinge=left,open=true,powered=false] - minecraft:weathered_copper_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:weathered_copper_door[facing=north,half=upper,hinge=right,open=true,powered=false] - minecraft:weathered_copper_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:weathered_copper_door[facing=south,half=lower,hinge=left,open=false,powered=false] - minecraft:weathered_copper_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:weathered_copper_door[facing=south,half=lower,hinge=right,open=false,powered=false] - minecraft:weathered_copper_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:weathered_copper_door[facing=south,half=upper,hinge=left,open=false,powered=false] - minecraft:weathered_copper_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:weathered_copper_door[facing=south,half=upper,hinge=right,open=false,powered=false] - minecraft:weathered_copper_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:weathered_copper_door[facing=south,half=lower,hinge=left,open=true,powered=false] - minecraft:weathered_copper_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:weathered_copper_door[facing=south,half=lower,hinge=right,open=true,powered=false] - minecraft:weathered_copper_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:weathered_copper_door[facing=south,half=upper,hinge=left,open=true,powered=false] - minecraft:weathered_copper_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:weathered_copper_door[facing=south,half=upper,hinge=right,open=true,powered=false] - minecraft:weathered_copper_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:weathered_copper_door[facing=west,half=lower,hinge=left,open=false,powered=false] - minecraft:weathered_copper_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:weathered_copper_door[facing=west,half=lower,hinge=right,open=false,powered=false] - minecraft:weathered_copper_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:weathered_copper_door[facing=west,half=upper,hinge=left,open=false,powered=false] - minecraft:weathered_copper_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:weathered_copper_door[facing=west,half=upper,hinge=right,open=false,powered=false] - minecraft:weathered_copper_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:weathered_copper_door[facing=west,half=lower,hinge=left,open=true,powered=false] - minecraft:weathered_copper_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:weathered_copper_door[facing=west,half=lower,hinge=right,open=true,powered=false] - minecraft:weathered_copper_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:weathered_copper_door[facing=west,half=upper,hinge=left,open=true,powered=false] - minecraft:weathered_copper_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:weathered_copper_door[facing=west,half=upper,hinge=right,open=true,powered=false] - minecraft:oxidized_copper_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:oxidized_copper_door[facing=east,half=lower,hinge=left,open=false,powered=false] - minecraft:oxidized_copper_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:oxidized_copper_door[facing=east,half=lower,hinge=right,open=false,powered=false] - minecraft:oxidized_copper_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:oxidized_copper_door[facing=east,half=upper,hinge=left,open=false,powered=false] - minecraft:oxidized_copper_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:oxidized_copper_door[facing=east,half=upper,hinge=right,open=false,powered=false] - minecraft:oxidized_copper_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:oxidized_copper_door[facing=east,half=lower,hinge=left,open=true,powered=false] - minecraft:oxidized_copper_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:oxidized_copper_door[facing=east,half=lower,hinge=right,open=true,powered=false] - minecraft:oxidized_copper_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:oxidized_copper_door[facing=east,half=upper,hinge=left,open=true,powered=false] - minecraft:oxidized_copper_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:oxidized_copper_door[facing=east,half=upper,hinge=right,open=true,powered=false] - minecraft:oxidized_copper_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:oxidized_copper_door[facing=north,half=lower,hinge=left,open=false,powered=false] - minecraft:oxidized_copper_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:oxidized_copper_door[facing=north,half=lower,hinge=right,open=false,powered=false] - minecraft:oxidized_copper_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:oxidized_copper_door[facing=north,half=upper,hinge=left,open=false,powered=false] - minecraft:oxidized_copper_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:oxidized_copper_door[facing=north,half=upper,hinge=right,open=false,powered=false] - minecraft:oxidized_copper_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:oxidized_copper_door[facing=north,half=lower,hinge=left,open=true,powered=false] - minecraft:oxidized_copper_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:oxidized_copper_door[facing=north,half=lower,hinge=right,open=true,powered=false] - minecraft:oxidized_copper_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:oxidized_copper_door[facing=north,half=upper,hinge=left,open=true,powered=false] - minecraft:oxidized_copper_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:oxidized_copper_door[facing=north,half=upper,hinge=right,open=true,powered=false] - minecraft:oxidized_copper_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:oxidized_copper_door[facing=south,half=lower,hinge=left,open=false,powered=false] - minecraft:oxidized_copper_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:oxidized_copper_door[facing=south,half=lower,hinge=right,open=false,powered=false] - minecraft:oxidized_copper_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:oxidized_copper_door[facing=south,half=upper,hinge=left,open=false,powered=false] - minecraft:oxidized_copper_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:oxidized_copper_door[facing=south,half=upper,hinge=right,open=false,powered=false] - minecraft:oxidized_copper_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:oxidized_copper_door[facing=south,half=lower,hinge=left,open=true,powered=false] - minecraft:oxidized_copper_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:oxidized_copper_door[facing=south,half=lower,hinge=right,open=true,powered=false] - minecraft:oxidized_copper_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:oxidized_copper_door[facing=south,half=upper,hinge=left,open=true,powered=false] - minecraft:oxidized_copper_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:oxidized_copper_door[facing=south,half=upper,hinge=right,open=true,powered=false] - minecraft:oxidized_copper_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:oxidized_copper_door[facing=west,half=lower,hinge=left,open=false,powered=false] - minecraft:oxidized_copper_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:oxidized_copper_door[facing=west,half=lower,hinge=right,open=false,powered=false] - minecraft:oxidized_copper_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:oxidized_copper_door[facing=west,half=upper,hinge=left,open=false,powered=false] - minecraft:oxidized_copper_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:oxidized_copper_door[facing=west,half=upper,hinge=right,open=false,powered=false] - minecraft:oxidized_copper_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:oxidized_copper_door[facing=west,half=lower,hinge=left,open=true,powered=false] - minecraft:oxidized_copper_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:oxidized_copper_door[facing=west,half=lower,hinge=right,open=true,powered=false] - minecraft:oxidized_copper_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:oxidized_copper_door[facing=west,half=upper,hinge=left,open=true,powered=false] - minecraft:oxidized_copper_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:oxidized_copper_door[facing=west,half=upper,hinge=right,open=true,powered=false] - minecraft:waxed_copper_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_copper_door[facing=east,half=lower,hinge=left,open=false,powered=false] - minecraft:waxed_copper_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_copper_door[facing=east,half=lower,hinge=right,open=false,powered=false] - minecraft:waxed_copper_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_copper_door[facing=east,half=upper,hinge=left,open=false,powered=false] - minecraft:waxed_copper_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_copper_door[facing=east,half=upper,hinge=right,open=false,powered=false] - minecraft:waxed_copper_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_copper_door[facing=east,half=lower,hinge=left,open=true,powered=false] - minecraft:waxed_copper_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_copper_door[facing=east,half=lower,hinge=right,open=true,powered=false] - minecraft:waxed_copper_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_copper_door[facing=east,half=upper,hinge=left,open=true,powered=false] - minecraft:waxed_copper_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_copper_door[facing=east,half=upper,hinge=right,open=true,powered=false] - minecraft:waxed_copper_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_copper_door[facing=north,half=lower,hinge=left,open=false,powered=false] - minecraft:waxed_copper_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_copper_door[facing=north,half=lower,hinge=right,open=false,powered=false] - minecraft:waxed_copper_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_copper_door[facing=north,half=upper,hinge=left,open=false,powered=false] - minecraft:waxed_copper_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_copper_door[facing=north,half=upper,hinge=right,open=false,powered=false] - minecraft:waxed_copper_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_copper_door[facing=north,half=lower,hinge=left,open=true,powered=false] - minecraft:waxed_copper_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_copper_door[facing=north,half=lower,hinge=right,open=true,powered=false] - minecraft:waxed_copper_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_copper_door[facing=north,half=upper,hinge=left,open=true,powered=false] - minecraft:waxed_copper_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_copper_door[facing=north,half=upper,hinge=right,open=true,powered=false] - minecraft:waxed_copper_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_copper_door[facing=south,half=lower,hinge=left,open=false,powered=false] - minecraft:waxed_copper_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_copper_door[facing=south,half=lower,hinge=right,open=false,powered=false] - minecraft:waxed_copper_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_copper_door[facing=south,half=upper,hinge=left,open=false,powered=false] - minecraft:waxed_copper_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_copper_door[facing=south,half=upper,hinge=right,open=false,powered=false] - minecraft:waxed_copper_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_copper_door[facing=south,half=lower,hinge=left,open=true,powered=false] - minecraft:waxed_copper_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_copper_door[facing=south,half=lower,hinge=right,open=true,powered=false] - minecraft:waxed_copper_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_copper_door[facing=south,half=upper,hinge=left,open=true,powered=false] - minecraft:waxed_copper_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_copper_door[facing=south,half=upper,hinge=right,open=true,powered=false] - minecraft:waxed_copper_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_copper_door[facing=west,half=lower,hinge=left,open=false,powered=false] - minecraft:waxed_copper_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_copper_door[facing=west,half=lower,hinge=right,open=false,powered=false] - minecraft:waxed_copper_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_copper_door[facing=west,half=upper,hinge=left,open=false,powered=false] - minecraft:waxed_copper_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_copper_door[facing=west,half=upper,hinge=right,open=false,powered=false] - minecraft:waxed_copper_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_copper_door[facing=west,half=lower,hinge=left,open=true,powered=false] - minecraft:waxed_copper_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_copper_door[facing=west,half=lower,hinge=right,open=true,powered=false] - minecraft:waxed_copper_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_copper_door[facing=west,half=upper,hinge=left,open=true,powered=false] - minecraft:waxed_copper_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_copper_door[facing=west,half=upper,hinge=right,open=true,powered=false] - minecraft:waxed_exposed_copper_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=east,half=lower,hinge=left,open=false,powered=false] - minecraft:waxed_exposed_copper_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=east,half=lower,hinge=right,open=false,powered=false] - minecraft:waxed_exposed_copper_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=east,half=upper,hinge=left,open=false,powered=false] - minecraft:waxed_exposed_copper_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=east,half=upper,hinge=right,open=false,powered=false] - minecraft:waxed_exposed_copper_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=east,half=lower,hinge=left,open=true,powered=false] - minecraft:waxed_exposed_copper_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=east,half=lower,hinge=right,open=true,powered=false] - minecraft:waxed_exposed_copper_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=east,half=upper,hinge=left,open=true,powered=false] - minecraft:waxed_exposed_copper_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=east,half=upper,hinge=right,open=true,powered=false] - minecraft:waxed_exposed_copper_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=north,half=lower,hinge=left,open=false,powered=false] - minecraft:waxed_exposed_copper_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=north,half=lower,hinge=right,open=false,powered=false] - minecraft:waxed_exposed_copper_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=north,half=upper,hinge=left,open=false,powered=false] - minecraft:waxed_exposed_copper_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=north,half=upper,hinge=right,open=false,powered=false] - minecraft:waxed_exposed_copper_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=north,half=lower,hinge=left,open=true,powered=false] - minecraft:waxed_exposed_copper_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=north,half=lower,hinge=right,open=true,powered=false] - minecraft:waxed_exposed_copper_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=north,half=upper,hinge=left,open=true,powered=false] - minecraft:waxed_exposed_copper_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=north,half=upper,hinge=right,open=true,powered=false] - minecraft:waxed_exposed_copper_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=south,half=lower,hinge=left,open=false,powered=false] - minecraft:waxed_exposed_copper_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=south,half=lower,hinge=right,open=false,powered=false] - minecraft:waxed_exposed_copper_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=south,half=upper,hinge=left,open=false,powered=false] - minecraft:waxed_exposed_copper_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=south,half=upper,hinge=right,open=false,powered=false] - minecraft:waxed_exposed_copper_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=south,half=lower,hinge=left,open=true,powered=false] - minecraft:waxed_exposed_copper_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=south,half=lower,hinge=right,open=true,powered=false] - minecraft:waxed_exposed_copper_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=south,half=upper,hinge=left,open=true,powered=false] - minecraft:waxed_exposed_copper_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=south,half=upper,hinge=right,open=true,powered=false] - minecraft:waxed_exposed_copper_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=west,half=lower,hinge=left,open=false,powered=false] - minecraft:waxed_exposed_copper_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=west,half=lower,hinge=right,open=false,powered=false] - minecraft:waxed_exposed_copper_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=west,half=upper,hinge=left,open=false,powered=false] - minecraft:waxed_exposed_copper_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=west,half=upper,hinge=right,open=false,powered=false] - minecraft:waxed_exposed_copper_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=west,half=lower,hinge=left,open=true,powered=false] - minecraft:waxed_exposed_copper_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=west,half=lower,hinge=right,open=true,powered=false] - minecraft:waxed_exposed_copper_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=west,half=upper,hinge=left,open=true,powered=false] - minecraft:waxed_exposed_copper_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=west,half=upper,hinge=right,open=true,powered=false] - minecraft:waxed_weathered_copper_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=east,half=lower,hinge=left,open=false,powered=false] - minecraft:waxed_weathered_copper_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=east,half=lower,hinge=right,open=false,powered=false] - minecraft:waxed_weathered_copper_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=east,half=upper,hinge=left,open=false,powered=false] - minecraft:waxed_weathered_copper_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=east,half=upper,hinge=right,open=false,powered=false] - minecraft:waxed_weathered_copper_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=east,half=lower,hinge=left,open=true,powered=false] - minecraft:waxed_weathered_copper_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=east,half=lower,hinge=right,open=true,powered=false] - minecraft:waxed_weathered_copper_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=east,half=upper,hinge=left,open=true,powered=false] - minecraft:waxed_weathered_copper_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=east,half=upper,hinge=right,open=true,powered=false] - minecraft:waxed_weathered_copper_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=north,half=lower,hinge=left,open=false,powered=false] - minecraft:waxed_weathered_copper_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=north,half=lower,hinge=right,open=false,powered=false] - minecraft:waxed_weathered_copper_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=north,half=upper,hinge=left,open=false,powered=false] - minecraft:waxed_weathered_copper_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=north,half=upper,hinge=right,open=false,powered=false] - minecraft:waxed_weathered_copper_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=north,half=lower,hinge=left,open=true,powered=false] - minecraft:waxed_weathered_copper_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=north,half=lower,hinge=right,open=true,powered=false] - minecraft:waxed_weathered_copper_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=north,half=upper,hinge=left,open=true,powered=false] - minecraft:waxed_weathered_copper_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=north,half=upper,hinge=right,open=true,powered=false] - minecraft:waxed_weathered_copper_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=south,half=lower,hinge=left,open=false,powered=false] - minecraft:waxed_weathered_copper_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=south,half=lower,hinge=right,open=false,powered=false] - minecraft:waxed_weathered_copper_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=south,half=upper,hinge=left,open=false,powered=false] - minecraft:waxed_weathered_copper_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=south,half=upper,hinge=right,open=false,powered=false] - minecraft:waxed_weathered_copper_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=south,half=lower,hinge=left,open=true,powered=false] - minecraft:waxed_weathered_copper_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=south,half=lower,hinge=right,open=true,powered=false] - minecraft:waxed_weathered_copper_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=south,half=upper,hinge=left,open=true,powered=false] - minecraft:waxed_weathered_copper_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=south,half=upper,hinge=right,open=true,powered=false] - minecraft:waxed_weathered_copper_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=west,half=lower,hinge=left,open=false,powered=false] - minecraft:waxed_weathered_copper_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=west,half=lower,hinge=right,open=false,powered=false] - minecraft:waxed_weathered_copper_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=west,half=upper,hinge=left,open=false,powered=false] - minecraft:waxed_weathered_copper_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=west,half=upper,hinge=right,open=false,powered=false] - minecraft:waxed_weathered_copper_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=west,half=lower,hinge=left,open=true,powered=false] - minecraft:waxed_weathered_copper_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=west,half=lower,hinge=right,open=true,powered=false] - minecraft:waxed_weathered_copper_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=west,half=upper,hinge=left,open=true,powered=false] - minecraft:waxed_weathered_copper_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=west,half=upper,hinge=right,open=true,powered=false] - minecraft:waxed_oxidized_copper_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=east,half=lower,hinge=left,open=false,powered=false] - minecraft:waxed_oxidized_copper_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=east,half=lower,hinge=right,open=false,powered=false] - minecraft:waxed_oxidized_copper_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=east,half=upper,hinge=left,open=false,powered=false] - minecraft:waxed_oxidized_copper_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=east,half=upper,hinge=right,open=false,powered=false] - minecraft:waxed_oxidized_copper_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=east,half=lower,hinge=left,open=true,powered=false] - minecraft:waxed_oxidized_copper_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=east,half=lower,hinge=right,open=true,powered=false] - minecraft:waxed_oxidized_copper_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=east,half=upper,hinge=left,open=true,powered=false] - minecraft:waxed_oxidized_copper_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=east,half=upper,hinge=right,open=true,powered=false] - minecraft:waxed_oxidized_copper_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=north,half=lower,hinge=left,open=false,powered=false] - minecraft:waxed_oxidized_copper_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=north,half=lower,hinge=right,open=false,powered=false] - minecraft:waxed_oxidized_copper_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=north,half=upper,hinge=left,open=false,powered=false] - minecraft:waxed_oxidized_copper_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=north,half=upper,hinge=right,open=false,powered=false] - minecraft:waxed_oxidized_copper_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=north,half=lower,hinge=left,open=true,powered=false] - minecraft:waxed_oxidized_copper_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=north,half=lower,hinge=right,open=true,powered=false] - minecraft:waxed_oxidized_copper_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=north,half=upper,hinge=left,open=true,powered=false] - minecraft:waxed_oxidized_copper_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=north,half=upper,hinge=right,open=true,powered=false] - minecraft:waxed_oxidized_copper_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=south,half=lower,hinge=left,open=false,powered=false] - minecraft:waxed_oxidized_copper_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=south,half=lower,hinge=right,open=false,powered=false] - minecraft:waxed_oxidized_copper_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=south,half=upper,hinge=left,open=false,powered=false] - minecraft:waxed_oxidized_copper_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=south,half=upper,hinge=right,open=false,powered=false] - minecraft:waxed_oxidized_copper_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=south,half=lower,hinge=left,open=true,powered=false] - minecraft:waxed_oxidized_copper_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=south,half=lower,hinge=right,open=true,powered=false] - minecraft:waxed_oxidized_copper_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=south,half=upper,hinge=left,open=true,powered=false] - minecraft:waxed_oxidized_copper_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=south,half=upper,hinge=right,open=true,powered=false] - minecraft:waxed_oxidized_copper_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=west,half=lower,hinge=left,open=false,powered=false] - minecraft:waxed_oxidized_copper_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=west,half=lower,hinge=right,open=false,powered=false] - minecraft:waxed_oxidized_copper_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=west,half=upper,hinge=left,open=false,powered=false] - minecraft:waxed_oxidized_copper_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=west,half=upper,hinge=right,open=false,powered=false] - minecraft:waxed_oxidized_copper_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=west,half=lower,hinge=left,open=true,powered=false] - minecraft:waxed_oxidized_copper_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=west,half=lower,hinge=right,open=true,powered=false] - minecraft:waxed_oxidized_copper_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=west,half=upper,hinge=left,open=true,powered=false] - minecraft:waxed_oxidized_copper_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=west,half=upper,hinge=right,open=true,powered=false] - -$$>=1.21.4#door: - minecraft:pale_oak_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:pale_oak_door[facing=east,half=lower,hinge=left,open=false,powered=false] - minecraft:pale_oak_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:pale_oak_door[facing=east,half=lower,hinge=right,open=false,powered=false] - minecraft:pale_oak_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:pale_oak_door[facing=east,half=upper,hinge=left,open=false,powered=false] - minecraft:pale_oak_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:pale_oak_door[facing=east,half=upper,hinge=right,open=false,powered=false] - minecraft:pale_oak_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:pale_oak_door[facing=east,half=lower,hinge=left,open=true,powered=false] - minecraft:pale_oak_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:pale_oak_door[facing=east,half=lower,hinge=right,open=true,powered=false] - minecraft:pale_oak_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:pale_oak_door[facing=east,half=upper,hinge=left,open=true,powered=false] - minecraft:pale_oak_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:pale_oak_door[facing=east,half=upper,hinge=right,open=true,powered=false] - minecraft:pale_oak_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:pale_oak_door[facing=north,half=lower,hinge=left,open=false,powered=false] - minecraft:pale_oak_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:pale_oak_door[facing=north,half=lower,hinge=right,open=false,powered=false] - minecraft:pale_oak_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:pale_oak_door[facing=north,half=upper,hinge=left,open=false,powered=false] - minecraft:pale_oak_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:pale_oak_door[facing=north,half=upper,hinge=right,open=false,powered=false] - minecraft:pale_oak_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:pale_oak_door[facing=north,half=lower,hinge=left,open=true,powered=false] - minecraft:pale_oak_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:pale_oak_door[facing=north,half=lower,hinge=right,open=true,powered=false] - minecraft:pale_oak_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:pale_oak_door[facing=north,half=upper,hinge=left,open=true,powered=false] - minecraft:pale_oak_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:pale_oak_door[facing=north,half=upper,hinge=right,open=true,powered=false] - minecraft:pale_oak_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:pale_oak_door[facing=south,half=lower,hinge=left,open=false,powered=false] - minecraft:pale_oak_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:pale_oak_door[facing=south,half=lower,hinge=right,open=false,powered=false] - minecraft:pale_oak_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:pale_oak_door[facing=south,half=upper,hinge=left,open=false,powered=false] - minecraft:pale_oak_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:pale_oak_door[facing=south,half=upper,hinge=right,open=false,powered=false] - minecraft:pale_oak_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:pale_oak_door[facing=south,half=lower,hinge=left,open=true,powered=false] - minecraft:pale_oak_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:pale_oak_door[facing=south,half=lower,hinge=right,open=true,powered=false] - minecraft:pale_oak_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:pale_oak_door[facing=south,half=upper,hinge=left,open=true,powered=false] - minecraft:pale_oak_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:pale_oak_door[facing=south,half=upper,hinge=right,open=true,powered=false] - minecraft:pale_oak_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:pale_oak_door[facing=west,half=lower,hinge=left,open=false,powered=false] - minecraft:pale_oak_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:pale_oak_door[facing=west,half=lower,hinge=right,open=false,powered=false] - minecraft:pale_oak_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:pale_oak_door[facing=west,half=upper,hinge=left,open=false,powered=false] - minecraft:pale_oak_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:pale_oak_door[facing=west,half=upper,hinge=right,open=false,powered=false] - minecraft:pale_oak_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:pale_oak_door[facing=west,half=lower,hinge=left,open=true,powered=false] - minecraft:pale_oak_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:pale_oak_door[facing=west,half=lower,hinge=right,open=true,powered=false] - minecraft:pale_oak_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:pale_oak_door[facing=west,half=upper,hinge=left,open=true,powered=false] - minecraft:pale_oak_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:pale_oak_door[facing=west,half=upper,hinge=right,open=true,powered=false] - -#### Fence Gate #### -minecraft:oak_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:oak_fence_gate[facing=east,in_wall=false,open=false,powered=false] -minecraft:oak_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:oak_fence_gate[facing=east,in_wall=false,open=true,powered=false] -minecraft:oak_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:oak_fence_gate[facing=east,in_wall=true,open=false,powered=false] -minecraft:oak_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:oak_fence_gate[facing=east,in_wall=true,open=true,powered=false] -minecraft:oak_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:oak_fence_gate[facing=south,in_wall=false,open=false,powered=false] -minecraft:oak_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:oak_fence_gate[facing=south,in_wall=false,open=true,powered=false] -minecraft:oak_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:oak_fence_gate[facing=south,in_wall=true,open=false,powered=false] -minecraft:oak_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:oak_fence_gate[facing=south,in_wall=true,open=true,powered=false] -minecraft:oak_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:oak_fence_gate[facing=west,in_wall=false,open=false,powered=false] -minecraft:oak_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:oak_fence_gate[facing=west,in_wall=false,open=true,powered=false] -minecraft:oak_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:oak_fence_gate[facing=west,in_wall=true,open=false,powered=false] -minecraft:oak_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:oak_fence_gate[facing=west,in_wall=true,open=true,powered=false] -minecraft:oak_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:oak_fence_gate[facing=north,in_wall=false,open=false,powered=false] -minecraft:oak_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:oak_fence_gate[facing=north,in_wall=false,open=true,powered=false] -minecraft:oak_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:oak_fence_gate[facing=north,in_wall=true,open=false,powered=false] -minecraft:oak_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:oak_fence_gate[facing=north,in_wall=true,open=true,powered=false] -minecraft:spruce_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:spruce_fence_gate[facing=east,in_wall=false,open=false,powered=false] -minecraft:spruce_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:spruce_fence_gate[facing=east,in_wall=false,open=true,powered=false] -minecraft:spruce_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:spruce_fence_gate[facing=east,in_wall=true,open=false,powered=false] -minecraft:spruce_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:spruce_fence_gate[facing=east,in_wall=true,open=true,powered=false] -minecraft:spruce_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:spruce_fence_gate[facing=south,in_wall=false,open=false,powered=false] -minecraft:spruce_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:spruce_fence_gate[facing=south,in_wall=false,open=true,powered=false] -minecraft:spruce_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:spruce_fence_gate[facing=south,in_wall=true,open=false,powered=false] -minecraft:spruce_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:spruce_fence_gate[facing=south,in_wall=true,open=true,powered=false] -minecraft:spruce_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:spruce_fence_gate[facing=west,in_wall=false,open=false,powered=false] -minecraft:spruce_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:spruce_fence_gate[facing=west,in_wall=false,open=true,powered=false] -minecraft:spruce_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:spruce_fence_gate[facing=west,in_wall=true,open=false,powered=false] -minecraft:spruce_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:spruce_fence_gate[facing=west,in_wall=true,open=true,powered=false] -minecraft:spruce_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:spruce_fence_gate[facing=north,in_wall=false,open=false,powered=false] -minecraft:spruce_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:spruce_fence_gate[facing=north,in_wall=false,open=true,powered=false] -minecraft:spruce_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:spruce_fence_gate[facing=north,in_wall=true,open=false,powered=false] -minecraft:spruce_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:spruce_fence_gate[facing=north,in_wall=true,open=true,powered=false] -minecraft:birch_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:birch_fence_gate[facing=east,in_wall=false,open=false,powered=false] -minecraft:birch_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:birch_fence_gate[facing=east,in_wall=false,open=true,powered=false] -minecraft:birch_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:birch_fence_gate[facing=east,in_wall=true,open=false,powered=false] -minecraft:birch_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:birch_fence_gate[facing=east,in_wall=true,open=true,powered=false] -minecraft:birch_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:birch_fence_gate[facing=south,in_wall=false,open=false,powered=false] -minecraft:birch_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:birch_fence_gate[facing=south,in_wall=false,open=true,powered=false] -minecraft:birch_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:birch_fence_gate[facing=south,in_wall=true,open=false,powered=false] -minecraft:birch_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:birch_fence_gate[facing=south,in_wall=true,open=true,powered=false] -minecraft:birch_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:birch_fence_gate[facing=west,in_wall=false,open=false,powered=false] -minecraft:birch_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:birch_fence_gate[facing=west,in_wall=false,open=true,powered=false] -minecraft:birch_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:birch_fence_gate[facing=west,in_wall=true,open=false,powered=false] -minecraft:birch_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:birch_fence_gate[facing=west,in_wall=true,open=true,powered=false] -minecraft:birch_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:birch_fence_gate[facing=north,in_wall=false,open=false,powered=false] -minecraft:birch_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:birch_fence_gate[facing=north,in_wall=false,open=true,powered=false] -minecraft:birch_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:birch_fence_gate[facing=north,in_wall=true,open=false,powered=false] -minecraft:birch_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:birch_fence_gate[facing=north,in_wall=true,open=true,powered=false] -minecraft:jungle_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:jungle_fence_gate[facing=east,in_wall=false,open=false,powered=false] -minecraft:jungle_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:jungle_fence_gate[facing=east,in_wall=false,open=true,powered=false] -minecraft:jungle_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:jungle_fence_gate[facing=east,in_wall=true,open=false,powered=false] -minecraft:jungle_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:jungle_fence_gate[facing=east,in_wall=true,open=true,powered=false] -minecraft:jungle_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:jungle_fence_gate[facing=south,in_wall=false,open=false,powered=false] -minecraft:jungle_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:jungle_fence_gate[facing=south,in_wall=false,open=true,powered=false] -minecraft:jungle_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:jungle_fence_gate[facing=south,in_wall=true,open=false,powered=false] -minecraft:jungle_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:jungle_fence_gate[facing=south,in_wall=true,open=true,powered=false] -minecraft:jungle_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:jungle_fence_gate[facing=west,in_wall=false,open=false,powered=false] -minecraft:jungle_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:jungle_fence_gate[facing=west,in_wall=false,open=true,powered=false] -minecraft:jungle_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:jungle_fence_gate[facing=west,in_wall=true,open=false,powered=false] -minecraft:jungle_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:jungle_fence_gate[facing=west,in_wall=true,open=true,powered=false] -minecraft:jungle_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:jungle_fence_gate[facing=north,in_wall=false,open=false,powered=false] -minecraft:jungle_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:jungle_fence_gate[facing=north,in_wall=false,open=true,powered=false] -minecraft:jungle_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:jungle_fence_gate[facing=north,in_wall=true,open=false,powered=false] -minecraft:jungle_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:jungle_fence_gate[facing=north,in_wall=true,open=true,powered=false] -minecraft:acacia_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:acacia_fence_gate[facing=east,in_wall=false,open=false,powered=false] -minecraft:acacia_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:acacia_fence_gate[facing=east,in_wall=false,open=true,powered=false] -minecraft:acacia_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:acacia_fence_gate[facing=east,in_wall=true,open=false,powered=false] -minecraft:acacia_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:acacia_fence_gate[facing=east,in_wall=true,open=true,powered=false] -minecraft:acacia_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:acacia_fence_gate[facing=south,in_wall=false,open=false,powered=false] -minecraft:acacia_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:acacia_fence_gate[facing=south,in_wall=false,open=true,powered=false] -minecraft:acacia_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:acacia_fence_gate[facing=south,in_wall=true,open=false,powered=false] -minecraft:acacia_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:acacia_fence_gate[facing=south,in_wall=true,open=true,powered=false] -minecraft:acacia_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:acacia_fence_gate[facing=west,in_wall=false,open=false,powered=false] -minecraft:acacia_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:acacia_fence_gate[facing=west,in_wall=false,open=true,powered=false] -minecraft:acacia_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:acacia_fence_gate[facing=west,in_wall=true,open=false,powered=false] -minecraft:acacia_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:acacia_fence_gate[facing=west,in_wall=true,open=true,powered=false] -minecraft:acacia_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:acacia_fence_gate[facing=north,in_wall=false,open=false,powered=false] -minecraft:acacia_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:acacia_fence_gate[facing=north,in_wall=false,open=true,powered=false] -minecraft:acacia_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:acacia_fence_gate[facing=north,in_wall=true,open=false,powered=false] -minecraft:acacia_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:acacia_fence_gate[facing=north,in_wall=true,open=true,powered=false] -minecraft:dark_oak_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:dark_oak_fence_gate[facing=east,in_wall=false,open=false,powered=false] -minecraft:dark_oak_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:dark_oak_fence_gate[facing=east,in_wall=false,open=true,powered=false] -minecraft:dark_oak_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:dark_oak_fence_gate[facing=east,in_wall=true,open=false,powered=false] -minecraft:dark_oak_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:dark_oak_fence_gate[facing=east,in_wall=true,open=true,powered=false] -minecraft:dark_oak_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:dark_oak_fence_gate[facing=south,in_wall=false,open=false,powered=false] -minecraft:dark_oak_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:dark_oak_fence_gate[facing=south,in_wall=false,open=true,powered=false] -minecraft:dark_oak_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:dark_oak_fence_gate[facing=south,in_wall=true,open=false,powered=false] -minecraft:dark_oak_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:dark_oak_fence_gate[facing=south,in_wall=true,open=true,powered=false] -minecraft:dark_oak_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:dark_oak_fence_gate[facing=west,in_wall=false,open=false,powered=false] -minecraft:dark_oak_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:dark_oak_fence_gate[facing=west,in_wall=false,open=true,powered=false] -minecraft:dark_oak_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:dark_oak_fence_gate[facing=west,in_wall=true,open=false,powered=false] -minecraft:dark_oak_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:dark_oak_fence_gate[facing=west,in_wall=true,open=true,powered=false] -minecraft:dark_oak_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:dark_oak_fence_gate[facing=north,in_wall=false,open=false,powered=false] -minecraft:dark_oak_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:dark_oak_fence_gate[facing=north,in_wall=false,open=true,powered=false] -minecraft:dark_oak_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:dark_oak_fence_gate[facing=north,in_wall=true,open=false,powered=false] -minecraft:dark_oak_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:dark_oak_fence_gate[facing=north,in_wall=true,open=true,powered=false] -minecraft:mangrove_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:mangrove_fence_gate[facing=east,in_wall=false,open=false,powered=false] -minecraft:mangrove_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:mangrove_fence_gate[facing=east,in_wall=false,open=true,powered=false] -minecraft:mangrove_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:mangrove_fence_gate[facing=east,in_wall=true,open=false,powered=false] -minecraft:mangrove_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:mangrove_fence_gate[facing=east,in_wall=true,open=true,powered=false] -minecraft:mangrove_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:mangrove_fence_gate[facing=south,in_wall=false,open=false,powered=false] -minecraft:mangrove_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:mangrove_fence_gate[facing=south,in_wall=false,open=true,powered=false] -minecraft:mangrove_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:mangrove_fence_gate[facing=south,in_wall=true,open=false,powered=false] -minecraft:mangrove_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:mangrove_fence_gate[facing=south,in_wall=true,open=true,powered=false] -minecraft:mangrove_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:mangrove_fence_gate[facing=west,in_wall=false,open=false,powered=false] -minecraft:mangrove_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:mangrove_fence_gate[facing=west,in_wall=false,open=true,powered=false] -minecraft:mangrove_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:mangrove_fence_gate[facing=west,in_wall=true,open=false,powered=false] -minecraft:mangrove_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:mangrove_fence_gate[facing=west,in_wall=true,open=true,powered=false] -minecraft:mangrove_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:mangrove_fence_gate[facing=north,in_wall=false,open=false,powered=false] -minecraft:mangrove_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:mangrove_fence_gate[facing=north,in_wall=false,open=true,powered=false] -minecraft:mangrove_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:mangrove_fence_gate[facing=north,in_wall=true,open=false,powered=false] -minecraft:mangrove_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:mangrove_fence_gate[facing=north,in_wall=true,open=true,powered=false] -minecraft:cherry_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:cherry_fence_gate[facing=east,in_wall=false,open=false,powered=false] -minecraft:cherry_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:cherry_fence_gate[facing=east,in_wall=false,open=true,powered=false] -minecraft:cherry_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:cherry_fence_gate[facing=east,in_wall=true,open=false,powered=false] -minecraft:cherry_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:cherry_fence_gate[facing=east,in_wall=true,open=true,powered=false] -minecraft:cherry_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:cherry_fence_gate[facing=south,in_wall=false,open=false,powered=false] -minecraft:cherry_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:cherry_fence_gate[facing=south,in_wall=false,open=true,powered=false] -minecraft:cherry_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:cherry_fence_gate[facing=south,in_wall=true,open=false,powered=false] -minecraft:cherry_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:cherry_fence_gate[facing=south,in_wall=true,open=true,powered=false] -minecraft:cherry_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:cherry_fence_gate[facing=west,in_wall=false,open=false,powered=false] -minecraft:cherry_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:cherry_fence_gate[facing=west,in_wall=false,open=true,powered=false] -minecraft:cherry_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:cherry_fence_gate[facing=west,in_wall=true,open=false,powered=false] -minecraft:cherry_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:cherry_fence_gate[facing=west,in_wall=true,open=true,powered=false] -minecraft:cherry_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:cherry_fence_gate[facing=north,in_wall=false,open=false,powered=false] -minecraft:cherry_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:cherry_fence_gate[facing=north,in_wall=false,open=true,powered=false] -minecraft:cherry_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:cherry_fence_gate[facing=north,in_wall=true,open=false,powered=false] -minecraft:cherry_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:cherry_fence_gate[facing=north,in_wall=true,open=true,powered=false] -minecraft:bamboo_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:bamboo_fence_gate[facing=east,in_wall=false,open=false,powered=false] -minecraft:bamboo_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:bamboo_fence_gate[facing=east,in_wall=false,open=true,powered=false] -minecraft:bamboo_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:bamboo_fence_gate[facing=east,in_wall=true,open=false,powered=false] -minecraft:bamboo_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:bamboo_fence_gate[facing=east,in_wall=true,open=true,powered=false] -minecraft:bamboo_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:bamboo_fence_gate[facing=south,in_wall=false,open=false,powered=false] -minecraft:bamboo_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:bamboo_fence_gate[facing=south,in_wall=false,open=true,powered=false] -minecraft:bamboo_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:bamboo_fence_gate[facing=south,in_wall=true,open=false,powered=false] -minecraft:bamboo_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:bamboo_fence_gate[facing=south,in_wall=true,open=true,powered=false] -minecraft:bamboo_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:bamboo_fence_gate[facing=west,in_wall=false,open=false,powered=false] -minecraft:bamboo_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:bamboo_fence_gate[facing=west,in_wall=false,open=true,powered=false] -minecraft:bamboo_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:bamboo_fence_gate[facing=west,in_wall=true,open=false,powered=false] -minecraft:bamboo_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:bamboo_fence_gate[facing=west,in_wall=true,open=true,powered=false] -minecraft:bamboo_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:bamboo_fence_gate[facing=north,in_wall=false,open=false,powered=false] -minecraft:bamboo_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:bamboo_fence_gate[facing=north,in_wall=false,open=true,powered=false] -minecraft:bamboo_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:bamboo_fence_gate[facing=north,in_wall=true,open=false,powered=false] -minecraft:bamboo_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:bamboo_fence_gate[facing=north,in_wall=true,open=true,powered=false] -minecraft:crimson_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:crimson_fence_gate[facing=east,in_wall=false,open=false,powered=false] -minecraft:crimson_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:crimson_fence_gate[facing=east,in_wall=false,open=true,powered=false] -minecraft:crimson_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:crimson_fence_gate[facing=east,in_wall=true,open=false,powered=false] -minecraft:crimson_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:crimson_fence_gate[facing=east,in_wall=true,open=true,powered=false] -minecraft:crimson_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:crimson_fence_gate[facing=south,in_wall=false,open=false,powered=false] -minecraft:crimson_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:crimson_fence_gate[facing=south,in_wall=false,open=true,powered=false] -minecraft:crimson_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:crimson_fence_gate[facing=south,in_wall=true,open=false,powered=false] -minecraft:crimson_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:crimson_fence_gate[facing=south,in_wall=true,open=true,powered=false] -minecraft:crimson_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:crimson_fence_gate[facing=west,in_wall=false,open=false,powered=false] -minecraft:crimson_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:crimson_fence_gate[facing=west,in_wall=false,open=true,powered=false] -minecraft:crimson_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:crimson_fence_gate[facing=west,in_wall=true,open=false,powered=false] -minecraft:crimson_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:crimson_fence_gate[facing=west,in_wall=true,open=true,powered=false] -minecraft:crimson_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:crimson_fence_gate[facing=north,in_wall=false,open=false,powered=false] -minecraft:crimson_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:crimson_fence_gate[facing=north,in_wall=false,open=true,powered=false] -minecraft:crimson_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:crimson_fence_gate[facing=north,in_wall=true,open=false,powered=false] -minecraft:crimson_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:crimson_fence_gate[facing=north,in_wall=true,open=true,powered=false] -minecraft:warped_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:warped_fence_gate[facing=east,in_wall=false,open=false,powered=false] -minecraft:warped_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:warped_fence_gate[facing=east,in_wall=false,open=true,powered=false] -minecraft:warped_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:warped_fence_gate[facing=east,in_wall=true,open=false,powered=false] -minecraft:warped_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:warped_fence_gate[facing=east,in_wall=true,open=true,powered=false] -minecraft:warped_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:warped_fence_gate[facing=south,in_wall=false,open=false,powered=false] -minecraft:warped_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:warped_fence_gate[facing=south,in_wall=false,open=true,powered=false] -minecraft:warped_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:warped_fence_gate[facing=south,in_wall=true,open=false,powered=false] -minecraft:warped_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:warped_fence_gate[facing=south,in_wall=true,open=true,powered=false] -minecraft:warped_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:warped_fence_gate[facing=west,in_wall=false,open=false,powered=false] -minecraft:warped_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:warped_fence_gate[facing=west,in_wall=false,open=true,powered=false] -minecraft:warped_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:warped_fence_gate[facing=west,in_wall=true,open=false,powered=false] -minecraft:warped_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:warped_fence_gate[facing=west,in_wall=true,open=true,powered=false] -minecraft:warped_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:warped_fence_gate[facing=north,in_wall=false,open=false,powered=false] -minecraft:warped_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:warped_fence_gate[facing=north,in_wall=false,open=true,powered=false] -minecraft:warped_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:warped_fence_gate[facing=north,in_wall=true,open=false,powered=false] -minecraft:warped_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:warped_fence_gate[facing=north,in_wall=true,open=true,powered=false] - -$$>=1.21.4#fence_gate: - minecraft:pale_oak_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:pale_oak_fence_gate[facing=east,in_wall=false,open=false,powered=false] - minecraft:pale_oak_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:pale_oak_fence_gate[facing=east,in_wall=false,open=true,powered=false] - minecraft:pale_oak_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:pale_oak_fence_gate[facing=east,in_wall=true,open=false,powered=false] - minecraft:pale_oak_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:pale_oak_fence_gate[facing=east,in_wall=true,open=true,powered=false] - minecraft:pale_oak_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:pale_oak_fence_gate[facing=south,in_wall=false,open=false,powered=false] - minecraft:pale_oak_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:pale_oak_fence_gate[facing=south,in_wall=false,open=true,powered=false] - minecraft:pale_oak_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:pale_oak_fence_gate[facing=south,in_wall=true,open=false,powered=false] - minecraft:pale_oak_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:pale_oak_fence_gate[facing=south,in_wall=true,open=true,powered=false] - minecraft:pale_oak_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:pale_oak_fence_gate[facing=west,in_wall=false,open=false,powered=false] - minecraft:pale_oak_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:pale_oak_fence_gate[facing=west,in_wall=false,open=true,powered=false] - minecraft:pale_oak_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:pale_oak_fence_gate[facing=west,in_wall=true,open=false,powered=false] - minecraft:pale_oak_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:pale_oak_fence_gate[facing=west,in_wall=true,open=true,powered=false] - minecraft:pale_oak_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:pale_oak_fence_gate[facing=north,in_wall=false,open=false,powered=false] - minecraft:pale_oak_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:pale_oak_fence_gate[facing=north,in_wall=false,open=true,powered=false] - minecraft:pale_oak_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:pale_oak_fence_gate[facing=north,in_wall=true,open=false,powered=false] - minecraft:pale_oak_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:pale_oak_fence_gate[facing=north,in_wall=true,open=true,powered=false] - -#### Slab #### -minecraft:petrified_oak_slab[type=bottom,waterlogged=false]: minecraft:oak_slab[type=bottom,waterlogged=false] -minecraft:petrified_oak_slab[type=top,waterlogged=false]: minecraft:oak_slab[type=top,waterlogged=false] -minecraft:petrified_oak_slab[type=double,waterlogged=false]: minecraft:oak_slab[type=double,waterlogged=false] -minecraft:petrified_oak_slab[type=bottom,waterlogged=true]: minecraft:oak_slab[type=bottom,waterlogged=true] -minecraft:petrified_oak_slab[type=top,waterlogged=true]: minecraft:oak_slab[type=top,waterlogged=true] -minecraft:petrified_oak_slab[type=double,waterlogged=true]: minecraft:oak_slab[type=double,waterlogged=true] -minecraft:cut_copper_slab[type=bottom,waterlogged=false]: minecraft:waxed_cut_copper_slab[type=bottom,waterlogged=false] -minecraft:cut_copper_slab[type=top,waterlogged=false]: minecraft:waxed_cut_copper_slab[type=top,waterlogged=false] -minecraft:cut_copper_slab[type=double,waterlogged=false]: minecraft:waxed_cut_copper_slab[type=double,waterlogged=false] -minecraft:cut_copper_slab[type=bottom,waterlogged=true]: minecraft:waxed_cut_copper_slab[type=bottom,waterlogged=true] -minecraft:cut_copper_slab[type=top,waterlogged=true]: minecraft:waxed_cut_copper_slab[type=top,waterlogged=true] -minecraft:cut_copper_slab[type=double,waterlogged=true]: minecraft:waxed_cut_copper_slab[type=double,waterlogged=true] -minecraft:exposed_cut_copper_slab[type=bottom,waterlogged=false]: minecraft:waxed_exposed_cut_copper_slab[type=bottom,waterlogged=false] -minecraft:exposed_cut_copper_slab[type=top,waterlogged=false]: minecraft:waxed_exposed_cut_copper_slab[type=top,waterlogged=false] -minecraft:exposed_cut_copper_slab[type=double,waterlogged=false]: minecraft:waxed_exposed_cut_copper_slab[type=double,waterlogged=false] -minecraft:exposed_cut_copper_slab[type=bottom,waterlogged=true]: minecraft:waxed_exposed_cut_copper_slab[type=bottom,waterlogged=true] -minecraft:exposed_cut_copper_slab[type=top,waterlogged=true]: minecraft:waxed_exposed_cut_copper_slab[type=top,waterlogged=true] -minecraft:exposed_cut_copper_slab[type=double,waterlogged=true]: minecraft:waxed_exposed_cut_copper_slab[type=double,waterlogged=true] -minecraft:weathered_cut_copper_slab[type=bottom,waterlogged=false]: minecraft:waxed_weathered_cut_copper_slab[type=bottom,waterlogged=false] -minecraft:weathered_cut_copper_slab[type=top,waterlogged=false]: minecraft:waxed_weathered_cut_copper_slab[type=top,waterlogged=false] -minecraft:weathered_cut_copper_slab[type=double,waterlogged=false]: minecraft:waxed_weathered_cut_copper_slab[type=double,waterlogged=false] -minecraft:weathered_cut_copper_slab[type=bottom,waterlogged=true]: minecraft:waxed_weathered_cut_copper_slab[type=bottom,waterlogged=true] -minecraft:weathered_cut_copper_slab[type=top,waterlogged=true]: minecraft:waxed_weathered_cut_copper_slab[type=top,waterlogged=true] -minecraft:weathered_cut_copper_slab[type=double,waterlogged=true]: minecraft:waxed_weathered_cut_copper_slab[type=double,waterlogged=true] -minecraft:oxidized_cut_copper_slab[type=bottom,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_slab[type=bottom,waterlogged=false] -minecraft:oxidized_cut_copper_slab[type=top,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_slab[type=top,waterlogged=false] -minecraft:oxidized_cut_copper_slab[type=double,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_slab[type=double,waterlogged=false] -minecraft:oxidized_cut_copper_slab[type=bottom,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_slab[type=bottom,waterlogged=true] -minecraft:oxidized_cut_copper_slab[type=top,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_slab[type=top,waterlogged=true] -minecraft:oxidized_cut_copper_slab[type=double,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_slab[type=double,waterlogged=true] - -#### Stairs #### -minecraft:cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=false] -minecraft:cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=true] -minecraft:cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=false] -minecraft:cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=true] -minecraft:cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=false] -minecraft:cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=true] -minecraft:cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=false] -minecraft:cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=true] -minecraft:cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=false] -minecraft:cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=true] -minecraft:cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=false] -minecraft:cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=true] -minecraft:cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=false] -minecraft:cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=true] -minecraft:cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=false] -minecraft:cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=true] -minecraft:cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=false] -minecraft:cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=true] -minecraft:cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=false] -minecraft:cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=true] -minecraft:cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=false] -minecraft:cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=true] -minecraft:cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=false] -minecraft:cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=true] -minecraft:cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=false] -minecraft:cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=true] -minecraft:cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=false] -minecraft:cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=true] -minecraft:cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=false] -minecraft:cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=true] -minecraft:cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=false] -minecraft:cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=true] -minecraft:cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=false] -minecraft:cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=true] -minecraft:cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=false] -minecraft:cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=true] -minecraft:cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=false] -minecraft:cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=true] -minecraft:cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=false] -minecraft:cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=true] -minecraft:cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=false] -minecraft:cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=true] -minecraft:cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=false] -minecraft:cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=true] -minecraft:cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=false] -minecraft:cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=true] -minecraft:cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=false] -minecraft:cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=true] -minecraft:cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=false] -minecraft:cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=true] -minecraft:cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=false] -minecraft:cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=true] -minecraft:cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=false] -minecraft:cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=true] -minecraft:cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=false] -minecraft:cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=true] -minecraft:cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=false] -minecraft:cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=true] -minecraft:cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=false] -minecraft:cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=true] -minecraft:cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=false] -minecraft:cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=true] -minecraft:cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=false] -minecraft:cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=true] -minecraft:cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=false] -minecraft:cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=true] -minecraft:cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=false] -minecraft:cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=true] -minecraft:cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=false] -minecraft:cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=true] -minecraft:cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=false] -minecraft:cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=true] -minecraft:cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=false] -minecraft:cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=true] -minecraft:cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=false] -minecraft:cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=true] -minecraft:cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=false] -minecraft:cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=true] -minecraft:cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=false] -minecraft:cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=true] -minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=false] -minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=true] -minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=false] -minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=true] -minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=false] -minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=true] - -#### Grate #### -# Suitable for making glass because it is completely transparent -$$>=1.20.3#grate: - minecraft:copper_grate[waterlogged=false]: minecraft:waxed_copper_grate[waterlogged=false] - minecraft:copper_grate[waterlogged=true]: minecraft:waxed_copper_grate[waterlogged=true] - minecraft:weathered_copper_grate[waterlogged=false]: minecraft:waxed_weathered_copper_grate[waterlogged=false] - minecraft:weathered_copper_grate[waterlogged=true]: minecraft:waxed_weathered_copper_grate[waterlogged=true] - minecraft:exposed_copper_grate[waterlogged=false]: minecraft:waxed_exposed_copper_grate[waterlogged=false] - minecraft:exposed_copper_grate[waterlogged=true]: minecraft:waxed_exposed_copper_grate[waterlogged=true] - minecraft:oxidized_copper_grate[waterlogged=false]: minecraft:waxed_oxidized_copper_grate[waterlogged=false] - minecraft:oxidized_copper_grate[waterlogged=true]: minecraft:waxed_oxidized_copper_grate[waterlogged=true] - -#### Pressure Plate #### -# Triggered pressure plates appear identical, even though they output different signal strengths. -minecraft:light_weighted_pressure_plate[power=2]: minecraft:light_weighted_pressure_plate[power=1] -minecraft:light_weighted_pressure_plate[power=3]: minecraft:light_weighted_pressure_plate[power=1] -minecraft:light_weighted_pressure_plate[power=4]: minecraft:light_weighted_pressure_plate[power=1] -minecraft:light_weighted_pressure_plate[power=5]: minecraft:light_weighted_pressure_plate[power=1] -minecraft:light_weighted_pressure_plate[power=6]: minecraft:light_weighted_pressure_plate[power=1] -minecraft:light_weighted_pressure_plate[power=7]: minecraft:light_weighted_pressure_plate[power=1] -minecraft:light_weighted_pressure_plate[power=8]: minecraft:light_weighted_pressure_plate[power=1] -minecraft:light_weighted_pressure_plate[power=9]: minecraft:light_weighted_pressure_plate[power=1] -minecraft:light_weighted_pressure_plate[power=10]: minecraft:light_weighted_pressure_plate[power=1] -minecraft:light_weighted_pressure_plate[power=11]: minecraft:light_weighted_pressure_plate[power=1] -minecraft:light_weighted_pressure_plate[power=12]: minecraft:light_weighted_pressure_plate[power=1] -minecraft:light_weighted_pressure_plate[power=13]: minecraft:light_weighted_pressure_plate[power=1] -minecraft:light_weighted_pressure_plate[power=14]: minecraft:light_weighted_pressure_plate[power=1] -minecraft:light_weighted_pressure_plate[power=15]: minecraft:light_weighted_pressure_plate[power=1] -minecraft:heavy_weighted_pressure_plate[power=2]: minecraft:heavy_weighted_pressure_plate[power=1] -minecraft:heavy_weighted_pressure_plate[power=3]: minecraft:heavy_weighted_pressure_plate[power=1] -minecraft:heavy_weighted_pressure_plate[power=4]: minecraft:heavy_weighted_pressure_plate[power=1] -minecraft:heavy_weighted_pressure_plate[power=5]: minecraft:heavy_weighted_pressure_plate[power=1] -minecraft:heavy_weighted_pressure_plate[power=6]: minecraft:heavy_weighted_pressure_plate[power=1] -minecraft:heavy_weighted_pressure_plate[power=7]: minecraft:heavy_weighted_pressure_plate[power=1] -minecraft:heavy_weighted_pressure_plate[power=8]: minecraft:heavy_weighted_pressure_plate[power=1] -minecraft:heavy_weighted_pressure_plate[power=9]: minecraft:heavy_weighted_pressure_plate[power=1] -minecraft:heavy_weighted_pressure_plate[power=10]: minecraft:heavy_weighted_pressure_plate[power=1] -minecraft:heavy_weighted_pressure_plate[power=11]: minecraft:heavy_weighted_pressure_plate[power=1] -minecraft:heavy_weighted_pressure_plate[power=12]: minecraft:heavy_weighted_pressure_plate[power=1] -minecraft:heavy_weighted_pressure_plate[power=13]: minecraft:heavy_weighted_pressure_plate[power=1] -minecraft:heavy_weighted_pressure_plate[power=14]: minecraft:heavy_weighted_pressure_plate[power=1] -minecraft:heavy_weighted_pressure_plate[power=15]: minecraft:heavy_weighted_pressure_plate[power=1] - -#### Corals #### -# Coral blocks are ideal for creating water blocks or wall-mounted blocks. But you have to sacrifice its dry appearance. -# minecraft:dead_brain_coral[waterlogged=false]: minecraft:brain_coral[waterlogged=false] -# minecraft:dead_brain_coral[waterlogged=true]: minecraft:brain_coral[waterlogged=true] -# minecraft:dead_brain_coral_fan[waterlogged=false]: minecraft:brain_coral_fan[waterlogged=false] -# minecraft:dead_brain_coral_fan[waterlogged=true]: minecraft:brain_coral_fan[waterlogged=true] -# minecraft:dead_brain_coral_wall_fan[waterlogged=false,facing=east]: minecraft:brain_coral_wall_fan[waterlogged=false,facing=east] -# minecraft:dead_brain_coral_wall_fan[waterlogged=false,facing=north]: minecraft:brain_coral_wall_fan[waterlogged=false,facing=north] -# minecraft:dead_brain_coral_wall_fan[waterlogged=false,facing=south]: minecraft:brain_coral_wall_fan[waterlogged=false,facing=south] -# minecraft:dead_brain_coral_wall_fan[waterlogged=false,facing=west]: minecraft:brain_coral_wall_fan[waterlogged=false,facing=west] -# minecraft:dead_brain_coral_wall_fan[waterlogged=true,facing=east]: minecraft:brain_coral_wall_fan[waterlogged=true,facing=east] -# minecraft:dead_brain_coral_wall_fan[waterlogged=true,facing=north]: minecraft:brain_coral_wall_fan[waterlogged=true,facing=north] -# minecraft:dead_brain_coral_wall_fan[waterlogged=true,facing=south]: minecraft:brain_coral_wall_fan[waterlogged=true,facing=south] -# minecraft:dead_brain_coral_wall_fan[waterlogged=true,facing=west]: minecraft:brain_coral_wall_fan[waterlogged=true,facing=west] -# minecraft:dead_bubble_coral[waterlogged=false]: minecraft:bubble_coral[waterlogged=false] -# minecraft:dead_bubble_coral[waterlogged=true]: minecraft:bubble_coral[waterlogged=true] -# minecraft:dead_bubble_coral_fan[waterlogged=false]: minecraft:bubble_coral_fan[waterlogged=false] -# minecraft:dead_bubble_coral_fan[waterlogged=true]: minecraft:bubble_coral_fan[waterlogged=true] -# minecraft:dead_bubble_coral_wall_fan[waterlogged=false,facing=east]: minecraft:bubble_coral_wall_fan[waterlogged=false,facing=east] -# minecraft:dead_bubble_coral_wall_fan[waterlogged=false,facing=north]: minecraft:bubble_coral_wall_fan[waterlogged=false,facing=north] -# minecraft:dead_bubble_coral_wall_fan[waterlogged=false,facing=south]: minecraft:bubble_coral_wall_fan[waterlogged=false,facing=south] -# minecraft:dead_bubble_coral_wall_fan[waterlogged=false,facing=west]: minecraft:bubble_coral_wall_fan[waterlogged=false,facing=west] -# minecraft:dead_bubble_coral_wall_fan[waterlogged=true,facing=east]: minecraft:bubble_coral_wall_fan[waterlogged=true,facing=east] -# minecraft:dead_bubble_coral_wall_fan[waterlogged=true,facing=north]: minecraft:bubble_coral_wall_fan[waterlogged=true,facing=north] -# minecraft:dead_bubble_coral_wall_fan[waterlogged=true,facing=south]: minecraft:bubble_coral_wall_fan[waterlogged=true,facing=south] -# minecraft:dead_bubble_coral_wall_fan[waterlogged=true,facing=west]: minecraft:bubble_coral_wall_fan[waterlogged=true,facing=west] -# minecraft:dead_fire_coral[waterlogged=false]: minecraft:fire_coral[waterlogged=false] -# minecraft:dead_fire_coral[waterlogged=true]: minecraft:fire_coral[waterlogged=true] -# minecraft:dead_fire_coral_fan[waterlogged=false]: minecraft:fire_coral_fan[waterlogged=false] -# minecraft:dead_fire_coral_fan[waterlogged=true]: minecraft:fire_coral_fan[waterlogged=true] -# minecraft:dead_fire_coral_wall_fan[waterlogged=false,facing=east]: minecraft:fire_coral_wall_fan[waterlogged=false,facing=east] -# minecraft:dead_fire_coral_wall_fan[waterlogged=false,facing=north]: minecraft:fire_coral_wall_fan[waterlogged=false,facing=north] -# minecraft:dead_fire_coral_wall_fan[waterlogged=false,facing=south]: minecraft:fire_coral_wall_fan[waterlogged=false,facing=south] -# minecraft:dead_fire_coral_wall_fan[waterlogged=false,facing=west]: minecraft:fire_coral_wall_fan[waterlogged=false,facing=west] -# minecraft:dead_fire_coral_wall_fan[waterlogged=true,facing=east]: minecraft:fire_coral_wall_fan[waterlogged=true,facing=east] -# minecraft:dead_fire_coral_wall_fan[waterlogged=true,facing=north]: minecraft:fire_coral_wall_fan[waterlogged=true,facing=north] -# minecraft:dead_fire_coral_wall_fan[waterlogged=true,facing=south]: minecraft:fire_coral_wall_fan[waterlogged=true,facing=south] -# minecraft:dead_fire_coral_wall_fan[waterlogged=true,facing=west]: minecraft:fire_coral_wall_fan[waterlogged=true,facing=west] -# minecraft:dead_horn_coral[waterlogged=false]: minecraft:horn_coral[waterlogged=false] -# minecraft:dead_horn_coral[waterlogged=true]: minecraft:horn_coral[waterlogged=true] -# minecraft:dead_horn_coral_fan[waterlogged=false]: minecraft:horn_coral_fan[waterlogged=false] -# minecraft:dead_horn_coral_fan[waterlogged=true]: minecraft:horn_coral_fan[waterlogged=true] -# minecraft:dead_horn_coral_wall_fan[waterlogged=false,facing=east]: minecraft:horn_coral_wall_fan[waterlogged=false,facing=east] -# minecraft:dead_horn_coral_wall_fan[waterlogged=false,facing=north]: minecraft:horn_coral_wall_fan[waterlogged=false,facing=north] -# minecraft:dead_horn_coral_wall_fan[waterlogged=false,facing=south]: minecraft:horn_coral_wall_fan[waterlogged=false,facing=south] -# minecraft:dead_horn_coral_wall_fan[waterlogged=false,facing=west]: minecraft:horn_coral_wall_fan[waterlogged=false,facing=west] -# minecraft:dead_horn_coral_wall_fan[waterlogged=true,facing=east]: minecraft:horn_coral_wall_fan[waterlogged=true,facing=east] -# minecraft:dead_horn_coral_wall_fan[waterlogged=true,facing=north]: minecraft:horn_coral_wall_fan[waterlogged=true,facing=north] -# minecraft:dead_horn_coral_wall_fan[waterlogged=true,facing=south]: minecraft:horn_coral_wall_fan[waterlogged=true,facing=south] -# minecraft:dead_horn_coral_wall_fan[waterlogged=true,facing=west]: minecraft:horn_coral_wall_fan[waterlogged=true,facing=west] -# minecraft:dead_tube_coral[waterlogged=false]: minecraft:tube_coral[waterlogged=false] -# minecraft:dead_tube_coral[waterlogged=true]: minecraft:tube_coral[waterlogged=true] -# minecraft:dead_tube_coral_fan[waterlogged=false]: minecraft:tube_coral_fan[waterlogged=false] -# minecraft:dead_tube_coral_fan[waterlogged=true]: minecraft:tube_coral_fan[waterlogged=true] -# minecraft:dead_tube_coral_wall_fan[waterlogged=false,facing=east]: minecraft:tube_coral_wall_fan[waterlogged=false,facing=east] -# minecraft:dead_tube_coral_wall_fan[waterlogged=false,facing=north]: minecraft:tube_coral_wall_fan[waterlogged=false,facing=north] -# minecraft:dead_tube_coral_wall_fan[waterlogged=false,facing=south]: minecraft:tube_coral_wall_fan[waterlogged=false,facing=south] -# minecraft:dead_tube_coral_wall_fan[waterlogged=false,facing=west]: minecraft:tube_coral_wall_fan[waterlogged=false,facing=west] -# minecraft:dead_tube_coral_wall_fan[waterlogged=true,facing=east]: minecraft:tube_coral_wall_fan[waterlogged=true,facing=east] -# minecraft:dead_tube_coral_wall_fan[waterlogged=true,facing=north]: minecraft:tube_coral_wall_fan[waterlogged=true,facing=north] -# minecraft:dead_tube_coral_wall_fan[waterlogged=true,facing=south]: minecraft:tube_coral_wall_fan[waterlogged=true,facing=south] -# minecraft:dead_tube_coral_wall_fan[waterlogged=true,facing=west]: minecraft:tube_coral_wall_fan[waterlogged=true,facing=west] - -#### Chorus Plant #### -# Chorus Plant does support transparent textures, but man... its hitbox is super weird. You're probably better off using leaves. -# minecraft:chorus_plant[down=false,east=false,north=false,south=false,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=false,north=false,south=false,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=true,north=false,south=false,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=true,north=false,south=false,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=false,north=true,south=false,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=false,north=true,south=false,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=true,north=true,south=false,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=true,north=true,south=false,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=false,north=false,south=true,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=false,north=false,south=true,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=true,north=false,south=true,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=true,north=false,south=true,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=false,north=true,south=true,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=false,north=true,south=true,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=true,north=true,south=true,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=false,north=false,south=false,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=false,north=false,south=false,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=true,north=false,south=false,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=true,north=false,south=false,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=false,north=true,south=false,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=false,north=true,south=false,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=true,north=true,south=false,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=true,north=true,south=false,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=false,north=false,south=true,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=false,north=false,south=true,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=true,north=false,south=true,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=true,north=false,south=true,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=false,north=true,south=true,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=false,north=true,south=true,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=true,north=true,south=true,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=false,north=false,south=false,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=false,north=false,south=false,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=true,north=false,south=false,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=true,north=false,south=false,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=false,north=true,south=false,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=false,north=true,south=false,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=true,north=true,south=false,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=true,north=true,south=false,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=false,north=false,south=true,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=false,north=false,south=true,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=true,north=false,south=true,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=true,north=false,south=true,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=false,north=true,south=true,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=false,north=true,south=true,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=true,north=true,south=true,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=false,north=false,south=false,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=false,north=false,south=false,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=true,north=false,south=false,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=true,north=false,south=false,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=false,north=true,south=false,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=false,north=true,south=false,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=true,north=true,south=false,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=true,north=true,south=false,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=false,north=false,south=true,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=false,north=false,south=true,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=true,north=false,south=true,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=true,north=false,south=true,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=false,north=true,south=true,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=true,east=false,north=true,south=true,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] -# minecraft:chorus_plant[down=false,east=true,north=true,south=true,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/block_name.yml b/common-files/src/main/resources/resources/default/configuration/block_name.yml deleted file mode 100644 index 0b89fc743..000000000 --- a/common-files/src/main/resources/resources/default/configuration/block_name.yml +++ /dev/null @@ -1,54 +0,0 @@ -# This file is for localizing internal block IDs (craftengine:xxx_xx). -# Some other plugins support displaying block names using lang components. -# This might be useful for the client-side, but it's not mandatory. -lang: - en_us: - block_name:default:chinese_lantern: Chinese Lantern - block_name:default:netherite_anvil: Netherite Anvil - block_name:default:topaz_ore: Topaz Ore - block_name:default:deepslate_topaz_ore: Deepslate Topaz Ore - block_name:default:palm_log: Palm Log - block_name:default:stripped_palm_log: Stripped Palm Log - block_name:default:palm_wood: Palm Wood - block_name:default:stripped_palm_wood: Stripped Palm Wood - block_name:default:palm_planks: Palm Planks - block_name:default:palm_sapling: Palm Sapling - block_name:default:palm_leaves: Palm Leaves - block_name:default:palm_trapdoor: Palm Trapdoor - block_name:default:palm_door: Palm Door - block_name:default:palm_fence_gate: Palm Fence Gate - block_name:default:palm_slab: Palm Slab - block_name:default:palm_stairs: Palm Stairs - block_name:default:fairy_flower: Fairy Flower - block_name:default:reed: Reed - block_name:default:flame_cane: Flame Cane - block_name:default:ender_pearl_flower: Ender Pearl Flower - block_name:default:gunpowder_block: GunPowder Block - block_name:default:solid_gunpowder_block: Solid GunPowder Block - block_name:default:copper_coil: Copper Coil - block_name:default:chessboard: Chessboard - zh_cn: - block_name:default:chinese_lantern: 灯笼 - block_name:default:netherite_anvil: 下界合金砧 - block_name:default:topaz_ore: 黄玉矿石 - block_name:default:deepslate_topaz_ore: 深层黄玉矿石 - block_name:default:palm_log: 棕榈原木 - block_name:default:stripped_palm_log: 去皮棕榈原木 - block_name:default:palm_wood: 棕榈木 - block_name:default:stripped_palm_wood: 去皮棕榈木 - block_name:default:palm_planks: 棕榈木板 - block_name:default:palm_sapling: 棕榈树苗 - block_name:default:palm_leaves: 棕榈树叶 - block_name:default:palm_trapdoor: 棕榈木活板门 - block_name:default:palm_door: 棕榈木门 - block_name:default:palm_fence_gate: 棕榈木栅栏门 - block_name:default:palm_slab: 棕榈木台阶 - block_name:default:palm_stairs: 棕榈木楼梯 - block_name:default:fairy_flower: 仙灵花 - block_name:default:reed: 芦苇 - block_name:default:flame_cane: 烈焰甘蔗 - block_name:default:ender_pearl_flower: 末影珍珠花 - block_name:default:gunpowder_block: 火药粉末 - block_name:default:solid_gunpowder_block: 凝固火药块 - block_name:default:copper_coil: 铜线圈 - block_name:default:chessboard: 棋盘 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/blocks.yml b/common-files/src/main/resources/resources/default/configuration/blocks.yml deleted file mode 100644 index 530b5f9b9..000000000 --- a/common-files/src/main/resources/resources/default/configuration/blocks.yml +++ /dev/null @@ -1,520 +0,0 @@ -items#misc: - default:chinese_lantern: - material: nether_brick - custom-model-data: 3000 - data: - item-name: - model: - type: minecraft:model - path: minecraft:item/custom/chinese_lantern - generation: - parent: minecraft:block/custom/chinese_lantern - behavior: - type: block_item - block: - loot: - template: default:loot_table/self - settings: - template: - - default:hardness/wool - - default:burn_data/planks - - default:sound/wood - - default:settings/solid_1x1x1 - overrides: - push-reaction: NORMAL - instrument: HARP - luminance: 15 - map-color: 36 - state: - id: 15 - state: note_block:15 - model: - path: minecraft:block/custom/chinese_lantern - generation: - parent: minecraft:block/cube_column - textures: - end: minecraft:block/custom/chinese_lantern_top - side: minecraft:block/custom/chinese_lantern - default:netherite_anvil: - material: nether_brick - custom-model-data: 3001 - data: - item-name: - model: - type: minecraft:model - path: minecraft:item/custom/netherite_anvil - generation: - parent: minecraft:block/custom/netherite_anvil - behavior: - type: block_item - block: - loot: - template: default:loot_table/self - behavior: - type: falling_block - hurt-amount: 4 - max-hurt: 80 - events: - - on: right_click - functions: - - type: open_window - gui-type: anvil - - type: cancel_event - conditions: - - type: expression - expression: '!' - settings: - template: - - default:pickaxe_power/level_4 - overrides: - tags: - - minecraft:mineable/pickaxe - - minecraft:anvil - sounds: - break: minecraft:block.anvil.break - step: minecraft:block.anvil.step - place: minecraft:block.anvil.place - hit: minecraft:block.anvil.hit - fall: minecraft:block.anvil.fall - land: minecraft:block.anvil.land - destroy: minecraft:block.anvil.destroy - map-color: 29 - hardness: 10.0 - resistance: 1200 - push-reaction: BLOCK - states: - properties: - facing_clockwise: - type: 4-direction - default: north - appearances: - axisX: - state: minecraft:anvil[facing=east] - model: - path: minecraft:block/custom/netherite_anvil - y: 90 - generation: - parent: minecraft:block/anvil - textures: - top: minecraft:block/custom/netherite_anvil_top - body: minecraft:block/custom/netherite_anvil - particle: minecraft:block/custom/netherite_anvil - axisZ: - state: minecraft:anvil[facing=north] - model: - path: minecraft:block/custom/netherite_anvil - variants: - facing_clockwise=east: - appearance: axisX - id: 0 - facing_clockwise=west: - appearance: axisX - id: 1 - facing_clockwise=north: - appearance: axisZ - id: 2 - facing_clockwise=south: - appearance: axisZ - id: 3 - default:gunpowder_block: - material: nether_brick - custom-model-data: 3002 - data: - item-name: - model: - type: minecraft:model - path: minecraft:item/custom/gunpowder_block - generation: - parent: minecraft:block/custom/gunpowder_block - behavior: - type: block_item - block: - behaviors: - - type: concrete_powder_block - solid-block: default:solid_gunpowder_block - - type: falling_block - loot: - template: default:loot_table/self - settings: - template: - - default:sound/sand - - default:settings/solid_1x1x1 - overrides: - hardness: 0.5 - resistance: 0.5 - instrument: SNARE - map-color: 45 - state: - id: 16 - state: note_block:16 - model: - path: minecraft:block/custom/gunpowder_block - generation: - parent: minecraft:block/cube_all - textures: - all: minecraft:block/custom/gunpowder_block - default:solid_gunpowder_block: - material: nether_brick - custom-model-data: 3003 - data: - item-name: - model: - type: minecraft:model - path: minecraft:item/custom/solid_gunpowder_block - generation: - parent: minecraft:block/custom/solid_gunpowder_block - behavior: - type: block_item - block: - loot: - template: default:loot_table/self - settings: - template: - - default:sound/stone - - default:pickaxe_power/level_1 - - default:settings/solid_1x1x1 - overrides: - hardness: 1.8 - resistance: 1.8 - instrument: BASEDRUM - map-color: 45 - state: - id: 17 - state: note_block:17 - model: - path: minecraft:block/custom/solid_gunpowder_block - generation: - parent: minecraft:block/cube_all - textures: - all: minecraft:block/custom/solid_gunpowder_block - default:copper_coil: - material: nether_brick - custom-model-data: 3004 - data: - item-name: - model: - type: minecraft:model - path: minecraft:item/custom/copper_coil - generation: - parent: minecraft:block/custom/copper_coil - behavior: - type: block_item - block: - loot: - template: default:loot_table/self - settings: - template: - - default:sound/metal - - default:pickaxe_power/level_1 - overrides: - hardness: 3.0 - resistance: 4.5 - replaceable: false - is-redstone-conductor: true - is-suffocating: true - instrument: BASEDRUM - map-color: 15 - behavior: - type: lamp_block - states: - properties: - lit: - type: boolean - default: false - appearances: - off: - state: cactus:0 - model: - path: minecraft:block/custom/copper_coil - generation: - parent: minecraft:block/cactus - textures: - particle: minecraft:block/custom/copper_coil - bottom: minecraft:block/custom/copper_coil - top: minecraft:block/custom/copper_coil - side: minecraft:block/custom/copper_coil_side - on: - state: cactus:1 - model: - path: minecraft:block/custom/copper_coil_on - generation: - parent: minecraft:block/cactus - textures: - particle: minecraft:block/custom/copper_coil_on - bottom: minecraft:block/custom/copper_coil_on - top: minecraft:block/custom/copper_coil_on - side: minecraft:block/custom/copper_coil_on_side - variants: - lit=false: - appearance: 'off' - id: 0 - lit=true: - appearance: 'on' - id: 1 - settings: - luminance: 8 - default:pebble: - material: nether_brick - custom-model-data: 3005 - data: - item-name: - model: - template: default:model/simplified_generated - arguments: - path: minecraft:item/custom/pebble - behavior: - - type: block_item - block: - settings: - template: - - default:sound/stone - - default:hardness/none - overrides: - map-color: 11 - push-reaction: DESTROY - behaviors: - - type: sturdy_base_block - direction: down - support-types: - - full - - type: stackable_block - property: pebble - items: - - default:pebble - sounds: - stack: minecraft:block.stone.fall - loot: - pools: - - rolls: 1 - entries: - - type: item - item: default:pebble - functions: - - type: set_count - count: 3 - add: false - conditions: - - type: match_block_property - properties: - pebble: 3 - - type: set_count - count: 2 - add: false - conditions: - - type: match_block_property - properties: - pebble: 2 - - type: explosion_decay - states: - properties: - pebble: - type: int - range: 1~3 - default: 1 - appearances: - one: - state: tripwire:2 - models: - - path: minecraft:block/custom/pebble_1 - weight: 1 - - path: minecraft:block/custom/pebble_1 - weight: 1 - y: 90 - - path: minecraft:block/custom/pebble_1 - weight: 1 - y: 180 - - path: minecraft:block/custom/pebble_1 - weight: 1 - y: 270 - two: - state: tripwire:3 - models: - - path: minecraft:block/custom/pebble_2 - weight: 1 - - path: minecraft:block/custom/pebble_2 - weight: 1 - y: 90 - - path: minecraft:block/custom/pebble_2 - weight: 1 - y: 180 - - path: minecraft:block/custom/pebble_2 - weight: 1 - y: 270 - three: - state: tripwire:4 - models: - - path: minecraft:block/custom/pebble_3 - weight: 1 - - path: minecraft:block/custom/pebble_3 - weight: 1 - y: 90 - - path: minecraft:block/custom/pebble_3 - weight: 1 - y: 180 - - path: minecraft:block/custom/pebble_3 - weight: 1 - y: 270 - variants: - pebble=1: - appearance: 'one' - id: 2 - pebble=2: - appearance: 'two' - id: 3 - pebble=3: - appearance: 'three' - id: 4 - default:chessboard_block: - material: nether_brick - custom-model-data: 3006 - data: - item-name: - model: - type: minecraft:model - path: minecraft:item/custom/chessboard_block - generation: - parent: minecraft:block/custom/chessboard_block - behavior: - type: block_item - block: - loot: - template: default:loot_table/self - settings: - hardness: 1.4 - resistance: 1.4 - is-suffocating: true - is-redstone-conductor: true - push-reaction: PUSH_ONLY - instrument: BASEDRUM - map-color: 36 - sounds: - break: minecraft:block.stone.break - fall: minecraft:block.stone.fall - hit: minecraft:block.stone.hit - place: minecraft:block.stone.place - step: minecraft:block.stone.step - states: - properties: - facing: - type: 4-direction - default: north - appearances: - east: - state: note_block:18 - model: - path: minecraft:block/custom/chessboard_block - y: 270 - generation: - parent: minecraft:block/template_glazed_terracotta - textures: - pattern: minecraft:block/custom/chessboard_block - north: - state: note_block:19 - model: - path: minecraft:block/custom/chessboard_block - y: 180 - south: - state: note_block:20 - model: - path: minecraft:block/custom/chessboard_block - west: - state: note_block:21 - model: - path: minecraft:block/custom/chessboard_block - y: 90 - variants: - facing=east: - appearance: east - id: 18 - facing=north: - appearance: north - id: 19 - facing=south: - appearance: south - id: 20 - facing=west: - appearance: west - id: 21 -recipes#misc: - default:chinese_lantern: - type: shaped - pattern: - - ABA - - BCB - - ABA - ingredients: - A: '#minecraft:planks' - B: minecraft:stick - C: minecraft:torch - result: - id: default:chinese_lantern - count: 1 - default:netherite_anvil: - type: shaped - pattern: - - ' B ' - - BAB - - ' B ' - ingredients: - A: minecraft:anvil - B: minecraft:netherite_ingot - result: - id: default:netherite_anvil - count: 1 - default:gunpowder_from_block: - type: shapeless - ingredients: - A: default:gunpowder_block - result: - id: minecraft:gunpowder - count: 9 - default:gunpowder_block: - type: shaped - pattern: - - AAA - - AAA - - AAA - ingredients: - A: minecraft:gunpowder - result: - id: default:gunpowder_block - count: 1 - default:copper_coil: - type: shaped - pattern: - - AAA - - A A - - AAA - ingredients: - A: minecraft:copper_ingot - result: - id: default:copper_coil - count: 1 - default:pebble: - type: shapeless - ingredients: - - minecraft:cobblestone - result: - id: default:pebble - count: 4 - default:cobblestone_from_pebble: - type: shaped - pattern: - - AA - - AA - ingredients: - A: default:pebble - result: - id: minecraft:cobblestone - count: 1 - default:chessboard_block: - type: shaped - pattern: - - AB - - BA - ingredients: - A: minecraft:white_terracotta - B: minecraft:black_terracotta - result: - id: default:chessboard_block - count: 4 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/amethyst_torch.yml b/common-files/src/main/resources/resources/default/configuration/blocks/amethyst_torch.yml new file mode 100644 index 000000000..773ea9c4e --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/amethyst_torch.yml @@ -0,0 +1,162 @@ +items: + default:amethyst_torch: + material: nether_brick + data: + item-name: + model: + type: minecraft:model + path: minecraft:item/custom/amethyst_torch + generation: + parent: minecraft:item/generated + textures: + layer0: minecraft:block/custom/amethyst_torch + behavior: + - type: wall_block_item + block: default:amethyst_wall_torch + - type: block_item + block: default:amethyst_torch + - type: block_item + block: default:amethyst_wall_torch + default:amethyst_standing_torch: + material: nether_brick + data: + item-name: + model: + type: minecraft:model + path: minecraft:block/custom/amethyst_torch + generation: + parent: minecraft:block/template_torch + textures: + torch: minecraft:block/custom/amethyst_torch + default:amethyst_wall_torch: + material: nether_brick + data: + item-name: + model: + type: minecraft:model + path: minecraft:block/custom/amethyst_wall_torch + generation: + parent: minecraft:block/template_torch_wall + textures: + torch: minecraft:block/custom/amethyst_torch +blocks: + default:amethyst_torch: + loot: + template: default:loot_table/basic + arguments: + item: default:amethyst_torch + settings: + template: + - default:sound/wood + - default:hardness/none + overrides: + push-reaction: destroy + replaceable: false + map-color: 24 + luminance: 15 + item: default:amethyst_torch + state: + state: redstone_torch[lit=false] + entity-renderer: + item: default:amethyst_standing_torch + scale: 1.01 + behavior: + - type: sturdy_base_block + direction: down + support-types: + - center + - type: liquid_flowable_block + - type: simple_particle_block + tick-interval: 10 + particles: + - particle: smoke + x: 0.5 + y: 0.7 + z: 0.5 + - particle: dust + color: 138,43,226 + x: 0.5 + y: 0.7 + z: 0.5 + default:amethyst_wall_torch: + loot: + template: default:loot_table/basic + arguments: + item: default:amethyst_torch + settings: + template: + - default:sound/wood + - default:hardness/none + overrides: + push-reaction: destroy + replaceable: false + map-color: 24 + luminance: 15 + item: default:amethyst_torch + behavior: + - type: directional_attached_block + - type: liquid_flowable_block + - type: wall_torch_particle_block + particles: + - particle: smoke + x: 0.27 + y: 0.42 + z: 0.27 + - particle: dust + color: 138,43,226 + x: 0.27 + y: 0.42 + z: 0.27 + states: + properties: + facing: + type: horizontal_direction + appearances: + north: + state: redstone_wall_torch[facing=north,lit=false] + entity-renderer: + item: default:amethyst_wall_torch + scale: 1.04 + yaw: 90 + translation: -0.02,0.01,0 + east: + state: redstone_wall_torch[facing=east,lit=false] + entity-renderer: + item: default:amethyst_wall_torch + scale: 1.04 + yaw: 180 + translation: -0.02,0.01,0 + west: + state: redstone_wall_torch[facing=west,lit=false] + entity-renderer: + item: default:amethyst_wall_torch + scale: 1.04 + translation: -0.02,0.01,0 + south: + state: redstone_wall_torch[facing=south,lit=false] + entity-renderer: + item: default:amethyst_wall_torch + scale: 1.04 + yaw: -90 + translation: -0.02,0.01,0 + variants: + facing=north: + appearance: north + facing=east: + appearance: east + facing=west: + appearance: west + facing=south: + appearance: south +recipes: + default:amethyst_torch: + type: shaped + pattern: + - 'A' + - 'B' + ingredients: + A: minecraft:amethyst_shard + B: minecraft:stick + result: + id: default:amethyst_torch + count: 1 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/chessboard_block.yml b/common-files/src/main/resources/resources/default/configuration/blocks/chessboard_block.yml new file mode 100644 index 000000000..7e36d6d45 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/chessboard_block.yml @@ -0,0 +1,79 @@ +items: + default:chessboard_block: + material: nether_brick + data: + item-name: + model: + type: minecraft:model + path: minecraft:item/custom/chessboard_block + generation: + parent: minecraft:block/custom/chessboard_block + behavior: + type: block_item + block: + loot: + template: default:loot_table/self + settings: + hardness: 1.4 + resistance: 1.4 + is-suffocating: true + is-redstone-conductor: true + push-reaction: push_only + instrument: basedrum + map-color: 36 + sounds: + break: minecraft:block.stone.break + fall: minecraft:block.stone.fall + hit: minecraft:block.stone.hit + place: minecraft:block.stone.place + step: minecraft:block.stone.step + states: + properties: + facing: + type: 4-direction + default: north + appearances: + east: + auto-state: solid + model: + path: minecraft:block/custom/chessboard_block + y: 270 + generation: + parent: minecraft:block/template_glazed_terracotta + textures: + pattern: minecraft:block/custom/chessboard_block + north: + auto-state: solid + model: + path: minecraft:block/custom/chessboard_block + y: 180 + south: + auto-state: solid + model: + path: minecraft:block/custom/chessboard_block + west: + auto-state: solid + model: + path: minecraft:block/custom/chessboard_block + y: 90 + variants: + facing=east: + appearance: east + facing=north: + appearance: north + facing=south: + appearance: south + facing=west: + appearance: west +recipes: + default:chessboard_block: + type: shaped + pattern: + - AB + - BA + ingredients: + A: minecraft:white_terracotta + B: minecraft:black_terracotta + result: + id: default:chessboard_block + count: 4 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/chinese_lantern.yml b/common-files/src/main/resources/resources/default/configuration/blocks/chinese_lantern.yml new file mode 100644 index 000000000..420809bc8 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/chinese_lantern.yml @@ -0,0 +1,49 @@ +items: + default:chinese_lantern: + material: nether_brick + data: + item-name: + model: + type: minecraft:model + path: minecraft:item/custom/chinese_lantern + generation: + parent: minecraft:block/custom/chinese_lantern + behavior: + type: block_item + block: + loot: + template: default:loot_table/self + settings: + template: + - default:hardness/wool + - default:burn_data/planks + - default:sound/wood + - default:settings/solid_1x1x1 + overrides: + push-reaction: normal + instrument: harp + luminance: 15 + map-color: 36 + state: + auto-state: solid + model: + path: minecraft:block/custom/chinese_lantern + generation: + parent: minecraft:block/cube_column + textures: + end: minecraft:block/custom/chinese_lantern_top + side: minecraft:block/custom/chinese_lantern +recipes: + default:chinese_lantern: + type: shaped + pattern: + - ABA + - BCB + - ABA + ingredients: + A: '#minecraft:planks' + B: minecraft:stick + C: minecraft:torch + result: + id: default:chinese_lantern + count: 1 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/copper_coil.yml b/common-files/src/main/resources/resources/default/configuration/blocks/copper_coil.yml new file mode 100644 index 000000000..9d9b8bf83 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/copper_coil.yml @@ -0,0 +1,79 @@ +items: + default:copper_coil: + material: nether_brick + data: + item-name: + model: + type: minecraft:model + path: minecraft:item/custom/copper_coil + generation: + parent: minecraft:block/custom/copper_coil + behavior: + type: block_item + block: + loot: + template: default:loot_table/self + settings: + template: + - default:sound/metal + - default:pickaxe_power/level_1 + overrides: + hardness: 3.0 + resistance: 4.5 + replaceable: false + is-redstone-conductor: true + is-suffocating: true + instrument: basedrum + map-color: 15 + tags: + - minecraft:mineable/pickaxe + behavior: + type: lamp_block + states: + properties: + lit: + type: boolean + default: false + appearances: + off: + auto-state: cactus + model: + path: minecraft:block/custom/copper_coil + generation: + parent: minecraft:block/cactus + textures: + particle: minecraft:block/custom/copper_coil + bottom: minecraft:block/custom/copper_coil + top: minecraft:block/custom/copper_coil + side: minecraft:block/custom/copper_coil_side + on: + auto-state: cactus + model: + path: minecraft:block/custom/copper_coil_on + generation: + parent: minecraft:block/cactus + textures: + particle: minecraft:block/custom/copper_coil_on + bottom: minecraft:block/custom/copper_coil_on + top: minecraft:block/custom/copper_coil_on + side: minecraft:block/custom/copper_coil_on_side + variants: + lit=false: + appearance: 'off' + lit=true: + appearance: 'on' + settings: + luminance: 8 +recipes: + default:copper_coil: + type: shaped + pattern: + - AAA + - ABA + - AAA + ingredients: + A: minecraft:copper_ingot + B: minecraft:redstone + result: + id: default:copper_coil + count: 1 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/ender_pearl_flower.yml b/common-files/src/main/resources/resources/default/configuration/blocks/ender_pearl_flower.yml new file mode 100644 index 000000000..0b61f7161 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/ender_pearl_flower.yml @@ -0,0 +1,141 @@ +items: + default:ender_pearl_flower_seeds: + material: nether_brick + data: + item-name: + model: + template: default:model/simplified_generated + arguments: + path: minecraft:item/custom/ender_pearl_flower_seeds + behavior: + type: block_item + block: default:ender_pearl_flower +blocks: + default:ender_pearl_flower: + settings: + template: + - default:hardness/none + - default:sound/grass + overrides: + item: default:ender_pearl_flower_seeds + push-reaction: destroy + map-color: 24 + is-randomly-ticking: true + behaviors: + - type: bush_block + bottom-blocks: + - minecraft:end_stone + - type: crop_block + grow-speed: 0.25 + light-requirement: 9 + is-bone-meal-target: true + bone-meal-age-bonus: 1 + loot: + template: default:loot_table/seed_crop + arguments: + crop_item: minecraft:ender_pearl + crop_seed: default:ender_pearl_flower_seeds + ripe_age: 2 + events: + - on: break + conditions: + - type: match_block_property + properties: + age: 2 + functions: + - type: particle + x: + 0.5 + y: + 0.5 + z: + 0.5 + particle: minecraft:end_rod + count: 15 + offset-x: 0.05 + offset-y: 0.05 + offset-z: 0.05 + speed: 0.1 + - type: play_sound + sound: minecraft:entity.enderman.teleport + x: + 0.5 + y: + 0.5 + z: + 0.5 + - on: right_click + conditions: + - type: match_block_property + properties: + age: 2 + - type: '!is_null' + argument: item_in_hand + - type: equals + value1: + value2: default:ender_pearl_flower_seeds + functions: + - type: break_block + x: + y: + z: + - type: place_block + x: + y: + z: + block-state: default:ender_pearl_flower[age=0] + - type: set_count + add: true + count: -1 + - type: swing_hand + states: + properties: + age: + type: int + default: 0 + range: 0~2 + appearances: + stage_0: + auto-state: lower_tripwire + models: + - path: minecraft:block/custom/ender_pearl_flower_stage_0 + generation: + parent: minecraft:block/cross + textures: + cross: minecraft:block/custom/ender_pearl_flower_stage_0 + stage_1: + auto-state: higher_tripwire + models: + - path: minecraft:block/custom/ender_pearl_flower_stage_1 + generation: + parent: minecraft:block/cross + textures: + cross: minecraft:block/custom/ender_pearl_flower_stage_1 + stage_2: + auto-state: sugar_cane + models: + - path: minecraft:block/custom/ender_pearl_flower_stage_2 + generation: + parent: minecraft:block/cross + textures: + cross: minecraft:block/custom/ender_pearl_flower_stage_2 + variants: + age=0: + appearance: stage_0 + age=1: + appearance: stage_1 + age=2: + appearance: stage_2 +vanilla-loots: + default:ender_pearl_flower_seeds_from_endermite: + type: entity + target: minecraft:endermite + override: false + loot: + pools: + - rolls: 1 + conditions: + - type: table_bonus + enchantment: minecraft:looting + chances: + - 0.1 + - 0.5 + - 0.8 + - 1 + entries: + - type: item + item: default:ender_pearl_flower_seeds \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/fairy_flower.yml b/common-files/src/main/resources/resources/default/configuration/blocks/fairy_flower.yml new file mode 100644 index 000000000..52a2815fd --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/fairy_flower.yml @@ -0,0 +1,50 @@ +items: + default:fairy_flower: + material: nether_brick + data: + item-name: + model: + template: default:model/simplified_generated + arguments: + path: minecraft:item/custom/fairy_flower + behavior: + type: block_item + block: + settings: + template: + - default:hardness/none + - default:sound/grass + overrides: + item: default:fairy_flower + push-reaction: destroy + map-color: 19 + behavior: + type: bush_block + bottom-block-tags: + - minecraft:dirt + - minecraft:farmland + loot: + template: default:loot_table/self + state: + auto-state: sugar_cane + models: + - path: minecraft:block/custom/fairy_flower_1 + weight: 100 + - path: minecraft:block/custom/fairy_flower_2 + weight: 5 + generation: + parent: minecraft:block/custom/fairy_flower_1 + textures: + '0': minecraft:block/custom/fairy_flower_2 + - path: minecraft:block/custom/fairy_flower_3 + weight: 5 + generation: + parent: minecraft:block/custom/fairy_flower_1 + textures: + '0': minecraft:block/custom/fairy_flower_3 + - path: minecraft:block/custom/fairy_flower_4 + weight: 5 + generation: + parent: minecraft:block/custom/fairy_flower_1 + textures: + '0': minecraft:block/custom/fairy_flower_4 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/flame_cane.yml b/common-files/src/main/resources/resources/default/configuration/blocks/flame_cane.yml new file mode 100644 index 000000000..4213b62bd --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/flame_cane.yml @@ -0,0 +1,105 @@ +items: + default:flame_cane: + material: nether_brick + data: + item-name: + model: + template: default:model/simplified_generated + arguments: + path: minecraft:item/custom/flame_cane + behavior: + type: block_item + block: + settings: + template: + - default:hardness/none + - default:sound/grass + overrides: + push-reaction: destroy + map-color: 15 + is-randomly-ticking: true + behaviors: + - type: vertical_crop_block + max-height: 4 + grow-speed: 0.333 + direction: up + - type: bush_block + stackable: true + delay: 1 + bottom-blocks: + - minecraft:netherrack + - minecraft:soul_sand + - minecraft:soul_soil + - minecraft:magma_block + - minecraft:warped_nylium + - minecraft:crimson_nylium + - minecraft:basalt + - type: near_liquid_block + liquid-type: lava + delay: 1 + stackable: true + positions: + - -1,-1,0 + - 1,-1,0 + - 0,-1,-1 + - 0,-1,1 + loot: + template: default:loot_table/self + states: + properties: + age: + type: int + default: 0 + range: 0~5 + appearances: + default: + auto-state: sugar_cane + models: + - path: minecraft:block/custom/flame_cane_1 + weight: 1 + generation: + parent: minecraft:block/sugar_cane + textures: + cross: minecraft:block/custom/flame_cane_1 + - path: minecraft:block/custom/flame_cane_2 + weight: 1 + generation: + parent: minecraft:block/sugar_cane + textures: + cross: minecraft:block/custom/flame_cane_2 + variants: + age=0: + appearance: default + age=1: + appearance: default + age=2: + appearance: default + age=3: + appearance: default + age=4: + appearance: default + age=5: + appearance: default +recipes: + default:magma_cream: + type: shaped + pattern: + - ' A ' + - 'ABA' + - ' A ' + ingredients: + A: default:flame_cane + B: minecraft:slime_ball + result: + id: minecraft:magma_cream + count: 1 + default:magma_block: + type: shapeless + ingredients: + A1: minecraft:cobblestone + A2: minecraft:cobblestone + B1: default:flame_cane + B2: default:flame_cane + result: + id: minecraft:magma_block + count: 2 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/gunpowder_block.yml b/common-files/src/main/resources/resources/default/configuration/blocks/gunpowder_block.yml new file mode 100644 index 000000000..e114b183c --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/gunpowder_block.yml @@ -0,0 +1,87 @@ +items: + default:gunpowder_block: + material: nether_brick + data: + item-name: + model: + type: minecraft:model + path: minecraft:item/custom/gunpowder_block + generation: + parent: minecraft:block/custom/gunpowder_block + behavior: + type: block_item + block: + behaviors: + - type: concrete_powder_block + solid-block: default:solid_gunpowder_block + - type: falling_block + loot: + template: default:loot_table/self + settings: + template: + - default:sound/sand + - default:settings/solid_1x1x1 + overrides: + hardness: 0.5 + resistance: 0.5 + instrument: snare + map-color: 45 + state: + auto-state: solid + model: + path: minecraft:block/custom/gunpowder_block + generation: + parent: minecraft:block/cube_all + textures: + all: minecraft:block/custom/gunpowder_block + default:solid_gunpowder_block: + material: nether_brick + data: + item-name: + model: + type: minecraft:model + path: minecraft:item/custom/solid_gunpowder_block + generation: + parent: minecraft:block/custom/solid_gunpowder_block + behavior: + type: block_item + block: + loot: + template: default:loot_table/self + settings: + template: + - default:sound/stone + - default:pickaxe_power/level_1 + - default:settings/solid_1x1x1 + overrides: + hardness: 1.8 + resistance: 1.8 + instrument: basedrum + map-color: 45 + state: + auto-state: solid + model: + path: minecraft:block/custom/solid_gunpowder_block + generation: + parent: minecraft:block/cube_all + textures: + all: minecraft:block/custom/solid_gunpowder_block +recipes: + default:gunpowder_from_block: + type: shapeless + ingredients: + A: default:gunpowder_block + result: + id: minecraft:gunpowder + count: 9 + default:gunpowder_block: + type: shaped + pattern: + - AAA + - AAA + - AAA + ingredients: + A: minecraft:gunpowder + result: + id: default:gunpowder_block + count: 1 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/hami_melon.yml b/common-files/src/main/resources/resources/default/configuration/blocks/hami_melon.yml new file mode 100644 index 000000000..00cc12d7c --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/hami_melon.yml @@ -0,0 +1,220 @@ +items: + default:hami_melon_slice: + material: melon_slice + data: + item-name: + $$>=1.20.5: + food: + nutrition: 2 + saturation: 1.0 + can-always-eat: false + $$<=1.20.4: + settings: + food: + nutrition: 2 + saturation: 1.0 + model: + template: default:model/simplified_generated + arguments: + path: minecraft:item/custom/hami_melon_slice + default:hami_melon: + material: nether_brick + data: + item-name: + model: + path: minecraft:item/custom/hami_melon + generation: + parent: minecraft:block/custom/hami_melon + behavior: + type: block_item + block: default:hami_melon + default:hami_melon_seeds: + material: nether_brick + data: + item-name: + model: + template: default:model/simplified_generated + arguments: + path: minecraft:item/custom/hami_melon_seeds + behavior: + type: block_item + block: default:hami_melon_stem +blocks: + default:hami_melon: + loot: + pools: + - rolls: 1 + entries: + - type: alternatives + children: + - type: item + item: default:hami_melon + conditions: + - type: enchantment + predicate: minecraft:silk_touch>=1 + - type: item + item: default:hami_melon_slice + functions: + - type: set_count + add: false + count: 3~7 + - type: apply_bonus + enchantment: minecraft:fortune + formula: + type: ore_drops + - type: limit_count + max: 9 + - type: explosion_decay + settings: + template: + - default:sound/wood + - default:hardness/melon + overrides: + map-color: 19 + push-reaction: DESTROY + is-suffocating: true + is-redstone-conductor: true + tags: + - minecraft:enderman_holdable + - minecraft:mineable/axe + - minecraft:sword_efficient + state: + auto-state: solid + model: + template: default:model/cube + arguments: + model: minecraft:block/custom/hami_melon + particle_texture: minecraft:block/custom/hami_melon + down_texture: minecraft:block/custom/hami_melon_bottom + up_texture: minecraft:block/custom/hami_melon_top + north_texture: minecraft:block/custom/hami_melon + east_texture: minecraft:block/custom/hami_melon + south_texture: minecraft:block/custom/hami_melon + west_texture: minecraft:block/custom/hami_melon + default:hami_melon_stem: + settings: + template: + - default:sound/stem_crop + - default:hardness/none + overrides: + map-color: 7 + push-reaction: DESTROY + is-suffocating: false + is-redstone-conductor: false + item: default:hami_melon_seeds + is-randomly-ticking: true + tags: + - minecraft:bee_growables + - minecraft:crops + - minecraft:maintains_farmland + behaviors: + - type: stem_block + fruit: default:hami_melon + attached-stem: default:attached_hami_melon_stem + - type: bush_block + bottom-blocks: + - minecraft:farmland + states: + properties: + age: + type: int + default: 0 + range: 0~7 + appearances: + age=0: + state: pumpkin_stem[age=0] + age=1: + state: pumpkin_stem[age=1] + age=2: + state: pumpkin_stem[age=2] + age=3: + state: pumpkin_stem[age=3] + age=4: + state: pumpkin_stem[age=4] + age=5: + state: pumpkin_stem[age=5] + age=6: + state: pumpkin_stem[age=6] + age=7: + state: pumpkin_stem[age=7] + variants: + age=0: + appearance: age=0 + age=1: + appearance: age=1 + age=2: + appearance: age=2 + age=3: + appearance: age=3 + age=4: + appearance: age=4 + age=5: + appearance: age=5 + age=6: + appearance: age=6 + age=7: + appearance: age=7 + default:attached_hami_melon_stem: + settings: + template: + - default:sound/stem_crop + - default:hardness/none + overrides: + map-color: 7 + push-reaction: DESTROY + is-suffocating: false + is-redstone-conductor: false + item: default:hami_melon_seeds + is-randomly-ticking: true + tags: + - minecraft:maintains_farmland + behaviors: + - type: attached_stem_block + fruit: default:hami_melon + stem: default:hami_melon_stem + - type: bush_block + blacklist: false + bottom-blocks: + - minecraft:farmland + states: + properties: + facing: + type: horizontal_direction + default: north + appearances: + facing=east: + state: attached_pumpkin_stem[facing=east] + facing=south: + state: attached_pumpkin_stem[facing=south] + facing=west: + state: attached_pumpkin_stem[facing=west] + facing=north: + state: attached_pumpkin_stem[facing=north] + variants: + facing=east: + appearance: facing=east + facing=south: + appearance: facing=south + facing=west: + appearance: facing=west + facing=north: + appearance: facing=north +recipes: + default:hami_melon: + type: shaped + pattern: + - AAA + - AAA + - AAA + ingredients: + A: default:hami_melon_slice + result: + id: default:hami_melon + count: 1 + default:hami_melon_seeds: + type: shapeless + ingredients: + - default:hami_melon_slice + result: + id: default:hami_melon_seeds + count: 1 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/netherite_anvil.yml b/common-files/src/main/resources/resources/default/configuration/blocks/netherite_anvil.yml new file mode 100644 index 000000000..467ff359b --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/netherite_anvil.yml @@ -0,0 +1,90 @@ +items: + default:netherite_anvil: + material: nether_brick + data: + item-name: + model: + type: minecraft:model + path: minecraft:item/custom/netherite_anvil + generation: + parent: minecraft:block/custom/netherite_anvil + behavior: + type: block_item + block: + loot: + template: default:loot_table/self + behavior: + type: falling_block + hurt-amount: 4 + max-hurt: 80 + sounds: + land: minecraft:block.anvil.land + destroy: minecraft:block.anvil.destroy + events: + - on: right_click + functions: + - type: open_window + gui-type: anvil + - type: cancel_event + conditions: + - type: expression + expression: '!' + settings: + template: + - default:pickaxe_power/level_4 + overrides: + tags: + - minecraft:mineable/pickaxe + sounds: + break: minecraft:block.anvil.break + step: minecraft:block.anvil.step + place: minecraft:block.anvil.place + hit: minecraft:block.anvil.hit + fall: minecraft:block.anvil.fall + map-color: 29 + hardness: 10.0 + resistance: 1200 + push-reaction: block + states: + properties: + facing_clockwise: + type: 4-direction + default: north + appearances: + axisX: + state: minecraft:anvil[facing=east] + model: + path: minecraft:block/custom/netherite_anvil + y: 90 + generation: + parent: minecraft:block/anvil + textures: + top: minecraft:block/custom/netherite_anvil_top + body: minecraft:block/custom/netherite_anvil + particle: minecraft:block/custom/netherite_anvil + axisZ: + state: minecraft:anvil[facing=north] + model: + path: minecraft:block/custom/netherite_anvil + variants: + facing_clockwise=east: + appearance: axisX + facing_clockwise=west: + appearance: axisX + facing_clockwise=north: + appearance: axisZ + facing_clockwise=south: + appearance: axisZ +recipes: + default:netherite_anvil: + type: shaped + pattern: + - ' B ' + - BAB + - ' B ' + ingredients: + A: minecraft:anvil + B: minecraft:netherite_ingot + result: + id: default:netherite_anvil + count: 1 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/palm_tree.yml b/common-files/src/main/resources/resources/default/configuration/blocks/palm_tree.yml similarity index 82% rename from common-files/src/main/resources/resources/default/configuration/palm_tree.yml rename to common-files/src/main/resources/resources/default/configuration/blocks/palm_tree.yml index 901dbbb00..6002d41ac 100644 --- a/common-files/src/main/resources/resources/default/configuration/palm_tree.yml +++ b/common-files/src/main/resources/resources/default/configuration/blocks/palm_tree.yml @@ -1,7 +1,6 @@ items: default:palm_log: material: nether_brick - custom-model-data: 1000 settings: fuel-time: 300 tags: @@ -28,22 +27,12 @@ items: states: template: default:block_state/pillar arguments: - base_block: note_block texture_top_path: minecraft:block/custom/palm_log_top texture_side_path: minecraft:block/custom/palm_log model_vertical_path: minecraft:block/custom/palm_log model_horizontal_path: minecraft:block/custom/palm_log_horizontal - vanilla_id: - type: self_increase_int - from: 0 - to: 2 - internal_id: - type: self_increase_int - from: 0 - to: 2 default:stripped_palm_log: material: nether_brick - custom-model-data: 1001 settings: fuel-time: 300 tags: @@ -67,22 +56,12 @@ items: states: template: default:block_state/pillar arguments: - base_block: note_block texture_top_path: minecraft:block/custom/stripped_palm_log_top texture_side_path: minecraft:block/custom/stripped_palm_log model_vertical_path: minecraft:block/custom/stripped_palm_log model_horizontal_path: minecraft:block/custom/stripped_palm_log_horizontal - vanilla_id: - type: self_increase_int - from: 3 - to: 5 - internal_id: - type: self_increase_int - from: 3 - to: 5 default:palm_wood: material: nether_brick - custom-model-data: 1002 settings: fuel-time: 300 tags: @@ -109,22 +88,12 @@ items: states: template: default:block_state/pillar arguments: - base_block: note_block texture_top_path: minecraft:block/custom/palm_log texture_side_path: minecraft:block/custom/palm_log model_vertical_path: minecraft:block/custom/palm_wood model_horizontal_path: minecraft:block/custom/palm_wood_horizontal - vanilla_id: - type: self_increase_int - from: 6 - to: 8 - internal_id: - type: self_increase_int - from: 6 - to: 8 default:stripped_palm_wood: material: nether_brick - custom-model-data: 1003 settings: fuel-time: 300 tags: @@ -148,22 +117,12 @@ items: states: template: default:block_state/pillar arguments: - base_block: note_block texture_top_path: minecraft:block/custom/stripped_palm_log texture_side_path: minecraft:block/custom/stripped_palm_log model_vertical_path: minecraft:block/custom/stripped_palm_wood model_horizontal_path: minecraft:block/custom/stripped_palm_wood_horizontal - vanilla_id: - type: self_increase_int - from: 9 - to: 11 - internal_id: - type: self_increase_int - from: 9 - to: 11 default:palm_planks: material: nether_brick - custom-model-data: 1004 settings: fuel-time: 300 tags: @@ -188,11 +147,9 @@ items: template: default:model/simplified_cube_all arguments: path: minecraft:block/custom/palm_planks - id: 12 - state: note_block:12 + auto-state: solid default:palm_sapling: material: nether_brick - custom-model-data: 1005 settings: fuel-time: 100 data: @@ -226,7 +183,7 @@ items: range: 0~1 appearances: default: - state: oak_sapling:0 + auto-state: sapling model: path: minecraft:block/custom/palm_sapling generation: @@ -236,13 +193,10 @@ items: variants: stage=0: appearance: default - id: 0 stage=1: appearance: default - id: 1 default:palm_leaves: material: oak_leaves - custom-model-data: 1000 data: item-name: components: @@ -277,13 +231,8 @@ items: waterlogged_state: oak_leaves[distance=1,persistent=false,waterlogged=true] model_path: minecraft:block/custom/palm_leaves texture_path: minecraft:block/custom/palm_leaves - internal_id: - type: self_increase_int - from: 0 - to: 27 default:palm_trapdoor: material: nether_brick - custom-model-data: 1006 data: item-name: settings: @@ -338,7 +287,6 @@ items: texture: minecraft:block/custom/palm_trapdoor default:palm_door: material: nether_brick - custom-model-data: 1007 data: item-name: settings: @@ -363,7 +311,7 @@ items: template: - default:sound/wood overrides: - push-reaction: DESTROY + push-reaction: destroy map-color: 2 instrument: bass hardness: 3.0 @@ -413,7 +361,6 @@ items: textures: *textures default:palm_fence_gate: material: nether_brick - custom-model-data: 1008 data: item-name: settings: @@ -470,7 +417,6 @@ items: textures: *textures default:palm_slab: material: nether_brick - custom-model-data: 1009 data: item-name: settings: @@ -517,7 +463,6 @@ items: model_double_path: minecraft:block/custom/palm_planks default:palm_stairs: material: nether_brick - custom-model-data: 1013 model: type: minecraft:model path: minecraft:item/custom/palm_stairs @@ -567,7 +512,6 @@ items: textures: *textures default:palm_pressure_plate: material: nether_brick - custom-model-data: 1014 model: type: minecraft:model path: minecraft:item/custom/palm_pressure_plate @@ -588,7 +532,7 @@ items: - default:hardness/planks overrides: burnable: true - push-reaction: DESTROY + push-reaction: destroy map-color: 2 instrument: bass tags: @@ -608,8 +552,6 @@ items: arguments: normal_state: light_weighted_pressure_plate:0 powered_state: light_weighted_pressure_plate:1 - normal_id: 0 - powered_id: 1 model_normal_path: minecraft:block/custom/palm_pressure_plate model_normal_generation: parent: minecraft:block/pressure_plate_up @@ -620,6 +562,140 @@ items: parent: minecraft:block/pressure_plate_down textures: texture: minecraft:block/custom/palm_planks + +items#pfence: + default:palm_fence: + material: nether_brick + data: + item-name: + model: + type: minecraft:model + path: minecraft:item/custom/palm_fence_inventory + generation: + parent: minecraft:block/fence_inventory + textures: + texture: minecraft:block/custom/palm_planks + behavior: + type: block_item + block: default:palm_fence + default:palm_fence_post: + material: nether_brick + model: + type: minecraft:model + path: minecraft:block/custom/palm_fence_post + generation: + parent: minecraft:block/fence_post + textures: + texture: minecraft:block/custom/palm_planks + default:palm_fence_side: + material: nether_brick + model: + type: minecraft:model + path: minecraft:block/custom/palm_fence_side + generation: + parent: minecraft:block/custom/fence_side + textures: + texture: minecraft:block/custom/palm_planks +blocks#fence: + default:palm_fence: + loot: + template: default:loot_table/self + settings: + template: + - default:hardness/planks + - default:sound/wood + overrides: + burn-chance: 5 + fire-spread-chance: 20 + burnable: true + is-suffocating: false + is-redstone-conductor: false + push-reaction: NORMAL + instrument: BASS + map-color: 2 + tags: + - minecraft:fences + - minecraft:mineable/axe + - minecraft:wooden_fences + behavior: + type: fence_block + self-tag: fences + connectable-tag: wooden_fences + can-leash: true + states: + template: default:block_state/fence + arguments: + base_block: oak_fence + fence_post_item: default:palm_fence_post + fence_side_item: default:palm_fence_side + +items#button: + default:palm_button: + material: nether_brick + model: + type: minecraft:model + path: minecraft:item/custom/palm_button + generation: + parent: minecraft:block/button_inventory + textures: + texture: minecraft:block/custom/palm_planks + data: + item-name: + settings: + fuel-time: 100 + behavior: + type: block_item + block: default:palm_button + default:palm_button_pressed: + material: nether_brick + model: + type: minecraft:model + path: minecraft:block/custom/palm_button_pressed + generation: + parent: minecraft:block/button_pressed + textures: + texture: minecraft:block/custom/palm_planks + default:palm_button_not_pressed: + material: nether_brick + model: + type: minecraft:model + path: minecraft:block/custom/palm_button_not_pressed + generation: + parent: minecraft:block/button + textures: + texture: minecraft:block/custom/palm_planks +blocks#button: + default:palm_button: + loot: + template: default:loot_table/self + settings: + template: + - default:sound/wood + - default:hardness/button + overrides: + burnable: true + push-reaction: destroy + map-color: 2 + instrument: harp + tags: + - minecraft:buttons + - minecraft:mineable/axe + - minecraft:wooden_buttons + behaviors: + - type: face_attached_horizontal_directional_block + - type: button_block + ticks-to-stay-pressed: 30 + can-be-activated-by-arrows: true + sounds: + on: minecraft:block.wooden_button.click_on + off: minecraft:block.wooden_button.click_off + states: + template: default:block_state/button + arguments: + base_block: birch_button + pressed_item: default:palm_button_pressed + not_pressed_item: default:palm_button_not_pressed + recipes: default:palm_planks: template: default:recipe/planks @@ -687,11 +763,29 @@ recipes: default:palm_stairs: type: shaped pattern: - - 'A ' - - 'AA ' - - 'AAA' + - "A " + - "AA " + - "AAA" ingredients: A: default:palm_planks result: id: default:palm_stairs count: 4 + default:palm_button: + type: shapeless + ingredients: + - default:palm_planks + result: + id: default:palm_button + count: 1 + default:palm_fence: + type: shaped + pattern: + - ABA + - ABA + ingredients: + A: default:palm_planks + B: minecraft:stick + result: + id: default:palm_fence + count: 3 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/pebble.yml b/common-files/src/main/resources/resources/default/configuration/blocks/pebble.yml new file mode 100644 index 000000000..23fe24d71 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/pebble.yml @@ -0,0 +1,126 @@ +items: + default:pebble: + material: nether_brick + data: + item-name: + model: + template: default:model/simplified_generated + arguments: + path: minecraft:item/custom/pebble + behavior: + - type: block_item + block: + settings: + template: + - default:sound/stone + - default:hardness/none + overrides: + map-color: 11 + push-reaction: destroy + behaviors: + - type: sturdy_base_block + direction: down + support-types: + - full + - type: stackable_block + property: pebble + items: + - default:pebble + sounds: + stack: minecraft:block.stone.fall + loot: + pools: + - rolls: 1 + entries: + - type: item + item: default:pebble + functions: + - type: set_count + count: 3 + add: false + conditions: + - type: match_block_property + properties: + pebble: 3 + - type: set_count + count: 2 + add: false + conditions: + - type: match_block_property + properties: + pebble: 2 + - type: explosion_decay + states: + properties: + pebble: + type: int + range: 1~3 + default: 1 + appearances: + one: + auto-state: lower_tripwire + models: + - path: minecraft:block/custom/pebble_1 + weight: 1 + - path: minecraft:block/custom/pebble_1 + weight: 1 + y: 90 + - path: minecraft:block/custom/pebble_1 + weight: 1 + y: 180 + - path: minecraft:block/custom/pebble_1 + weight: 1 + y: 270 + two: + auto-state: lower_tripwire + models: + - path: minecraft:block/custom/pebble_2 + weight: 1 + - path: minecraft:block/custom/pebble_2 + weight: 1 + y: 90 + - path: minecraft:block/custom/pebble_2 + weight: 1 + y: 180 + - path: minecraft:block/custom/pebble_2 + weight: 1 + y: 270 + three: + auto-state: higher_tripwire + models: + - path: minecraft:block/custom/pebble_3 + weight: 1 + - path: minecraft:block/custom/pebble_3 + weight: 1 + y: 90 + - path: minecraft:block/custom/pebble_3 + weight: 1 + y: 180 + - path: minecraft:block/custom/pebble_3 + weight: 1 + y: 270 + variants: + pebble=1: + appearance: 'one' + pebble=2: + appearance: 'two' + pebble=3: + appearance: 'three' +recipes: + default:pebble: + type: shapeless + ingredients: + - minecraft:cobblestone + result: + id: default:pebble + count: 4 + default:cobblestone_from_pebble: + type: shaped + pattern: + - AA + - AA + ingredients: + A: default:pebble + result: + id: minecraft:cobblestone + count: 1 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/reed.yml b/common-files/src/main/resources/resources/default/configuration/blocks/reed.yml new file mode 100644 index 000000000..de1e00be9 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/reed.yml @@ -0,0 +1,40 @@ +items: + default:reed: + material: nether_brick + data: + item-name: + model: + template: default:model/simplified_generated + arguments: + path: minecraft:item/custom/reed + behavior: + type: liquid_collision_block_item + block: + settings: + template: + - default:hardness/none + - default:sound/grass + overrides: + push-reaction: destroy + map-color: 60 + behavior: + type: on_liquid_block + liquid-type: water + positions: + - 0,-1,0 + loot: + template: default:loot_table/self + state: + auto-state: sugar_cane + model: + path: minecraft:block/custom/reed +recipes: + default:paper_from_reed: + type: shaped + pattern: + - AAA + ingredients: + A: default:reed + result: + id: minecraft:paper + count: 3 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/safe_block.yml b/common-files/src/main/resources/resources/default/configuration/blocks/safe_block.yml new file mode 100644 index 000000000..63b13e353 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/safe_block.yml @@ -0,0 +1,129 @@ +items: + default:safe_block: + material: nether_brick + data: + item-name: + model: + type: minecraft:model + path: minecraft:item/custom/safe_block + generation: + parent: minecraft:block/custom/safe_block + behavior: + type: block_item + block: + loot: + template: default:loot_table/self + settings: + hardness: 5 + resistance: 1200 + is-suffocating: true + is-redstone-conductor: true + push-reaction: block + instrument: basedrum + map-color: 6 + tags: + - minecraft:mineable/pickaxe + sounds: + break: minecraft:block.stone.break + fall: minecraft:block.stone.fall + hit: minecraft:block.stone.hit + place: minecraft:block.stone.place + step: minecraft:block.stone.step + behavior: + type: simple_storage_block + title: "" + rows: 1 + sounds: + open: minecraft:block.iron_trapdoor.open + close: minecraft:block.iron_trapdoor.close + allow-input: true + allow-output: false + states: + properties: + facing: + type: 4-direction + default: north + open: + type: boolean + default: false + appearances: + east: + auto-state: note_block + model: + path: minecraft:block/custom/safe_block + y: 90 + generation: + parent: minecraft:block/orientable + textures: + front: minecraft:block/custom/safe_block_front + side: minecraft:block/custom/safe_block_side + top: minecraft:block/custom/safe_block_top + east_open: + auto-state: note_block + model: + path: minecraft:block/custom/safe_block_open + y: 90 + generation: + parent: minecraft:block/orientable + textures: + front: minecraft:block/custom/safe_block_front_open + side: minecraft:block/custom/safe_block_side + top: minecraft:block/custom/safe_block_top + north: + auto-state: note_block + model: + path: minecraft:block/custom/safe_block + north_open: + auto-state: note_block + model: + path: minecraft:block/custom/safe_block_open + south: + auto-state: note_block + model: + path: minecraft:block/custom/safe_block + y: 180 + south_open: + auto-state: note_block + model: + path: minecraft:block/custom/safe_block_open + y: 180 + west: + auto-state: note_block + model: + path: minecraft:block/custom/safe_block + y: 270 + west_open: + auto-state: note_block + model: + path: minecraft:block/custom/safe_block_open + y: 270 + variants: + facing=east,open=false: + appearance: east + facing=east,open=true: + appearance: east_open + facing=north,open=false: + appearance: north + facing=north,open=true: + appearance: north_open + facing=south,open=false: + appearance: south + facing=south,open=true: + appearance: south_open + facing=west,open=false: + appearance: west + facing=west,open=true: + appearance: west_open +recipes: + default:safe_block: + type: shaped + pattern: + - 'AAA' + - 'ABA' + - 'AAA' + ingredients: + A: minecraft:iron_ingot + B: ["minecraft:barrel", "minecraft:chest"] + result: + id: default:safe_block + count: 1 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/sofa.yml b/common-files/src/main/resources/resources/default/configuration/blocks/sofa.yml new file mode 100644 index 000000000..73ffa6029 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/sofa.yml @@ -0,0 +1,170 @@ +items: + default:sleeper_sofa: + material: nether_brick + data: + item-name: + model: + type: minecraft:model + path: minecraft:item/custom/sleeper_sofa + behavior: + type: block_item + block: + loot: + template: default:loot_table/self + settings: + hardness: 0.5 + resistance: 0.5 + map-color: 27 + burn-chance: 5 + fire-spread-chance: 20 + burnable: true + is-suffocating: false + is-redstone-conductor: false + push-reaction: block + instrument: bass + support-shape: cobweb + sounds: + break: minecraft:block.wood.break + fall: minecraft:block.wood.fall + hit: minecraft:block.wood.hit + place: minecraft:block.wood.place + step: minecraft:block.wood.step + tags: + - minecraft:mineable/axe + behaviors: + - type: bouncing_block + bounce-height: 0.66 + sync-player-position: false + state: + state: white_bed[facing=west,occupied=false,part=foot] + entity-renderer: + item: default:sleeper_sofa + default:sofa_inner: + material: nether_brick + model: + type: minecraft:model + path: minecraft:item/custom/sofa_inner + default:sofa: + material: nether_brick + data: + item-name: + model: + type: minecraft:model + path: minecraft:item/custom/sofa + behavior: + type: block_item + block: + loot: + template: default:loot_table/self + settings: + hardness: 0.5 + resistance: 0.5 + map-color: 27 + burn-chance: 5 + fire-spread-chance: 20 + burnable: true + is-suffocating: false + is-redstone-conductor: false + push-reaction: block + instrument: bass + sounds: + break: minecraft:block.wood.break + fall: minecraft:block.wood.fall + hit: minecraft:block.wood.hit + place: minecraft:block.wood.place + step: minecraft:block.wood.step + tags: + - minecraft:mineable/axe + behaviors: + - type: sofa_block + - type: bouncing_block + bounce-height: 0.66 + states: + properties: + facing: + type: horizontal_direction + shape: + type: sofa_shape + appearances: + facing=east,shape=straight: + state: barrier + entity-renderer: + item: default:sofa + yaw: 90 + facing=north,shape=straight: + state: barrier + entity-renderer: + item: default:sofa + facing=south,shape=straight: + state: barrier + entity-renderer: + item: default:sofa + yaw: 180 + facing=west,shape=straight: + state: barrier + entity-renderer: + item: default:sofa + yaw: 270 + facing=east,shape=inner_left: + state: barrier + entity-renderer: + item: default:sofa_inner + facing=north,shape=inner_left: + state: barrier + entity-renderer: + item: default:sofa_inner + yaw: 270 + facing=south,shape=inner_left: + state: barrier + entity-renderer: + item: default:sofa_inner + yaw: 90 + facing=west,shape=inner_left: + state: barrier + entity-renderer: + item: default:sofa_inner + yaw: 180 + facing=east,shape=inner_right: + state: barrier + entity-renderer: + item: default:sofa_inner + yaw: 90 + facing=north,shape=inner_right: + state: barrier + entity-renderer: + item: default:sofa_inner + facing=south,shape=inner_right: + state: barrier + entity-renderer: + item: default:sofa_inner + yaw: 180 + facing=west,shape=inner_right: + state: barrier + entity-renderer: + item: default:sofa_inner + yaw: 270 + variants: + facing=east,shape=inner_left: + appearance: facing=east,shape=inner_left + facing=east,shape=inner_right: + appearance: facing=east,shape=inner_right + facing=east,shape=straight: + appearance: facing=east,shape=straight + facing=north,shape=inner_left: + appearance: facing=north,shape=inner_left + facing=north,shape=inner_right: + appearance: facing=north,shape=inner_right + facing=north,shape=straight: + appearance: facing=north,shape=straight + facing=south,shape=inner_left: + appearance: facing=south,shape=inner_left + facing=south,shape=inner_right: + appearance: facing=south,shape=inner_right + facing=south,shape=straight: + appearance: facing=south,shape=straight + facing=west,shape=inner_left: + appearance: facing=west,shape=inner_left + facing=west,shape=inner_right: + appearance: facing=west,shape=inner_right + facing=west,shape=straight: + appearance: facing=west,shape=straight \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/table_lamp.yml b/common-files/src/main/resources/resources/default/configuration/blocks/table_lamp.yml new file mode 100644 index 000000000..7f7165c56 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/table_lamp.yml @@ -0,0 +1,126 @@ +items: + default:table_lamp: + material: nether_brick + data: + item-name: + model: + type: minecraft:model + path: minecraft:item/custom/table_lamp + behavior: + type: block_item + block: + loot: + template: default:loot_table/self + settings: + template: + - default:sound/lantern + overrides: + hardness: 0.5 + resistance: 1 + replaceable: false + is-redstone-conductor: false + is-suffocating: false + support-shape: cobweb + behaviors: + - type: toggleable_lamp_block + can-toggle-with-hand: true + - type: sturdy_base_block + direction: down + support-types: + - full + - center + states: + properties: + lit: + type: boolean + default: false + facing: + type: 4-direction + default: north + appearances: + east_off: + state: barrier + entity-renderer: + item: default:table_lamp + north_off: + state: barrier + entity-renderer: + item: default:table_lamp + yaw: -90 + south_off: + state: barrier + entity-renderer: + item: default:table_lamp + yaw: 90 + west_off: + state: barrier + entity-renderer: + item: default:table_lamp + yaw: 180 + east_on: + state: barrier + entity-renderer: + item: default:table_lamp_on + north_on: + state: barrier + entity-renderer: + item: default:table_lamp_on + yaw: -90 + south_on: + state: barrier + entity-renderer: + item: default:table_lamp_on + yaw: 90 + west_on: + state: barrier + entity-renderer: + item: default:table_lamp_on + yaw: 180 + variants: + facing=east,lit=false: + appearance: east_off + facing=north,lit=false: + appearance: north_off + facing=south,lit=false: + appearance: south_off + facing=west,lit=false: + appearance: west_off + facing=east,lit=true: + appearance: east_on + settings: + luminance: 15 + facing=north,lit=true: + appearance: north_on + settings: + luminance: 15 + facing=south,lit=true: + appearance: south_on + settings: + luminance: 15 + facing=west,lit=true: + appearance: west_on + settings: + luminance: 15 + default:table_lamp_on: + material: nether_brick + model: + type: minecraft:model + path: minecraft:item/custom/table_lamp_on + generation: + parent: minecraft:item/custom/table_lamp + textures: + "0": "minecraft:item/custom/table_lamp_on" +recipes: + default:table_lamp: + type: shaped + pattern: + - 'BA' + - ' A' + - 'CA' + ingredients: + A: minecraft:iron_ingot + B: minecraft:redstone_lamp + C: minecraft:lever + result: + id: default:table_lamp + count: 1 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/ores.yml b/common-files/src/main/resources/resources/default/configuration/blocks/topaz_ore.yml similarity index 94% rename from common-files/src/main/resources/resources/default/configuration/ores.yml rename to common-files/src/main/resources/resources/default/configuration/blocks/topaz_ore.yml index 0513a1191..f120a9776 100644 --- a/common-files/src/main/resources/resources/default/configuration/ores.yml +++ b/common-files/src/main/resources/resources/default/configuration/blocks/topaz_ore.yml @@ -1,7 +1,6 @@ items: default:topaz_ore: material: nether_brick - custom-model-data: 1010 data: item-name: model: @@ -14,7 +13,6 @@ items: block: default:topaz_ore default:deepslate_topaz_ore: material: nether_brick - custom-model-data: 1011 data: item-name: model: @@ -27,7 +25,6 @@ items: block: default:deepslate_topaz_ore default:topaz: material: nether_brick - custom-model-data: 1012 settings: anvil-repair-item: - target: @@ -53,8 +50,7 @@ blocks: arguments: break_power: 2 state: - id: 13 - state: note_block:13 + auto-state: solid model: template: default:model/simplified_cube_all arguments: @@ -72,8 +68,7 @@ blocks: arguments: break_power: 2 state: - id: 14 - state: note_block:14 + auto-state: solid model: template: default:model/simplified_cube_all arguments: diff --git a/common-files/src/main/resources/resources/default/configuration/categories.yml b/common-files/src/main/resources/resources/default/configuration/categories.yml index 79c4e3fa9..a1a9a71c8 100644 --- a/common-files/src/main/resources/resources/default/configuration/categories.yml +++ b/common-files/src/main/resources/resources/default/configuration/categories.yml @@ -8,7 +8,6 @@ categories: list: - '#default:palm_tree' - '#default:topaz' - - '#default:furniture' - '#default:misc' default:palm_tree: name: @@ -18,16 +17,18 @@ categories: - default:palm_sapling - default:palm_leaves - default:palm_log - - default:stripped_palm_log - default:palm_wood + - default:stripped_palm_log - default:stripped_palm_wood - default:palm_planks - - default:palm_trapdoor - - default:palm_door - - default:palm_fence_gate - - default:palm_slab - default:palm_stairs + - default:palm_slab + - default:palm_fence + - default:palm_fence_gate + - default:palm_door + - default:palm_trapdoor - default:palm_pressure_plate + - default:palm_button default:topaz: name: <#FF8C00> hidden: true @@ -49,15 +50,6 @@ categories: - default:topaz_chestplate - default:topaz_leggings - default:topaz_boots - default:furniture: - name: <#FFD700> - hidden: true - icon: default:table_lamp - list: - - default:bench - - default:table_lamp - - default:wooden_chair - - default:flower_basket default:misc: name: hidden: true @@ -78,4 +70,17 @@ categories: - default:flame_elytra - default:cap - default:pebble - - default:chessboard_block \ No newline at end of file + - default:chessboard_block + - default:safe_block + - default:sleeper_sofa + - minecraft:air + - minecraft:air + - default:sofa + - default:table_lamp + - default:bench + - default:wooden_chair + - default:flower_basket + - default:amethyst_torch + - default:hami_melon_seeds + - default:hami_melon_slice + - default:hami_melon \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/emoji.yml b/common-files/src/main/resources/resources/default/configuration/emoji.yml index ef8173f57..06262afd2 100644 --- a/common-files/src/main/resources/resources/default/configuration/emoji.yml +++ b/common-files/src/main/resources/resources/default/configuration/emoji.yml @@ -1,28 +1,7 @@ templates: default:emoji/basic: content: '> - default:emoji/addition_info: - content: '>${text} emoji: - default:emoji_location: - template: default:emoji/addition_info - arguments: - text: - overrides: - image: default:icons:0:0 - permission: emoji.location - keywords: - - ':location:' - - ':pos:' - default:emoji_time: - template: default:emoji/addition_info - arguments: - text: - overrides: - image: default:icons:0:1 - permission: emoji.time - keywords: - - ':time:' default:emoji_smiley: template: default:emoji/basic overrides: @@ -143,8 +122,4 @@ images: ascent: 9 font: minecraft:emoji file: minecraft:font/image/emojis.png - chars: - - \ub000\ub001\ub002\ub003 - - \ub004\ub005\ub006\ub007 - - \ub008\ub009\ub00a\ub00b - - \ub00c\ub00d\ub00e\ub00f \ No newline at end of file + grid-size: 4,4 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/fix_client_visual.yml b/common-files/src/main/resources/resources/default/configuration/fix_client_visual.yml deleted file mode 100644 index 440e9964b..000000000 --- a/common-files/src/main/resources/resources/default/configuration/fix_client_visual.yml +++ /dev/null @@ -1,30 +0,0 @@ -# When placing tripwires, the client predicts their block states. -# By specifying the state type, we can prevent certain visual issues during placement. -items: - minecraft:string: - client-bound-data: - components: - minecraft:block_state: - attached: 'false' - disarmed: 'false' - east: 'true' - north: 'true' - powered: 'true' - south: 'true' - west: 'true' - minecraft:note_block: - client-bound-data: - components: - minecraft:block_state: - instrument: harp - powered: 'false' - note: '0' -# For the client to determine if a beacon can activate, it needs the beacon_base_blocks tag to render the beam. -# This allows custom blocks (like note blocks) to work as beacon bases. -# However, whether the beacon actually grants potion effects depends on the block's real tag (server-side check). -blocks: - minecraft:note_block: - settings: - client-bound-tags: - - minecraft:beacon_base_blocks - - minecraft:mineable/axe \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/furniture.yml b/common-files/src/main/resources/resources/default/configuration/furniture.yml deleted file mode 100644 index 23212868d..000000000 --- a/common-files/src/main/resources/resources/default/configuration/furniture.yml +++ /dev/null @@ -1,244 +0,0 @@ -items: - default:bench: - material: nether_brick - custom-model-data: 2000 - data: - item-name: - model: - type: minecraft:model - path: minecraft:item/custom/bench - behavior: - type: furniture_item - furniture: - settings: - item: default:bench - sounds: - break: minecraft:block.bamboo_wood.break - place: minecraft:block.bamboo_wood.place - placement: - ground: - loot-spawn-offset: 0.5,0.5,0 - rules: - rotation: FOUR - alignment: CENTER - elements: - - item: default:bench - display-transform: NONE - billboard: FIXED - position: 0.5,0,0 - translation: 0,0.5,0 - hitboxes: - - position: 0,0,0 - type: shulker - direction: east - peek: 100 - blocks-building: true - interactive: true - interaction-entity: true - seats: - - 0,0,-0.1 0 - - 1,0,-0.1 0 - loot: - template: default:loot_table/furniture - arguments: - item: default:bench - default:table_lamp: - material: nether_brick - custom-model-data: 2001 - data: - item-name: - model: - type: minecraft:model - path: minecraft:item/custom/table_lamp - behavior: - type: furniture_item - furniture: - settings: - item: default:table_lamp - sounds: - break: minecraft:block.lantern.break - place: minecraft:block.lantern.place - placement: - ground: - loot-spawn-offset: 0,0.2,0 - rules: - rotation: ANY - alignment: QUARTER - elements: - - item: default:table_lamp - display-transform: NONE - billboard: FIXED - translation: 0,0.5,0 - rotation: 90 - hitboxes: - - position: 0,0,0 - type: interaction - blocks-building: true - width: 0.7 - height: 0.1 - interactive: true - - position: 0,0.1,-0.1 - type: interaction - blocks-building: true - width: 0.1 - height: 0.6 - interactive: true - - position: 0,0.6,0.15 - type: interaction - blocks-building: true - width: 0.4 - height: 0.4 - interactive: true - loot: - template: default:loot_table/furniture - arguments: - item: default:table_lamp - default:wooden_chair: - material: nether_brick - custom-model-data: 2002 - data: - item-name: - model: - type: minecraft:model - path: minecraft:item/custom/wooden_chair - behavior: - type: furniture_item - furniture: - settings: - item: default:wooden_chair - sounds: - break: minecraft:block.bamboo_wood.break - place: minecraft:block.bamboo_wood.place - placement: - ground: - loot-spawn-offset: 0,0.4,0 - rules: - rotation: ANY - alignment: ANY - elements: - - item: default:wooden_chair - display-transform: NONE - billboard: FIXED - translation: 0,0.5,0 - hitboxes: - - position: 0,0,0 - type: interaction - blocks-building: true - width: 0.7 - height: 1.2 - interactive: true - seats: - - 0,0,-0.1 0 - loot: - template: default:loot_table/furniture - arguments: - item: default:wooden_chair - -items#flower_basket: - default:flower_basket: - material: nether_brick - custom-model-data: 2003 - data: - item-name: - model: - template: default:model/simplified_generated - arguments: - path: minecraft:item/custom/flower_basket_2d - behavior: - type: furniture_item - furniture: default:flower_basket - default:flower_basket_ground: - material: nether_brick - custom-model-data: 2004 - model: - type: minecraft:model - path: minecraft:item/custom/flower_basket_ground - default:flower_basket_wall: - material: nether_brick - custom-model-data: 2005 - model: - type: minecraft:model - path: minecraft:item/custom/flower_basket_wall - default:flower_basket_ceiling: - material: nether_brick - custom-model-data: 2006 - model: - type: minecraft:model - path: minecraft:item/custom/flower_basket_ceiling - -furniture#flower_basket: - default:flower_basket: - settings: - item: default:flower_basket - sounds: - break: minecraft:block.grass.break - place: minecraft:block.grass.place - loot: - template: default:loot_table/furniture - arguments: - item: default:flower_basket - placement: - ground: - rules: - rotation: ANY - alignment: ANY - elements: - - item: default:flower_basket_ground - display-transform: NONE - billboard: FIXED - position: 0,0,0 - translation: 0,0.5,0 - hitboxes: - - type: interaction - can-use-item-on: true - can-be-hit-by-projectile: true - blocks-building: true - position: 0,0,0 - width: 0.7 - height: 0.5 - interactive: true - wall: - rules: - alignment: ANY - elements: - - item: default:flower_basket_wall - display-transform: NONE - billboard: FIXED - position: 0,0,0.2 - translation: 0,0,0 - hitboxes: - - type: interaction - can-use-item-on: true - can-be-hit-by-projectile: true - blocks-building: true - position: 0.215,-0.3,0.23 - width: 0.46 - height: 0.75 - interactive: true - - type: interaction - can-use-item-on: true - can-be-hit-by-projectile: true - blocks-building: true - position: -0.215,-0.3,0.23 - width: 0.46 - height: 0.75 - interactive: true - ceiling: - rules: - rotation: ANY - alignment: ANY - elements: - - item: default:flower_basket_ceiling - display-transform: NONE - billboard: FIXED - position: 0,-0.46,0 - translation: 0,0,0 - hitboxes: - - type: interaction - can-use-item-on: true - can-be-hit-by-projectile: true - blocks-building: true - position: 0,-0.7,0 - width: 0.7 - height: 0.7 - interactive: true \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/furniture/bench.yml b/common-files/src/main/resources/resources/default/configuration/furniture/bench.yml new file mode 100644 index 000000000..16bab5968 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/furniture/bench.yml @@ -0,0 +1,43 @@ +items: + default:bench: + material: nether_brick + data: + item-name: + model: + type: minecraft:model + path: minecraft:item/custom/bench + behavior: + type: furniture_item + furniture: + settings: + item: default:bench + sounds: + break: minecraft:block.bamboo_wood.break + place: minecraft:block.bamboo_wood.place + placement: + ground: + loot-spawn-offset: 0.5,0.5,0 + rules: + rotation: FOUR + alignment: CENTER + elements: + - item: default:bench + display-transform: NONE + billboard: FIXED + position: 0.5,0,0 + translation: 0,0.5,0 + hitboxes: + - position: 0,0,0 + type: shulker + direction: east + peek: 100 + blocks-building: true + interactive: true + interaction-entity: true + seats: + - 0,0,-0.1 0 + - 1,0,-0.1 0 + loot: + template: default:loot_table/furniture + arguments: + item: default:bench \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/furniture/flower_basket.yml b/common-files/src/main/resources/resources/default/configuration/furniture/flower_basket.yml new file mode 100644 index 000000000..fff66d0d4 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/furniture/flower_basket.yml @@ -0,0 +1,103 @@ +items: + default:flower_basket: + material: nether_brick + data: + item-name: + model: + template: default:model/simplified_generated + arguments: + path: minecraft:item/custom/flower_basket_2d + behavior: + type: furniture_item + furniture: default:flower_basket + default:flower_basket_ground: + material: nether_brick + model: + type: minecraft:model + path: minecraft:item/custom/flower_basket_ground + default:flower_basket_wall: + material: nether_brick + model: + type: minecraft:model + path: minecraft:item/custom/flower_basket_wall + default:flower_basket_ceiling: + material: nether_brick + model: + type: minecraft:model + path: minecraft:item/custom/flower_basket_ceiling +furniture: + default:flower_basket: + settings: + item: default:flower_basket + sounds: + break: minecraft:block.grass.break + place: minecraft:block.grass.place + loot: + template: default:loot_table/furniture + arguments: + item: default:flower_basket + placement: + ground: + rules: + rotation: ANY + alignment: ANY + elements: + - item: default:flower_basket_ground + display-transform: NONE + billboard: FIXED + position: 0,0,0 + translation: 0,0.5,0 + hitboxes: + - type: interaction + can-use-item-on: true + can-be-hit-by-projectile: true + blocks-building: true + position: 0,0,0 + width: 0.7 + height: 0.5 + interactive: true + wall: + rules: + alignment: ANY + elements: + - item: default:flower_basket_wall + display-transform: NONE + billboard: FIXED + position: 0,0,0.2 + translation: 0,0,0 + hitboxes: + - type: interaction + can-use-item-on: true + can-be-hit-by-projectile: true + blocks-building: true + position: 0.215,-0.3,0.23 + width: 0.46 + height: 0.75 + interactive: true + - type: interaction + can-use-item-on: true + can-be-hit-by-projectile: true + blocks-building: true + position: -0.215,-0.3,0.23 + width: 0.46 + height: 0.75 + interactive: true + ceiling: + rules: + rotation: ANY + alignment: ANY + elements: + - item: default:flower_basket_ceiling + display-transform: NONE + billboard: FIXED + position: 0,-0.46,0 + translation: 0,0,0 + hitboxes: + - type: interaction + can-use-item-on: true + can-be-hit-by-projectile: true + blocks-building: true + position: 0,-0.7,0 + width: 0.7 + height: 0.7 + interactive: true \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml b/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml new file mode 100644 index 000000000..ef848f6fc --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml @@ -0,0 +1,40 @@ +items: + default:wooden_chair: + material: nether_brick + data: + item-name: + model: + type: minecraft:model + path: minecraft:item/custom/wooden_chair + behavior: + type: furniture_item + furniture: + settings: + item: default:wooden_chair + sounds: + break: minecraft:block.bamboo_wood.break + place: minecraft:block.bamboo_wood.place + placement: + ground: + loot-spawn-offset: 0,0.4,0 + rules: + rotation: ANY + alignment: ANY + elements: + - item: default:wooden_chair + display-transform: NONE + billboard: FIXED + translation: 0,0.5,0 + hitboxes: + - position: 0,0,0 + type: interaction + blocks-building: true + width: 0.7 + height: 1.2 + interactive: true + seats: + - 0,0,-0.1 0 + loot: + template: default:loot_table/furniture + arguments: + item: default:wooden_chair \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/i18n.yml b/common-files/src/main/resources/resources/default/configuration/i18n.yml index a2cfc0ddc..a0c7eea40 100644 --- a/common-files/src/main/resources/resources/default/configuration/i18n.yml +++ b/common-files/src/main/resources/resources/default/configuration/i18n.yml @@ -46,11 +46,18 @@ i18n: item.cap: Cap item.flower_basket: Flower Basket item.chessboard_block: Chessboard Block + item.safe_block: Safe Block + item.sofa: Sofa + item.amethyst_torch: Amethyst Torch + item.hami_melon_slice: Hami Melon Slice + item.hami_melon: Hami Melon + item.hami_melon_seeds: Hami Melon Seeds + item.palm_button: Palm Button + item.palm_fence: Palm Fence category.default.name: Default Assets category.default.lore: Contains the default configuration of CraftEngine category.palm_tree: Palm Tree category.topaz: Topaz - category.furniture: Furniture category.misc: Misc emoji.tip: Use '' to send the '' emoji emoji.time: 'Current time: ' @@ -102,12 +109,93 @@ i18n: item.cap: 鸭舌帽 item.flower_basket: 花篮 item.chessboard_block: 棋盘方块 + item.safe_block: 保险柜 + item.sofa: 沙发 + item.amethyst_torch: 紫水晶火把 + item.hami_melon_slice: 哈密瓜片 + item.hami_melon: 哈密瓜 + item.hami_melon_seeds: 哈密瓜种子 + item.palm_button: 棕榈木按钮 + item.palm_fence: 棕榈木栅栏 category.default.name: 默认资产 category.default.lore: 包含了CraftEngine的默认配置 category.palm_tree: 棕榈树 category.topaz: 黄玉 - category.furniture: 家具 category.misc: 杂项 emoji.tip: 使用''来发送表情'' emoji.time: '当前时间: ' - emoji.location: '当前坐标: ,,' \ No newline at end of file + emoji.location: '当前坐标: ,,' +# This section is for localizing internal block IDs (craftengine:xxx_xx). +# Some other plugins support displaying block names using lang components. +# This might be useful for the client-side, but it's not mandatory. +lang: + en_us: + block_name:default:chinese_lantern: Chinese Lantern + block_name:default:netherite_anvil: Netherite Anvil + block_name:default:topaz_ore: Topaz Ore + block_name:default:deepslate_topaz_ore: Deepslate Topaz Ore + block_name:default:palm_log: Palm Log + block_name:default:stripped_palm_log: Stripped Palm Log + block_name:default:palm_wood: Palm Wood + block_name:default:stripped_palm_wood: Stripped Palm Wood + block_name:default:palm_planks: Palm Planks + block_name:default:palm_sapling: Palm Sapling + block_name:default:palm_leaves: Palm Leaves + block_name:default:palm_trapdoor: Palm Trapdoor + block_name:default:palm_door: Palm Door + block_name:default:palm_fence_gate: Palm Fence Gate + block_name:default:palm_slab: Palm Slab + block_name:default:palm_stairs: Palm Stairs + block_name:default:fairy_flower: Fairy Flower + block_name:default:reed: Reed + block_name:default:flame_cane: Flame Cane + block_name:default:ender_pearl_flower: Ender Pearl Flower + block_name:default:gunpowder_block: GunPowder Block + block_name:default:solid_gunpowder_block: Solid GunPowder Block + block_name:default:copper_coil: Copper Coil + block_name:default:chessboard_block: Chessboard Block + block_name:default:safe_block: Safe Block + block_name:default:sleeper_sofa: Sofa + block_name:default:sofa: Sofa + block_name:default:amethyst_torch: Amethyst Torch + block_name:default:amethyst_wall_torch: Amethyst Torch + block_name:default:hami_melon: Hami Melon + block_name:default:hami_melon_stem: Hami Melon Stem + block_name:default:attached_hami_melon_stem: Hami Melon Stem + block_name:default:palm_button: Palm Button + block_name:default:palm_fence: Palm Fence + zh_cn: + block_name:default:chinese_lantern: 灯笼 + block_name:default:netherite_anvil: 下界合金砧 + block_name:default:topaz_ore: 黄玉矿石 + block_name:default:deepslate_topaz_ore: 深层黄玉矿石 + block_name:default:palm_log: 棕榈原木 + block_name:default:stripped_palm_log: 去皮棕榈原木 + block_name:default:palm_wood: 棕榈木 + block_name:default:stripped_palm_wood: 去皮棕榈木 + block_name:default:palm_planks: 棕榈木板 + block_name:default:palm_sapling: 棕榈树苗 + block_name:default:palm_leaves: 棕榈树叶 + block_name:default:palm_trapdoor: 棕榈木活板门 + block_name:default:palm_door: 棕榈木门 + block_name:default:palm_fence_gate: 棕榈木栅栏门 + block_name:default:palm_slab: 棕榈木台阶 + block_name:default:palm_stairs: 棕榈木楼梯 + block_name:default:fairy_flower: 仙灵花 + block_name:default:reed: 芦苇 + block_name:default:flame_cane: 烈焰甘蔗 + block_name:default:ender_pearl_flower: 末影珍珠花 + block_name:default:gunpowder_block: 火药粉末 + block_name:default:solid_gunpowder_block: 凝固火药块 + block_name:default:copper_coil: 铜线圈 + block_name:default:chessboard_block: 棋盘方块 + block_name:default:safe_block: 保险柜 + block_name:default:sleeper_sofa: 沙发 + block_name:default:sofa: 沙发 + block_name:default:amethyst_torch: 紫水晶火把 + block_name:default:amethyst_wall_torch: 紫水晶火把 + block_name:default:hami_melon: 哈密瓜 + block_name:default:hami_melon_stem: 哈密瓜茎 + block_name:default:attached_hami_melon_stem: 哈密瓜茎 + block_name:default:palm_button: 棕榈木按钮 + block_name:default:palm_fence: 棕榈木栅栏 diff --git a/common-files/src/main/resources/resources/default/configuration/icons.yml b/common-files/src/main/resources/resources/default/configuration/icons.yml deleted file mode 100644 index 62b8500e6..000000000 --- a/common-files/src/main/resources/resources/default/configuration/icons.yml +++ /dev/null @@ -1,9 +0,0 @@ -images: - default:icons: - height: 10 - ascent: 9 - font: minecraft:icons - file: minecraft:font/image/icons.png - chars: - - \ub000\ub001 - - \ub002\ub003 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/items/cap.yml b/common-files/src/main/resources/resources/default/configuration/items/cap.yml new file mode 100644 index 000000000..f682b7cc5 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/items/cap.yml @@ -0,0 +1,15 @@ +items: + default:cap: + material: leather_helmet + client-bound-material: leather_horse_armor + data: + item-name: + unbreakable: true + remove-components: + - attribute_modifiers + model: + type: minecraft:model + path: minecraft:item/custom/cap + tints: + - type: minecraft:dye + default: -6265536 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/items/flame_elytra.yml b/common-files/src/main/resources/resources/default/configuration/items/flame_elytra.yml new file mode 100644 index 000000000..43425ae92 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/items/flame_elytra.yml @@ -0,0 +1,16 @@ +items: + $$>=1.21.2#flame_elytra: + default:flame_elytra: + material: elytra + settings: + equippable: + slot: chest + asset-id: flame + wings: flame_elytra + data: + item-name: <#FF8C00> + model: + template: default:model/simplified_elytra + arguments: + path: minecraft:item/custom/flame_elytra + broken_path: minecraft:item/custom/broken_flame_elytra \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/items/gui_head.yml b/common-files/src/main/resources/resources/default/configuration/items/gui_head.yml new file mode 100644 index 000000000..1c13ffa1e --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/items/gui_head.yml @@ -0,0 +1,29 @@ +items: + default:gui_head_size_1: + material: player_head + model: + type: minecraft:special + path: minecraft:item/custom/gui_head_size_1 + generation: + parent: minecraft:item/template_skull + gui-light: front + display: + gui: + translation: 0,8,0 + scale: 2,2,2 + model: + type: minecraft:player_head + default:gui_head_size_4: + material: player_head + model: + type: minecraft:special + path: minecraft:item/custom/gui_head_size_4 + generation: + parent: minecraft:item/template_skull + gui-light: front + display: + gui: + translation: 9,7,0 + scale: 4,4,4 + model: + type: minecraft:player_head \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/items/topaz_armor.yml b/common-files/src/main/resources/resources/default/configuration/items/topaz_armor.yml new file mode 100644 index 000000000..fcbaf8c86 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/items/topaz_armor.yml @@ -0,0 +1,103 @@ +templates: + default:armor/topaz: + material: chainmail_${part} + data: + item-name: <#FF8C00> + tooltip-style: minecraft:topaz + settings: + tags: + - default:topaz_tools + - minecraft:trimmable_armor + equipment: + asset-id: default:topaz + $$>=1.21.2: + slot: ${slot} + model: + template: default:model/armor_trim +items: + default:topaz_helmet: + template: + - default:armor/topaz + arguments: + part: helmet + slot: head + material: topaz + default:topaz_chestplate: + template: + - default:armor/topaz + arguments: + part: chestplate + slot: chest + material: topaz + default:topaz_leggings: + template: + - default:armor/topaz + arguments: + part: leggings + slot: legs + material: topaz + default:topaz_boots: + template: + - default:armor/topaz + arguments: + part: boots + slot: feet + material: topaz +equipments: + $$>=1.21.2: + default:topaz: + type: component + humanoid: minecraft:topaz + humanoid-leggings: minecraft:topaz + $$<1.21.2: + default:topaz: + type: trim + humanoid: minecraft:entity/equipment/humanoid/topaz + humanoid-leggings: minecraft:entity/equipment/humanoid_leggings/topaz +recipes: + default:topaz_helmet: + type: shaped + category: equipment + pattern: + - AAA + - A A + ingredients: + A: default:topaz + result: + id: default:topaz_helmet + count: 1 + default:topaz_chestplate: + type: shaped + category: equipment + pattern: + - A A + - AAA + - AAA + ingredients: + A: default:topaz + result: + id: default:topaz_chestplate + count: 1 + default:topaz_leggings: + type: shaped + category: equipment + pattern: + - AAA + - A A + - A A + ingredients: + A: default:topaz + result: + id: default:topaz_leggings + count: 1 + default:topaz_boots: + type: shaped + category: equipment + pattern: + - A A + - A A + ingredients: + A: default:topaz + result: + id: default:topaz_boots + count: 1 \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/items.yml b/common-files/src/main/resources/resources/default/configuration/items/topaz_tool_weapon.yml similarity index 65% rename from common-files/src/main/resources/resources/default/configuration/items.yml rename to common-files/src/main/resources/resources/default/configuration/items/topaz_tool_weapon.yml index b25111d18..3e2114151 100644 --- a/common-files/src/main/resources/resources/default/configuration/items.yml +++ b/common-files/src/main/resources/resources/default/configuration/items/topaz_tool_weapon.yml @@ -1,38 +1,6 @@ -items#gui_head: - default:gui_head_size_1: - material: player_head - custom-model-data: 1000 - model: - type: minecraft:special - path: minecraft:item/custom/gui_head_size_1 - generation: - parent: minecraft:item/template_skull - gui-light: front - display: - gui: - translation: 0,8,0 - scale: 2,2,2 - model: - type: minecraft:player_head - default:gui_head_size_4: - material: player_head - custom-model-data: 1001 - model: - type: minecraft:special - path: minecraft:item/custom/gui_head_size_4 - generation: - parent: minecraft:item/template_skull - gui-light: front - display: - gui: - translation: 9,7,0 - scale: 4,4,4 - model: - type: minecraft:player_head -items#topaz_gears: +items: default:topaz_rod: material: fishing_rod - custom-model-data: 1000 settings: tags: - default:topaz_tools @@ -46,7 +14,6 @@ items#topaz_gears: cast_path: minecraft:item/custom/topaz_rod_cast default:topaz_bow: material: bow - custom-model-data: 1000 settings: tags: - default:topaz_tools @@ -62,7 +29,6 @@ items#topaz_gears: pulling_2_path: minecraft:item/custom/topaz_bow_pulling_2 default:topaz_crossbow: material: crossbow - custom-model-data: 1000 settings: tags: - default:topaz_tools @@ -80,7 +46,6 @@ items#topaz_gears: firework_path: minecraft:item/custom/topaz_crossbow_firework default:topaz_pickaxe: material: golden_pickaxe - custom-model-data: 1000 settings: tags: - default:topaz_tools @@ -95,7 +60,6 @@ items#topaz_gears: path: minecraft:item/custom/topaz_pickaxe default:topaz_axe: material: golden_axe - custom-model-data: 1000 settings: tags: - default:topaz_tools @@ -110,7 +74,6 @@ items#topaz_gears: path: minecraft:item/custom/topaz_axe default:topaz_hoe: material: golden_hoe - custom-model-data: 1000 settings: tags: - default:topaz_tools @@ -125,7 +88,6 @@ items#topaz_gears: path: minecraft:item/custom/topaz_hoe default:topaz_shovel: material: golden_shovel - custom-model-data: 1000 settings: tags: - default:topaz_tools @@ -140,7 +102,6 @@ items#topaz_gears: path: minecraft:item/custom/topaz_shovel default:topaz_sword: material: golden_sword - custom-model-data: 1000 settings: tags: - default:topaz_tools @@ -156,7 +117,6 @@ items#topaz_gears: $$>=1.21.4#topaz_trident: default:topaz_trident: material: trident - custom-model-data: 1000 settings: projectile: item: default:topaz_trident @@ -207,7 +167,6 @@ items#topaz_gears: client-bound-material: $$1.20.1~1.21.1: bow $$1.21.2~1.21.3: honey_bottle - custom-model-data: 1001 data: item-name: <#FF8C00> components: @@ -236,94 +195,7 @@ items#topaz_gears: on-false: type: minecraft:model path: minecraft:item/custom/topaz_trident_in_hand - $$>=1.21.2#flame_elytra: - default:flame_elytra: - material: elytra - custom-model-data: 1000 - settings: - equippable: - slot: chest - asset-id: flame - wings: flame_elytra - data: - item-name: <#FF8C00> - model: - template: default:model/simplified_elytra - arguments: - path: minecraft:item/custom/flame_elytra - broken_path: minecraft:item/custom/broken_flame_elytra - default:cap: - material: leather_helmet - client-bound-material: leather_horse_armor - custom-model-data: 1000 - data: - item-name: - unbreakable: true - remove-components: - - attribute_modifiers - model: - type: minecraft:model - path: minecraft:item/custom/cap - tints: - - type: minecraft:dye - default: -6265536 - default:topaz_helmet: - template: - - default:armor/topaz - arguments: - part: helmet - slot: head - material: topaz - default:topaz_chestplate: - template: - - default:armor/topaz - arguments: - part: chestplate - slot: chest - material: topaz - default:topaz_leggings: - template: - - default:armor/topaz - arguments: - part: leggings - slot: legs - material: topaz - default:topaz_boots: - template: - - default:armor/topaz - arguments: - part: boots - slot: feet - material: topaz -templates: - default:armor/topaz: - material: chainmail_${part} - custom-model-data: 1000 - data: - item-name: <#FF8C00> - tooltip-style: minecraft:topaz - settings: - tags: - - default:topaz_tools - - minecraft:trimmable_armor - equipment: - asset-id: default:topaz - $$>=1.21.2: - slot: ${slot} - model: - template: default:model/armor_trim -equipments#topaz: - $$>=1.21.2: - default:topaz: - type: component - humanoid: minecraft:topaz - humanoid-leggings: minecraft:topaz - $$<1.21.2: - default:topaz: - type: trim - humanoid: minecraft:entity/equipment/humanoid/topaz - humanoid-leggings: minecraft:entity/equipment/humanoid_leggings/topaz -recipes#topaz: +recipes: default:topaz_shovel: type: shaped category: equipment @@ -389,52 +261,6 @@ recipes#topaz: result: id: default:topaz_pickaxe count: 1 - default:topaz_helmet: - type: shaped - category: equipment - pattern: - - AAA - - A A - ingredients: - A: default:topaz - result: - id: default:topaz_helmet - count: 1 - default:topaz_chestplate: - type: shaped - category: equipment - pattern: - - A A - - AAA - - AAA - ingredients: - A: default:topaz - result: - id: default:topaz_chestplate - count: 1 - default:topaz_leggings: - type: shaped - category: equipment - pattern: - - AAA - - A A - - A A - ingredients: - A: default:topaz - result: - id: default:topaz_leggings - count: 1 - default:topaz_boots: - type: shaped - category: equipment - pattern: - - A A - - A A - ingredients: - A: default:topaz - result: - id: default:topaz_boots - count: 1 default:topaz_bow: type: smithing_transform base: minecraft:bow diff --git a/common-files/src/main/resources/resources/default/configuration/plants.yml b/common-files/src/main/resources/resources/default/configuration/plants.yml deleted file mode 100644 index b5bf8d252..000000000 --- a/common-files/src/main/resources/resources/default/configuration/plants.yml +++ /dev/null @@ -1,350 +0,0 @@ -items: - default:fairy_flower: - material: nether_brick - custom-model-data: 4000 - data: - item-name: - model: - template: default:model/simplified_generated - arguments: - path: minecraft:item/custom/fairy_flower - behavior: - type: block_item - block: default:fairy_flower - default:reed: - material: nether_brick - custom-model-data: 4001 - data: - item-name: - model: - template: default:model/simplified_generated - arguments: - path: minecraft:item/custom/reed - behavior: - type: liquid_collision_block_item - block: default:reed - default:flame_cane: - material: nether_brick - custom-model-data: 4002 - data: - item-name: - model: - template: default:model/simplified_generated - arguments: - path: minecraft:item/custom/flame_cane - behavior: - type: block_item - block: default:flame_cane - default:ender_pearl_flower_seeds: - material: nether_brick - custom-model-data: 4003 - data: - item-name: - model: - template: default:model/simplified_generated - arguments: - path: minecraft:item/custom/ender_pearl_flower_seeds - behavior: - type: block_item - block: default:ender_pearl_flower -blocks: - default:fairy_flower: - settings: - template: - - default:hardness/none - - default:sound/grass - overrides: - item: default:fairy_flower - push-reaction: DESTROY - map-color: 19 - behavior: - type: bush_block - bottom-block-tags: - - minecraft:dirt - - minecraft:farmland - loot: - template: default:loot_table/self - state: - id: 0 - state: sugar_cane:0 - models: - - path: minecraft:block/custom/fairy_flower_1 - weight: 100 - - path: minecraft:block/custom/fairy_flower_2 - weight: 5 - generation: - parent: minecraft:block/custom/fairy_flower_1 - textures: - '0': minecraft:block/custom/fairy_flower_2 - - path: minecraft:block/custom/fairy_flower_3 - weight: 5 - generation: - parent: minecraft:block/custom/fairy_flower_1 - textures: - '0': minecraft:block/custom/fairy_flower_3 - - path: minecraft:block/custom/fairy_flower_4 - weight: 5 - generation: - parent: minecraft:block/custom/fairy_flower_1 - textures: - '0': minecraft:block/custom/fairy_flower_4 - default:reed: - settings: - template: - - default:hardness/none - - default:sound/grass - overrides: - push-reaction: DESTROY - map-color: 60 - behavior: - type: on_liquid_block - liquid-type: water - positions: - - 0,-1,0 - loot: - template: default:loot_table/self - state: - id: 1 - state: sugar_cane:1 - model: - path: minecraft:block/custom/reed - default:flame_cane: - settings: - template: - - default:hardness/none - - default:sound/grass - overrides: - push-reaction: DESTROY - map-color: 15 - is-randomly-ticking: true - behaviors: - - type: vertical_crop_block - max-height: 4 - grow-speed: 0.333 - direction: up - - type: bush_block - stackable: true - delay: 1 - bottom-blocks: - - minecraft:netherrack - - minecraft:soul_sand - - minecraft:soul_soil - - minecraft:magma_block - - minecraft:warped_nylium - - minecraft:crimson_nylium - - minecraft:basalt - - type: near_liquid_block - liquid-type: lava - delay: 1 - stackable: true - positions: - - -1,-1,0 - - 1,-1,0 - - 0,-1,-1 - - 0,-1,1 - loot: - template: default:loot_table/self - states: - properties: - age: - type: int - default: 0 - range: 0~5 - appearances: - default: - state: sugar_cane:2 - models: - - path: minecraft:block/custom/flame_cane_1 - weight: 1 - generation: - parent: minecraft:block/sugar_cane - textures: - cross: minecraft:block/custom/flame_cane_1 - - path: minecraft:block/custom/flame_cane_2 - weight: 1 - generation: - parent: minecraft:block/sugar_cane - textures: - cross: minecraft:block/custom/flame_cane_2 - variants: - age=0: - appearance: default - id: 2 - age=1: - appearance: default - id: 3 - age=2: - appearance: default - id: 4 - age=3: - appearance: default - id: 5 - age=4: - appearance: default - id: 6 - age=5: - appearance: default - id: 7 - default:ender_pearl_flower: - settings: - template: - - default:hardness/none - - default:sound/grass - overrides: - item: default:ender_pearl_flower_seeds - push-reaction: DESTROY - map-color: 24 - is-randomly-ticking: true - behaviors: - - type: bush_block - bottom-blocks: - - minecraft:end_stone - - type: crop_block - grow-speed: 0.25 - light-requirement: 9 - is-bone-meal-target: true - bone-meal-age-bonus: 1 - loot: - template: default:loot_table/seed_crop - arguments: - crop_item: minecraft:ender_pearl - crop_seed: default:ender_pearl_flower_seeds - ripe_age: 2 - events: - - on: break - conditions: - - type: match_block_property - properties: - age: 2 - functions: - - type: particle - x: + 0.5 - y: + 0.5 - z: + 0.5 - particle: minecraft:end_rod - count: 15 - offset-x: 0.05 - offset-y: 0.05 - offset-z: 0.05 - speed: 0.1 - - type: play_sound - sound: minecraft:entity.enderman.teleport - x: + 0.5 - y: + 0.5 - z: + 0.5 - - on: right_click - conditions: - - type: match_block_property - properties: - age: 2 - - type: '!is_null' - argument: item_in_hand - - type: equals - value1: - value2: default:ender_pearl_flower_seeds - functions: - - type: break_block - x: - y: - z: - - type: place_block - x: - y: - z: - block-state: default:ender_pearl_flower[age=0] - - type: set_count - add: true - count: -1 - - type: swing_hand - states: - properties: - age: - type: int - default: 0 - range: 0~2 - appearances: - stage_0: - state: tripwire:1 - models: - - path: minecraft:block/custom/ender_pearl_flower_stage_0 - generation: - parent: minecraft:block/cross - textures: - cross: minecraft:block/custom/ender_pearl_flower_stage_0 - stage_1: - state: tripwire:0 - models: - - path: minecraft:block/custom/ender_pearl_flower_stage_1 - generation: - parent: minecraft:block/cross - textures: - cross: minecraft:block/custom/ender_pearl_flower_stage_1 - stage_2: - state: sugar_cane:3 - models: - - path: minecraft:block/custom/ender_pearl_flower_stage_2 - generation: - parent: minecraft:block/cross - textures: - cross: minecraft:block/custom/ender_pearl_flower_stage_2 - variants: - age=0: - appearance: stage_0 - id: 0 - age=1: - appearance: stage_1 - id: 1 - age=2: - appearance: stage_2 - id: 8 -recipes: - default:paper_from_reed: - type: shaped - pattern: - - AAA - ingredients: - A: default:reed - result: - id: minecraft:paper - count: 3 - default:magma_cream: - type: shaped - pattern: - - ' A ' - - ABA - - ' A ' - ingredients: - A: default:flame_cane - B: minecraft:slime_ball - result: - id: minecraft:magma_cream - count: 1 - default:magma_block: - type: shapeless - ingredients: - A1: minecraft:cobblestone - A2: minecraft:cobblestone - B1: default:flame_cane - B2: default:flame_cane - result: - id: minecraft:magma_block - count: 2 -vanilla-loots: - minecraft:ender_pearl_flower_seeds_from_endermite: - type: entity - target: minecraft:endermite - override: false - loot: - pools: - - rolls: 1 - conditions: - - type: table_bonus - enchantment: minecraft:looting - chances: - - 0.1 - - 0.5 - - 0.8 - - 1 - entries: - - type: item - item: default:ender_pearl_flower_seeds \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/templates.yml b/common-files/src/main/resources/resources/default/configuration/templates.yml index 5760c9c18..da609c8b0 100644 --- a/common-files/src/main/resources/resources/default/configuration/templates.yml +++ b/common-files/src/main/resources/resources/default/configuration/templates.yml @@ -48,13 +48,13 @@ templates#models#block: generation: parent: minecraft:block/cube_column textures: - particle: minecraft:block/custom/block_particle - down: minecraft:block/custom/block_down - up: minecraft:block/custom/block_up - north: minecraft:block/custom/block_north - east: minecraft:block/custom/block_east - south: minecraft:block/custom/block_south - west: minecraft:block/custom/block_west + particle: ${particle_texture} + down: ${down_texture} + up: ${up_texture} + north: ${north_texture} + east: ${east_texture} + south: ${south_texture} + west: ${west_texture} # 2D items templates#models#2d: @@ -576,6 +576,13 @@ templates#settings#sounds: place: minecraft:item.crop.plant hit: minecraft:block.grass.hit fall: minecraft:block.grass.fall + default:sound/stem_crop: + sounds: + break: minecraft:block.crop.break + step: minecraft:block.wood.step + place: minecraft:item.crop.plant + hit: minecraft:block.wood.hit + fall: minecraft:block.wood.fall default:sound/grass: template: default:sound/block_template arguments: @@ -693,6 +700,12 @@ templates#settings#hardness: default:hardness/planks: hardness: 2.0 resistance: 3.0 + default:hardness/button: + hardness: 0.5 + resistance: 0.5 + default:hardness/melon: + hardness: 1.0 + resistance: 1.0 # break level templates#settings#break_level: @@ -747,7 +760,7 @@ templates#settings#blocks: - default:hardness/none - default:sound/grass overrides: - push-reaction: DESTROY + push-reaction: destroy is-randomly-ticking: true map-color: 7 tags: @@ -763,11 +776,11 @@ templates#settings#blocks: overrides: hardness: 0.2 resistance: 0.2 - push-reaction: DESTROY + push-reaction: destroy replaceable: false is-redstone-conductor: false is-suffocating: false - instrument: HARP + instrument: harp tags: - minecraft:mineable/hoe - minecraft:sword_efficient @@ -780,11 +793,11 @@ templates#settings#blocks: - default:burn_data/wood - default:hardness/wood overrides: - push-reaction: NORMAL + push-reaction: normal replaceable: false is-redstone-conductor: true is-suffocating: true - instrument: BASS + instrument: bass can-occlude: true tags: - minecraft:mineable/axe @@ -798,11 +811,11 @@ templates#settings#blocks: - default:burn_data/planks - default:hardness/planks overrides: - push-reaction: NORMAL + push-reaction: normal replaceable: false is-redstone-conductor: true is-suffocating: true - instrument: BASS + instrument: bass can-occlude: true tags: - minecraft:mineable/axe @@ -814,10 +827,10 @@ templates#settings#blocks: overrides: hardness: 3.0 resistance: 3.0 - push-reaction: NORMAL + push-reaction: normal is-redstone-conductor: true is-suffocating: true - instrument: BASEDRUM + instrument: basedrum can-occlude: true map-color: 11 tags: @@ -830,10 +843,10 @@ templates#settings#blocks: overrides: hardness: 4.5 resistance: 3.0 - push-reaction: NORMAL + push-reaction: normal is-redstone-conductor: true is-suffocating: true - instrument: BASEDRUM + instrument: basedrum can-occlude: true map-color: 59 tags: @@ -849,7 +862,7 @@ templates#block_states: default: y appearances: axisY: - state: ${base_block}:${vanilla_id} + auto-state: solid model: path: ${model_vertical_path} generation: @@ -858,7 +871,7 @@ templates#block_states: end: ${texture_top_path} side: ${texture_side_path} axisX: - state: ${base_block}:${vanilla_id} + auto-state: solid model: x: 90 y: 90 @@ -869,7 +882,7 @@ templates#block_states: end: ${texture_top_path} side: ${texture_side_path} axisZ: - state: ${base_block}:${vanilla_id} + auto-state: solid model: x: 90 path: ${model_horizontal_path} @@ -881,13 +894,10 @@ templates#block_states: variants: axis=x: appearance: axisX - id: ${internal_id} axis=y: appearance: axisY - id: ${internal_id} axis=z: appearance: axisZ - id: ${internal_id} # leaves block default:block_state/leaves: properties: @@ -903,7 +913,7 @@ templates#block_states: range: 1~7 appearances: default: - state: ${default_state} + auto-state: leaves model: path: ${model_path} generation: @@ -911,153 +921,21 @@ templates#block_states: textures: all: ${texture_path} waterlogged: - state: ${waterlogged_state} + auto-state: waterlogged_leaves model: path: ${model_path} variants: - distance=1,persistent=false,waterlogged=false: + waterlogged=false: appearance: default - id: ${internal_id} - distance=2,persistent=false,waterlogged=false: - appearance: default - id: ${internal_id} - distance=3,persistent=false,waterlogged=false: - appearance: default - id: ${internal_id} - distance=4,persistent=false,waterlogged=false: - appearance: default - id: ${internal_id} - distance=5,persistent=false,waterlogged=false: - appearance: default - id: ${internal_id} - distance=6,persistent=false,waterlogged=false: - appearance: default - id: ${internal_id} - distance=7,persistent=false,waterlogged=false: - appearance: default - id: ${internal_id} + waterlogged=true: + appearance: waterlogged + settings: + resistance: 1200.0 + burnable: false + fluid-state: water + distance=7,persistent=false: settings: is-randomly-ticking: true - distance=1,persistent=true,waterlogged=false: - appearance: default - id: ${internal_id} - distance=2,persistent=true,waterlogged=false: - appearance: default - id: ${internal_id} - distance=3,persistent=true,waterlogged=false: - appearance: default - id: ${internal_id} - distance=4,persistent=true,waterlogged=false: - appearance: default - id: ${internal_id} - distance=5,persistent=true,waterlogged=false: - appearance: default - id: ${internal_id} - distance=6,persistent=true,waterlogged=false: - appearance: default - id: ${internal_id} - distance=7,persistent=true,waterlogged=false: - appearance: default - id: ${internal_id} - distance=1,persistent=false,waterlogged=true: - appearance: waterlogged - id: ${internal_id} - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - distance=2,persistent=false,waterlogged=true: - appearance: waterlogged - id: ${internal_id} - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - distance=3,persistent=false,waterlogged=true: - appearance: waterlogged - id: ${internal_id} - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - distance=4,persistent=false,waterlogged=true: - appearance: waterlogged - id: ${internal_id} - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - distance=5,persistent=false,waterlogged=true: - appearance: waterlogged - id: ${internal_id} - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - distance=6,persistent=false,waterlogged=true: - appearance: waterlogged - id: ${internal_id} - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - distance=7,persistent=false,waterlogged=true: - appearance: waterlogged - id: ${internal_id} - settings: - resistance: 1200.0 - burnable: false - is-randomly-ticking: true - fluid-state: water - distance=1,persistent=true,waterlogged=true: - appearance: waterlogged - id: ${internal_id} - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - distance=2,persistent=true,waterlogged=true: - appearance: waterlogged - id: ${internal_id} - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - distance=3,persistent=true,waterlogged=true: - appearance: waterlogged - id: ${internal_id} - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - distance=4,persistent=true,waterlogged=true: - appearance: waterlogged - id: ${internal_id} - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - distance=5,persistent=true,waterlogged=true: - appearance: waterlogged - id: ${internal_id} - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - distance=6,persistent=true,waterlogged=true: - appearance: waterlogged - id: ${internal_id} - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - distance=7,persistent=true,waterlogged=true: - appearance: waterlogged - id: ${internal_id} - settings: - resistance: 1200.0 - burnable: false - fluid-state: water # trapdoor block default:block_state/trapdoor: properties: @@ -1241,324 +1119,132 @@ templates#block_states: x: 180 y: 90 variants: - facing=east,half=bottom,open=false,powered=false,waterlogged=false: + facing=east,half=bottom,open=false,waterlogged=false: appearance: facing=east,half=bottom,open=false,waterlogged=false - id: 0 - facing=east,half=bottom,open=false,powered=true,waterlogged=false: - appearance: facing=east,half=bottom,open=false,waterlogged=false - id: 1 - facing=east,half=bottom,open=true,powered=false,waterlogged=false: + facing=east,half=bottom,open=true,waterlogged=false: appearance: facing=east,half=bottom,open=true,waterlogged=false - id: 2 - facing=east,half=bottom,open=true,powered=true,waterlogged=false: - appearance: facing=east,half=bottom,open=true,waterlogged=false - id: 3 - facing=east,half=top,open=false,powered=false,waterlogged=false: + facing=east,half=top,open=false,waterlogged=false: appearance: facing=east,half=top,open=false,waterlogged=false - id: 4 - facing=east,half=top,open=false,powered=true,waterlogged=false: - appearance: facing=east,half=top,open=false,waterlogged=false - id: 5 - facing=east,half=top,open=true,powered=false,waterlogged=false: + facing=east,half=top,open=true,waterlogged=false: appearance: facing=east,half=top,open=true,waterlogged=false - id: 6 - facing=east,half=top,open=true,powered=true,waterlogged=false: - appearance: facing=east,half=top,open=true,waterlogged=false - id: 7 - facing=north,half=bottom,open=false,powered=false,waterlogged=false: + facing=north,half=bottom,open=false,waterlogged=false: appearance: facing=north,half=bottom,open=false,waterlogged=false - id: 8 - facing=north,half=bottom,open=false,powered=true,waterlogged=false: - appearance: facing=north,half=bottom,open=false,waterlogged=false - id: 9 - facing=north,half=bottom,open=true,powered=false,waterlogged=false: + facing=north,half=bottom,open=true,waterlogged=false: appearance: facing=north,half=bottom,open=true,waterlogged=false - id: 10 - facing=north,half=bottom,open=true,powered=true,waterlogged=false: - appearance: facing=north,half=bottom,open=true,waterlogged=false - id: 11 - facing=north,half=top,open=false,powered=false,waterlogged=false: + facing=north,half=top,open=false,waterlogged=false: appearance: facing=north,half=top,open=false,waterlogged=false - id: 12 - facing=north,half=top,open=false,powered=true,waterlogged=false: - appearance: facing=north,half=top,open=false,waterlogged=false - id: 13 - facing=north,half=top,open=true,powered=false,waterlogged=false: + facing=north,half=top,open=true,waterlogged=false: appearance: facing=north,half=top,open=true,waterlogged=false - id: 14 - facing=north,half=top,open=true,powered=true,waterlogged=false: - appearance: facing=north,half=top,open=true,waterlogged=false - id: 15 - facing=south,half=bottom,open=false,powered=false,waterlogged=false: + facing=south,half=bottom,open=false,waterlogged=false: appearance: facing=south,half=bottom,open=false,waterlogged=false - id: 16 - facing=south,half=bottom,open=false,powered=true,waterlogged=false: - appearance: facing=south,half=bottom,open=false,waterlogged=false - id: 17 - facing=south,half=bottom,open=true,powered=false,waterlogged=false: + facing=south,half=bottom,open=true,waterlogged=false: appearance: facing=south,half=bottom,open=true,waterlogged=false - id: 18 - facing=south,half=bottom,open=true,powered=true,waterlogged=false: - appearance: facing=south,half=bottom,open=true,waterlogged=false - id: 19 - facing=south,half=top,open=false,powered=false,waterlogged=false: + facing=south,half=top,open=false,waterlogged=false: appearance: facing=south,half=top,open=false,waterlogged=false - id: 20 - facing=south,half=top,open=false,powered=true,waterlogged=false: - appearance: facing=south,half=top,open=false,waterlogged=false - id: 21 - facing=south,half=top,open=true,powered=false,waterlogged=false: + facing=south,half=top,open=true,waterlogged=false: appearance: facing=south,half=top,open=true,waterlogged=false - id: 22 - facing=south,half=top,open=true,powered=true,waterlogged=false: - appearance: facing=south,half=top,open=true,waterlogged=false - id: 23 - facing=west,half=bottom,open=false,powered=false,waterlogged=false: + facing=west,half=bottom,open=false,waterlogged=false: appearance: facing=west,half=bottom,open=false,waterlogged=false - id: 24 - facing=west,half=bottom,open=false,powered=true,waterlogged=false: - appearance: facing=west,half=bottom,open=false,waterlogged=false - id: 25 - facing=west,half=bottom,open=true,powered=false,waterlogged=false: + facing=west,half=bottom,open=true,waterlogged=false: appearance: facing=west,half=bottom,open=true,waterlogged=false - id: 26 - facing=west,half=bottom,open=true,powered=true,waterlogged=false: - appearance: facing=west,half=bottom,open=true,waterlogged=false - id: 27 - facing=west,half=top,open=false,powered=false,waterlogged=false: + facing=west,half=top,open=false,waterlogged=false: appearance: facing=west,half=top,open=false,waterlogged=false - id: 28 - facing=west,half=top,open=false,powered=true,waterlogged=false: - appearance: facing=west,half=top,open=false,waterlogged=false - id: 29 - facing=west,half=top,open=true,powered=false,waterlogged=false: + facing=west,half=top,open=true,waterlogged=false: appearance: facing=west,half=top,open=true,waterlogged=false - id: 30 - facing=west,half=top,open=true,powered=true,waterlogged=false: - appearance: facing=west,half=top,open=true,waterlogged=false - id: 31 - facing=east,half=bottom,open=false,powered=false,waterlogged=true: + facing=east,half=bottom,open=false,waterlogged=true: appearance: facing=east,half=bottom,open=false,waterlogged=true - id: 32 settings: resistance: 1200.0 burnable: false fluid-state: water - facing=east,half=bottom,open=false,powered=true,waterlogged=true: - appearance: facing=east,half=bottom,open=false,waterlogged=true - id: 33 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - facing=east,half=bottom,open=true,powered=false,waterlogged=true: + facing=east,half=bottom,open=true,waterlogged=true: appearance: facing=east,half=bottom,open=true,waterlogged=true - id: 34 settings: resistance: 1200.0 burnable: false fluid-state: water - facing=east,half=bottom,open=true,powered=true,waterlogged=true: - appearance: facing=east,half=bottom,open=true,waterlogged=true - id: 35 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - facing=east,half=top,open=false,powered=false,waterlogged=true: + facing=east,half=top,open=false,waterlogged=true: appearance: facing=east,half=top,open=false,waterlogged=true - id: 36 settings: resistance: 1200.0 burnable: false fluid-state: water - facing=east,half=top,open=false,powered=true,waterlogged=true: - appearance: facing=east,half=top,open=false,waterlogged=true - id: 37 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - facing=east,half=top,open=true,powered=false,waterlogged=true: + facing=east,half=top,open=true,waterlogged=true: appearance: facing=east,half=top,open=true,waterlogged=true - id: 38 settings: resistance: 1200.0 burnable: false fluid-state: water - facing=east,half=top,open=true,powered=true,waterlogged=true: - appearance: facing=east,half=top,open=true,waterlogged=true - id: 39 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - facing=north,half=bottom,open=false,powered=false,waterlogged=true: + facing=north,half=bottom,open=false,waterlogged=true: appearance: facing=north,half=bottom,open=false,waterlogged=true - id: 40 settings: resistance: 1200.0 burnable: false fluid-state: water - facing=north,half=bottom,open=false,powered=true,waterlogged=true: - appearance: facing=north,half=bottom,open=false,waterlogged=true - id: 41 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - facing=north,half=bottom,open=true,powered=false,waterlogged=true: + facing=north,half=bottom,open=true,waterlogged=true: appearance: facing=north,half=bottom,open=true,waterlogged=true - id: 42 settings: resistance: 1200.0 burnable: false fluid-state: water - facing=north,half=bottom,open=true,powered=true,waterlogged=true: - appearance: facing=north,half=bottom,open=true,waterlogged=true - id: 43 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - facing=north,half=top,open=false,powered=false,waterlogged=true: + facing=north,half=top,open=false,waterlogged=true: appearance: facing=north,half=top,open=false,waterlogged=true - id: 44 settings: resistance: 1200.0 burnable: false fluid-state: water - facing=north,half=top,open=false,powered=true,waterlogged=true: - appearance: facing=north,half=top,open=false,waterlogged=true - id: 45 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - facing=north,half=top,open=true,powered=false,waterlogged=true: + facing=north,half=top,open=true,waterlogged=true: appearance: facing=north,half=top,open=true,waterlogged=true - id: 46 settings: resistance: 1200.0 burnable: false fluid-state: water - facing=north,half=top,open=true,powered=true,waterlogged=true: - appearance: facing=north,half=top,open=true,waterlogged=true - id: 47 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - facing=south,half=bottom,open=false,powered=false,waterlogged=true: + facing=south,half=bottom,open=false,waterlogged=true: appearance: facing=south,half=bottom,open=false,waterlogged=true - id: 48 settings: resistance: 1200.0 burnable: false fluid-state: water - facing=south,half=bottom,open=false,powered=true,waterlogged=true: - appearance: facing=south,half=bottom,open=false,waterlogged=true - id: 49 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - facing=south,half=bottom,open=true,powered=false,waterlogged=true: + facing=south,half=bottom,open=true,waterlogged=true: appearance: facing=south,half=bottom,open=true,waterlogged=true - id: 50 settings: resistance: 1200.0 burnable: false fluid-state: water - facing=south,half=bottom,open=true,powered=true,waterlogged=true: - appearance: facing=south,half=bottom,open=true,waterlogged=true - id: 51 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - facing=south,half=top,open=false,powered=false,waterlogged=true: + facing=south,half=top,open=false,waterlogged=true: appearance: facing=south,half=top,open=false,waterlogged=true - id: 52 settings: resistance: 1200.0 burnable: false fluid-state: water - facing=south,half=top,open=false,powered=true,waterlogged=true: - appearance: facing=south,half=top,open=false,waterlogged=true - id: 53 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - facing=south,half=top,open=true,powered=false,waterlogged=true: + facing=south,half=top,open=true,waterlogged=true: appearance: facing=south,half=top,open=true,waterlogged=true - id: 54 settings: resistance: 1200.0 burnable: false fluid-state: water - facing=south,half=top,open=true,powered=true,waterlogged=true: - appearance: facing=south,half=top,open=true,waterlogged=true - id: 55 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - facing=west,half=bottom,open=false,powered=false,waterlogged=true: + facing=west,half=bottom,open=false,waterlogged=true: appearance: facing=west,half=bottom,open=false,waterlogged=true - id: 56 settings: resistance: 1200.0 burnable: false fluid-state: water - facing=west,half=bottom,open=false,powered=true,waterlogged=true: - appearance: facing=west,half=bottom,open=false,waterlogged=true - id: 57 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - facing=west,half=bottom,open=true,powered=false,waterlogged=true: + facing=west,half=bottom,open=true,waterlogged=true: appearance: facing=west,half=bottom,open=true,waterlogged=true - id: 58 settings: resistance: 1200.0 burnable: false fluid-state: water - facing=west,half=bottom,open=true,powered=true,waterlogged=true: - appearance: facing=west,half=bottom,open=true,waterlogged=true - id: 59 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - facing=west,half=top,open=false,powered=false,waterlogged=true: + facing=west,half=top,open=false,waterlogged=true: appearance: facing=west,half=top,open=false,waterlogged=true - id: 60 settings: resistance: 1200.0 burnable: false fluid-state: water - facing=west,half=top,open=false,powered=true,waterlogged=true: - appearance: facing=west,half=top,open=false,waterlogged=true - id: 61 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water - facing=west,half=top,open=true,powered=false,waterlogged=true: + facing=west,half=top,open=true,waterlogged=true: appearance: facing=west,half=top,open=true,waterlogged=true - id: 62 settings: fluid-state: water - facing=west,half=top,open=true,powered=true,waterlogged=true: - appearance: facing=west,half=top,open=true,waterlogged=true - id: 63 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water # door block default:block_state/door: properties: @@ -1736,198 +1422,70 @@ templates#block_states: path: ${model_top_right_open_path} y: 90 variants: - facing=east,half=lower,hinge=left,open=false,powered=true: + facing=east,half=lower,hinge=left,open=false: appearance: facing=east,half=lower,hinge=left,open=false - id: 0 - facing=east,half=lower,hinge=left,open=false,powered=false: - appearance: facing=east,half=lower,hinge=left,open=false - id: 1 - facing=east,half=lower,hinge=right,open=false,powered=true: + facing=east,half=lower,hinge=right,open=false: appearance: facing=east,half=lower,hinge=right,open=false - id: 2 - facing=east,half=lower,hinge=right,open=false,powered=false: - appearance: facing=east,half=lower,hinge=right,open=false - id: 3 - facing=east,half=upper,hinge=left,open=false,powered=true: + facing=east,half=upper,hinge=left,open=false: appearance: facing=east,half=upper,hinge=left,open=false - id: 4 - facing=east,half=upper,hinge=left,open=false,powered=false: - appearance: facing=east,half=upper,hinge=left,open=false - id: 5 - facing=east,half=upper,hinge=right,open=false,powered=true: + facing=east,half=upper,hinge=right,open=false: appearance: facing=east,half=upper,hinge=right,open=false - id: 6 - facing=east,half=upper,hinge=right,open=false,powered=false: - appearance: facing=east,half=upper,hinge=right,open=false - id: 7 - facing=north,half=lower,hinge=left,open=false,powered=true: + facing=north,half=lower,hinge=left,open=false: appearance: facing=north,half=lower,hinge=left,open=false - id: 8 - facing=north,half=lower,hinge=left,open=false,powered=false: - appearance: facing=north,half=lower,hinge=left,open=false - id: 9 - facing=north,half=lower,hinge=right,open=false,powered=true: + facing=north,half=lower,hinge=right,open=false: appearance: facing=north,half=lower,hinge=right,open=false - id: 10 - facing=north,half=lower,hinge=right,open=false,powered=false: - appearance: facing=north,half=lower,hinge=right,open=false - id: 11 - facing=north,half=upper,hinge=left,open=false,powered=true: + facing=north,half=upper,hinge=left,open=false: appearance: facing=north,half=upper,hinge=left,open=false - id: 12 - facing=north,half=upper,hinge=left,open=false,powered=false: - appearance: facing=north,half=upper,hinge=left,open=false - id: 13 - facing=north,half=upper,hinge=right,open=false,powered=true: + facing=north,half=upper,hinge=right,open=false: appearance: facing=north,half=upper,hinge=right,open=false - id: 14 - facing=north,half=upper,hinge=right,open=false,powered=false: - appearance: facing=north,half=upper,hinge=right,open=false - id: 15 - facing=south,half=lower,hinge=left,open=false,powered=true: + facing=south,half=lower,hinge=left,open=false: appearance: facing=south,half=lower,hinge=left,open=false - id: 16 - facing=south,half=lower,hinge=left,open=false,powered=false: - appearance: facing=south,half=lower,hinge=left,open=false - id: 17 - facing=south,half=lower,hinge=right,open=false,powered=true: + facing=south,half=lower,hinge=right,open=false: appearance: facing=south,half=lower,hinge=right,open=false - id: 18 - facing=south,half=lower,hinge=right,open=false,powered=false: - appearance: facing=south,half=lower,hinge=right,open=false - id: 19 - facing=south,half=upper,hinge=left,open=false,powered=true: + facing=south,half=upper,hinge=left,open=false: appearance: facing=south,half=upper,hinge=left,open=false - id: 20 - facing=south,half=upper,hinge=left,open=false,powered=false: - appearance: facing=south,half=upper,hinge=left,open=false - id: 21 - facing=south,half=upper,hinge=right,open=false,powered=true: + facing=south,half=upper,hinge=right,open=false: appearance: facing=south,half=upper,hinge=right,open=false - id: 22 - facing=south,half=upper,hinge=right,open=false,powered=false: - appearance: facing=south,half=upper,hinge=right,open=false - id: 23 - facing=west,half=lower,hinge=left,open=false,powered=true: + facing=west,half=lower,hinge=left,open=false: appearance: facing=west,half=lower,hinge=left,open=false - id: 24 - facing=west,half=lower,hinge=left,open=false,powered=false: - appearance: facing=west,half=lower,hinge=left,open=false - id: 25 - facing=west,half=lower,hinge=right,open=false,powered=true: + facing=west,half=lower,hinge=right,open=false: appearance: facing=west,half=lower,hinge=right,open=false - id: 26 - facing=west,half=lower,hinge=right,open=false,powered=false: - appearance: facing=west,half=lower,hinge=right,open=false - id: 27 - facing=west,half=upper,hinge=left,open=false,powered=true: + facing=west,half=upper,hinge=left,open=false: appearance: facing=west,half=upper,hinge=left,open=false - id: 28 - facing=west,half=upper,hinge=left,open=false,powered=false: - appearance: facing=west,half=upper,hinge=left,open=false - id: 29 - facing=west,half=upper,hinge=right,open=false,powered=true: + facing=west,half=upper,hinge=right,open=false: appearance: facing=west,half=upper,hinge=right,open=false - id: 30 - facing=west,half=upper,hinge=right,open=false,powered=false: - appearance: facing=west,half=upper,hinge=right,open=false - id: 31 - facing=east,half=lower,hinge=left,open=true,powered=true: + facing=east,half=lower,hinge=left,open=true: appearance: facing=east,half=lower,hinge=left,open=true - id: 32 - facing=east,half=lower,hinge=left,open=true,powered=false: - appearance: facing=east,half=lower,hinge=left,open=true - id: 33 - facing=east,half=lower,hinge=right,open=true,powered=true: + facing=east,half=lower,hinge=right,open=true: appearance: facing=east,half=lower,hinge=right,open=true - id: 34 - facing=east,half=lower,hinge=right,open=true,powered=false: - appearance: facing=east,half=lower,hinge=right,open=true - id: 35 - facing=east,half=upper,hinge=left,open=true,powered=true: + facing=east,half=upper,hinge=left,open=true: appearance: facing=east,half=upper,hinge=left,open=true - id: 36 - facing=east,half=upper,hinge=left,open=true,powered=false: - appearance: facing=east,half=upper,hinge=left,open=true - id: 37 - facing=east,half=upper,hinge=right,open=true,powered=true: + facing=east,half=upper,hinge=right,open=true: appearance: facing=east,half=upper,hinge=right,open=true - id: 38 - facing=east,half=upper,hinge=right,open=true,powered=false: - appearance: facing=east,half=upper,hinge=right,open=true - id: 39 - facing=north,half=lower,hinge=left,open=true,powered=true: + facing=north,half=lower,hinge=left,open=true: appearance: facing=north,half=lower,hinge=left,open=true - id: 40 - facing=north,half=lower,hinge=left,open=true,powered=false: - appearance: facing=north,half=lower,hinge=left,open=true - id: 41 - facing=north,half=lower,hinge=right,open=true,powered=true: + facing=north,half=lower,hinge=right,open=true: appearance: facing=north,half=lower,hinge=right,open=true - id: 42 - facing=north,half=lower,hinge=right,open=true,powered=false: - appearance: facing=north,half=lower,hinge=right,open=true - id: 43 - facing=north,half=upper,hinge=left,open=true,powered=true: + facing=north,half=upper,hinge=left,open=true: appearance: facing=north,half=upper,hinge=left,open=true - id: 44 - facing=north,half=upper,hinge=left,open=true,powered=false: - appearance: facing=north,half=upper,hinge=left,open=true - id: 45 - facing=north,half=upper,hinge=right,open=true,powered=true: + facing=north,half=upper,hinge=right,open=true: appearance: facing=north,half=upper,hinge=right,open=true - id: 46 - facing=north,half=upper,hinge=right,open=true,powered=false: - appearance: facing=north,half=upper,hinge=right,open=true - id: 47 - facing=south,half=lower,hinge=left,open=true,powered=true: + facing=south,half=lower,hinge=left,open=true: appearance: facing=south,half=lower,hinge=left,open=true - id: 48 - facing=south,half=lower,hinge=left,open=true,powered=false: - appearance: facing=south,half=lower,hinge=left,open=true - id: 49 - facing=south,half=lower,hinge=right,open=true,powered=true: + facing=south,half=lower,hinge=right,open=true: appearance: facing=south,half=lower,hinge=right,open=true - id: 50 - facing=south,half=lower,hinge=right,open=true,powered=false: - appearance: facing=south,half=lower,hinge=right,open=true - id: 51 - facing=south,half=upper,hinge=left,open=true,powered=true: + facing=south,half=upper,hinge=left,open=true: appearance: facing=south,half=upper,hinge=left,open=true - id: 52 - facing=south,half=upper,hinge=left,open=true,powered=false: - appearance: facing=south,half=upper,hinge=left,open=true - id: 53 - facing=south,half=upper,hinge=right,open=true,powered=true: + facing=south,half=upper,hinge=right,open=true: appearance: facing=south,half=upper,hinge=right,open=true - id: 54 - facing=south,half=upper,hinge=right,open=true,powered=false: - appearance: facing=south,half=upper,hinge=right,open=true - id: 55 - facing=west,half=lower,hinge=left,open=true,powered=true: + facing=west,half=lower,hinge=left,open=true: appearance: facing=west,half=lower,hinge=left,open=true - id: 56 - facing=west,half=lower,hinge=left,open=true,powered=false: - appearance: facing=west,half=lower,hinge=left,open=true - id: 57 - facing=west,half=lower,hinge=right,open=true,powered=true: + facing=west,half=lower,hinge=right,open=true: appearance: facing=west,half=lower,hinge=right,open=true - id: 58 - facing=west,half=lower,hinge=right,open=true,powered=false: - appearance: facing=west,half=lower,hinge=right,open=true - id: 59 - facing=west,half=upper,hinge=left,open=true,powered=true: + facing=west,half=upper,hinge=left,open=true: appearance: facing=west,half=upper,hinge=left,open=true - id: 60 - facing=west,half=upper,hinge=left,open=true,powered=false: - appearance: facing=west,half=upper,hinge=left,open=true - id: 61 - facing=west,half=upper,hinge=right,open=true,powered=true: + facing=west,half=upper,hinge=right,open=true: appearance: facing=west,half=upper,hinge=right,open=true - id: 62 - facing=west,half=upper,hinge=right,open=true,powered=false: - appearance: facing=west,half=upper,hinge=right,open=true - id: 63 # fence gate block default:block_state/fence_gate: properties: @@ -2042,100 +1600,68 @@ templates#block_states: variants: facing=east,in_wall=false,open=false,powered=true: appearance: facing=east,in_wall=false,open=false - id: 0 facing=east,in_wall=false,open=false,powered=false: appearance: facing=east,in_wall=false,open=false - id: 1 facing=east,in_wall=false,open=true,powered=true: appearance: facing=east,in_wall=false,open=true - id: 2 facing=east,in_wall=false,open=true,powered=false: appearance: facing=east,in_wall=false,open=true - id: 3 facing=east,in_wall=true,open=false,powered=true: appearance: facing=east,in_wall=true,open=false - id: 4 facing=east,in_wall=true,open=false,powered=false: appearance: facing=east,in_wall=true,open=false - id: 5 facing=east,in_wall=true,open=true,powered=true: appearance: facing=east,in_wall=true,open=true - id: 6 facing=east,in_wall=true,open=true,powered=false: appearance: facing=east,in_wall=true,open=true - id: 7 facing=south,in_wall=false,open=false,powered=true: appearance: facing=south,in_wall=false,open=false - id: 8 facing=south,in_wall=false,open=false,powered=false: appearance: facing=south,in_wall=false,open=false - id: 9 facing=south,in_wall=false,open=true,powered=true: appearance: facing=south,in_wall=false,open=true - id: 10 facing=south,in_wall=false,open=true,powered=false: appearance: facing=south,in_wall=false,open=true - id: 11 facing=south,in_wall=true,open=false,powered=true: appearance: facing=south,in_wall=true,open=false - id: 12 facing=south,in_wall=true,open=false,powered=false: appearance: facing=south,in_wall=true,open=false - id: 13 facing=south,in_wall=true,open=true,powered=true: appearance: facing=south,in_wall=true,open=true - id: 14 facing=south,in_wall=true,open=true,powered=false: appearance: facing=south,in_wall=true,open=true - id: 15 facing=west,in_wall=false,open=false,powered=true: appearance: facing=west,in_wall=false,open=false - id: 16 facing=west,in_wall=false,open=false,powered=false: appearance: facing=west,in_wall=false,open=false - id: 17 facing=west,in_wall=false,open=true,powered=true: appearance: facing=west,in_wall=false,open=true - id: 18 facing=west,in_wall=false,open=true,powered=false: appearance: facing=west,in_wall=false,open=true - id: 19 facing=west,in_wall=true,open=false,powered=true: appearance: facing=west,in_wall=true,open=false - id: 20 facing=west,in_wall=true,open=false,powered=false: appearance: facing=west,in_wall=true,open=false - id: 21 facing=west,in_wall=true,open=true,powered=true: appearance: facing=west,in_wall=true,open=true - id: 22 facing=west,in_wall=true,open=true,powered=false: appearance: facing=west,in_wall=true,open=true - id: 23 facing=north,in_wall=false,open=false,powered=true: appearance: facing=north,in_wall=false,open=false - id: 24 facing=north,in_wall=false,open=false,powered=false: appearance: facing=north,in_wall=false,open=false - id: 25 facing=north,in_wall=false,open=true,powered=true: appearance: facing=north,in_wall=false,open=true - id: 26 facing=north,in_wall=false,open=true,powered=false: appearance: facing=north,in_wall=false,open=true - id: 27 facing=north,in_wall=true,open=false,powered=true: appearance: facing=north,in_wall=true,open=false - id: 28 facing=north,in_wall=true,open=false,powered=false: appearance: facing=north,in_wall=true,open=false - id: 29 facing=north,in_wall=true,open=true,powered=true: appearance: facing=north,in_wall=true,open=true - id: 30 facing=north,in_wall=true,open=true,powered=false: appearance: facing=north,in_wall=true,open=true - id: 31 # slab block default:block_state/slab: properties: @@ -2176,30 +1702,24 @@ templates#block_states: variants: type=top,waterlogged=false: appearance: type=top,waterlogged=false - id: 0 type=bottom,waterlogged=false: appearance: type=bottom,waterlogged=false - id: 1 type=double,waterlogged=false: appearance: type=double,waterlogged=false - id: 2 type=top,waterlogged=true: appearance: type=top,waterlogged=true - id: 3 settings: resistance: 1200.0 burnable: false fluid-state: water type=bottom,waterlogged=true: appearance: type=bottom,waterlogged=true - id: 4 settings: resistance: 1200.0 burnable: false fluid-state: water type=double,waterlogged=true: appearance: type=double,waterlogged=true - id: 5 settings: resistance: 1200.0 burnable: false @@ -2711,406 +2231,172 @@ templates#block_states: x: 180 y: 180 variants: + waterlogged=true: + settings: + resistance: 1200.0 + burnable: false + fluid-state: water facing=east,half=bottom,shape=inner_left,waterlogged=false: appearance: facing=east,half=bottom,shape=inner_left,waterlogged=false - id: 0 facing=east,half=bottom,shape=inner_right,waterlogged=false: appearance: facing=east,half=bottom,shape=inner_right,waterlogged=false - id: 1 facing=east,half=bottom,shape=outer_left,waterlogged=false: appearance: facing=east,half=bottom,shape=outer_left,waterlogged=false - id: 2 facing=east,half=bottom,shape=outer_right,waterlogged=false: appearance: facing=east,half=bottom,shape=outer_right,waterlogged=false - id: 3 facing=east,half=bottom,shape=straight,waterlogged=false: appearance: facing=east,half=bottom,shape=straight,waterlogged=false - id: 4 facing=east,half=top,shape=inner_left,waterlogged=false: appearance: facing=east,half=top,shape=inner_left,waterlogged=false - id: 5 facing=east,half=top,shape=inner_right,waterlogged=false: appearance: facing=east,half=top,shape=inner_right,waterlogged=false - id: 6 facing=east,half=top,shape=outer_left,waterlogged=false: appearance: facing=east,half=top,shape=outer_left,waterlogged=false - id: 7 facing=east,half=top,shape=outer_right,waterlogged=false: appearance: facing=east,half=top,shape=outer_right,waterlogged=false - id: 8 facing=east,half=top,shape=straight,waterlogged=false: appearance: facing=east,half=top,shape=straight,waterlogged=false - id: 9 facing=north,half=bottom,shape=inner_left,waterlogged=false: appearance: facing=north,half=bottom,shape=inner_left,waterlogged=false - id: 10 facing=north,half=bottom,shape=inner_right,waterlogged=false: appearance: facing=north,half=bottom,shape=inner_right,waterlogged=false - id: 11 facing=north,half=bottom,shape=outer_left,waterlogged=false: appearance: facing=north,half=bottom,shape=outer_left,waterlogged=false - id: 12 facing=north,half=bottom,shape=outer_right,waterlogged=false: appearance: facing=north,half=bottom,shape=outer_right,waterlogged=false - id: 13 facing=north,half=bottom,shape=straight,waterlogged=false: appearance: facing=north,half=bottom,shape=straight,waterlogged=false - id: 14 facing=north,half=top,shape=inner_left,waterlogged=false: appearance: facing=north,half=top,shape=inner_left,waterlogged=false - id: 15 facing=north,half=top,shape=inner_right,waterlogged=false: appearance: facing=north,half=top,shape=inner_right,waterlogged=false - id: 16 facing=north,half=top,shape=outer_left,waterlogged=false: appearance: facing=north,half=top,shape=outer_left,waterlogged=false - id: 17 facing=north,half=top,shape=outer_right,waterlogged=false: appearance: facing=north,half=top,shape=outer_right,waterlogged=false - id: 18 facing=north,half=top,shape=straight,waterlogged=false: appearance: facing=north,half=top,shape=straight,waterlogged=false - id: 19 facing=south,half=bottom,shape=inner_left,waterlogged=false: appearance: facing=south,half=bottom,shape=inner_left,waterlogged=false - id: 20 facing=south,half=bottom,shape=inner_right,waterlogged=false: appearance: facing=south,half=bottom,shape=inner_right,waterlogged=false - id: 21 facing=south,half=bottom,shape=outer_left,waterlogged=false: appearance: facing=south,half=bottom,shape=outer_left,waterlogged=false - id: 22 facing=south,half=bottom,shape=outer_right,waterlogged=false: appearance: facing=south,half=bottom,shape=outer_right,waterlogged=false - id: 23 facing=south,half=bottom,shape=straight,waterlogged=false: appearance: facing=south,half=bottom,shape=straight,waterlogged=false - id: 24 facing=south,half=top,shape=inner_left,waterlogged=false: appearance: facing=south,half=top,shape=inner_left,waterlogged=false - id: 25 facing=south,half=top,shape=inner_right,waterlogged=false: appearance: facing=south,half=top,shape=inner_right,waterlogged=false - id: 26 facing=south,half=top,shape=outer_left,waterlogged=false: appearance: facing=south,half=top,shape=outer_left,waterlogged=false - id: 27 facing=south,half=top,shape=outer_right,waterlogged=false: appearance: facing=south,half=top,shape=outer_right,waterlogged=false - id: 28 facing=south,half=top,shape=straight,waterlogged=false: appearance: facing=south,half=top,shape=straight,waterlogged=false - id: 29 facing=west,half=bottom,shape=inner_left,waterlogged=false: appearance: facing=west,half=bottom,shape=inner_left,waterlogged=false - id: 30 facing=west,half=bottom,shape=inner_right,waterlogged=false: appearance: facing=west,half=bottom,shape=inner_right,waterlogged=false - id: 31 facing=west,half=bottom,shape=outer_left,waterlogged=false: appearance: facing=west,half=bottom,shape=outer_left,waterlogged=false - id: 32 facing=west,half=bottom,shape=outer_right,waterlogged=false: appearance: facing=west,half=bottom,shape=outer_right,waterlogged=false - id: 33 facing=west,half=bottom,shape=straight,waterlogged=false: appearance: facing=west,half=bottom,shape=straight,waterlogged=false - id: 34 facing=west,half=top,shape=inner_left,waterlogged=false: appearance: facing=west,half=top,shape=inner_left,waterlogged=false - id: 35 facing=west,half=top,shape=inner_right,waterlogged=false: appearance: facing=west,half=top,shape=inner_right,waterlogged=false - id: 36 facing=west,half=top,shape=outer_left,waterlogged=false: appearance: facing=west,half=top,shape=outer_left,waterlogged=false - id: 37 facing=west,half=top,shape=outer_right,waterlogged=false: appearance: facing=west,half=top,shape=outer_right,waterlogged=false - id: 38 facing=west,half=top,shape=straight,waterlogged=false: appearance: facing=west,half=top,shape=straight,waterlogged=false - id: 39 facing=east,half=bottom,shape=inner_left,waterlogged=true: appearance: facing=east,half=bottom,shape=inner_left,waterlogged=true - id: 40 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=east,half=bottom,shape=inner_right,waterlogged=true: appearance: facing=east,half=bottom,shape=inner_right,waterlogged=true - id: 41 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=east,half=bottom,shape=outer_left,waterlogged=true: appearance: facing=east,half=bottom,shape=outer_left,waterlogged=true - id: 42 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=east,half=bottom,shape=outer_right,waterlogged=true: appearance: facing=east,half=bottom,shape=outer_right,waterlogged=true - id: 43 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=east,half=bottom,shape=straight,waterlogged=true: appearance: facing=east,half=bottom,shape=straight,waterlogged=true - id: 44 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=east,half=top,shape=inner_left,waterlogged=true: appearance: facing=east,half=top,shape=inner_left,waterlogged=true - id: 45 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=east,half=top,shape=inner_right,waterlogged=true: appearance: facing=east,half=top,shape=inner_right,waterlogged=true - id: 46 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=east,half=top,shape=outer_left,waterlogged=true: appearance: facing=east,half=top,shape=outer_left,waterlogged=true - id: 47 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=east,half=top,shape=outer_right,waterlogged=true: appearance: facing=east,half=top,shape=outer_right,waterlogged=true - id: 48 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=east,half=top,shape=straight,waterlogged=true: appearance: facing=east,half=top,shape=straight,waterlogged=true - id: 49 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=north,half=bottom,shape=inner_left,waterlogged=true: appearance: facing=north,half=bottom,shape=inner_left,waterlogged=true - id: 50 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=north,half=bottom,shape=inner_right,waterlogged=true: appearance: facing=north,half=bottom,shape=inner_right,waterlogged=true - id: 51 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=north,half=bottom,shape=outer_left,waterlogged=true: appearance: facing=north,half=bottom,shape=outer_left,waterlogged=true - id: 52 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=north,half=bottom,shape=outer_right,waterlogged=true: appearance: facing=north,half=bottom,shape=outer_right,waterlogged=true - id: 53 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=north,half=bottom,shape=straight,waterlogged=true: appearance: facing=north,half=bottom,shape=straight,waterlogged=true - id: 54 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=north,half=top,shape=inner_left,waterlogged=true: appearance: facing=north,half=top,shape=inner_left,waterlogged=true - id: 55 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=north,half=top,shape=inner_right,waterlogged=true: appearance: facing=north,half=top,shape=inner_right,waterlogged=true - id: 56 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=north,half=top,shape=outer_left,waterlogged=true: appearance: facing=north,half=top,shape=outer_left,waterlogged=true - id: 57 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=north,half=top,shape=outer_right,waterlogged=true: appearance: facing=north,half=top,shape=outer_right,waterlogged=true - id: 58 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=north,half=top,shape=straight,waterlogged=true: appearance: facing=north,half=top,shape=straight,waterlogged=true - id: 59 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=south,half=bottom,shape=inner_left,waterlogged=true: appearance: facing=south,half=bottom,shape=inner_left,waterlogged=true - id: 60 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=south,half=bottom,shape=inner_right,waterlogged=true: appearance: facing=south,half=bottom,shape=inner_right,waterlogged=true - id: 61 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=south,half=bottom,shape=outer_left,waterlogged=true: appearance: facing=south,half=bottom,shape=outer_left,waterlogged=true - id: 62 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=south,half=bottom,shape=outer_right,waterlogged=true: appearance: facing=south,half=bottom,shape=outer_right,waterlogged=true - id: 63 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=south,half=bottom,shape=straight,waterlogged=true: appearance: facing=south,half=bottom,shape=straight,waterlogged=true - id: 64 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=south,half=top,shape=inner_left,waterlogged=true: appearance: facing=south,half=top,shape=inner_left,waterlogged=true - id: 65 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=south,half=top,shape=inner_right,waterlogged=true: appearance: facing=south,half=top,shape=inner_right,waterlogged=true - id: 66 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=south,half=top,shape=outer_left,waterlogged=true: appearance: facing=south,half=top,shape=outer_left,waterlogged=true - id: 67 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=south,half=top,shape=outer_right,waterlogged=true: appearance: facing=south,half=top,shape=outer_right,waterlogged=true - id: 68 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=south,half=top,shape=straight,waterlogged=true: appearance: facing=south,half=top,shape=straight,waterlogged=true - id: 69 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=west,half=bottom,shape=inner_left,waterlogged=true: appearance: facing=west,half=bottom,shape=inner_left,waterlogged=true - id: 70 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=west,half=bottom,shape=inner_right,waterlogged=true: appearance: facing=west,half=bottom,shape=inner_right,waterlogged=true - id: 71 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=west,half=bottom,shape=outer_left,waterlogged=true: appearance: facing=west,half=bottom,shape=outer_left,waterlogged=true - id: 72 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=west,half=bottom,shape=outer_right,waterlogged=true: appearance: facing=west,half=bottom,shape=outer_right,waterlogged=true - id: 73 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=west,half=bottom,shape=straight,waterlogged=true: appearance: facing=west,half=bottom,shape=straight,waterlogged=true - id: 74 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=west,half=top,shape=inner_left,waterlogged=true: appearance: facing=west,half=top,shape=inner_left,waterlogged=true - id: 75 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=west,half=top,shape=inner_right,waterlogged=true: appearance: facing=west,half=top,shape=inner_right,waterlogged=true - id: 76 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=west,half=top,shape=outer_left,waterlogged=true: appearance: facing=west,half=top,shape=outer_left,waterlogged=true - id: 77 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=west,half=top,shape=outer_right,waterlogged=true: appearance: facing=west,half=top,shape=outer_right,waterlogged=true - id: 78 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water facing=west,half=top,shape=straight,waterlogged=true: appearance: facing=west,half=top,shape=straight,waterlogged=true - id: 79 - settings: - resistance: 1200.0 - burnable: false - fluid-state: water + # pressure plate block default:block_state/pressure_plate: properties: @@ -3131,10 +2417,676 @@ templates#block_states: variants: powered=false: appearance: normal - id: ${normal_id} powered=true: appearance: powered - id: ${powered_id} + default:block_state/button: + properties: + powered: + type: boolean + default: false + face: + type: anchor_type + default: floor + facing: + type: 4-direction + default: north + appearances: + face=floor,facing=east,powered=true: + state: ${base_block}[face=floor,facing=east,powered=true] + entity-renderer: + item: ${pressed_item} + rotation: 0,90,0 + scale: 1.0005 + translation: 0,0.00023,0 + face=floor,facing=west,powered=true: + state: ${base_block}[face=floor,facing=west,powered=true] + entity-renderer: + item: ${pressed_item} + rotation: 0,-90,0 + scale: 1.0005 + translation: 0,0.00023,0 + face=floor,facing=east,powered=false: + state: ${base_block}[face=floor,facing=east,powered=false] + entity-renderer: + item: ${not_pressed_item} + rotation: 0,90,0 + scale: 1.0005 + translation: 0,0.00023,0 + face=floor,facing=west,powered=false: + state: ${base_block}[face=floor,facing=west,powered=false] + entity-renderer: + item: ${not_pressed_item} + rotation: 0,-90,0 + scale: 1.0005 + translation: 0,0.00023,0 + face=floor,facing=south,powered=true: + state: ${base_block}[face=floor,facing=south,powered=true] + entity-renderer: + item: ${pressed_item} + scale: 1.0005 + translation: 0,0.00023,0 + face=floor,facing=north,powered=true: + state: ${base_block}[face=floor,facing=north,powered=true] + entity-renderer: + item: ${pressed_item} + rotation: 0,180,0 + scale: 1.0005 + translation: 0,0.00023,0 + face=floor,facing=south,powered=false: + state: ${base_block}[face=floor,facing=south,powered=false] + entity-renderer: + item: ${not_pressed_item} + scale: 1.0005 + translation: 0,0.00023,0 + face=floor,facing=north,powered=false: + state: ${base_block}[face=floor,facing=north,powered=false] + entity-renderer: + item: ${not_pressed_item} + rotation: 0,180,0 + scale: 1.0005 + translation: 0,0.00023,0 + face=wall,facing=north,powered=true: + state: ${base_block}[face=wall,facing=north,powered=true] + entity-renderer: + item: ${pressed_item} + rotation: -90,0,0 + scale: 1.0005 + translation: 0,0,-0.00023 + face=wall,facing=south,powered=true: + state: ${base_block}[face=wall,facing=south,powered=true] + entity-renderer: + item: ${pressed_item} + rotation: 90,0,180 + scale: 1.0005 + translation: 0,0,0.00023 + face=wall,facing=north,powered=false: + state: ${base_block}[face=wall,facing=north,powered=false] + entity-renderer: + item: ${not_pressed_item} + rotation: -90,0,0 + scale: 1.0005 + translation: 0,0,-0.00023 + face=wall,facing=south,powered=false: + state: ${base_block}[face=wall,facing=south,powered=false] + entity-renderer: + item: ${not_pressed_item} + rotation: 90,0,180 + scale: 1.0005 + translation: 0,0,0.00023 + face=wall,facing=west,powered=true: + state: ${base_block}[face=wall,facing=west,powered=true] + entity-renderer: + item: ${pressed_item} + rotation: 0,90,90 + scale: 1.0005 + translation: -0.00023,0,0 + face=wall,facing=east,powered=true: + state: ${base_block}[face=wall,facing=east,powered=true] + entity-renderer: + item: ${pressed_item} + rotation: 0,270,-90 + scale: 1.0005 + translation: 0.00023,0,0 + face=wall,facing=west,powered=false: + state: ${base_block}[face=wall,facing=west,powered=false] + entity-renderer: + item: ${not_pressed_item} + rotation: 0,90,90 + scale: 1.0005 + translation: -0.00023,0,0 + face=wall,facing=east,powered=false: + state: ${base_block}[face=wall,facing=east,powered=false] + entity-renderer: + item: ${not_pressed_item} + rotation: 0,270,-90 + scale: 1.0005 + translation: 0.00023,0,0 + face=ceiling,facing=north,powered=true: + state: ${base_block}[face=ceiling,facing=north,powered=true] + entity-renderer: + item: ${pressed_item} + rotation: 0,180,180 + scale: 1.0005 + translation: 0,-0.00023,0 + face=ceiling,facing=south,powered=true: + state: ${base_block}[face=ceiling,facing=south,powered=true] + entity-renderer: + item: ${pressed_item} + rotation: 0,0,180 + scale: 1.0005 + translation: 0,-0.00023,0 + face=ceiling,facing=north,powered=false: + state: ${base_block}[face=ceiling,facing=north,powered=false] + entity-renderer: + item: ${not_pressed_item} + rotation: 0,180,180 + scale: 1.0005 + translation: 0,-0.00023,0 + face=ceiling,facing=south,powered=false: + state: ${base_block}[face=ceiling,facing=south,powered=false] + entity-renderer: + item: ${not_pressed_item} + rotation: 0,0,180 + scale: 1.0005 + translation: 0,-0.00023,0 + face=ceiling,facing=west,powered=true: + state: ${base_block}[face=ceiling,facing=west,powered=true] + entity-renderer: + item: ${pressed_item} + rotation: 0,90,180 + scale: 1.0005 + translation: 0,-0.00023,0 + face=ceiling,facing=east,powered=true: + state: ${base_block}[face=ceiling,facing=east,powered=true] + entity-renderer: + item: ${pressed_item} + rotation: -90,-90,-90 + scale: 1.0005 + translation: 0,-0.00023,0 + face=ceiling,facing=west,powered=false: + state: ${base_block}[face=ceiling,facing=west,powered=false] + entity-renderer: + item: ${not_pressed_item} + rotation: 0,90,180 + scale: 1.0005 + translation: 0,-0.00023,0 + face=ceiling,facing=east,powered=false: + state: ${base_block}[face=ceiling,facing=east,powered=false] + entity-renderer: + item: ${not_pressed_item} + rotation: -90,-90,-90 + scale: 1.0005 + translation: 0,-0.00023,0 + variants: + face=floor,facing=east,powered=true: + appearance: face=floor,facing=east,powered=true + face=floor,facing=west,powered=true: + appearance: face=floor,facing=west,powered=true + face=floor,facing=east,powered=false: + appearance: face=floor,facing=east,powered=false + face=floor,facing=west,powered=false: + appearance: face=floor,facing=west,powered=false + face=floor,facing=south,powered=true: + appearance: face=floor,facing=south,powered=true + face=floor,facing=north,powered=true: + appearance: face=floor,facing=north,powered=true + face=floor,facing=south,powered=false: + appearance: face=floor,facing=south,powered=false + face=floor,facing=north,powered=false: + appearance: face=floor,facing=north,powered=false + face=wall,facing=north,powered=true: + appearance: face=wall,facing=north,powered=true + face=wall,facing=south,powered=true: + appearance: face=wall,facing=south,powered=true + face=wall,facing=north,powered=false: + appearance: face=wall,facing=north,powered=false + face=wall,facing=south,powered=false: + appearance: face=wall,facing=south,powered=false + face=wall,facing=west,powered=true: + appearance: face=wall,facing=west,powered=true + face=wall,facing=east,powered=true: + appearance: face=wall,facing=east,powered=true + face=wall,facing=west,powered=false: + appearance: face=wall,facing=west,powered=false + face=wall,facing=east,powered=false: + appearance: face=wall,facing=east,powered=false + face=ceiling,facing=north,powered=true: + appearance: face=ceiling,facing=north,powered=true + face=ceiling,facing=south,powered=true: + appearance: face=ceiling,facing=south,powered=true + face=ceiling,facing=north,powered=false: + appearance: face=ceiling,facing=north,powered=false + face=ceiling,facing=south,powered=false: + appearance: face=ceiling,facing=south,powered=false + face=ceiling,facing=west,powered=true: + appearance: face=ceiling,facing=west,powered=true + face=ceiling,facing=east,powered=true: + appearance: face=ceiling,facing=east,powered=true + face=ceiling,facing=west,powered=false: + appearance: face=ceiling,facing=west,powered=false + face=ceiling,facing=east,powered=false: + appearance: face=ceiling,facing=east,powered=false + default:block_state/fence: + properties: + north: + type: boolean + default: false + east: + type: boolean + default: false + south: + type: boolean + default: false + west: + type: boolean + default: false + waterlogged: + type: boolean + default: false + appearances: + east=false,north=false,south=false,waterlogged=false,west=false: + state: ${base_block}[east=false,north=false,south=false,waterlogged=false,west=false] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + east=true,north=false,south=false,waterlogged=false,west=false: + state: ${base_block}[east=true,north=false,south=false,waterlogged=false,west=false] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 270 + east=false,north=true,south=false,waterlogged=false,west=false: + state: ${base_block}[east=false,north=true,south=false,waterlogged=false,west=false] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 180 + east=false,north=false,south=true,waterlogged=false,west=false: + state: ${base_block}[east=false,north=false,south=true,waterlogged=false,west=false] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 0 + east=false,north=false,south=false,waterlogged=false,west=true: + state: ${base_block}[east=false,north=false,south=false,waterlogged=false,west=true] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 90 + east=true,north=true,south=false,waterlogged=false,west=false: + state: ${base_block}[east=true,north=true,south=false,waterlogged=false,west=false] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 180 + - item: ${fence_side_item} + rotation: 270 + east=true,north=false,south=true,waterlogged=false,west=false: + state: ${base_block}[east=true,north=false,south=true,waterlogged=false,west=false] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 0 + - item: ${fence_side_item} + rotation: 270 + east=true,north=false,south=false,waterlogged=false,west=true: + state: ${base_block}[east=true,north=false,south=false,waterlogged=false,west=true] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 90 + - item: ${fence_side_item} + rotation: 270 + east=false,north=true,south=true,waterlogged=false,west=false: + state: ${base_block}[east=false,north=true,south=true,waterlogged=false,west=false] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 0 + - item: ${fence_side_item} + rotation: 180 + east=false,north=true,south=false,waterlogged=false,west=true: + state: ${base_block}[east=false,north=true,south=false,waterlogged=false,west=true] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 90 + - item: ${fence_side_item} + rotation: 180 + east=false,north=false,south=true,waterlogged=false,west=true: + state: ${base_block}[east=false,north=false,south=true,waterlogged=false,west=true] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 0 + - item: ${fence_side_item} + rotation: 90 + east=true,north=true,south=true,waterlogged=false,west=false: + state: ${base_block}[east=true,north=true,south=true,waterlogged=false,west=false] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 0 + - item: ${fence_side_item} + rotation: 180 + - item: ${fence_side_item} + rotation: 270 + east=true,north=true,south=false,waterlogged=false,west=true: + state: ${base_block}[east=true,north=true,south=false,waterlogged=false,west=true] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 90 + - item: ${fence_side_item} + rotation: 180 + - item: ${fence_side_item} + rotation: 270 + east=true,north=false,south=true,waterlogged=false,west=true: + state: ${base_block}[east=true,north=false,south=true,waterlogged=false,west=true] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 0 + - item: ${fence_side_item} + rotation: 90 + - item: ${fence_side_item} + rotation: 270 + east=false,north=true,south=true,waterlogged=false,west=true: + state: ${base_block}[east=false,north=true,south=true,waterlogged=false,west=true] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 0 + - item: ${fence_side_item} + rotation: 90 + - item: ${fence_side_item} + rotation: 180 + east=true,north=true,south=true,waterlogged=false,west=true: + state: ${base_block}[east=true,north=true,south=true,waterlogged=false,west=true] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 0 + - item: ${fence_side_item} + rotation: 90 + - item: ${fence_side_item} + rotation: 180 + - item: ${fence_side_item} + rotation: 270 + east=false,north=false,south=false,waterlogged=true,west=false: + state: ${base_block}[east=false,north=false,south=false,waterlogged=true,west=false] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + east=true,north=false,south=false,waterlogged=true,west=false: + state: ${base_block}[east=true,north=false,south=false,waterlogged=true,west=false] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 270 + east=false,north=true,south=false,waterlogged=true,west=false: + state: ${base_block}[east=false,north=true,south=false,waterlogged=true,west=false] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 180 + east=false,north=false,south=true,waterlogged=true,west=false: + state: ${base_block}[east=false,north=false,south=true,waterlogged=true,west=false] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 0 + east=false,north=false,south=false,waterlogged=true,west=true: + state: ${base_block}[east=false,north=false,south=false,waterlogged=true,west=true] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 90 + east=true,north=true,south=false,waterlogged=true,west=false: + state: ${base_block}[east=true,north=true,south=false,waterlogged=true,west=false] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 180 + - item: ${fence_side_item} + rotation: 270 + east=true,north=false,south=true,waterlogged=true,west=false: + state: ${base_block}[east=true,north=false,south=true,waterlogged=true,west=false] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 0 + - item: ${fence_side_item} + rotation: 270 + east=true,north=false,south=false,waterlogged=true,west=true: + state: ${base_block}[east=true,north=false,south=false,waterlogged=true,west=true] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 90 + - item: ${fence_side_item} + rotation: 270 + east=false,north=true,south=true,waterlogged=true,west=false: + state: ${base_block}[east=false,north=true,south=true,waterlogged=true,west=false] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 0 + - item: ${fence_side_item} + rotation: 180 + east=false,north=true,south=false,waterlogged=true,west=true: + state: ${base_block}[east=false,north=true,south=false,waterlogged=true,west=true] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 90 + - item: ${fence_side_item} + rotation: 180 + east=false,north=false,south=true,waterlogged=true,west=true: + state: ${base_block}[east=false,north=false,south=true,waterlogged=true,west=true] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 0 + - item: ${fence_side_item} + rotation: 90 + east=true,north=true,south=true,waterlogged=true,west=false: + state: ${base_block}[east=true,north=true,south=true,waterlogged=true,west=false] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 0 + - item: ${fence_side_item} + rotation: 180 + - item: ${fence_side_item} + rotation: 270 + east=true,north=true,south=false,waterlogged=true,west=true: + state: ${base_block}[east=true,north=true,south=false,waterlogged=true,west=true] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 90 + - item: ${fence_side_item} + rotation: 180 + - item: ${fence_side_item} + rotation: 270 + east=true,north=false,south=true,waterlogged=true,west=true: + state: ${base_block}[east=true,north=false,south=true,waterlogged=true,west=true] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 0 + - item: ${fence_side_item} + rotation: 90 + - item: ${fence_side_item} + rotation: 270 + east=false,north=true,south=true,waterlogged=true,west=true: + state: ${base_block}[east=false,north=true,south=true,waterlogged=true,west=true] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 0 + - item: ${fence_side_item} + rotation: 90 + - item: ${fence_side_item} + rotation: 180 + east=true,north=true,south=true,waterlogged=true,west=true: + state: ${base_block}[east=true,north=true,south=true,waterlogged=true,west=true] + entity-renderer: + - item: ${fence_post_item} + rotation: 180 + scale: 1.0003 + translation: 0,0.0001,0 + - item: ${fence_side_item} + rotation: 0 + - item: ${fence_side_item} + rotation: 90 + - item: ${fence_side_item} + rotation: 180 + - item: ${fence_side_item} + rotation: 270 + variants: + waterlogged=true: + settings: + resistance: 1200.0 + burnable: false + fluid-state: water + east=false,north=false,south=false,waterlogged=false,west=false: + appearance: east=false,north=false,south=false,waterlogged=false,west=false + east=true,north=false,south=false,waterlogged=false,west=false: + appearance: east=true,north=false,south=false,waterlogged=false,west=false + east=false,north=true,south=false,waterlogged=false,west=false: + appearance: east=false,north=true,south=false,waterlogged=false,west=false + east=false,north=false,south=true,waterlogged=false,west=false: + appearance: east=false,north=false,south=true,waterlogged=false,west=false + east=false,north=false,south=false,waterlogged=false,west=true: + appearance: east=false,north=false,south=false,waterlogged=false,west=true + east=true,north=true,south=false,waterlogged=false,west=false: + appearance: east=true,north=true,south=false,waterlogged=false,west=false + east=true,north=false,south=true,waterlogged=false,west=false: + appearance: east=true,north=false,south=true,waterlogged=false,west=false + east=true,north=false,south=false,waterlogged=false,west=true: + appearance: east=true,north=false,south=false,waterlogged=false,west=true + east=false,north=true,south=true,waterlogged=false,west=false: + appearance: east=false,north=true,south=true,waterlogged=false,west=false + east=false,north=true,south=false,waterlogged=false,west=true: + appearance: east=false,north=true,south=false,waterlogged=false,west=true + east=false,north=false,south=true,waterlogged=false,west=true: + appearance: east=false,north=false,south=true,waterlogged=false,west=true + east=true,north=true,south=true,waterlogged=false,west=false: + appearance: east=true,north=true,south=true,waterlogged=false,west=false + east=true,north=true,south=false,waterlogged=false,west=true: + appearance: east=true,north=true,south=false,waterlogged=false,west=true + east=true,north=false,south=true,waterlogged=false,west=true: + appearance: east=true,north=false,south=true,waterlogged=false,west=true + east=false,north=true,south=true,waterlogged=false,west=true: + appearance: east=false,north=true,south=true,waterlogged=false,west=true + east=true,north=true,south=true,waterlogged=false,west=true: + appearance: east=true,north=true,south=true,waterlogged=false,west=true + east=false,north=false,south=false,waterlogged=true,west=false: + appearance: east=false,north=false,south=false,waterlogged=true,west=false + east=true,north=false,south=false,waterlogged=true,west=false: + appearance: east=true,north=false,south=false,waterlogged=true,west=false + east=false,north=true,south=false,waterlogged=true,west=false: + appearance: east=false,north=true,south=false,waterlogged=true,west=false + east=false,north=false,south=true,waterlogged=true,west=false: + appearance: east=false,north=false,south=true,waterlogged=true,west=false + east=false,north=false,south=false,waterlogged=true,west=true: + appearance: east=false,north=false,south=false,waterlogged=true,west=true + east=true,north=true,south=false,waterlogged=true,west=false: + appearance: east=true,north=true,south=false,waterlogged=true,west=false + east=true,north=false,south=true,waterlogged=true,west=false: + appearance: east=true,north=false,south=true,waterlogged=true,west=false + east=true,north=false,south=false,waterlogged=true,west=true: + appearance: east=true,north=false,south=false,waterlogged=true,west=true + east=false,north=true,south=true,waterlogged=true,west=false: + appearance: east=false,north=true,south=true,waterlogged=true,west=false + east=false,north=true,south=false,waterlogged=true,west=true: + appearance: east=false,north=true,south=false,waterlogged=true,west=true + east=false,north=false,south=true,waterlogged=true,west=true: + appearance: east=false,north=false,south=true,waterlogged=true,west=true + east=true,north=true,south=true,waterlogged=true,west=false: + appearance: east=true,north=true,south=true,waterlogged=true,west=false + east=true,north=true,south=false,waterlogged=true,west=true: + appearance: east=true,north=true,south=false,waterlogged=true,west=true + east=true,north=false,south=true,waterlogged=true,west=true: + appearance: east=true,north=false,south=true,waterlogged=true,west=true + east=false,north=true,south=true,waterlogged=true,west=true: + appearance: east=false,north=true,south=true,waterlogged=true,west=true + east=true,north=true,south=true,waterlogged=true,west=true: + appearance: east=true,north=true,south=true,waterlogged=true,west=true + # recipes templates#recipes: default:recipe/planks: @@ -3142,7 +3094,7 @@ templates#recipes: category: building group: planks ingredients: - A: '#default:${wood_type}_logs' + - '#default:${wood_type}_logs' result: id: default:${wood_type}_planks count: 4 diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/fence_side.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/fence_side.json new file mode 100644 index 000000000..2c3c53932 --- /dev/null +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/fence_side.json @@ -0,0 +1,29 @@ +{ + "textures": { + "particle": "#texture" + }, + "elements": [ + { + "from": [6.9989, 11.99835, -0.00495], + "to": [9.0011, 15.00165, 9.00495], + "rotation": {"angle": 0, "axis": "y", "origin": [-0.0088, -0.01485, -0.00495]}, + "faces": { + "east": {"uv": [0, 1, 9, 4], "texture": "#texture"}, + "west": {"uv": [0, 1, 9, 4], "texture": "#texture"}, + "up": {"uv": [7, 0, 9, 9], "texture": "#texture"}, + "down": {"uv": [7, 0, 9, 9], "texture": "#texture"} + } + }, + { + "from": [6.9989, 5.99835, -0.00495], + "to": [9.0011, 9.00165, 9.00495], + "rotation": {"angle": 0, "axis": "y", "origin": [-0.0088, -0.00825, -0.00495]}, + "faces": { + "east": {"uv": [0, 7, 9, 10], "texture": "#texture"}, + "west": {"uv": [0, 7, 9, 10], "texture": "#texture"}, + "up": {"uv": [7, 0, 9, 9], "texture": "#texture"}, + "down": {"uv": [7, 0, 9, 9], "texture": "#texture"} + } + } + ] +} \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/large_mushroom.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/large_mushroom.json new file mode 100644 index 000000000..356812b0e --- /dev/null +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/large_mushroom.json @@ -0,0 +1,172 @@ +{ + "textures": { + "0": "block/custom/mushroom", + "particle": "block/custom/mushroom" + }, + "elements": [ + { + "from": [8.5, 3, 11.5], + "to": [13.5, 3, 17.5], + "rotation": {"angle": 0, "axis": "y", "origin": [10, 4, 13]}, + "faces": { + "north": {"uv": [0, 0, 2.5, 0], "texture": "#0"}, + "east": {"uv": [0, 0, 3, 0], "texture": "#0"}, + "south": {"uv": [0, 0, 2.5, 0], "texture": "#0"}, + "west": {"uv": [0, 0, 3, 0], "texture": "#0"}, + "up": {"uv": [8, 0, 10.5, 3], "texture": "#0"}, + "down": {"uv": [8, 3, 10.5, 0], "texture": "#0"} + } + }, + { + "from": [9.5, 11, 10.5], + "to": [14.5, 11, 16.5], + "rotation": {"angle": -22.5, "axis": "x", "origin": [12, 11, 16.5]}, + "faces": { + "north": {"uv": [2.5, 0, 0, 0], "texture": "#0"}, + "east": {"uv": [3, 0, 0, 0], "texture": "#0"}, + "south": {"uv": [2.5, 0, 0, 0], "texture": "#0"}, + "west": {"uv": [3, 0, 0, 0], "texture": "#0"}, + "up": {"uv": [8, 0, 5.5, 3], "texture": "#0"}, + "down": {"uv": [8, 3, 5.5, 0], "texture": "#0"} + } + }, + { + "from": [1.5, 7, 10.5], + "to": [6.5, 7, 16.5], + "rotation": {"angle": 0, "axis": "y", "origin": [3, 8, 12]}, + "faces": { + "north": {"uv": [0, 0, 2.5, 0], "texture": "#0"}, + "east": {"uv": [0, 0, 3, 0], "texture": "#0"}, + "south": {"uv": [0, 0, 2.5, 0], "texture": "#0"}, + "west": {"uv": [0, 0, 3, 0], "texture": "#0"}, + "up": {"uv": [5.5, 0, 8, 3], "texture": "#0"}, + "down": {"uv": [5.5, 3, 8, 0], "texture": "#0"} + } + }, + { + "from": [5.5, 9, 14.5], + "to": [5.5, 11, 16.5], + "rotation": {"angle": 45, "axis": "y", "origin": [5.5, 10, 15.5]}, + "faces": { + "north": {"uv": [0, 0, 0, 1], "texture": "#0"}, + "east": {"uv": [2.5, 10, 3.5, 11], "texture": "#0"}, + "south": {"uv": [0, 0, 0, 1], "texture": "#0"}, + "west": {"uv": [3.5, 10, 2.5, 11], "texture": "#0"}, + "up": {"uv": [0, 0, 0, 1], "texture": "#0"}, + "down": {"uv": [0, 0, 0, 1], "texture": "#0"} + } + }, + { + "from": [4.5, 2, 14.5], + "to": [4.5, 4, 16.5], + "rotation": {"angle": -45, "axis": "y", "origin": [4.5, 3, 15.5]}, + "faces": { + "north": {"uv": [0, 0, 0, 1], "texture": "#0"}, + "east": {"uv": [2.5, 10, 3.5, 11], "texture": "#0"}, + "south": {"uv": [0, 0, 0, 1], "texture": "#0"}, + "west": {"uv": [3.5, 10, 2.5, 11], "texture": "#0"}, + "up": {"uv": [0, 0, 0, 1], "texture": "#0"}, + "down": {"uv": [0, 0, 0, 1], "texture": "#0"} + } + }, + { + "from": [9.5, 6, 14.5], + "to": [11.5, 8, 16.5], + "rotation": {"angle": 0, "axis": "y", "origin": [10, 6, 16]}, + "faces": { + "north": {"uv": [3.5, 2, 4.5, 3], "texture": "#0"}, + "east": {"uv": [5.5, 3, 4.5, 4], "texture": "#0"}, + "south": {"uv": [3.5, 4, 4.5, 5], "texture": "#0"}, + "west": {"uv": [4.5, 3, 5.5, 4], "texture": "#0"}, + "up": {"uv": [3.5, 3, 4.5, 4], "texture": "#0"}, + "down": {"uv": [4.5, 2, 5.5, 3], "texture": "#0"} + } + }, + { + "from": [7, 7, 11], + "to": [14, 9, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [7, 7, 11]}, + "faces": { + "north": {"uv": [0, 5, 3.5, 6], "texture": "#0"}, + "east": {"uv": [3.5, 0, 5.5, 1], "texture": "#0"}, + "south": {"uv": [0, 4, 3.5, 5], "texture": "#0"}, + "west": {"uv": [3.5, 1, 5.5, 2], "texture": "#0"}, + "up": {"uv": [3.5, 2, 0, 0], "texture": "#0"}, + "down": {"uv": [3.5, 2, 0, 4], "texture": "#0"} + } + }, + { + "from": [7, 13, 14], + "to": [12, 14, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [7, 13, 14]}, + "faces": { + "north": {"uv": [0, 9, 2.5, 9.5], "texture": "#0"}, + "east": {"uv": [2.5, 11.5, 3.5, 12], "texture": "#0"}, + "south": {"uv": [0, 9.5, 2.5, 10], "texture": "#0"}, + "west": {"uv": [2.5, 11, 3.5, 11.5], "texture": "#0"}, + "up": {"uv": [2.5, 11, 0, 10], "texture": "#0"}, + "down": {"uv": [2.5, 11, 0, 12], "texture": "#0"} + } + }, + { + "from": [3, 11, 13], + "to": [8, 12, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [3, 11, 13]}, + "faces": { + "north": {"uv": [0, 15.5, 2.5, 16], "texture": "#0"}, + "east": {"uv": [2.5, 15, 4, 15.5], "texture": "#0"}, + "south": {"uv": [0, 15, 2.5, 15.5], "texture": "#0"}, + "west": {"uv": [2.5, 15.5, 4, 16], "texture": "#0"}, + "up": {"uv": [2.5, 13.5, 0, 12], "texture": "#0"}, + "down": {"uv": [2.5, 13.5, 0, 15], "texture": "#0"} + } + }, + { + "from": [2, 4, 13], + "to": [7, 5, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [2, 4, 13]}, + "faces": { + "north": {"uv": [0, 15.5, 2.5, 16], "texture": "#0"}, + "east": {"uv": [2.5, 15, 4, 15.5], "texture": "#0"}, + "south": {"uv": [0, 15, 2.5, 15.5], "texture": "#0"}, + "west": {"uv": [2.5, 15.5, 4, 16], "texture": "#0"}, + "up": {"uv": [2.5, 13.5, 0, 12], "texture": "#0"}, + "down": {"uv": [2.5, 13.5, 0, 15], "texture": "#0"} + } + } + ], + "display": { + "ground": { + "translation": [0, 2.25, 0], + "scale": [0.5, 0.5, 0.5] + }, + "gui": { + "rotation": [30, -150, 0], + "translation": [3.75, -2.5, 0] + }, + "fixed": { + "translation": [0, 0, -16], + "scale": [2, 2, 2] + }, + "thirdperson_righthand": { + "rotation": [75, 45, 0], + "translation": [0, 2.5, 0], + "scale": [0.375, 0.375, 0.375] + }, + "thirdperson_lefthand": { + "rotation": [75, 45, 0], + "translation": [0, 2.5, 0], + "scale": [0.375, 0.375, 0.375] + }, + "firstperson_righthand": { + "rotation": [0, 45, 0], + "translation": [-1.5, 2.5, 1], + "scale": [0.4, 0.4, 0.4] + }, + "firstperson_lefthand": { + "rotation": [0, 45, 0], + "translation": [-1.5, 2.5, 1], + "scale": [0.4, 0.4, 0.4] + } + } +} \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/medium_mushroom.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/medium_mushroom.json new file mode 100644 index 000000000..e00ea901d --- /dev/null +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/medium_mushroom.json @@ -0,0 +1,107 @@ +{ + "textures": { + "0": "block/custom/mushroom", + "particle": "block/custom/mushroom" + }, + "elements": [ + { + "from": [8.5, 13, 11.5], + "to": [13.5, 13, 17.5], + "rotation": {"angle": -22.5, "axis": "x", "origin": [11, 13, 14.5]}, + "faces": { + "north": {"uv": [2.5, 0, 0, 0], "texture": "#0"}, + "east": {"uv": [3, 0, 0, 0], "texture": "#0"}, + "south": {"uv": [2.5, 0, 0, 0], "texture": "#0"}, + "west": {"uv": [3, 0, 0, 0], "texture": "#0"}, + "up": {"uv": [8, 0, 5.5, 3], "texture": "#0"}, + "down": {"uv": [8, 3, 5.5, 0], "texture": "#0"} + } + }, + { + "from": [8.5, 3, 12.5], + "to": [13.5, 3, 18.5], + "rotation": {"angle": -22.5, "axis": "x", "origin": [11, 3, 15.5]}, + "faces": { + "north": {"uv": [0, 0, 2.5, 0], "texture": "#0"}, + "east": {"uv": [0, 0, 3, 0], "texture": "#0"}, + "south": {"uv": [0, 0, 2.5, 0], "texture": "#0"}, + "west": {"uv": [0, 0, 3, 0], "texture": "#0"}, + "up": {"uv": [8, 0, 10.5, 3], "texture": "#0"}, + "down": {"uv": [8, 3, 10.5, 0], "texture": "#0"} + } + }, + { + "from": [6, 7, 12], + "to": [13, 9, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [6, 7, 12]}, + "faces": { + "north": {"uv": [0, 5, 3.5, 6], "texture": "#0"}, + "east": {"uv": [3.5, 0, 5.5, 1], "texture": "#0"}, + "south": {"uv": [0, 4, 3.5, 5], "texture": "#0"}, + "west": {"uv": [3.5, 1, 5.5, 2], "texture": "#0"}, + "up": {"uv": [3.5, 2, 0, 0], "texture": "#0"}, + "down": {"uv": [3.5, 2, 0, 4], "texture": "#0"} + } + }, + { + "from": [3, 11, 14], + "to": [8, 12, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [3, 11, 14]}, + "faces": { + "north": {"uv": [0, 9, 2.5, 9.5], "texture": "#0"}, + "east": {"uv": [2.5, 11.5, 3.5, 12], "texture": "#0"}, + "south": {"uv": [0, 9.5, 2.5, 10], "texture": "#0"}, + "west": {"uv": [2.5, 11, 3.5, 11.5], "texture": "#0"}, + "up": {"uv": [2.5, 11, 0, 10], "texture": "#0"}, + "down": {"uv": [2.5, 11, 0, 12], "texture": "#0"} + } + }, + { + "from": [3, 4, 13], + "to": [8, 5, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [3, 4, 13]}, + "faces": { + "north": {"uv": [0, 15.5, 2.5, 16], "texture": "#0"}, + "east": {"uv": [2.5, 15, 4, 15.5], "texture": "#0"}, + "south": {"uv": [0, 15, 2.5, 15.5], "texture": "#0"}, + "west": {"uv": [2.5, 15.5, 4, 16], "texture": "#0"}, + "up": {"uv": [2.5, 13.5, 0, 12], "texture": "#0"}, + "down": {"uv": [2.5, 13.5, 0, 15], "texture": "#0"} + } + } + ], + "display": { + "ground": { + "translation": [0, 2.25, 0], + "scale": [0.5, 0.5, 0.5] + }, + "gui": { + "rotation": [30, -150, 0], + "translation": [3.75, -2.5, 0] + }, + "fixed": { + "translation": [0, 0, -16], + "scale": [2, 2, 2] + }, + "thirdperson_righthand": { + "rotation": [75, 45, 0], + "translation": [0, 2.5, 0], + "scale": [0.375, 0.375, 0.375] + }, + "thirdperson_lefthand": { + "rotation": [75, 45, 0], + "translation": [0, 2.5, 0], + "scale": [0.375, 0.375, 0.375] + }, + "firstperson_righthand": { + "rotation": [0, 45, 0], + "translation": [-1.5, 2.5, 1], + "scale": [0.4, 0.4, 0.4] + }, + "firstperson_lefthand": { + "rotation": [0, 45, 0], + "translation": [-1.5, 2.5, 1], + "scale": [0.4, 0.4, 0.4] + } + } +} \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/small_mushroom.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/small_mushroom.json new file mode 100644 index 000000000..bbc245d23 --- /dev/null +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/small_mushroom.json @@ -0,0 +1,95 @@ +{ + "texture_size": [32, 32], + "textures": { + "0": "block/custom/mushroom", + "particle": "block/custom/mushroom" + }, + "elements": [ + { + "from": [9.5, 3, 11.5], + "to": [14.5, 3, 17.5], + "rotation": {"angle": 0, "axis": "y", "origin": [12, 3, 14.5]}, + "faces": { + "north": {"uv": [2.5, 0, 0, 0], "texture": "#0"}, + "east": {"uv": [3, 0, 0, 0], "texture": "#0"}, + "south": {"uv": [2.5, 0, 0, 0], "texture": "#0"}, + "west": {"uv": [3, 0, 0, 0], "texture": "#0"}, + "up": {"uv": [8, 0, 5.5, 3], "texture": "#0"}, + "down": {"uv": [8, 3, 5.5, 0], "texture": "#0"} + } + }, + { + "from": [2.5, 11, 10.5], + "to": [7.5, 11, 16.5], + "rotation": {"angle": -22.5, "axis": "x", "origin": [5, 11, 16.5]}, + "faces": { + "north": {"uv": [0, 0, 2.5, 0], "texture": "#0"}, + "east": {"uv": [0, 0, 3, 0], "texture": "#0"}, + "south": {"uv": [0, 0, 2.5, 0], "texture": "#0"}, + "west": {"uv": [0, 0, 3, 0], "texture": "#0"}, + "up": {"uv": [5.5, 0, 8, 3], "texture": "#0"}, + "down": {"uv": [5.5, 3, 8, 0], "texture": "#0"} + } + }, + { + "from": [3, 4, 14], + "to": [8, 5, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [3, 4, 14]}, + "faces": { + "north": {"uv": [0, 9, 2.5, 9.5], "texture": "#0"}, + "east": {"uv": [2.5, 11.5, 3.5, 12], "texture": "#0"}, + "south": {"uv": [0, 9.5, 2.5, 10], "texture": "#0"}, + "west": {"uv": [2.5, 11, 3.5, 11.5], "texture": "#0"}, + "up": {"uv": [2.5, 11, 0, 10], "texture": "#0"}, + "down": {"uv": [2.5, 11, 0, 12], "texture": "#0"} + } + }, + { + "from": [6, 7, 13], + "to": [13, 9, 17], + "rotation": {"angle": 0, "axis": "y", "origin": [6, 7, 13]}, + "faces": { + "north": {"uv": [0, 5, 3.5, 6], "texture": "#0"}, + "east": {"uv": [3.5, 0, 5.5, 1], "texture": "#0"}, + "south": {"uv": [0, 4, 3.5, 5], "texture": "#0"}, + "west": {"uv": [3.5, 1, 5.5, 2], "texture": "#0"}, + "up": {"uv": [3.5, 2, 0, 0], "texture": "#0"}, + "down": {"uv": [3.5, 2, 0, 4], "texture": "#0"} + } + } + ], + "display": { + "ground": { + "translation": [0, 2.25, 0], + "scale": [0.5, 0.5, 0.5] + }, + "gui": { + "rotation": [30, -150, 0], + "translation": [3.75, -2.5, 0] + }, + "fixed": { + "translation": [0, 0, -16.25], + "scale": [2, 2, 2] + }, + "thirdperson_righthand": { + "rotation": [75, 45, 0], + "translation": [0, 2.5, 0], + "scale": [0.375, 0.375, 0.375] + }, + "thirdperson_lefthand": { + "rotation": [75, 45, 0], + "translation": [0, 2.5, 0], + "scale": [0.375, 0.375, 0.375] + }, + "firstperson_righthand": { + "rotation": [0, 45, 0], + "translation": [-1.5, 2.5, 1], + "scale": [0.4, 0.4, 0.4] + }, + "firstperson_lefthand": { + "rotation": [0, 45, 0], + "translation": [-1.5, 2.5, 1], + "scale": [0.4, 0.4, 0.4] + } + } +} \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/cap.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/cap.json index 40cfcc334..2ab8bff7a 100644 --- a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/cap.json +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/cap.json @@ -1,6 +1,4 @@ { - "format_version": "1.21.6", - "credit": "Made with Blockbench", "texture_size": [32, 32], "textures": { "0": "item/custom/cap", diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_ceiling.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_ceiling.json index 31e1ef903..0066e3414 100644 --- a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_ceiling.json +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_ceiling.json @@ -1,5 +1,4 @@ { - "credit": "Made with Blockbench", "textures": { "1": "item/custom/flower_basket", "particle": "item/custom/flower_basket" diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_ground.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_ground.json index 38d9eba22..7c8c9c386 100644 --- a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_ground.json +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_ground.json @@ -1,5 +1,4 @@ { - "credit": "Made with Blockbench", "textures": { "0": "item/custom/flower_basket" }, diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_wall.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_wall.json index 1fbe404cf..b7801a8cb 100644 --- a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_wall.json +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_wall.json @@ -1,6 +1,4 @@ { - "format_version": "1.21.6", - "credit": "Made with Blockbench", "textures": { "0": "item/custom/flower_basket" }, diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/sleeper_sofa.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/sleeper_sofa.json new file mode 100644 index 000000000..dd572f72c --- /dev/null +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/sleeper_sofa.json @@ -0,0 +1,104 @@ +{ + "textures": { + "0": "item/custom/sofa", + "particle": "item/custom/sofa" + }, + "elements": [ + { + "from": [-0.025, 2.975, -0.025], + "to": [16.025, 9.025, 16.025], + "faces": { + "north": {"uv": [4, 11.5, 8, 13], "texture": "#0"}, + "east": {"uv": [8, 11.5, 4, 13], "texture": "#0"}, + "south": {"uv": [4, 11.5, 8, 13], "texture": "#0"}, + "west": {"uv": [4, 11.5, 8, 13], "texture": "#0"}, + "up": {"uv": [4, 4, 0, 0], "texture": "#0"}, + "down": {"uv": [4, 12, 0, 16], "texture": "#0"} + } + }, + { + "from": [-0.005, -0.005, 12.995], + "to": [3.005, 3.005, 16.005], + "faces": { + "north": {"uv": [15.5, 15.25, 15, 16], "texture": "#0"}, + "east": {"uv": [15.5, 15.25, 15, 16], "texture": "#0"}, + "south": {"uv": [15, 15.25, 14.5, 16], "texture": "#0"}, + "west": {"uv": [14.5, 15.25, 15, 16], "texture": "#0"}, + "up": {"uv": [16, 15, 15.5, 15.5], "texture": "#0"}, + "down": {"uv": [16, 16, 15.5, 15.5], "texture": "#0"} + } + }, + { + "from": [12.995, -0.005, 12.995], + "to": [16.005, 3.005, 16.005], + "faces": { + "north": {"uv": [15, 15.25, 15.5, 16], "texture": "#0"}, + "east": {"uv": [15, 15.25, 14.5, 16], "texture": "#0"}, + "south": {"uv": [14.5, 15.25, 15, 16], "texture": "#0"}, + "west": {"uv": [15, 15.25, 15.5, 16], "texture": "#0"}, + "up": {"uv": [15.5, 15, 16, 15.5], "texture": "#0"}, + "down": {"uv": [15.5, 16, 16, 15.5], "texture": "#0"} + } + }, + { + "from": [12.995, -0.005, -0.005], + "to": [16.005, 3.005, 3.005], + "faces": { + "north": {"uv": [15, 15.25, 14.5, 16], "texture": "#0"}, + "east": {"uv": [14.5, 15.25, 15, 16], "texture": "#0"}, + "south": {"uv": [15.5, 15.25, 15, 16], "texture": "#0"}, + "west": {"uv": [15.5, 15.25, 15, 16], "texture": "#0"}, + "up": {"uv": [15.5, 15.5, 16, 15], "texture": "#0"}, + "down": {"uv": [15.5, 15.5, 16, 16], "texture": "#0"} + } + }, + { + "from": [-0.005, -0.005, -0.005], + "to": [3.005, 3.005, 3.005], + "faces": { + "north": {"uv": [14.5, 15.25, 15, 16], "texture": "#0"}, + "east": {"uv": [15, 15.25, 15.5, 16], "texture": "#0"}, + "south": {"uv": [15, 15.25, 15.5, 16], "texture": "#0"}, + "west": {"uv": [15, 15.25, 14.5, 16], "texture": "#0"}, + "up": {"uv": [16, 15.5, 15.5, 15], "texture": "#0"}, + "down": {"uv": [16, 15.5, 15.5, 16], "texture": "#0"} + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [60, -34, 0], + "translation": [-0.5, 3, 0.25], + "scale": [0.25, 0.25, 0.25] + }, + "thirdperson_lefthand": { + "rotation": [60, -34, 0], + "translation": [-0.5, 3, 0.25], + "scale": [0.25, 0.25, 0.25] + }, + "firstperson_righthand": { + "rotation": [0, -180, 0], + "translation": [0.5, 0.5, 0], + "scale": [0.5, 0.5, 0.5] + }, + "firstperson_lefthand": { + "rotation": [0, -180, 0], + "translation": [0.5, 0.5, 0], + "scale": [0.5, 0.5, 0.5] + }, + "ground": { + "translation": [0, 2.75, 0], + "scale": [0.35, 0.35, 0.35] + }, + "gui": { + "rotation": [25, -135, 0], + "translation": [0.25, -1, 0], + "scale": [0.5, 0.5, 0.5] + }, + "fixed": { + "rotation": [-90, 0, 0], + "translation": [0, 0, -16], + "scale": [2, 2, 2] + } + } +} \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/sofa.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/sofa.json new file mode 100644 index 000000000..52f13c82e --- /dev/null +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/sofa.json @@ -0,0 +1,131 @@ +{ + "texture_size": [64, 64], + "textures": { + "0": "item/custom/sofa", + "particle": "item/custom/sofa" + }, + "elements": [ + { + "from": [1, 0, 1], + "to": [3, 3, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [1, 0, 1]}, + "faces": { + "north": {"uv": [14.5, 15.25, 15, 16], "texture": "#0"}, + "east": {"uv": [15, 15.25, 15.5, 16], "texture": "#0"}, + "south": {"uv": [15, 15.25, 15.5, 16], "texture": "#0"}, + "west": {"uv": [15, 15.25, 14.5, 16], "texture": "#0"}, + "up": {"uv": [16, 15.5, 15.5, 15], "texture": "#0"}, + "down": {"uv": [16, 15.5, 15.5, 16], "texture": "#0"} + } + }, + { + "from": [13, 0, 1], + "to": [15, 3, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [15, 0, 1]}, + "faces": { + "north": {"uv": [15, 15.25, 14.5, 16], "texture": "#0"}, + "east": {"uv": [14.5, 15.25, 15, 16], "texture": "#0"}, + "south": {"uv": [15.5, 15.25, 15, 16], "texture": "#0"}, + "west": {"uv": [15.5, 15.25, 15, 16], "texture": "#0"}, + "up": {"uv": [15.5, 15.5, 16, 15], "texture": "#0"}, + "down": {"uv": [15.5, 15.5, 16, 16], "texture": "#0"} + } + }, + { + "from": [13, 0, 13], + "to": [15, 3, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [15, 0, 15]}, + "faces": { + "north": {"uv": [15, 15.25, 15.5, 16], "texture": "#0"}, + "east": {"uv": [15, 15.25, 14.5, 16], "texture": "#0"}, + "south": {"uv": [14.5, 15.25, 15, 16], "texture": "#0"}, + "west": {"uv": [15, 15.25, 15.5, 16], "texture": "#0"}, + "up": {"uv": [15.5, 15, 16, 15.5], "texture": "#0"}, + "down": {"uv": [15.5, 16, 16, 15.5], "texture": "#0"} + } + }, + { + "from": [1, 0, 13], + "to": [3, 3, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [1, 0, 15]}, + "faces": { + "north": {"uv": [15.5, 15.25, 15, 16], "texture": "#0"}, + "east": {"uv": [15.5, 15.25, 15, 16], "texture": "#0"}, + "south": {"uv": [15, 15.25, 14.5, 16], "texture": "#0"}, + "west": {"uv": [14.5, 15.25, 15, 16], "texture": "#0"}, + "up": {"uv": [16, 15, 15.5, 15.5], "texture": "#0"}, + "down": {"uv": [16, 16, 15.5, 15.5], "texture": "#0"} + } + }, + { + "from": [0, 3, 0], + "to": [16, 9, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 2, 0]}, + "faces": { + "north": {"uv": [4, 11.5, 8, 13], "texture": "#0"}, + "east": {"uv": [8, 14.5, 4, 16], "texture": "#0"}, + "south": {"uv": [4, 13, 8, 14.5], "texture": "#0"}, + "west": {"uv": [4, 14.5, 8, 16], "texture": "#0"}, + "up": {"uv": [4, 12, 0, 8], "texture": "#0"}, + "down": {"uv": [4, 12, 0, 16], "texture": "#0"} + } + }, + { + "from": [0.02, 6.02, 12.02], + "to": [15.98, 19.98, 15.98], + "rotation": {"angle": 22.5, "axis": "x", "origin": [7, 8, 14]}, + "faces": { + "north": {"uv": [9, 8.5, 13, 12], "texture": "#0"}, + "east": {"uv": [8, 12.5, 9, 16], "texture": "#0"}, + "south": {"uv": [9, 12.5, 13, 16], "texture": "#0"}, + "west": {"uv": [9, 12.5, 8, 16], "texture": "#0"}, + "up": {"uv": [9, 12, 8, 8.5], "rotation": 90, "texture": "#0"}, + "down": {"uv": [7.75, 10.5, 4.25, 11.5], "texture": "#0"} + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [60, -34, 0], + "translation": [-0.5, 3, 0.25], + "scale": [0.25, 0.25, 0.25] + }, + "thirdperson_lefthand": { + "rotation": [60, -34, 0], + "translation": [-0.5, 3, 0.25], + "scale": [0.25, 0.25, 0.25] + }, + "firstperson_righthand": { + "rotation": [0, -180, 0], + "translation": [0.5, 0.5, 0], + "scale": [0.5, 0.5, 0.5] + }, + "firstperson_lefthand": { + "rotation": [0, -180, 0], + "translation": [0.5, 0.5, 0], + "scale": [0.5, 0.5, 0.5] + }, + "ground": { + "translation": [0, 2.75, 0], + "scale": [0.35, 0.35, 0.35] + }, + "gui": { + "rotation": [25, -135, 0], + "translation": [0.5, -1, 0], + "scale": [0.5, 0.5, 0.5] + }, + "fixed": { + "rotation": [-90, 0, 0], + "translation": [0, 0, -16], + "scale": [2, 2, 2] + } + }, + "groups": [ + { + "name": "group", + "origin": [0, 0, 0], + "color": 0, + "children": [0, 1, 2, 3, 4, 5] + } + ] +} \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/sofa_inner.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/sofa_inner.json new file mode 100644 index 000000000..af89f6c41 --- /dev/null +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/sofa_inner.json @@ -0,0 +1,143 @@ +{ + "textures": { + "0": "item/custom/sofa", + "particle": "item/custom/sofa" + }, + "elements": [ + { + "from": [1, 0, 1], + "to": [3, 3, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [1, 0, 1]}, + "faces": { + "north": {"uv": [14.5, 15.25, 15, 16], "texture": "#0"}, + "east": {"uv": [15, 15.25, 15.5, 16], "texture": "#0"}, + "south": {"uv": [15, 15.25, 15.5, 16], "texture": "#0"}, + "west": {"uv": [15, 15.25, 14.5, 16], "texture": "#0"}, + "up": {"uv": [16, 15.5, 15.5, 15], "texture": "#0"}, + "down": {"uv": [16, 15.5, 15.5, 16], "texture": "#0"} + } + }, + { + "from": [13, 0, 1], + "to": [15, 3, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [15, 0, 1]}, + "faces": { + "north": {"uv": [15, 15.25, 14.5, 16], "texture": "#0"}, + "east": {"uv": [14.5, 15.25, 15, 16], "texture": "#0"}, + "south": {"uv": [15.5, 15.25, 15, 16], "texture": "#0"}, + "west": {"uv": [15.5, 15.25, 15, 16], "texture": "#0"}, + "up": {"uv": [15.5, 15.5, 16, 15], "texture": "#0"}, + "down": {"uv": [15.5, 15.5, 16, 16], "texture": "#0"} + } + }, + { + "from": [13, 0, 13], + "to": [15, 3, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [15, 0, 15]}, + "faces": { + "north": {"uv": [15, 15.25, 15.5, 16], "texture": "#0"}, + "east": {"uv": [15, 15.25, 14.5, 16], "texture": "#0"}, + "south": {"uv": [14.5, 15.25, 15, 16], "texture": "#0"}, + "west": {"uv": [15, 15.25, 15.5, 16], "texture": "#0"}, + "up": {"uv": [15.5, 15, 16, 15.5], "texture": "#0"}, + "down": {"uv": [15.5, 16, 16, 15.5], "texture": "#0"} + } + }, + { + "from": [1, 0, 13], + "to": [3, 3, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [1, 0, 15]}, + "faces": { + "north": {"uv": [15.5, 15.25, 15, 16], "texture": "#0"}, + "east": {"uv": [15.5, 15.25, 15, 16], "texture": "#0"}, + "south": {"uv": [15, 15.25, 14.5, 16], "texture": "#0"}, + "west": {"uv": [14.5, 15.25, 15, 16], "texture": "#0"}, + "up": {"uv": [16, 15, 15.5, 15.5], "texture": "#0"}, + "down": {"uv": [16, 16, 15.5, 15.5], "texture": "#0"} + } + }, + { + "from": [0, 3, 0], + "to": [16, 9, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 2, 0]}, + "faces": { + "north": {"uv": [4, 11.5, 8, 13], "texture": "#0"}, + "east": {"uv": [8, 14.5, 4, 16], "texture": "#0"}, + "south": {"uv": [4, 13, 8, 14.5], "texture": "#0"}, + "west": {"uv": [4, 14.5, 8, 16], "texture": "#0"}, + "up": {"uv": [4, 8, 0, 4], "texture": "#0"}, + "down": {"uv": [4, 12, 0, 16], "texture": "#0"} + } + }, + { + "from": [0.02, 6.02, 12.02], + "to": [15.98, 19.98, 15.98], + "rotation": {"angle": 22.5, "axis": "x", "origin": [7, 8, 14]}, + "faces": { + "north": {"uv": [13, 8.5, 9, 12], "texture": "#0"}, + "east": {"uv": [8, 12.5, 9, 16], "texture": "#0"}, + "south": {"uv": [9, 12.5, 13, 16], "texture": "#0"}, + "west": {"uv": [9, 12.5, 8, 16], "texture": "#0"}, + "up": {"uv": [9, 12, 8, 8.5], "rotation": 90, "texture": "#0"}, + "down": {"uv": [7.75, 10.5, 4.25, 11.5], "texture": "#0"} + } + }, + { + "from": [0.02, 6.02, 0.02], + "to": [3.98, 19.98, 15.98], + "rotation": {"angle": 22.5, "axis": "z", "origin": [2, 8, 8]}, + "faces": { + "north": {"uv": [9, 8.5, 8, 12], "texture": "#0"}, + "east": {"uv": [9, 8.5, 13, 12], "texture": "#0"}, + "south": {"uv": [8, 12.5, 9, 16], "texture": "#0"}, + "west": {"uv": [13, 12.5, 9, 16], "texture": "#0"}, + "up": {"uv": [8, 12, 9, 8.5], "texture": "#0"}, + "down": {"uv": [4.25, 11.5, 7.75, 10.5], "rotation": 90, "texture": "#0"} + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [60, -180, 0], + "translation": [-0.5, 3, 0.25], + "scale": [0.25, 0.25, 0.25] + }, + "thirdperson_lefthand": { + "rotation": [60, 0, 0], + "translation": [-0.5, 3, 0.25], + "scale": [0.25, 0.25, 0.25] + }, + "firstperson_righthand": { + "rotation": [0, -180, 0], + "translation": [0.5, 0.5, 0], + "scale": [0.5, 0.5, 0.5] + }, + "firstperson_lefthand": { + "rotation": [0, 90, 0], + "translation": [0.5, 0.5, 0], + "scale": [0.5, 0.5, 0.5] + }, + "ground": { + "translation": [0, 2.75, 0], + "scale": [0.35, 0.35, 0.35] + }, + "gui": { + "rotation": [25, -135, 0], + "translation": [0.25, -1, 0], + "scale": [0.5, 0.5, 0.5] + }, + "fixed": { + "rotation": [-90, 0, 0], + "translation": [0, 0, -16], + "scale": [2, 2, 2] + } + }, + "groups": [ + { + "name": "group", + "origin": [0, 0, 0], + "color": 0, + "children": [0, 1, 2, 3, 4, 5, 6] + } + ] +} \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/topaz_trident_in_hand.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/topaz_trident_in_hand.json index a56ed9681..984b06ef5 100644 --- a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/topaz_trident_in_hand.json +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/topaz_trident_in_hand.json @@ -1,5 +1,4 @@ { - "credit": "Made with Blockbench", "textures": { "0": "item/custom/topaz_trident_3d", "particle": "item/custom/topaz_trident_3d" diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/topaz_trident_throwing.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/topaz_trident_throwing.json index 90b814746..6e8a181f3 100644 --- a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/topaz_trident_throwing.json +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/topaz_trident_throwing.json @@ -1,5 +1,4 @@ { - "credit": "Made with Blockbench", "textures": { "0": "item/custom/topaz_trident_3d", "particle": "item/custom/topaz_trident_3d" diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/amethyst_torch.png b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/amethyst_torch.png new file mode 100644 index 000000000..a190ec9d5 Binary files /dev/null and b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/amethyst_torch.png differ diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/hami_melon.png b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/hami_melon.png new file mode 100644 index 000000000..df74b134b Binary files /dev/null and b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/hami_melon.png differ diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/hami_melon_bottom.png b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/hami_melon_bottom.png new file mode 100644 index 000000000..a21c39cec Binary files /dev/null and b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/hami_melon_bottom.png differ diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/hami_melon_top.png b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/hami_melon_top.png new file mode 100644 index 000000000..5fffae735 Binary files /dev/null and b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/hami_melon_top.png differ diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/mushroom.png b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/mushroom.png new file mode 100644 index 000000000..2a5460ef3 Binary files /dev/null and b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/mushroom.png differ diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_bottom.png b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_bottom.png new file mode 100644 index 000000000..c7b03c170 Binary files /dev/null and b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_bottom.png differ diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_front.png b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_front.png new file mode 100644 index 000000000..e523f39e5 Binary files /dev/null and b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_front.png differ diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_front_open.png b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_front_open.png new file mode 100644 index 000000000..50cd8f450 Binary files /dev/null and b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_front_open.png differ diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_side.png b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_side.png new file mode 100644 index 000000000..c345df934 Binary files /dev/null and b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_side.png differ diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_top.png b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_top.png new file mode 100644 index 000000000..86e1624ed Binary files /dev/null and b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_top.png differ diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/font/image/icons.png b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/font/image/icons.png deleted file mode 100644 index c65accf81..000000000 Binary files a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/font/image/icons.png and /dev/null differ diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/hami_melon_seeds.png b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/hami_melon_seeds.png new file mode 100644 index 000000000..74c7165ec Binary files /dev/null and b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/hami_melon_seeds.png differ diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/hami_melon_slice.png b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/hami_melon_slice.png new file mode 100644 index 000000000..5c46ede57 Binary files /dev/null and b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/hami_melon_slice.png differ diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/sofa.png b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/sofa.png new file mode 100644 index 000000000..25e76651c Binary files /dev/null and b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/sofa.png differ diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/table_lamp_on.png b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/table_lamp_on.png new file mode 100644 index 000000000..9b880deb1 Binary files /dev/null and b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/table_lamp_on.png differ diff --git a/common-files/src/main/resources/resources/default/resourcepack/pack.mcmeta b/common-files/src/main/resources/resources/default/resourcepack/pack.mcmeta index 0ae3705e1..4d5e8390e 100644 --- a/common-files/src/main/resources/resources/default/resourcepack/pack.mcmeta +++ b/common-files/src/main/resources/resources/default/resourcepack/pack.mcmeta @@ -1,10 +1,12 @@ { - "pack":{ + "pack": { "pack_format": 15, - "description":"CraftEngine", + "description": "CraftEngine", "supported_formats": { "min_inclusive": 15, "max_inclusive": 1000 - } + }, + "min_format": 15, + "max_format": 1000 } } \ No newline at end of file diff --git a/common-files/src/main/resources/resources/internal/configuration/fix_client_visual.yml b/common-files/src/main/resources/resources/internal/configuration/fix_client_visual.yml new file mode 100644 index 000000000..17def2bac --- /dev/null +++ b/common-files/src/main/resources/resources/internal/configuration/fix_client_visual.yml @@ -0,0 +1,48 @@ +# When placing tripwires, the client predicts their block states. +# By specifying the state type, we can prevent certain visual issues during placement. +items: + minecraft:string: + client-bound-data: + $$>=1.20.5: + components: + minecraft:block_state: + attached: 'false' + disarmed: 'false' + east: 'true' + north: 'true' + powered: 'true' + south: 'true' + west: 'true' + $$fallback: + nbt: + BlockStateTag: + attached: 'false' + disarmed: 'false' + east: 'true' + north: 'true' + powered: 'true' + south: 'true' + west: 'true' + minecraft:note_block: + client-bound-data: + $$>=1.20.5: + components: + minecraft:block_state: + instrument: harp + powered: 'false' + note: '0' + $$fallback: + nbt: + BlockStateTag: + instrument: harp + powered: 'false' + note: '0' +# For the client to determine if a beacon can activate, it needs the beacon_base_blocks tag to render the beam. +# This allows custom blocks (like note blocks) to work as beacon bases. +# However, whether the beacon actually grants potion effects depends on the block's real tag (server-side check). +blocks: + minecraft:note_block: + settings: + client-bound-tags: + - minecraft:beacon_base_blocks + - minecraft:mineable/axe \ No newline at end of file diff --git a/common-files/src/main/resources/resources/internal/configuration/gui.yml b/common-files/src/main/resources/resources/internal/configuration/gui.yml index 344d37d55..87efc97c5 100644 --- a/common-files/src/main/resources/resources/internal/configuration/gui.yml +++ b/common-files/src/main/resources/resources/internal/configuration/gui.yml @@ -4,73 +4,61 @@ images: ascent: 18 font: minecraft:gui file: minecraft:font/gui/custom/item_browser.png - char: \ub000 internal:category: height: 140 ascent: 18 font: minecraft:gui file: minecraft:font/gui/custom/category.png - char: \ub001 internal:crafting_recipe: height: 142 ascent: 20 font: minecraft:gui file: minecraft:font/gui/custom/crafting_recipe.png - char: \ub002 internal:cooking_recipe: height: 138 ascent: 16 font: minecraft:gui file: minecraft:font/gui/custom/cooking_recipe.png - char: \ub003 internal:smelting: height: 23 ascent: 20 font: minecraft:gui file: minecraft:font/gui/custom/smelting.png - char: \ub004 internal:smoking: height: 23 ascent: 20 font: minecraft:gui file: minecraft:font/gui/custom/smoking.png - char: \ub005 internal:blasting: height: 23 ascent: 20 font: minecraft:gui file: minecraft:font/gui/custom/blasting.png - char: \ub006 internal:campfire: height: 23 ascent: 20 font: minecraft:gui file: minecraft:font/gui/custom/campfire.png - char: \ub007 internal:stonecutting_recipe: height: 142 ascent: 20 font: minecraft:gui file: minecraft:font/gui/custom/stonecutting_recipe.png - char: \ub008 internal:smithing_transform_recipe: height: 142 ascent: 20 font: minecraft:gui file: minecraft:font/gui/custom/smithing_transform_recipe.png - char: \ub009 internal:brewing_recipe: height: 142 ascent: 20 font: minecraft:gui file: minecraft:font/gui/custom/brewing_recipe.png - char: \ub00a internal:no_recipe: height: 140 ascent: 18 font: minecraft:gui file: minecraft:font/gui/custom/no_recipe.png - char: \ub00b templates: internal:icon/2d: material: arrow diff --git a/common-files/src/main/resources/resources/internal/configuration/i18n.yml b/common-files/src/main/resources/resources/internal/configuration/i18n.yml index f338e11d7..febd3d010 100644 --- a/common-files/src/main/resources/resources/internal/configuration/i18n.yml +++ b/common-files/src/main/resources/resources/internal/configuration/i18n.yml @@ -24,4 +24,17 @@ i18n: internal.get_item.1: 右键单击取一组 internal.cooking_info: 配方信息 internal.cooking_info.0: '时间: 刻' - internal.cooking_info.1: '经验: ' \ No newline at end of file + internal.cooking_info.1: '经验: ' + de: + internal.next_page: Nächste Seite + internal.previous_page: Vorherige Seite + internal.return: Zurück zur vorherigen Seite + internal.exit: Verlassen + internal.next_recipe: Nächstes Rezept + internal.previous_recipe: Vorheriges Rezept + internal.get_item: Gegenstand nehmen + internal.get_item.0: Linksklick, um eines zu nehmen + internal.get_item.1: Rechtsklick, um einen Stapel zu nehmen + internal.cooking_info: Rezeptinformationen + internal.cooking_info.0: 'Zeit: Ticks' + internal.cooking_info.1: 'Erfahrung: ' \ No newline at end of file diff --git a/common-files/src/main/resources/resources/internal/configuration/mappings.yml b/common-files/src/main/resources/resources/internal/configuration/mappings.yml new file mode 100644 index 000000000..c113c0d3b --- /dev/null +++ b/common-files/src/main/resources/resources/internal/configuration/mappings.yml @@ -0,0 +1,4451 @@ +# This is one of the plugin's core settings - it basically controls how many block states you can use in your config files. +# Below is the default setup - feel free to add or remove stuff as needed. +block-state-mappings: + #### Anvil #### + # An anvil has four possible orientations, but the east-west and north-south orientations look exactly the same. + minecraft:anvil[facing=north]: minecraft:anvil[facing=south] + minecraft:anvil[facing=east]: minecraft:anvil[facing=west] + minecraft:chipped_anvil[facing=north]: minecraft:chipped_anvil[facing=south] + minecraft:chipped_anvil[facing=east]: minecraft:chipped_anvil[facing=west] + minecraft:damaged_anvil[facing=north]: minecraft:damaged_anvil[facing=south] + minecraft:damaged_anvil[facing=east]: minecraft:damaged_anvil[facing=west] + + #### Sapling #### + # Every sapling has two stages, 0 and 1, but they look exactly the same. + minecraft:oak_sapling[stage=1]: minecraft:oak_sapling[stage=0] + minecraft:birch_sapling[stage=1]: minecraft:birch_sapling[stage=0] + minecraft:spruce_sapling[stage=1]: minecraft:spruce_sapling[stage=0] + minecraft:jungle_sapling[stage=1]: minecraft:jungle_sapling[stage=0] + minecraft:dark_oak_sapling[stage=1]: minecraft:dark_oak_sapling[stage=0] + minecraft:acacia_sapling[stage=1]: minecraft:acacia_sapling[stage=0] + minecraft:cherry_sapling[stage=1]: minecraft:cherry_sapling[stage=0] + $$>=1.21.4#sapling: + minecraft:pale_oak_sapling[stage=1]: minecraft:pale_oak_sapling[stage=0] + + #### Sculk Sensor #### + # The Sculk Sensor's hitbox is exactly half a block. Plus, its appearance only changes based on the sculk_sensor_phase, + # not the power level. That means we can repurpose the extra states to make bottom-half slabs + minecraft:sculk_sensor[power=1,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:sculk_sensor[power=2,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:sculk_sensor[power=3,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:sculk_sensor[power=4,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:sculk_sensor[power=5,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:sculk_sensor[power=6,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:sculk_sensor[power=7,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:sculk_sensor[power=8,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:sculk_sensor[power=9,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:sculk_sensor[power=10,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:sculk_sensor[power=11,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:sculk_sensor[power=12,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:sculk_sensor[power=13,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:sculk_sensor[power=14,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:sculk_sensor[power=15,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:sculk_sensor[power=1,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:sculk_sensor[power=2,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:sculk_sensor[power=3,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:sculk_sensor[power=4,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:sculk_sensor[power=5,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:sculk_sensor[power=6,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:sculk_sensor[power=7,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:sculk_sensor[power=8,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:sculk_sensor[power=9,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:sculk_sensor[power=10,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:sculk_sensor[power=11,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:sculk_sensor[power=12,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:sculk_sensor[power=13,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:sculk_sensor[power=14,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:sculk_sensor[power=15,sculk_sensor_phase=active,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:sculk_sensor[power=1,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:sculk_sensor[power=2,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:sculk_sensor[power=3,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:sculk_sensor[power=4,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:sculk_sensor[power=5,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:sculk_sensor[power=6,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:sculk_sensor[power=7,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:sculk_sensor[power=8,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:sculk_sensor[power=9,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:sculk_sensor[power=10,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:sculk_sensor[power=11,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:sculk_sensor[power=12,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:sculk_sensor[power=13,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:sculk_sensor[power=14,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:sculk_sensor[power=15,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:sculk_sensor[power=1,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:sculk_sensor[power=2,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:sculk_sensor[power=3,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:sculk_sensor[power=4,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:sculk_sensor[power=5,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:sculk_sensor[power=6,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:sculk_sensor[power=7,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:sculk_sensor[power=8,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:sculk_sensor[power=9,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:sculk_sensor[power=10,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:sculk_sensor[power=11,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:sculk_sensor[power=12,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:sculk_sensor[power=13,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:sculk_sensor[power=14,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:sculk_sensor[power=15,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:sculk_sensor[power=1,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:sculk_sensor[power=2,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:sculk_sensor[power=3,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:sculk_sensor[power=4,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:sculk_sensor[power=5,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:sculk_sensor[power=6,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:sculk_sensor[power=7,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:sculk_sensor[power=8,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:sculk_sensor[power=9,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:sculk_sensor[power=10,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:sculk_sensor[power=11,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:sculk_sensor[power=12,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:sculk_sensor[power=13,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:sculk_sensor[power=14,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:sculk_sensor[power=15,sculk_sensor_phase=active,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:sculk_sensor[power=1,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:sculk_sensor[power=2,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:sculk_sensor[power=3,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:sculk_sensor[power=4,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:sculk_sensor[power=5,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:sculk_sensor[power=6,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:sculk_sensor[power=7,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:sculk_sensor[power=8,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:sculk_sensor[power=9,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:sculk_sensor[power=10,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:sculk_sensor[power=11,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:sculk_sensor[power=12,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:sculk_sensor[power=13,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:sculk_sensor[power=14,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:sculk_sensor[power=15,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:sculk_sensor[power=0,sculk_sensor_phase=cooldown,waterlogged=true] + + #### Calibrated Sculk Sensor #### + # Just like the regular Sculk Sensor, but the Calibrated Sculk Sensor has directional facing - which gives us way more usable states to play with + minecraft:calibrated_sculk_sensor[facing=north,power=1,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=2,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=3,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=4,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=5,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=6,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=7,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=8,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=9,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=10,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=11,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=12,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=13,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=14,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=15,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=1,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=2,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=3,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=4,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=5,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=6,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=7,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=8,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=9,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=10,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=11,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=12,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=13,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=14,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=15,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=1,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=2,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=3,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=4,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=5,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=6,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=7,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=8,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=9,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=10,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=11,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=12,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=13,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=14,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=15,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=north,power=1,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=2,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=3,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=4,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=5,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=6,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=7,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=8,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=9,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=10,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=11,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=12,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=13,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=14,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=15,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=1,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=2,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=3,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=4,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=5,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=6,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=7,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=8,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=9,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=10,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=11,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=12,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=13,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=14,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=15,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=1,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=2,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=3,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=4,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=5,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=6,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=7,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=8,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=9,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=10,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=11,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=12,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=13,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=14,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=north,power=15,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=north,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=1,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=2,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=3,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=4,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=5,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=6,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=7,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=8,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=9,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=10,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=11,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=12,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=13,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=14,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=15,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=1,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=2,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=3,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=4,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=5,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=6,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=7,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=8,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=9,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=10,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=11,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=12,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=13,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=14,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=15,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=1,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=2,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=3,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=4,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=5,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=6,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=7,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=8,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=9,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=10,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=11,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=12,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=13,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=14,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=15,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=west,power=1,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=2,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=3,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=4,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=5,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=6,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=7,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=8,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=9,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=10,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=11,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=12,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=13,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=14,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=15,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=1,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=2,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=3,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=4,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=5,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=6,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=7,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=8,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=9,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=10,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=11,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=12,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=13,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=14,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=15,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=1,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=2,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=3,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=4,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=5,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=6,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=7,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=8,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=9,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=10,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=11,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=12,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=13,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=14,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=west,power=15,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=west,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=1,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=2,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=3,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=4,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=5,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=6,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=7,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=8,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=9,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=10,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=11,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=12,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=13,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=14,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=15,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=1,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=2,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=3,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=4,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=5,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=6,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=7,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=8,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=9,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=10,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=11,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=12,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=13,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=14,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=15,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=1,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=2,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=3,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=4,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=5,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=6,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=7,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=8,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=9,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=10,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=11,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=12,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=13,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=14,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=15,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=east,power=1,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=2,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=3,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=4,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=5,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=6,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=7,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=8,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=9,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=10,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=11,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=12,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=13,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=14,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=15,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=1,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=2,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=3,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=4,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=5,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=6,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=7,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=8,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=9,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=10,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=11,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=12,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=13,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=14,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=15,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=1,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=2,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=3,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=4,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=5,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=6,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=7,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=8,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=9,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=10,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=11,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=12,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=13,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=14,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=east,power=15,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=east,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=1,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=2,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=3,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=4,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=5,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=6,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=7,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=8,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=9,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=10,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=11,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=12,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=13,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=14,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=15,sculk_sensor_phase=inactive,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=1,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=2,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=3,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=4,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=5,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=6,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=7,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=8,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=9,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=10,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=11,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=12,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=13,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=14,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=15,sculk_sensor_phase=active,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=1,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=2,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=3,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=4,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=5,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=6,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=7,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=8,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=9,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=10,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=11,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=12,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=13,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=14,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=15,sculk_sensor_phase=cooldown,waterlogged=false]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=false] + minecraft:calibrated_sculk_sensor[facing=south,power=1,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=2,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=3,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=4,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=5,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=6,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=7,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=8,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=9,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=10,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=11,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=12,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=13,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=14,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=15,sculk_sensor_phase=inactive,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=inactive,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=1,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=2,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=3,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=4,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=5,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=6,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=7,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=8,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=9,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=10,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=11,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=12,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=13,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=14,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=15,sculk_sensor_phase=active,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=active,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=1,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=2,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=3,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=4,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=5,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=6,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=7,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=8,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=9,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=10,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=11,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=12,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=13,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=14,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + minecraft:calibrated_sculk_sensor[facing=south,power=15,sculk_sensor_phase=cooldown,waterlogged=true]: minecraft:calibrated_sculk_sensor[facing=south,power=0,sculk_sensor_phase=cooldown,waterlogged=true] + + #### Mushroom #### + # Most people probably don't mind that mushroom blocks look the same on all six sides. So that means each type can free up like, 63 different states we could use for other stuff + minecraft:mushroom_stem[down=false,east=false,north=false,south=false,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=false,north=false,south=false,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=true,north=false,south=false,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=true,north=false,south=false,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=false,north=true,south=false,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=false,north=true,south=false,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=true,north=true,south=false,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=true,north=true,south=false,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=false,north=false,south=true,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=false,north=false,south=true,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=true,north=false,south=true,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=true,north=false,south=true,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=false,north=true,south=true,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=false,north=true,south=true,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=true,north=true,south=true,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=false,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=false,north=false,south=false,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=false,north=false,south=false,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=true,north=false,south=false,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=true,north=false,south=false,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=false,north=true,south=false,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=false,north=true,south=false,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=true,north=true,south=false,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=true,north=true,south=false,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=false,north=false,south=true,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=false,north=false,south=true,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=true,north=false,south=true,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=true,north=false,south=true,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=false,north=true,south=true,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=false,north=true,south=true,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=true,north=true,south=true,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=false]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=false,north=false,south=false,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=false,north=false,south=false,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=true,north=false,south=false,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=true,north=false,south=false,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=false,north=true,south=false,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=false,north=true,south=false,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=true,north=true,south=false,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=true,north=true,south=false,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=false,north=false,south=true,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=false,north=false,south=true,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=true,north=false,south=true,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=true,north=false,south=true,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=false,north=true,south=true,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=false,north=true,south=true,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=true,north=true,south=true,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=false,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=false,north=false,south=false,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=false,north=false,south=false,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=true,north=false,south=false,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=true,north=false,south=false,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=false,north=true,south=false,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=false,north=true,south=false,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=true,north=true,south=false,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=true,north=true,south=false,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=false,north=false,south=true,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=false,north=false,south=true,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=true,north=false,south=true,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=true,north=false,south=true,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=false,north=true,south=true,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=true,east=false,north=true,south=true,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:mushroom_stem[down=false,east=true,north=true,south=true,up=true,west=true]: minecraft:mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=false,north=false,south=false,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=false,north=false,south=false,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=true,north=false,south=false,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=true,north=false,south=false,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=false,north=true,south=false,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=false,north=true,south=false,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=true,north=true,south=false,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=true,north=true,south=false,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=false,north=false,south=true,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=false,north=false,south=true,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=true,north=false,south=true,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=true,north=false,south=true,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=false,north=true,south=true,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=false,north=true,south=true,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=true,north=true,south=true,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=false,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=false,north=false,south=false,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=false,north=false,south=false,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=true,north=false,south=false,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=true,north=false,south=false,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=false,north=true,south=false,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=false,north=true,south=false,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=true,north=true,south=false,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=true,north=true,south=false,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=false,north=false,south=true,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=false,north=false,south=true,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=true,north=false,south=true,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=true,north=false,south=true,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=false,north=true,south=true,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=false,north=true,south=true,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=true,north=true,south=true,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=false]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=false,north=false,south=false,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=false,north=false,south=false,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=true,north=false,south=false,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=true,north=false,south=false,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=false,north=true,south=false,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=false,north=true,south=false,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=true,north=true,south=false,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=true,north=true,south=false,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=false,north=false,south=true,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=false,north=false,south=true,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=true,north=false,south=true,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=true,north=false,south=true,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=false,north=true,south=true,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=false,north=true,south=true,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=true,north=true,south=true,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=false,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=false,north=false,south=false,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=false,north=false,south=false,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=true,north=false,south=false,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=true,north=false,south=false,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=false,north=true,south=false,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=false,north=true,south=false,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=true,north=true,south=false,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=true,north=true,south=false,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=false,north=false,south=true,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=false,north=false,south=true,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=true,north=false,south=true,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=true,north=false,south=true,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=false,north=true,south=true,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=true,east=false,north=true,south=true,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:brown_mushroom_block[down=false,east=true,north=true,south=true,up=true,west=true]: minecraft:brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=false,north=false,south=false,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=false,north=false,south=false,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=true,north=false,south=false,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=true,north=false,south=false,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=false,north=true,south=false,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=false,north=true,south=false,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=true,north=true,south=false,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=true,north=true,south=false,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=false,north=false,south=true,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=false,north=false,south=true,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=true,north=false,south=true,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=true,north=false,south=true,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=false,north=true,south=true,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=false,north=true,south=true,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=true,north=true,south=true,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=false,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=false,north=false,south=false,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=false,north=false,south=false,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=true,north=false,south=false,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=true,north=false,south=false,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=false,north=true,south=false,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=false,north=true,south=false,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=true,north=true,south=false,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=true,north=true,south=false,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=false,north=false,south=true,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=false,north=false,south=true,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=true,north=false,south=true,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=true,north=false,south=true,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=false,north=true,south=true,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=false,north=true,south=true,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=true,north=true,south=true,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=false]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=false,north=false,south=false,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=false,north=false,south=false,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=true,north=false,south=false,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=true,north=false,south=false,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=false,north=true,south=false,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=false,north=true,south=false,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=true,north=true,south=false,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=true,north=true,south=false,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=false,north=false,south=true,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=false,north=false,south=true,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=true,north=false,south=true,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=true,north=false,south=true,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=false,north=true,south=true,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=false,north=true,south=true,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=true,north=true,south=true,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=false,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=false,north=false,south=false,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=false,north=false,south=false,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=true,north=false,south=false,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=true,north=false,south=false,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=false,north=true,south=false,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=false,north=true,south=false,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=true,north=true,south=false,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=true,north=true,south=false,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=false,north=false,south=true,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=false,north=false,south=true,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=true,north=false,south=true,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=true,north=false,south=true,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=false,north=true,south=true,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=true,east=false,north=true,south=true,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + minecraft:red_mushroom_block[down=false,east=true,north=true,south=true,up=true,west=true]: minecraft:red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true] + + #### Kelp #### + # 'kelp' here means specifically the top block of the kelp plant. Great for making aquatic crops. + minecraft:kelp[age=1]: minecraft:kelp[age=0] + minecraft:kelp[age=2]: minecraft:kelp[age=0] + minecraft:kelp[age=3]: minecraft:kelp[age=0] + minecraft:kelp[age=4]: minecraft:kelp[age=0] + minecraft:kelp[age=5]: minecraft:kelp[age=0] + minecraft:kelp[age=6]: minecraft:kelp[age=0] + minecraft:kelp[age=7]: minecraft:kelp[age=0] + minecraft:kelp[age=8]: minecraft:kelp[age=0] + minecraft:kelp[age=9]: minecraft:kelp[age=0] + minecraft:kelp[age=10]: minecraft:kelp[age=0] + minecraft:kelp[age=11]: minecraft:kelp[age=0] + minecraft:kelp[age=12]: minecraft:kelp[age=0] + minecraft:kelp[age=13]: minecraft:kelp[age=0] + minecraft:kelp[age=14]: minecraft:kelp[age=0] + minecraft:kelp[age=15]: minecraft:kelp[age=0] + minecraft:kelp[age=16]: minecraft:kelp[age=0] + minecraft:kelp[age=17]: minecraft:kelp[age=0] + minecraft:kelp[age=18]: minecraft:kelp[age=0] + minecraft:kelp[age=19]: minecraft:kelp[age=0] + minecraft:kelp[age=20]: minecraft:kelp[age=0] + minecraft:kelp[age=21]: minecraft:kelp[age=0] + minecraft:kelp[age=22]: minecraft:kelp[age=0] + minecraft:kelp[age=23]: minecraft:kelp[age=0] + minecraft:kelp[age=24]: minecraft:kelp[age=0] + minecraft:kelp[age=25]: minecraft:kelp[age=0] + + #### Vines #### + # Unless you tweak the vine's block tag, the client will always think it's climbable. + # Since vines look identical at different growth stages, we can repurpose those extra states to make custom blocks like ropes. + minecraft:weeping_vines[age=1]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=2]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=3]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=4]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=5]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=6]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=7]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=8]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=9]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=10]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=11]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=12]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=13]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=14]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=15]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=16]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=17]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=18]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=19]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=20]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=21]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=22]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=23]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=24]: minecraft:weeping_vines[age=0] + minecraft:weeping_vines[age=25]: minecraft:weeping_vines[age=0] + minecraft:twisting_vines[age=1]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=2]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=3]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=4]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=5]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=6]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=7]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=8]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=9]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=10]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=11]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=12]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=13]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=14]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=15]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=16]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=17]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=18]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=19]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=20]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=21]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=22]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=23]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=24]: minecraft:twisting_vines[age=0] + minecraft:twisting_vines[age=25]: minecraft:twisting_vines[age=0] + minecraft:cave_vines[age=1,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=2,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=3,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=4,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=5,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=6,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=7,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=8,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=9,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=10,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=11,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=12,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=13,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=14,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=15,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=16,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=17,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=18,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=19,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=20,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=21,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=22,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=23,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=24,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=25,berries=false]: minecraft:cave_vines[age=0,berries=false] + minecraft:cave_vines[age=1,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=2,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=3,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=4,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=5,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=6,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=7,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=8,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=9,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=10,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=11,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=12,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=13,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=14,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=15,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=16,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=17,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=18,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=19,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=20,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=21,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=22,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=23,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=24,berries=true]: minecraft:cave_vines[age=0,berries=true] + minecraft:cave_vines[age=25,berries=true]: minecraft:cave_vines[age=0,berries=true] + + #### SugarCane #### + # Sugar cane looks exactly the same no matter its growth stage. Plus, it's got this perfect hitbox that makes it awesome for taller plants + minecraft:sugar_cane[age=1]: minecraft:sugar_cane[age=0] + minecraft:sugar_cane[age=2]: minecraft:sugar_cane[age=0] + minecraft:sugar_cane[age=3]: minecraft:sugar_cane[age=0] + minecraft:sugar_cane[age=4]: minecraft:sugar_cane[age=0] + minecraft:sugar_cane[age=5]: minecraft:sugar_cane[age=0] + minecraft:sugar_cane[age=6]: minecraft:sugar_cane[age=0] + minecraft:sugar_cane[age=7]: minecraft:sugar_cane[age=0] + minecraft:sugar_cane[age=8]: minecraft:sugar_cane[age=0] + minecraft:sugar_cane[age=9]: minecraft:sugar_cane[age=0] + minecraft:sugar_cane[age=10]: minecraft:sugar_cane[age=0] + minecraft:sugar_cane[age=11]: minecraft:sugar_cane[age=0] + minecraft:sugar_cane[age=12]: minecraft:sugar_cane[age=0] + minecraft:sugar_cane[age=13]: minecraft:sugar_cane[age=0] + minecraft:sugar_cane[age=14]: minecraft:sugar_cane[age=0] + minecraft:sugar_cane[age=15]: minecraft:sugar_cane[age=0] + + #### Cactus #### + # Cactus looks the same at all growth stages.Its hitbox is 14x14x15, making it perfect for creating blocks that are just slightly smaller than full-size + minecraft:cactus[age=1]: minecraft:cactus[age=0] + minecraft:cactus[age=2]: minecraft:cactus[age=0] + minecraft:cactus[age=3]: minecraft:cactus[age=0] + minecraft:cactus[age=4]: minecraft:cactus[age=0] + minecraft:cactus[age=5]: minecraft:cactus[age=0] + minecraft:cactus[age=6]: minecraft:cactus[age=0] + minecraft:cactus[age=7]: minecraft:cactus[age=0] + minecraft:cactus[age=8]: minecraft:cactus[age=0] + minecraft:cactus[age=9]: minecraft:cactus[age=0] + minecraft:cactus[age=10]: minecraft:cactus[age=0] + minecraft:cactus[age=11]: minecraft:cactus[age=0] + minecraft:cactus[age=12]: minecraft:cactus[age=0] + minecraft:cactus[age=13]: minecraft:cactus[age=0] + minecraft:cactus[age=14]: minecraft:cactus[age=0] + minecraft:cactus[age=15]: minecraft:cactus[age=0] + + #### Leaves #### + # The 'distance' and 'persistent' properties are used under the hood to optimize how leaves decay, but visually? They look exactly the same. + # These are some of the few block types that actually support transparent textures. + minecraft:oak_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:oak_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:oak_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:oak_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:oak_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:oak_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:oak_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:oak_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:oak_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:oak_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:oak_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:oak_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:oak_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:oak_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:oak_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:oak_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:oak_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:oak_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:oak_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:oak_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:oak_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:oak_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:oak_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:oak_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:oak_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:oak_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:acacia_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:acacia_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:acacia_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:acacia_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:acacia_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:acacia_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:acacia_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:acacia_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:acacia_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:acacia_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:acacia_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:acacia_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:acacia_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:acacia_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:acacia_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:acacia_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:acacia_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:acacia_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:acacia_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:acacia_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:acacia_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:acacia_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:acacia_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:acacia_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:acacia_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:acacia_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:acacia_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:jungle_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:jungle_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:jungle_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:jungle_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:jungle_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:jungle_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:jungle_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:jungle_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:jungle_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:jungle_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:jungle_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:jungle_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:jungle_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:jungle_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:jungle_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:jungle_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:jungle_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:jungle_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:jungle_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:jungle_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:jungle_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:jungle_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:jungle_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:jungle_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:jungle_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:jungle_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:jungle_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:birch_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:birch_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:birch_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:birch_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:birch_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:birch_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:birch_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:birch_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:birch_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:birch_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:birch_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:birch_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:birch_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:birch_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:birch_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:birch_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:birch_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:birch_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:birch_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:birch_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:birch_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:birch_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:birch_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:birch_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:birch_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:birch_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:birch_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:mangrove_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:mangrove_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:mangrove_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:mangrove_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:mangrove_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:mangrove_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:mangrove_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:mangrove_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:mangrove_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:mangrove_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:mangrove_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:mangrove_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:mangrove_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:mangrove_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:mangrove_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:mangrove_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:mangrove_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:mangrove_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:mangrove_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:mangrove_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:mangrove_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:mangrove_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:mangrove_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:mangrove_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:mangrove_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:mangrove_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:mangrove_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:cherry_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:cherry_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:cherry_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:cherry_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:cherry_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:cherry_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:cherry_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:cherry_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:cherry_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:cherry_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:cherry_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:cherry_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:cherry_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:cherry_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:cherry_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:cherry_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:cherry_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:cherry_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:cherry_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:cherry_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:cherry_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:cherry_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:cherry_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:cherry_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:cherry_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:cherry_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:cherry_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:dark_oak_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:dark_oak_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:dark_oak_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:dark_oak_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:dark_oak_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:dark_oak_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:dark_oak_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:dark_oak_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:dark_oak_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:dark_oak_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:dark_oak_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:dark_oak_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:dark_oak_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:dark_oak_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:dark_oak_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:dark_oak_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:dark_oak_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:dark_oak_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:dark_oak_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:dark_oak_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:dark_oak_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:dark_oak_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:dark_oak_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:dark_oak_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:dark_oak_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:dark_oak_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:dark_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:azalea_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:azalea_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:azalea_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:azalea_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:azalea_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:azalea_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:azalea_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:azalea_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:azalea_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:azalea_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:azalea_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:azalea_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:azalea_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:azalea_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:azalea_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:azalea_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:azalea_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:azalea_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:azalea_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:azalea_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:azalea_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:azalea_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:azalea_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:azalea_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:azalea_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:azalea_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:flowering_azalea_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:flowering_azalea_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:flowering_azalea_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:flowering_azalea_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:flowering_azalea_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:flowering_azalea_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:flowering_azalea_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:flowering_azalea_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:flowering_azalea_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:flowering_azalea_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:flowering_azalea_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:flowering_azalea_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:flowering_azalea_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:flowering_azalea_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:flowering_azalea_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:flowering_azalea_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:flowering_azalea_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:flowering_azalea_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:flowering_azalea_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:flowering_azalea_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:flowering_azalea_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:flowering_azalea_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:flowering_azalea_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:flowering_azalea_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:flowering_azalea_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:flowering_azalea_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:flowering_azalea_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:spruce_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:spruce_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:spruce_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:spruce_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:spruce_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:spruce_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:spruce_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:spruce_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:spruce_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:spruce_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:spruce_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:spruce_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:spruce_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:spruce_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:spruce_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:spruce_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:spruce_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:spruce_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:spruce_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:spruce_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:spruce_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:spruce_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:spruce_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:spruce_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:spruce_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:spruce_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:spruce_leaves[distance=7,persistent=true,waterlogged=true] + + $$>=1.21.4#leaves: + minecraft:pale_oak_leaves[distance=1,persistent=false,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:pale_oak_leaves[distance=2,persistent=false,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:pale_oak_leaves[distance=3,persistent=false,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:pale_oak_leaves[distance=4,persistent=false,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:pale_oak_leaves[distance=5,persistent=false,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:pale_oak_leaves[distance=6,persistent=false,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:pale_oak_leaves[distance=7,persistent=false,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:pale_oak_leaves[distance=1,persistent=true,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:pale_oak_leaves[distance=2,persistent=true,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:pale_oak_leaves[distance=3,persistent=true,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:pale_oak_leaves[distance=4,persistent=true,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:pale_oak_leaves[distance=5,persistent=true,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:pale_oak_leaves[distance=6,persistent=true,waterlogged=false]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=false] + minecraft:pale_oak_leaves[distance=1,persistent=false,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:pale_oak_leaves[distance=2,persistent=false,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:pale_oak_leaves[distance=3,persistent=false,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:pale_oak_leaves[distance=4,persistent=false,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:pale_oak_leaves[distance=5,persistent=false,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:pale_oak_leaves[distance=6,persistent=false,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:pale_oak_leaves[distance=7,persistent=false,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:pale_oak_leaves[distance=1,persistent=true,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:pale_oak_leaves[distance=2,persistent=true,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:pale_oak_leaves[distance=3,persistent=true,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:pale_oak_leaves[distance=4,persistent=true,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:pale_oak_leaves[distance=5,persistent=true,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] + minecraft:pale_oak_leaves[distance=6,persistent=true,waterlogged=true]: minecraft:pale_oak_leaves[distance=7,persistent=true,waterlogged=true] + + #### Tripwire #### + # Tripwires actually have 128 different states, but we're keeping just two of them to match vanilla's visual styles. + # Honestly, as long as the tripwire works properly, most players won't even mind what it looks like. + # Tripwire hitboxes aren't all the same - the triggered ones are way shorter. + minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=false,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=false,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=false,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=false]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=false,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=false,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=false,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=false,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=true,north=false,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=true,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=true,east=false,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=false,disarmed=false,east=true,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=false,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + minecraft:tripwire[attached=true,disarmed=false,east=true,north=true,south=true,west=true,powered=true]: minecraft:tripwire[attached=true,disarmed=true,east=true,north=true,south=true,west=true,powered=true] + + #### Note Block #### + # This block has the most unused states in Minecraft, but the client always thinks it's interactive. + # Plus, there's some visual glitches when the client try predicting instrument changes. + # We've kept a full set of note settings by default - that way it plays nice with resource packs that show notes + minecraft:note_block[instrument=hat,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=hat,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=hat,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=hat,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=hat,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=hat,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=hat,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=hat,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=hat,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=hat,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=hat,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=hat,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=hat,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=hat,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=hat,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=hat,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=hat,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=hat,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=hat,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=hat,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=hat,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=hat,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=hat,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=hat,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=hat,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=hat,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=hat,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=hat,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=hat,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=hat,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=hat,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=hat,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=hat,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=hat,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=hat,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=hat,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=hat,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=hat,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=hat,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=hat,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=hat,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=hat,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=hat,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=hat,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=hat,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=hat,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=hat,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=hat,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=hat,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=hat,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=basedrum,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=basedrum,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=basedrum,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=basedrum,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=basedrum,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=basedrum,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=basedrum,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=basedrum,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=basedrum,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=basedrum,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=basedrum,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=basedrum,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=basedrum,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=basedrum,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=basedrum,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=basedrum,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=basedrum,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=basedrum,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=basedrum,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=basedrum,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=basedrum,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=basedrum,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=basedrum,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=basedrum,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=basedrum,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=basedrum,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=basedrum,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=basedrum,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=basedrum,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=basedrum,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=basedrum,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=basedrum,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=basedrum,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=basedrum,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=basedrum,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=basedrum,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=basedrum,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=basedrum,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=basedrum,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=basedrum,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=basedrum,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=basedrum,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=basedrum,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=basedrum,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=basedrum,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=basedrum,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=basedrum,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=basedrum,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=basedrum,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=basedrum,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=snare,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=snare,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=snare,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=snare,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=snare,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=snare,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=snare,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=snare,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=snare,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=snare,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=snare,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=snare,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=snare,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=snare,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=snare,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=snare,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=snare,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=snare,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=snare,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=snare,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=snare,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=snare,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=snare,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=snare,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=snare,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=snare,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=snare,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=snare,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=snare,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=snare,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=snare,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=snare,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=snare,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=snare,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=snare,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=snare,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=snare,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=snare,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=snare,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=snare,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=snare,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=snare,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=snare,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=snare,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=snare,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=snare,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=snare,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=snare,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=snare,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=snare,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=bass,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=bass,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=bass,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=bass,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=bass,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=bass,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=bass,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=bass,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=bass,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=bass,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=bass,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=bass,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=bass,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=bass,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=bass,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=bass,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=bass,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=bass,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=bass,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=bass,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=bass,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=bass,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=bass,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=bass,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=bass,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=bass,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=bass,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=bass,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=bass,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=bass,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=bass,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=bass,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=bass,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=bass,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=bass,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=bass,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=bass,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=bass,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=bass,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=bass,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=bass,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=bass,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=bass,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=bass,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=bass,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=bass,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=bass,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=bass,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=bass,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=bass,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=flute,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=flute,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=flute,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=flute,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=flute,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=flute,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=flute,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=flute,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=flute,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=flute,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=flute,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=flute,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=flute,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=flute,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=flute,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=flute,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=flute,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=flute,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=flute,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=flute,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=flute,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=flute,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=flute,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=flute,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=flute,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=flute,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=flute,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=flute,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=flute,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=flute,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=flute,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=flute,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=flute,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=flute,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=flute,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=flute,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=flute,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=flute,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=flute,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=flute,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=flute,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=flute,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=flute,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=flute,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=flute,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=flute,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=flute,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=flute,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=flute,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=flute,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=bell,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=bell,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=bell,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=bell,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=bell,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=bell,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=bell,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=bell,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=bell,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=bell,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=bell,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=bell,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=bell,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=bell,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=bell,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=bell,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=bell,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=bell,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=bell,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=bell,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=bell,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=bell,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=bell,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=bell,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=bell,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=bell,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=bell,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=bell,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=bell,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=bell,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=bell,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=bell,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=bell,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=bell,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=bell,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=bell,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=bell,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=bell,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=bell,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=bell,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=bell,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=bell,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=bell,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=bell,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=bell,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=bell,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=bell,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=bell,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=bell,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=bell,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=guitar,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=guitar,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=guitar,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=guitar,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=guitar,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=guitar,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=guitar,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=guitar,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=guitar,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=guitar,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=guitar,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=guitar,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=guitar,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=guitar,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=guitar,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=guitar,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=guitar,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=guitar,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=guitar,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=guitar,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=guitar,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=guitar,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=guitar,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=guitar,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=guitar,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=guitar,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=guitar,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=guitar,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=guitar,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=guitar,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=guitar,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=guitar,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=guitar,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=guitar,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=guitar,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=guitar,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=guitar,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=guitar,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=guitar,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=guitar,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=guitar,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=guitar,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=guitar,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=guitar,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=guitar,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=guitar,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=guitar,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=guitar,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=guitar,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=guitar,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=chime,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=chime,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=chime,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=chime,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=chime,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=chime,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=chime,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=chime,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=chime,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=chime,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=chime,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=chime,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=chime,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=chime,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=chime,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=chime,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=chime,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=chime,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=chime,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=chime,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=chime,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=chime,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=chime,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=chime,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=chime,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=chime,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=chime,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=chime,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=chime,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=chime,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=chime,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=chime,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=chime,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=chime,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=chime,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=chime,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=chime,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=chime,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=chime,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=chime,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=chime,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=chime,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=chime,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=chime,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=chime,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=chime,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=chime,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=chime,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=chime,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=chime,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=xylophone,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=xylophone,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=xylophone,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=xylophone,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=xylophone,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=xylophone,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=xylophone,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=xylophone,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=xylophone,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=xylophone,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=xylophone,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=xylophone,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=xylophone,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=xylophone,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=xylophone,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=xylophone,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=xylophone,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=xylophone,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=xylophone,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=xylophone,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=xylophone,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=xylophone,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=xylophone,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=xylophone,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=xylophone,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=xylophone,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=xylophone,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=xylophone,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=xylophone,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=xylophone,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=xylophone,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=xylophone,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=xylophone,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=xylophone,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=xylophone,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=xylophone,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=xylophone,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=xylophone,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=xylophone,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=xylophone,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=xylophone,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=xylophone,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=xylophone,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=xylophone,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=xylophone,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=xylophone,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=xylophone,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=xylophone,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=xylophone,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=xylophone,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=iron_xylophone,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=iron_xylophone,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=cow_bell,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=cow_bell,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=cow_bell,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=cow_bell,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=cow_bell,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=cow_bell,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=cow_bell,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=cow_bell,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=cow_bell,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=cow_bell,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=cow_bell,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=cow_bell,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=cow_bell,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=cow_bell,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=cow_bell,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=cow_bell,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=cow_bell,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=cow_bell,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=cow_bell,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=cow_bell,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=cow_bell,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=cow_bell,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=cow_bell,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=cow_bell,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=cow_bell,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=cow_bell,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=cow_bell,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=cow_bell,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=cow_bell,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=cow_bell,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=cow_bell,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=cow_bell,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=cow_bell,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=cow_bell,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=cow_bell,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=cow_bell,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=cow_bell,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=cow_bell,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=cow_bell,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=cow_bell,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=cow_bell,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=cow_bell,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=cow_bell,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=cow_bell,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=cow_bell,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=cow_bell,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=cow_bell,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=cow_bell,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=cow_bell,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=cow_bell,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=didgeridoo,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=didgeridoo,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=didgeridoo,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=didgeridoo,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=didgeridoo,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=didgeridoo,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=didgeridoo,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=didgeridoo,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=didgeridoo,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=didgeridoo,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=didgeridoo,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=didgeridoo,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=didgeridoo,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=didgeridoo,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=didgeridoo,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=didgeridoo,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=didgeridoo,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=didgeridoo,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=didgeridoo,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=didgeridoo,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=didgeridoo,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=didgeridoo,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=didgeridoo,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=didgeridoo,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=didgeridoo,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=didgeridoo,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=didgeridoo,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=didgeridoo,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=didgeridoo,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=didgeridoo,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=didgeridoo,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=didgeridoo,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=didgeridoo,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=didgeridoo,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=didgeridoo,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=didgeridoo,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=didgeridoo,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=didgeridoo,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=didgeridoo,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=didgeridoo,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=didgeridoo,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=didgeridoo,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=didgeridoo,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=didgeridoo,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=didgeridoo,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=didgeridoo,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=didgeridoo,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=didgeridoo,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=didgeridoo,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=didgeridoo,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=bit,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=bit,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=bit,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=bit,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=bit,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=bit,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=bit,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=bit,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=bit,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=bit,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=bit,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=bit,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=bit,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=bit,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=bit,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=bit,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=bit,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=bit,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=bit,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=bit,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=bit,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=bit,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=bit,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=bit,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=bit,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=bit,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=bit,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=bit,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=bit,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=bit,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=bit,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=bit,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=bit,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=bit,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=bit,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=bit,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=bit,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=bit,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=bit,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=bit,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=bit,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=bit,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=bit,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=bit,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=bit,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=bit,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=bit,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=bit,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=bit,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=bit,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=banjo,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=banjo,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=banjo,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=banjo,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=banjo,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=banjo,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=banjo,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=banjo,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=banjo,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=banjo,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=banjo,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=banjo,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=banjo,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=banjo,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=banjo,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=banjo,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=banjo,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=banjo,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=banjo,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=banjo,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=banjo,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=banjo,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=banjo,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=banjo,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=banjo,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=banjo,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=banjo,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=banjo,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=banjo,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=banjo,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=banjo,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=banjo,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=banjo,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=banjo,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=banjo,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=banjo,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=banjo,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=banjo,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=banjo,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=banjo,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=banjo,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=banjo,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=banjo,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=banjo,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=banjo,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=banjo,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=banjo,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=banjo,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=banjo,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=banjo,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=pling,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=pling,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=pling,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=pling,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=pling,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=pling,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=pling,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=pling,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=pling,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=pling,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=pling,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=pling,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=pling,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=pling,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=pling,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=pling,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=pling,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=pling,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=pling,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=pling,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=pling,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=pling,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=pling,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=pling,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=pling,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=pling,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=pling,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=pling,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=pling,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=pling,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=pling,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=pling,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=pling,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=pling,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=pling,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=pling,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=pling,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=pling,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=pling,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=pling,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=pling,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=pling,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=pling,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=pling,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=pling,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=pling,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=pling,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=pling,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=pling,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=pling,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=zombie,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=zombie,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=zombie,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=zombie,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=zombie,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=zombie,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=zombie,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=zombie,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=zombie,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=zombie,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=zombie,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=zombie,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=zombie,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=zombie,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=zombie,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=zombie,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=zombie,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=zombie,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=zombie,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=zombie,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=zombie,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=zombie,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=zombie,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=zombie,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=zombie,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=zombie,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=zombie,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=zombie,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=zombie,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=zombie,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=zombie,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=zombie,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=zombie,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=zombie,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=zombie,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=zombie,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=zombie,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=zombie,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=zombie,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=zombie,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=zombie,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=zombie,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=zombie,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=zombie,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=zombie,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=zombie,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=zombie,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=zombie,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=zombie,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=zombie,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=skeleton,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=skeleton,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=skeleton,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=skeleton,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=skeleton,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=skeleton,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=skeleton,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=skeleton,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=skeleton,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=skeleton,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=skeleton,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=skeleton,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=skeleton,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=skeleton,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=skeleton,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=skeleton,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=skeleton,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=skeleton,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=skeleton,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=skeleton,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=skeleton,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=skeleton,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=skeleton,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=skeleton,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=skeleton,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=skeleton,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=skeleton,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=skeleton,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=skeleton,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=skeleton,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=skeleton,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=skeleton,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=skeleton,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=skeleton,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=skeleton,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=skeleton,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=skeleton,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=skeleton,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=skeleton,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=skeleton,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=skeleton,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=skeleton,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=skeleton,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=skeleton,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=skeleton,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=skeleton,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=skeleton,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=skeleton,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=skeleton,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=skeleton,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=creeper,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=creeper,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=creeper,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=creeper,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=creeper,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=creeper,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=creeper,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=creeper,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=creeper,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=creeper,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=creeper,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=creeper,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=creeper,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=creeper,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=creeper,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=creeper,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=creeper,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=creeper,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=creeper,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=creeper,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=creeper,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=creeper,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=creeper,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=creeper,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=creeper,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=creeper,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=creeper,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=creeper,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=creeper,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=creeper,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=creeper,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=creeper,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=creeper,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=creeper,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=creeper,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=creeper,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=creeper,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=creeper,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=creeper,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=creeper,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=creeper,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=creeper,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=creeper,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=creeper,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=creeper,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=creeper,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=creeper,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=creeper,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=creeper,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=creeper,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=dragon,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=dragon,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=dragon,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=dragon,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=dragon,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=dragon,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=dragon,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=dragon,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=dragon,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=dragon,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=dragon,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=dragon,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=dragon,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=dragon,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=dragon,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=dragon,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=dragon,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=dragon,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=dragon,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=dragon,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=dragon,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=dragon,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=dragon,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=dragon,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=dragon,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=dragon,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=dragon,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=dragon,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=dragon,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=dragon,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=dragon,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=dragon,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=dragon,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=dragon,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=dragon,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=dragon,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=dragon,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=dragon,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=dragon,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=dragon,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=dragon,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=dragon,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=dragon,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=dragon,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=dragon,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=dragon,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=dragon,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=dragon,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=dragon,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=dragon,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=wither_skeleton,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=wither_skeleton,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=piglin,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=piglin,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=piglin,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=piglin,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=piglin,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=piglin,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=piglin,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=piglin,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=piglin,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=piglin,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=piglin,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=piglin,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=piglin,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=piglin,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=piglin,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=piglin,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=piglin,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=piglin,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=piglin,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=piglin,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=piglin,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=piglin,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=piglin,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=piglin,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=piglin,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=piglin,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=piglin,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=piglin,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=piglin,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=piglin,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=piglin,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=piglin,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=piglin,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=piglin,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=piglin,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=piglin,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=piglin,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=piglin,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=piglin,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=piglin,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=piglin,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=piglin,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=piglin,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=piglin,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=piglin,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=piglin,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=piglin,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=piglin,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=piglin,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=piglin,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + minecraft:note_block[instrument=custom_head,note=0,powered=false]: minecraft:note_block[instrument=harp,note=0,powered=false] + minecraft:note_block[instrument=custom_head,note=1,powered=false]: minecraft:note_block[instrument=harp,note=1,powered=false] + minecraft:note_block[instrument=custom_head,note=2,powered=false]: minecraft:note_block[instrument=harp,note=2,powered=false] + minecraft:note_block[instrument=custom_head,note=3,powered=false]: minecraft:note_block[instrument=harp,note=3,powered=false] + minecraft:note_block[instrument=custom_head,note=4,powered=false]: minecraft:note_block[instrument=harp,note=4,powered=false] + minecraft:note_block[instrument=custom_head,note=5,powered=false]: minecraft:note_block[instrument=harp,note=5,powered=false] + minecraft:note_block[instrument=custom_head,note=6,powered=false]: minecraft:note_block[instrument=harp,note=6,powered=false] + minecraft:note_block[instrument=custom_head,note=7,powered=false]: minecraft:note_block[instrument=harp,note=7,powered=false] + minecraft:note_block[instrument=custom_head,note=8,powered=false]: minecraft:note_block[instrument=harp,note=8,powered=false] + minecraft:note_block[instrument=custom_head,note=9,powered=false]: minecraft:note_block[instrument=harp,note=9,powered=false] + minecraft:note_block[instrument=custom_head,note=10,powered=false]: minecraft:note_block[instrument=harp,note=10,powered=false] + minecraft:note_block[instrument=custom_head,note=11,powered=false]: minecraft:note_block[instrument=harp,note=11,powered=false] + minecraft:note_block[instrument=custom_head,note=12,powered=false]: minecraft:note_block[instrument=harp,note=12,powered=false] + minecraft:note_block[instrument=custom_head,note=13,powered=false]: minecraft:note_block[instrument=harp,note=13,powered=false] + minecraft:note_block[instrument=custom_head,note=14,powered=false]: minecraft:note_block[instrument=harp,note=14,powered=false] + minecraft:note_block[instrument=custom_head,note=15,powered=false]: minecraft:note_block[instrument=harp,note=15,powered=false] + minecraft:note_block[instrument=custom_head,note=16,powered=false]: minecraft:note_block[instrument=harp,note=16,powered=false] + minecraft:note_block[instrument=custom_head,note=17,powered=false]: minecraft:note_block[instrument=harp,note=17,powered=false] + minecraft:note_block[instrument=custom_head,note=18,powered=false]: minecraft:note_block[instrument=harp,note=18,powered=false] + minecraft:note_block[instrument=custom_head,note=19,powered=false]: minecraft:note_block[instrument=harp,note=19,powered=false] + minecraft:note_block[instrument=custom_head,note=20,powered=false]: minecraft:note_block[instrument=harp,note=20,powered=false] + minecraft:note_block[instrument=custom_head,note=21,powered=false]: minecraft:note_block[instrument=harp,note=21,powered=false] + minecraft:note_block[instrument=custom_head,note=22,powered=false]: minecraft:note_block[instrument=harp,note=22,powered=false] + minecraft:note_block[instrument=custom_head,note=23,powered=false]: minecraft:note_block[instrument=harp,note=23,powered=false] + minecraft:note_block[instrument=custom_head,note=24,powered=false]: minecraft:note_block[instrument=harp,note=24,powered=false] + minecraft:note_block[instrument=custom_head,note=0,powered=true]: minecraft:note_block[instrument=harp,note=0,powered=true] + minecraft:note_block[instrument=custom_head,note=1,powered=true]: minecraft:note_block[instrument=harp,note=1,powered=true] + minecraft:note_block[instrument=custom_head,note=2,powered=true]: minecraft:note_block[instrument=harp,note=2,powered=true] + minecraft:note_block[instrument=custom_head,note=3,powered=true]: minecraft:note_block[instrument=harp,note=3,powered=true] + minecraft:note_block[instrument=custom_head,note=4,powered=true]: minecraft:note_block[instrument=harp,note=4,powered=true] + minecraft:note_block[instrument=custom_head,note=5,powered=true]: minecraft:note_block[instrument=harp,note=5,powered=true] + minecraft:note_block[instrument=custom_head,note=6,powered=true]: minecraft:note_block[instrument=harp,note=6,powered=true] + minecraft:note_block[instrument=custom_head,note=7,powered=true]: minecraft:note_block[instrument=harp,note=7,powered=true] + minecraft:note_block[instrument=custom_head,note=8,powered=true]: minecraft:note_block[instrument=harp,note=8,powered=true] + minecraft:note_block[instrument=custom_head,note=9,powered=true]: minecraft:note_block[instrument=harp,note=9,powered=true] + minecraft:note_block[instrument=custom_head,note=10,powered=true]: minecraft:note_block[instrument=harp,note=10,powered=true] + minecraft:note_block[instrument=custom_head,note=11,powered=true]: minecraft:note_block[instrument=harp,note=11,powered=true] + minecraft:note_block[instrument=custom_head,note=12,powered=true]: minecraft:note_block[instrument=harp,note=12,powered=true] + minecraft:note_block[instrument=custom_head,note=13,powered=true]: minecraft:note_block[instrument=harp,note=13,powered=true] + minecraft:note_block[instrument=custom_head,note=14,powered=true]: minecraft:note_block[instrument=harp,note=14,powered=true] + minecraft:note_block[instrument=custom_head,note=15,powered=true]: minecraft:note_block[instrument=harp,note=15,powered=true] + minecraft:note_block[instrument=custom_head,note=16,powered=true]: minecraft:note_block[instrument=harp,note=16,powered=true] + minecraft:note_block[instrument=custom_head,note=17,powered=true]: minecraft:note_block[instrument=harp,note=17,powered=true] + minecraft:note_block[instrument=custom_head,note=18,powered=true]: minecraft:note_block[instrument=harp,note=18,powered=true] + minecraft:note_block[instrument=custom_head,note=19,powered=true]: minecraft:note_block[instrument=harp,note=19,powered=true] + minecraft:note_block[instrument=custom_head,note=20,powered=true]: minecraft:note_block[instrument=harp,note=20,powered=true] + minecraft:note_block[instrument=custom_head,note=21,powered=true]: minecraft:note_block[instrument=harp,note=21,powered=true] + minecraft:note_block[instrument=custom_head,note=22,powered=true]: minecraft:note_block[instrument=harp,note=22,powered=true] + minecraft:note_block[instrument=custom_head,note=23,powered=true]: minecraft:note_block[instrument=harp,note=23,powered=true] + minecraft:note_block[instrument=custom_head,note=24,powered=true]: minecraft:note_block[instrument=harp,note=24,powered=true] + + #### Trapdoor #### + # Trapdoors look identical whether they're powered or not - which means we can double our trapdoors by using both states + minecraft:iron_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:iron_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:iron_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:iron_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:iron_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:iron_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:iron_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:iron_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:iron_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:iron_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:iron_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:iron_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:iron_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:iron_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:iron_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:iron_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:iron_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:iron_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:iron_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:iron_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:iron_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:iron_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:iron_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:iron_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:iron_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:iron_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:iron_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:iron_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:iron_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:iron_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:iron_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:iron_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:iron_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:iron_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:acacia_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:acacia_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:acacia_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:acacia_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:acacia_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:acacia_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:acacia_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:acacia_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:acacia_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:acacia_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:acacia_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:acacia_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:acacia_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:acacia_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:acacia_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:acacia_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:acacia_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:acacia_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:acacia_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:acacia_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:acacia_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:acacia_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:acacia_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:acacia_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:acacia_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:acacia_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:acacia_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:acacia_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:acacia_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:acacia_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:acacia_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:acacia_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:acacia_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:acacia_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:oak_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:oak_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:oak_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:oak_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:oak_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:oak_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:oak_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:oak_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:oak_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:oak_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:oak_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:oak_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:oak_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:oak_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:oak_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:oak_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:oak_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:oak_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:oak_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:oak_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:oak_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:oak_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:oak_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:oak_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:oak_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:oak_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:oak_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:oak_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:oak_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:oak_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:oak_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:oak_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:oak_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:oak_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:spruce_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:spruce_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:spruce_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:spruce_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:spruce_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:spruce_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:spruce_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:spruce_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:spruce_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:spruce_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:spruce_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:spruce_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:spruce_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:spruce_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:spruce_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:spruce_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:spruce_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:spruce_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:spruce_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:spruce_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:spruce_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:spruce_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:spruce_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:spruce_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:spruce_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:spruce_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:spruce_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:spruce_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:spruce_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:spruce_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:spruce_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:spruce_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:spruce_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:spruce_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:birch_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:birch_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:birch_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:birch_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:birch_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:birch_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:birch_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:birch_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:birch_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:birch_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:birch_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:birch_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:birch_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:birch_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:birch_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:birch_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:birch_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:birch_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:birch_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:birch_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:birch_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:birch_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:birch_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:birch_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:birch_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:birch_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:birch_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:birch_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:birch_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:birch_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:birch_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:birch_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:birch_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:birch_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:jungle_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:jungle_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:jungle_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:jungle_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:jungle_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:jungle_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:jungle_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:jungle_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:jungle_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:jungle_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:jungle_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:jungle_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:jungle_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:jungle_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:jungle_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:jungle_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:jungle_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:jungle_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:jungle_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:jungle_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:jungle_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:jungle_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:jungle_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:jungle_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:jungle_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:jungle_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:jungle_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:jungle_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:jungle_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:jungle_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:jungle_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:jungle_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:jungle_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:jungle_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:dark_oak_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:dark_oak_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:dark_oak_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:dark_oak_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:dark_oak_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:dark_oak_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:dark_oak_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:dark_oak_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:dark_oak_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:dark_oak_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:dark_oak_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:dark_oak_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:dark_oak_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:dark_oak_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:dark_oak_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:dark_oak_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:dark_oak_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:dark_oak_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:dark_oak_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:dark_oak_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:dark_oak_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:dark_oak_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:dark_oak_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:dark_oak_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:dark_oak_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:dark_oak_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:dark_oak_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:dark_oak_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:dark_oak_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:dark_oak_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:dark_oak_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:dark_oak_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:dark_oak_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:dark_oak_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:mangrove_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:mangrove_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:mangrove_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:mangrove_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:mangrove_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:mangrove_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:mangrove_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:mangrove_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:mangrove_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:mangrove_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:mangrove_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:mangrove_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:mangrove_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:mangrove_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:mangrove_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:mangrove_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:mangrove_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:mangrove_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:mangrove_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:mangrove_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:mangrove_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:mangrove_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:mangrove_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:mangrove_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:mangrove_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:mangrove_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:mangrove_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:mangrove_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:mangrove_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:mangrove_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:mangrove_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:mangrove_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:mangrove_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:mangrove_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:cherry_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:cherry_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:cherry_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:cherry_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:cherry_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:cherry_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:cherry_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:cherry_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:cherry_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:cherry_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:cherry_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:cherry_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:cherry_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:cherry_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:cherry_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:cherry_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:cherry_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:cherry_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:cherry_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:cherry_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:cherry_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:cherry_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:cherry_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:cherry_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:cherry_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:cherry_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:cherry_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:cherry_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:cherry_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:cherry_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:cherry_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:cherry_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:cherry_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:cherry_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:bamboo_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:bamboo_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:bamboo_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:bamboo_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:bamboo_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:bamboo_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:bamboo_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:bamboo_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:bamboo_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:bamboo_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:bamboo_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:bamboo_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:bamboo_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:bamboo_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:bamboo_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:bamboo_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:bamboo_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:bamboo_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:bamboo_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:bamboo_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:bamboo_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:bamboo_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:bamboo_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:bamboo_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:bamboo_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:bamboo_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:bamboo_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:bamboo_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:bamboo_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:bamboo_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:bamboo_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:bamboo_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:bamboo_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:bamboo_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:crimson_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:crimson_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:crimson_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:crimson_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:crimson_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:crimson_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:crimson_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:crimson_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:crimson_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:crimson_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:crimson_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:crimson_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:crimson_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:crimson_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:crimson_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:crimson_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:crimson_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:crimson_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:crimson_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:crimson_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:crimson_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:crimson_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:crimson_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:crimson_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:crimson_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:crimson_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:crimson_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:crimson_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:crimson_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:crimson_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:crimson_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:crimson_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:crimson_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:crimson_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:warped_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:warped_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:warped_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:warped_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:warped_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:warped_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:warped_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:warped_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:warped_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:warped_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:warped_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:warped_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:warped_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:warped_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:warped_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:warped_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:warped_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:warped_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:warped_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:warped_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:warped_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:warped_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:warped_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:warped_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:warped_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:warped_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:warped_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:warped_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:warped_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:warped_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:warped_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:warped_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:warped_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:warped_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + + $$>=1.20.3#trapdoor: + minecraft:copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:exposed_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:exposed_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:exposed_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:exposed_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:exposed_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:exposed_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:exposed_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:exposed_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:exposed_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:exposed_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:exposed_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:exposed_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:exposed_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:exposed_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:exposed_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:exposed_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:exposed_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:exposed_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:exposed_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:exposed_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:exposed_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:exposed_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:exposed_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:exposed_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:exposed_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:exposed_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:exposed_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:exposed_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:exposed_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:exposed_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:exposed_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:exposed_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:exposed_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:exposed_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:weathered_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:weathered_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:weathered_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:weathered_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:weathered_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:weathered_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:weathered_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:weathered_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:weathered_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:weathered_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:weathered_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:weathered_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:weathered_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:weathered_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:weathered_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:weathered_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:weathered_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:weathered_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:weathered_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:weathered_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:weathered_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:weathered_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:weathered_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:weathered_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:weathered_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:weathered_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:weathered_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:weathered_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:weathered_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:weathered_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:weathered_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:weathered_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:weathered_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:weathered_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:oxidized_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:oxidized_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:oxidized_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:oxidized_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:oxidized_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:oxidized_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:oxidized_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:oxidized_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:oxidized_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:oxidized_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:oxidized_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:oxidized_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:oxidized_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:oxidized_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:oxidized_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:oxidized_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:oxidized_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:oxidized_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:oxidized_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:oxidized_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:oxidized_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:oxidized_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:oxidized_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:oxidized_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:oxidized_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:oxidized_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:oxidized_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:oxidized_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:oxidized_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:oxidized_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:oxidized_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:oxidized_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:oxidized_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:oxidized_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + # Fun fact: copper blocks look the same whether waxed or not. + # We're playing it safe with the default setup - keeping vanilla's waxed states recognizable. + # But you can always change it to convert waxed blocks back to regular ones. + minecraft:waxed_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:waxed_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:waxed_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:waxed_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:waxed_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:waxed_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:waxed_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:waxed_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:waxed_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:waxed_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:waxed_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:waxed_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:waxed_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:waxed_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:waxed_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:waxed_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:waxed_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:waxed_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:waxed_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:waxed_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:waxed_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:waxed_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:waxed_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:waxed_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:waxed_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:waxed_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:waxed_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:waxed_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:waxed_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:waxed_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:waxed_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:waxed_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:waxed_exposed_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:waxed_exposed_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:waxed_exposed_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:waxed_exposed_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:waxed_exposed_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:waxed_exposed_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:waxed_exposed_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:waxed_exposed_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:waxed_exposed_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:waxed_exposed_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:waxed_exposed_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:waxed_exposed_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:waxed_exposed_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:waxed_exposed_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:waxed_exposed_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:waxed_exposed_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:waxed_exposed_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:waxed_exposed_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:waxed_exposed_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:waxed_exposed_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:waxed_exposed_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:waxed_exposed_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:waxed_exposed_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:waxed_exposed_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_exposed_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:waxed_exposed_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:waxed_exposed_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:waxed_exposed_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:waxed_exposed_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:waxed_exposed_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:waxed_exposed_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:waxed_exposed_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:waxed_exposed_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_exposed_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:waxed_weathered_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:waxed_weathered_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:waxed_weathered_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:waxed_weathered_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:waxed_weathered_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:waxed_weathered_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:waxed_weathered_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:waxed_weathered_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:waxed_weathered_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:waxed_weathered_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:waxed_weathered_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:waxed_weathered_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:waxed_weathered_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:waxed_weathered_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:waxed_weathered_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:waxed_weathered_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:waxed_weathered_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:waxed_weathered_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:waxed_weathered_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:waxed_weathered_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:waxed_weathered_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:waxed_weathered_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:waxed_weathered_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:waxed_weathered_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_weathered_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:waxed_weathered_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:waxed_weathered_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:waxed_weathered_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:waxed_weathered_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:waxed_weathered_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:waxed_weathered_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:waxed_weathered_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:waxed_weathered_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_weathered_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:waxed_oxidized_copper_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + + $$>=1.21.4#trapdoor: + minecraft:pale_oak_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=false] + minecraft:pale_oak_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=false] + minecraft:pale_oak_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=false] + minecraft:pale_oak_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=false] + minecraft:pale_oak_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=false] + minecraft:pale_oak_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=false] + minecraft:pale_oak_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=false] + minecraft:pale_oak_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=false] + minecraft:pale_oak_trapdoor[facing=north,half=top,open=true,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=north,half=top,open=true,powered=false,waterlogged=true] + minecraft:pale_oak_trapdoor[facing=south,half=top,open=true,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=south,half=top,open=true,powered=false,waterlogged=true] + minecraft:pale_oak_trapdoor[facing=east,half=top,open=true,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=east,half=top,open=true,powered=false,waterlogged=true] + minecraft:pale_oak_trapdoor[facing=west,half=top,open=true,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=west,half=top,open=true,powered=false,waterlogged=true] + minecraft:pale_oak_trapdoor[facing=north,half=top,open=false,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=north,half=top,open=false,powered=false,waterlogged=true] + minecraft:pale_oak_trapdoor[facing=south,half=top,open=false,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=south,half=top,open=false,powered=false,waterlogged=true] + minecraft:pale_oak_trapdoor[facing=east,half=top,open=false,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=east,half=top,open=false,powered=false,waterlogged=true] + minecraft:pale_oak_trapdoor[facing=west,half=top,open=false,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=west,half=top,open=false,powered=false,waterlogged=true] + minecraft:pale_oak_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:pale_oak_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:pale_oak_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:pale_oak_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=false] + minecraft:pale_oak_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:pale_oak_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:pale_oak_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:pale_oak_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=false]: minecraft:pale_oak_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=false] + minecraft:pale_oak_trapdoor[facing=north,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=north,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:pale_oak_trapdoor[facing=south,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=south,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:pale_oak_trapdoor[facing=east,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=east,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:pale_oak_trapdoor[facing=west,half=bottom,open=true,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=west,half=bottom,open=true,powered=false,waterlogged=true] + minecraft:pale_oak_trapdoor[facing=north,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=north,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:pale_oak_trapdoor[facing=south,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=south,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:pale_oak_trapdoor[facing=east,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=east,half=bottom,open=false,powered=false,waterlogged=true] + minecraft:pale_oak_trapdoor[facing=west,half=bottom,open=false,powered=true,waterlogged=true]: minecraft:pale_oak_trapdoor[facing=west,half=bottom,open=false,powered=false,waterlogged=true] + + #### Door #### + # A door look exactly the same whether it's powered on or off, just like how a trapdoor works + minecraft:oak_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:oak_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:oak_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:oak_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:oak_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:oak_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:oak_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:oak_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:oak_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:oak_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:oak_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:oak_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:oak_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:oak_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:oak_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:oak_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:oak_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:oak_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:oak_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:oak_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:oak_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:oak_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:oak_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:oak_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:oak_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:oak_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:oak_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:oak_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:oak_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:oak_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:oak_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:oak_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:oak_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:oak_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:oak_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:oak_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:oak_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:oak_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:oak_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:oak_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:oak_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:oak_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:oak_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:oak_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:oak_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:oak_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:oak_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:oak_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:oak_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:oak_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:oak_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:oak_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:oak_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:oak_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:oak_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:oak_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:oak_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:oak_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:oak_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:oak_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:oak_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:oak_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:oak_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:oak_door[facing=west,half=upper,hinge=right,open=true,powered=false] + minecraft:spruce_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:spruce_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:spruce_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:spruce_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:spruce_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:spruce_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:spruce_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:spruce_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:spruce_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:spruce_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:spruce_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:spruce_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:spruce_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:spruce_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:spruce_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:spruce_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:spruce_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:spruce_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:spruce_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:spruce_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:spruce_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:spruce_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:spruce_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:spruce_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:spruce_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:spruce_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:spruce_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:spruce_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:spruce_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:spruce_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:spruce_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:spruce_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:spruce_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:spruce_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:spruce_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:spruce_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:spruce_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:spruce_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:spruce_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:spruce_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:spruce_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:spruce_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:spruce_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:spruce_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:spruce_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:spruce_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:spruce_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:spruce_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:spruce_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:spruce_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:spruce_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:spruce_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:spruce_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:spruce_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:spruce_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:spruce_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:spruce_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:spruce_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:spruce_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:spruce_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:spruce_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:spruce_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:spruce_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:spruce_door[facing=west,half=upper,hinge=right,open=true,powered=false] + minecraft:birch_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:birch_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:birch_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:birch_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:birch_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:birch_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:birch_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:birch_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:birch_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:birch_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:birch_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:birch_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:birch_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:birch_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:birch_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:birch_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:birch_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:birch_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:birch_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:birch_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:birch_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:birch_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:birch_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:birch_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:birch_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:birch_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:birch_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:birch_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:birch_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:birch_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:birch_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:birch_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:birch_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:birch_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:birch_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:birch_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:birch_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:birch_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:birch_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:birch_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:birch_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:birch_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:birch_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:birch_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:birch_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:birch_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:birch_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:birch_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:birch_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:birch_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:birch_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:birch_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:birch_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:birch_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:birch_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:birch_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:birch_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:birch_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:birch_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:birch_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:birch_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:birch_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:birch_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:birch_door[facing=west,half=upper,hinge=right,open=true,powered=false] + minecraft:jungle_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:jungle_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:jungle_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:jungle_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:jungle_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:jungle_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:jungle_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:jungle_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:jungle_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:jungle_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:jungle_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:jungle_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:jungle_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:jungle_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:jungle_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:jungle_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:jungle_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:jungle_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:jungle_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:jungle_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:jungle_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:jungle_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:jungle_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:jungle_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:jungle_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:jungle_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:jungle_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:jungle_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:jungle_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:jungle_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:jungle_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:jungle_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:jungle_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:jungle_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:jungle_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:jungle_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:jungle_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:jungle_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:jungle_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:jungle_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:jungle_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:jungle_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:jungle_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:jungle_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:jungle_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:jungle_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:jungle_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:jungle_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:jungle_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:jungle_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:jungle_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:jungle_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:jungle_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:jungle_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:jungle_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:jungle_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:jungle_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:jungle_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:jungle_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:jungle_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:jungle_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:jungle_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:jungle_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:jungle_door[facing=west,half=upper,hinge=right,open=true,powered=false] + minecraft:acacia_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:acacia_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:acacia_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:acacia_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:acacia_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:acacia_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:acacia_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:acacia_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:acacia_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:acacia_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:acacia_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:acacia_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:acacia_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:acacia_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:acacia_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:acacia_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:acacia_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:acacia_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:acacia_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:acacia_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:acacia_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:acacia_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:acacia_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:acacia_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:acacia_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:acacia_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:acacia_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:acacia_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:acacia_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:acacia_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:acacia_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:acacia_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:acacia_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:acacia_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:acacia_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:acacia_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:acacia_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:acacia_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:acacia_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:acacia_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:acacia_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:acacia_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:acacia_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:acacia_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:acacia_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:acacia_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:acacia_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:acacia_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:acacia_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:acacia_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:acacia_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:acacia_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:acacia_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:acacia_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:acacia_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:acacia_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:acacia_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:acacia_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:acacia_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:acacia_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:acacia_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:acacia_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:acacia_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:acacia_door[facing=west,half=upper,hinge=right,open=true,powered=false] + minecraft:dark_oak_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:dark_oak_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:dark_oak_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:dark_oak_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:dark_oak_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:dark_oak_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:dark_oak_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:dark_oak_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:dark_oak_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:dark_oak_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:dark_oak_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:dark_oak_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:dark_oak_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:dark_oak_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:dark_oak_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:dark_oak_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:dark_oak_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:dark_oak_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:dark_oak_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:dark_oak_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:dark_oak_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:dark_oak_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:dark_oak_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:dark_oak_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:dark_oak_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:dark_oak_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:dark_oak_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:dark_oak_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:dark_oak_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:dark_oak_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:dark_oak_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:dark_oak_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:dark_oak_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:dark_oak_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:dark_oak_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:dark_oak_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:dark_oak_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:dark_oak_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:dark_oak_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:dark_oak_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:dark_oak_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:dark_oak_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:dark_oak_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:dark_oak_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:dark_oak_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:dark_oak_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:dark_oak_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:dark_oak_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:dark_oak_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:dark_oak_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:dark_oak_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:dark_oak_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:dark_oak_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:dark_oak_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:dark_oak_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:dark_oak_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:dark_oak_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:dark_oak_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:dark_oak_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:dark_oak_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:dark_oak_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:dark_oak_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:dark_oak_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:dark_oak_door[facing=west,half=upper,hinge=right,open=true,powered=false] + minecraft:mangrove_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:mangrove_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:mangrove_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:mangrove_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:mangrove_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:mangrove_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:mangrove_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:mangrove_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:mangrove_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:mangrove_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:mangrove_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:mangrove_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:mangrove_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:mangrove_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:mangrove_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:mangrove_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:mangrove_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:mangrove_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:mangrove_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:mangrove_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:mangrove_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:mangrove_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:mangrove_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:mangrove_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:mangrove_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:mangrove_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:mangrove_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:mangrove_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:mangrove_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:mangrove_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:mangrove_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:mangrove_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:mangrove_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:mangrove_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:mangrove_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:mangrove_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:mangrove_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:mangrove_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:mangrove_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:mangrove_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:mangrove_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:mangrove_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:mangrove_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:mangrove_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:mangrove_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:mangrove_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:mangrove_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:mangrove_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:mangrove_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:mangrove_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:mangrove_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:mangrove_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:mangrove_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:mangrove_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:mangrove_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:mangrove_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:mangrove_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:mangrove_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:mangrove_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:mangrove_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:mangrove_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:mangrove_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:mangrove_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:mangrove_door[facing=west,half=upper,hinge=right,open=true,powered=false] + minecraft:cherry_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:cherry_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:cherry_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:cherry_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:cherry_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:cherry_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:cherry_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:cherry_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:cherry_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:cherry_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:cherry_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:cherry_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:cherry_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:cherry_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:cherry_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:cherry_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:cherry_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:cherry_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:cherry_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:cherry_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:cherry_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:cherry_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:cherry_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:cherry_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:cherry_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:cherry_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:cherry_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:cherry_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:cherry_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:cherry_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:cherry_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:cherry_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:cherry_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:cherry_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:cherry_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:cherry_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:cherry_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:cherry_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:cherry_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:cherry_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:cherry_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:cherry_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:cherry_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:cherry_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:cherry_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:cherry_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:cherry_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:cherry_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:cherry_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:cherry_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:cherry_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:cherry_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:cherry_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:cherry_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:cherry_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:cherry_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:cherry_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:cherry_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:cherry_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:cherry_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:cherry_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:cherry_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:cherry_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:cherry_door[facing=west,half=upper,hinge=right,open=true,powered=false] + minecraft:bamboo_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:bamboo_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:bamboo_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:bamboo_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:bamboo_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:bamboo_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:bamboo_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:bamboo_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:bamboo_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:bamboo_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:bamboo_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:bamboo_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:bamboo_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:bamboo_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:bamboo_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:bamboo_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:bamboo_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:bamboo_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:bamboo_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:bamboo_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:bamboo_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:bamboo_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:bamboo_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:bamboo_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:bamboo_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:bamboo_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:bamboo_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:bamboo_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:bamboo_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:bamboo_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:bamboo_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:bamboo_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:bamboo_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:bamboo_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:bamboo_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:bamboo_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:bamboo_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:bamboo_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:bamboo_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:bamboo_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:bamboo_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:bamboo_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:bamboo_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:bamboo_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:bamboo_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:bamboo_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:bamboo_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:bamboo_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:bamboo_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:bamboo_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:bamboo_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:bamboo_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:bamboo_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:bamboo_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:bamboo_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:bamboo_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:bamboo_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:bamboo_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:bamboo_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:bamboo_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:bamboo_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:bamboo_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:bamboo_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:bamboo_door[facing=west,half=upper,hinge=right,open=true,powered=false] + minecraft:crimson_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:crimson_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:crimson_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:crimson_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:crimson_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:crimson_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:crimson_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:crimson_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:crimson_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:crimson_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:crimson_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:crimson_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:crimson_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:crimson_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:crimson_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:crimson_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:crimson_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:crimson_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:crimson_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:crimson_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:crimson_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:crimson_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:crimson_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:crimson_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:crimson_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:crimson_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:crimson_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:crimson_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:crimson_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:crimson_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:crimson_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:crimson_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:crimson_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:crimson_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:crimson_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:crimson_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:crimson_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:crimson_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:crimson_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:crimson_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:crimson_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:crimson_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:crimson_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:crimson_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:crimson_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:crimson_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:crimson_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:crimson_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:crimson_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:crimson_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:crimson_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:crimson_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:crimson_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:crimson_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:crimson_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:crimson_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:crimson_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:crimson_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:crimson_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:crimson_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:crimson_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:crimson_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:crimson_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:crimson_door[facing=west,half=upper,hinge=right,open=true,powered=false] + minecraft:warped_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:warped_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:warped_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:warped_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:warped_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:warped_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:warped_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:warped_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:warped_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:warped_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:warped_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:warped_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:warped_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:warped_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:warped_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:warped_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:warped_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:warped_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:warped_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:warped_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:warped_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:warped_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:warped_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:warped_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:warped_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:warped_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:warped_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:warped_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:warped_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:warped_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:warped_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:warped_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:warped_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:warped_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:warped_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:warped_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:warped_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:warped_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:warped_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:warped_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:warped_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:warped_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:warped_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:warped_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:warped_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:warped_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:warped_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:warped_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:warped_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:warped_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:warped_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:warped_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:warped_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:warped_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:warped_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:warped_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:warped_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:warped_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:warped_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:warped_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:warped_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:warped_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:warped_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:warped_door[facing=west,half=upper,hinge=right,open=true,powered=false] + minecraft:iron_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:iron_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:iron_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:iron_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:iron_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:iron_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:iron_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:iron_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:iron_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:iron_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:iron_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:iron_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:iron_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:iron_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:iron_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:iron_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:iron_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:iron_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:iron_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:iron_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:iron_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:iron_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:iron_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:iron_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:iron_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:iron_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:iron_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:iron_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:iron_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:iron_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:iron_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:iron_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:iron_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:iron_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:iron_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:iron_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:iron_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:iron_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:iron_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:iron_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:iron_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:iron_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:iron_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:iron_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:iron_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:iron_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:iron_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:iron_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:iron_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:iron_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:iron_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:iron_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:iron_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:iron_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:iron_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:iron_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:iron_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:iron_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:iron_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:iron_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:iron_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:iron_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:iron_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:iron_door[facing=west,half=upper,hinge=right,open=true,powered=false] + + $$>=1.20.3#door: + minecraft:copper_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:copper_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:copper_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:copper_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:copper_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:copper_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:copper_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:copper_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:copper_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:copper_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:copper_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:copper_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:copper_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:copper_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:copper_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:copper_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:copper_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:copper_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:copper_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:copper_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:copper_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:copper_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:copper_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:copper_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:copper_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:copper_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:copper_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:copper_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:copper_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:copper_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:copper_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:copper_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:copper_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:copper_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:copper_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:copper_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:copper_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:copper_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:copper_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:copper_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:copper_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:copper_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:copper_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:copper_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:copper_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:copper_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:copper_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:copper_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:copper_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:copper_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:copper_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:copper_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:copper_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:copper_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:copper_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:copper_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:copper_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:copper_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:copper_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:copper_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:copper_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:copper_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:copper_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:copper_door[facing=west,half=upper,hinge=right,open=true,powered=false] + minecraft:exposed_copper_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:exposed_copper_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:exposed_copper_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:exposed_copper_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:exposed_copper_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:exposed_copper_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:exposed_copper_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:exposed_copper_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:exposed_copper_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:exposed_copper_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:exposed_copper_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:exposed_copper_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:exposed_copper_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:exposed_copper_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:exposed_copper_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:exposed_copper_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:exposed_copper_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:exposed_copper_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:exposed_copper_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:exposed_copper_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:exposed_copper_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:exposed_copper_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:exposed_copper_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:exposed_copper_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:exposed_copper_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:exposed_copper_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:exposed_copper_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:exposed_copper_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:exposed_copper_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:exposed_copper_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:exposed_copper_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:exposed_copper_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:exposed_copper_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:exposed_copper_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:exposed_copper_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:exposed_copper_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:exposed_copper_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:exposed_copper_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:exposed_copper_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:exposed_copper_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:exposed_copper_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:exposed_copper_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:exposed_copper_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:exposed_copper_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:exposed_copper_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:exposed_copper_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:exposed_copper_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:exposed_copper_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:exposed_copper_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:exposed_copper_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:exposed_copper_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:exposed_copper_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:exposed_copper_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:exposed_copper_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:exposed_copper_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:exposed_copper_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:exposed_copper_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:exposed_copper_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:exposed_copper_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:exposed_copper_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:exposed_copper_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:exposed_copper_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:exposed_copper_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:exposed_copper_door[facing=west,half=upper,hinge=right,open=true,powered=false] + minecraft:weathered_copper_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:weathered_copper_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:weathered_copper_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:weathered_copper_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:weathered_copper_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:weathered_copper_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:weathered_copper_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:weathered_copper_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:weathered_copper_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:weathered_copper_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:weathered_copper_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:weathered_copper_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:weathered_copper_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:weathered_copper_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:weathered_copper_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:weathered_copper_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:weathered_copper_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:weathered_copper_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:weathered_copper_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:weathered_copper_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:weathered_copper_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:weathered_copper_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:weathered_copper_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:weathered_copper_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:weathered_copper_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:weathered_copper_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:weathered_copper_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:weathered_copper_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:weathered_copper_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:weathered_copper_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:weathered_copper_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:weathered_copper_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:weathered_copper_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:weathered_copper_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:weathered_copper_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:weathered_copper_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:weathered_copper_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:weathered_copper_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:weathered_copper_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:weathered_copper_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:weathered_copper_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:weathered_copper_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:weathered_copper_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:weathered_copper_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:weathered_copper_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:weathered_copper_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:weathered_copper_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:weathered_copper_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:weathered_copper_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:weathered_copper_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:weathered_copper_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:weathered_copper_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:weathered_copper_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:weathered_copper_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:weathered_copper_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:weathered_copper_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:weathered_copper_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:weathered_copper_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:weathered_copper_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:weathered_copper_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:weathered_copper_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:weathered_copper_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:weathered_copper_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:weathered_copper_door[facing=west,half=upper,hinge=right,open=true,powered=false] + minecraft:oxidized_copper_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:oxidized_copper_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:oxidized_copper_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:oxidized_copper_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:oxidized_copper_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:oxidized_copper_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:oxidized_copper_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:oxidized_copper_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:oxidized_copper_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:oxidized_copper_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:oxidized_copper_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:oxidized_copper_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:oxidized_copper_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:oxidized_copper_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:oxidized_copper_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:oxidized_copper_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:oxidized_copper_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:oxidized_copper_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:oxidized_copper_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:oxidized_copper_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:oxidized_copper_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:oxidized_copper_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:oxidized_copper_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:oxidized_copper_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:oxidized_copper_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:oxidized_copper_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:oxidized_copper_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:oxidized_copper_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:oxidized_copper_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:oxidized_copper_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:oxidized_copper_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:oxidized_copper_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:oxidized_copper_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:oxidized_copper_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:oxidized_copper_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:oxidized_copper_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:oxidized_copper_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:oxidized_copper_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:oxidized_copper_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:oxidized_copper_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:oxidized_copper_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:oxidized_copper_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:oxidized_copper_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:oxidized_copper_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:oxidized_copper_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:oxidized_copper_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:oxidized_copper_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:oxidized_copper_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:oxidized_copper_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:oxidized_copper_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:oxidized_copper_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:oxidized_copper_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:oxidized_copper_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:oxidized_copper_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:oxidized_copper_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:oxidized_copper_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:oxidized_copper_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:oxidized_copper_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:oxidized_copper_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:oxidized_copper_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:oxidized_copper_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:oxidized_copper_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:oxidized_copper_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:oxidized_copper_door[facing=west,half=upper,hinge=right,open=true,powered=false] + minecraft:waxed_copper_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_copper_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:waxed_copper_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_copper_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:waxed_copper_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_copper_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:waxed_copper_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_copper_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:waxed_copper_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_copper_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:waxed_copper_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_copper_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:waxed_copper_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_copper_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:waxed_copper_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_copper_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:waxed_copper_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_copper_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:waxed_copper_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_copper_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:waxed_copper_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_copper_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:waxed_copper_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_copper_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:waxed_copper_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_copper_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:waxed_copper_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_copper_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:waxed_copper_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_copper_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:waxed_copper_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_copper_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:waxed_copper_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_copper_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:waxed_copper_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_copper_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:waxed_copper_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_copper_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:waxed_copper_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_copper_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:waxed_copper_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_copper_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:waxed_copper_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_copper_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:waxed_copper_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_copper_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:waxed_copper_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_copper_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:waxed_copper_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_copper_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:waxed_copper_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_copper_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:waxed_copper_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_copper_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:waxed_copper_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_copper_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:waxed_copper_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_copper_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:waxed_copper_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_copper_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:waxed_copper_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_copper_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:waxed_copper_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_copper_door[facing=west,half=upper,hinge=right,open=true,powered=false] + minecraft:waxed_exposed_copper_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:waxed_exposed_copper_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:waxed_exposed_copper_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:waxed_exposed_copper_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:waxed_exposed_copper_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:waxed_exposed_copper_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:waxed_exposed_copper_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:waxed_exposed_copper_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:waxed_exposed_copper_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:waxed_exposed_copper_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:waxed_exposed_copper_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:waxed_exposed_copper_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:waxed_exposed_copper_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:waxed_exposed_copper_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:waxed_exposed_copper_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:waxed_exposed_copper_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:waxed_exposed_copper_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:waxed_exposed_copper_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:waxed_exposed_copper_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:waxed_exposed_copper_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:waxed_exposed_copper_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:waxed_exposed_copper_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:waxed_exposed_copper_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:waxed_exposed_copper_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:waxed_exposed_copper_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:waxed_exposed_copper_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:waxed_exposed_copper_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:waxed_exposed_copper_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_exposed_copper_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:waxed_exposed_copper_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:waxed_exposed_copper_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:waxed_exposed_copper_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:waxed_exposed_copper_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_exposed_copper_door[facing=west,half=upper,hinge=right,open=true,powered=false] + minecraft:waxed_weathered_copper_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:waxed_weathered_copper_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:waxed_weathered_copper_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:waxed_weathered_copper_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:waxed_weathered_copper_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:waxed_weathered_copper_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:waxed_weathered_copper_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:waxed_weathered_copper_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:waxed_weathered_copper_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:waxed_weathered_copper_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:waxed_weathered_copper_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:waxed_weathered_copper_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:waxed_weathered_copper_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:waxed_weathered_copper_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:waxed_weathered_copper_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:waxed_weathered_copper_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:waxed_weathered_copper_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:waxed_weathered_copper_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:waxed_weathered_copper_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:waxed_weathered_copper_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:waxed_weathered_copper_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:waxed_weathered_copper_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:waxed_weathered_copper_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:waxed_weathered_copper_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:waxed_weathered_copper_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:waxed_weathered_copper_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:waxed_weathered_copper_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:waxed_weathered_copper_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_weathered_copper_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:waxed_weathered_copper_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:waxed_weathered_copper_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:waxed_weathered_copper_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:waxed_weathered_copper_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_weathered_copper_door[facing=west,half=upper,hinge=right,open=true,powered=false] + minecraft:waxed_oxidized_copper_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:waxed_oxidized_copper_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:waxed_oxidized_copper_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:waxed_oxidized_copper_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:waxed_oxidized_copper_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:waxed_oxidized_copper_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:waxed_oxidized_copper_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:waxed_oxidized_copper_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:waxed_oxidized_copper_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:waxed_oxidized_copper_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:waxed_oxidized_copper_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:waxed_oxidized_copper_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:waxed_oxidized_copper_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:waxed_oxidized_copper_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:waxed_oxidized_copper_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:waxed_oxidized_copper_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:waxed_oxidized_copper_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:waxed_oxidized_copper_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:waxed_oxidized_copper_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:waxed_oxidized_copper_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:waxed_oxidized_copper_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:waxed_oxidized_copper_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:waxed_oxidized_copper_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:waxed_oxidized_copper_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:waxed_oxidized_copper_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:waxed_oxidized_copper_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:waxed_oxidized_copper_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:waxed_oxidized_copper_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:waxed_oxidized_copper_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:waxed_oxidized_copper_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:waxed_oxidized_copper_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:waxed_oxidized_copper_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:waxed_oxidized_copper_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:waxed_oxidized_copper_door[facing=west,half=upper,hinge=right,open=true,powered=false] + + $$>=1.21.4#door: + minecraft:pale_oak_door[facing=east,half=lower,hinge=left,open=false,powered=true]: minecraft:pale_oak_door[facing=east,half=lower,hinge=left,open=false,powered=false] + minecraft:pale_oak_door[facing=east,half=lower,hinge=right,open=false,powered=true]: minecraft:pale_oak_door[facing=east,half=lower,hinge=right,open=false,powered=false] + minecraft:pale_oak_door[facing=east,half=upper,hinge=left,open=false,powered=true]: minecraft:pale_oak_door[facing=east,half=upper,hinge=left,open=false,powered=false] + minecraft:pale_oak_door[facing=east,half=upper,hinge=right,open=false,powered=true]: minecraft:pale_oak_door[facing=east,half=upper,hinge=right,open=false,powered=false] + minecraft:pale_oak_door[facing=east,half=lower,hinge=left,open=true,powered=true]: minecraft:pale_oak_door[facing=east,half=lower,hinge=left,open=true,powered=false] + minecraft:pale_oak_door[facing=east,half=lower,hinge=right,open=true,powered=true]: minecraft:pale_oak_door[facing=east,half=lower,hinge=right,open=true,powered=false] + minecraft:pale_oak_door[facing=east,half=upper,hinge=left,open=true,powered=true]: minecraft:pale_oak_door[facing=east,half=upper,hinge=left,open=true,powered=false] + minecraft:pale_oak_door[facing=east,half=upper,hinge=right,open=true,powered=true]: minecraft:pale_oak_door[facing=east,half=upper,hinge=right,open=true,powered=false] + minecraft:pale_oak_door[facing=north,half=lower,hinge=left,open=false,powered=true]: minecraft:pale_oak_door[facing=north,half=lower,hinge=left,open=false,powered=false] + minecraft:pale_oak_door[facing=north,half=lower,hinge=right,open=false,powered=true]: minecraft:pale_oak_door[facing=north,half=lower,hinge=right,open=false,powered=false] + minecraft:pale_oak_door[facing=north,half=upper,hinge=left,open=false,powered=true]: minecraft:pale_oak_door[facing=north,half=upper,hinge=left,open=false,powered=false] + minecraft:pale_oak_door[facing=north,half=upper,hinge=right,open=false,powered=true]: minecraft:pale_oak_door[facing=north,half=upper,hinge=right,open=false,powered=false] + minecraft:pale_oak_door[facing=north,half=lower,hinge=left,open=true,powered=true]: minecraft:pale_oak_door[facing=north,half=lower,hinge=left,open=true,powered=false] + minecraft:pale_oak_door[facing=north,half=lower,hinge=right,open=true,powered=true]: minecraft:pale_oak_door[facing=north,half=lower,hinge=right,open=true,powered=false] + minecraft:pale_oak_door[facing=north,half=upper,hinge=left,open=true,powered=true]: minecraft:pale_oak_door[facing=north,half=upper,hinge=left,open=true,powered=false] + minecraft:pale_oak_door[facing=north,half=upper,hinge=right,open=true,powered=true]: minecraft:pale_oak_door[facing=north,half=upper,hinge=right,open=true,powered=false] + minecraft:pale_oak_door[facing=south,half=lower,hinge=left,open=false,powered=true]: minecraft:pale_oak_door[facing=south,half=lower,hinge=left,open=false,powered=false] + minecraft:pale_oak_door[facing=south,half=lower,hinge=right,open=false,powered=true]: minecraft:pale_oak_door[facing=south,half=lower,hinge=right,open=false,powered=false] + minecraft:pale_oak_door[facing=south,half=upper,hinge=left,open=false,powered=true]: minecraft:pale_oak_door[facing=south,half=upper,hinge=left,open=false,powered=false] + minecraft:pale_oak_door[facing=south,half=upper,hinge=right,open=false,powered=true]: minecraft:pale_oak_door[facing=south,half=upper,hinge=right,open=false,powered=false] + minecraft:pale_oak_door[facing=south,half=lower,hinge=left,open=true,powered=true]: minecraft:pale_oak_door[facing=south,half=lower,hinge=left,open=true,powered=false] + minecraft:pale_oak_door[facing=south,half=lower,hinge=right,open=true,powered=true]: minecraft:pale_oak_door[facing=south,half=lower,hinge=right,open=true,powered=false] + minecraft:pale_oak_door[facing=south,half=upper,hinge=left,open=true,powered=true]: minecraft:pale_oak_door[facing=south,half=upper,hinge=left,open=true,powered=false] + minecraft:pale_oak_door[facing=south,half=upper,hinge=right,open=true,powered=true]: minecraft:pale_oak_door[facing=south,half=upper,hinge=right,open=true,powered=false] + minecraft:pale_oak_door[facing=west,half=lower,hinge=left,open=false,powered=true]: minecraft:pale_oak_door[facing=west,half=lower,hinge=left,open=false,powered=false] + minecraft:pale_oak_door[facing=west,half=lower,hinge=right,open=false,powered=true]: minecraft:pale_oak_door[facing=west,half=lower,hinge=right,open=false,powered=false] + minecraft:pale_oak_door[facing=west,half=upper,hinge=left,open=false,powered=true]: minecraft:pale_oak_door[facing=west,half=upper,hinge=left,open=false,powered=false] + minecraft:pale_oak_door[facing=west,half=upper,hinge=right,open=false,powered=true]: minecraft:pale_oak_door[facing=west,half=upper,hinge=right,open=false,powered=false] + minecraft:pale_oak_door[facing=west,half=lower,hinge=left,open=true,powered=true]: minecraft:pale_oak_door[facing=west,half=lower,hinge=left,open=true,powered=false] + minecraft:pale_oak_door[facing=west,half=lower,hinge=right,open=true,powered=true]: minecraft:pale_oak_door[facing=west,half=lower,hinge=right,open=true,powered=false] + minecraft:pale_oak_door[facing=west,half=upper,hinge=left,open=true,powered=true]: minecraft:pale_oak_door[facing=west,half=upper,hinge=left,open=true,powered=false] + minecraft:pale_oak_door[facing=west,half=upper,hinge=right,open=true,powered=true]: minecraft:pale_oak_door[facing=west,half=upper,hinge=right,open=true,powered=false] + + #### Fence Gate #### + minecraft:oak_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:oak_fence_gate[facing=east,in_wall=false,open=false,powered=false] + minecraft:oak_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:oak_fence_gate[facing=east,in_wall=false,open=true,powered=false] + minecraft:oak_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:oak_fence_gate[facing=east,in_wall=true,open=false,powered=false] + minecraft:oak_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:oak_fence_gate[facing=east,in_wall=true,open=true,powered=false] + minecraft:oak_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:oak_fence_gate[facing=south,in_wall=false,open=false,powered=false] + minecraft:oak_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:oak_fence_gate[facing=south,in_wall=false,open=true,powered=false] + minecraft:oak_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:oak_fence_gate[facing=south,in_wall=true,open=false,powered=false] + minecraft:oak_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:oak_fence_gate[facing=south,in_wall=true,open=true,powered=false] + minecraft:oak_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:oak_fence_gate[facing=west,in_wall=false,open=false,powered=false] + minecraft:oak_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:oak_fence_gate[facing=west,in_wall=false,open=true,powered=false] + minecraft:oak_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:oak_fence_gate[facing=west,in_wall=true,open=false,powered=false] + minecraft:oak_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:oak_fence_gate[facing=west,in_wall=true,open=true,powered=false] + minecraft:oak_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:oak_fence_gate[facing=north,in_wall=false,open=false,powered=false] + minecraft:oak_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:oak_fence_gate[facing=north,in_wall=false,open=true,powered=false] + minecraft:oak_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:oak_fence_gate[facing=north,in_wall=true,open=false,powered=false] + minecraft:oak_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:oak_fence_gate[facing=north,in_wall=true,open=true,powered=false] + minecraft:spruce_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:spruce_fence_gate[facing=east,in_wall=false,open=false,powered=false] + minecraft:spruce_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:spruce_fence_gate[facing=east,in_wall=false,open=true,powered=false] + minecraft:spruce_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:spruce_fence_gate[facing=east,in_wall=true,open=false,powered=false] + minecraft:spruce_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:spruce_fence_gate[facing=east,in_wall=true,open=true,powered=false] + minecraft:spruce_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:spruce_fence_gate[facing=south,in_wall=false,open=false,powered=false] + minecraft:spruce_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:spruce_fence_gate[facing=south,in_wall=false,open=true,powered=false] + minecraft:spruce_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:spruce_fence_gate[facing=south,in_wall=true,open=false,powered=false] + minecraft:spruce_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:spruce_fence_gate[facing=south,in_wall=true,open=true,powered=false] + minecraft:spruce_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:spruce_fence_gate[facing=west,in_wall=false,open=false,powered=false] + minecraft:spruce_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:spruce_fence_gate[facing=west,in_wall=false,open=true,powered=false] + minecraft:spruce_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:spruce_fence_gate[facing=west,in_wall=true,open=false,powered=false] + minecraft:spruce_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:spruce_fence_gate[facing=west,in_wall=true,open=true,powered=false] + minecraft:spruce_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:spruce_fence_gate[facing=north,in_wall=false,open=false,powered=false] + minecraft:spruce_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:spruce_fence_gate[facing=north,in_wall=false,open=true,powered=false] + minecraft:spruce_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:spruce_fence_gate[facing=north,in_wall=true,open=false,powered=false] + minecraft:spruce_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:spruce_fence_gate[facing=north,in_wall=true,open=true,powered=false] + minecraft:birch_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:birch_fence_gate[facing=east,in_wall=false,open=false,powered=false] + minecraft:birch_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:birch_fence_gate[facing=east,in_wall=false,open=true,powered=false] + minecraft:birch_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:birch_fence_gate[facing=east,in_wall=true,open=false,powered=false] + minecraft:birch_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:birch_fence_gate[facing=east,in_wall=true,open=true,powered=false] + minecraft:birch_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:birch_fence_gate[facing=south,in_wall=false,open=false,powered=false] + minecraft:birch_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:birch_fence_gate[facing=south,in_wall=false,open=true,powered=false] + minecraft:birch_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:birch_fence_gate[facing=south,in_wall=true,open=false,powered=false] + minecraft:birch_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:birch_fence_gate[facing=south,in_wall=true,open=true,powered=false] + minecraft:birch_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:birch_fence_gate[facing=west,in_wall=false,open=false,powered=false] + minecraft:birch_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:birch_fence_gate[facing=west,in_wall=false,open=true,powered=false] + minecraft:birch_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:birch_fence_gate[facing=west,in_wall=true,open=false,powered=false] + minecraft:birch_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:birch_fence_gate[facing=west,in_wall=true,open=true,powered=false] + minecraft:birch_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:birch_fence_gate[facing=north,in_wall=false,open=false,powered=false] + minecraft:birch_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:birch_fence_gate[facing=north,in_wall=false,open=true,powered=false] + minecraft:birch_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:birch_fence_gate[facing=north,in_wall=true,open=false,powered=false] + minecraft:birch_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:birch_fence_gate[facing=north,in_wall=true,open=true,powered=false] + minecraft:jungle_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:jungle_fence_gate[facing=east,in_wall=false,open=false,powered=false] + minecraft:jungle_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:jungle_fence_gate[facing=east,in_wall=false,open=true,powered=false] + minecraft:jungle_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:jungle_fence_gate[facing=east,in_wall=true,open=false,powered=false] + minecraft:jungle_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:jungle_fence_gate[facing=east,in_wall=true,open=true,powered=false] + minecraft:jungle_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:jungle_fence_gate[facing=south,in_wall=false,open=false,powered=false] + minecraft:jungle_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:jungle_fence_gate[facing=south,in_wall=false,open=true,powered=false] + minecraft:jungle_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:jungle_fence_gate[facing=south,in_wall=true,open=false,powered=false] + minecraft:jungle_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:jungle_fence_gate[facing=south,in_wall=true,open=true,powered=false] + minecraft:jungle_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:jungle_fence_gate[facing=west,in_wall=false,open=false,powered=false] + minecraft:jungle_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:jungle_fence_gate[facing=west,in_wall=false,open=true,powered=false] + minecraft:jungle_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:jungle_fence_gate[facing=west,in_wall=true,open=false,powered=false] + minecraft:jungle_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:jungle_fence_gate[facing=west,in_wall=true,open=true,powered=false] + minecraft:jungle_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:jungle_fence_gate[facing=north,in_wall=false,open=false,powered=false] + minecraft:jungle_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:jungle_fence_gate[facing=north,in_wall=false,open=true,powered=false] + minecraft:jungle_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:jungle_fence_gate[facing=north,in_wall=true,open=false,powered=false] + minecraft:jungle_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:jungle_fence_gate[facing=north,in_wall=true,open=true,powered=false] + minecraft:acacia_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:acacia_fence_gate[facing=east,in_wall=false,open=false,powered=false] + minecraft:acacia_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:acacia_fence_gate[facing=east,in_wall=false,open=true,powered=false] + minecraft:acacia_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:acacia_fence_gate[facing=east,in_wall=true,open=false,powered=false] + minecraft:acacia_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:acacia_fence_gate[facing=east,in_wall=true,open=true,powered=false] + minecraft:acacia_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:acacia_fence_gate[facing=south,in_wall=false,open=false,powered=false] + minecraft:acacia_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:acacia_fence_gate[facing=south,in_wall=false,open=true,powered=false] + minecraft:acacia_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:acacia_fence_gate[facing=south,in_wall=true,open=false,powered=false] + minecraft:acacia_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:acacia_fence_gate[facing=south,in_wall=true,open=true,powered=false] + minecraft:acacia_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:acacia_fence_gate[facing=west,in_wall=false,open=false,powered=false] + minecraft:acacia_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:acacia_fence_gate[facing=west,in_wall=false,open=true,powered=false] + minecraft:acacia_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:acacia_fence_gate[facing=west,in_wall=true,open=false,powered=false] + minecraft:acacia_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:acacia_fence_gate[facing=west,in_wall=true,open=true,powered=false] + minecraft:acacia_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:acacia_fence_gate[facing=north,in_wall=false,open=false,powered=false] + minecraft:acacia_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:acacia_fence_gate[facing=north,in_wall=false,open=true,powered=false] + minecraft:acacia_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:acacia_fence_gate[facing=north,in_wall=true,open=false,powered=false] + minecraft:acacia_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:acacia_fence_gate[facing=north,in_wall=true,open=true,powered=false] + minecraft:dark_oak_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:dark_oak_fence_gate[facing=east,in_wall=false,open=false,powered=false] + minecraft:dark_oak_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:dark_oak_fence_gate[facing=east,in_wall=false,open=true,powered=false] + minecraft:dark_oak_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:dark_oak_fence_gate[facing=east,in_wall=true,open=false,powered=false] + minecraft:dark_oak_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:dark_oak_fence_gate[facing=east,in_wall=true,open=true,powered=false] + minecraft:dark_oak_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:dark_oak_fence_gate[facing=south,in_wall=false,open=false,powered=false] + minecraft:dark_oak_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:dark_oak_fence_gate[facing=south,in_wall=false,open=true,powered=false] + minecraft:dark_oak_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:dark_oak_fence_gate[facing=south,in_wall=true,open=false,powered=false] + minecraft:dark_oak_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:dark_oak_fence_gate[facing=south,in_wall=true,open=true,powered=false] + minecraft:dark_oak_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:dark_oak_fence_gate[facing=west,in_wall=false,open=false,powered=false] + minecraft:dark_oak_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:dark_oak_fence_gate[facing=west,in_wall=false,open=true,powered=false] + minecraft:dark_oak_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:dark_oak_fence_gate[facing=west,in_wall=true,open=false,powered=false] + minecraft:dark_oak_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:dark_oak_fence_gate[facing=west,in_wall=true,open=true,powered=false] + minecraft:dark_oak_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:dark_oak_fence_gate[facing=north,in_wall=false,open=false,powered=false] + minecraft:dark_oak_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:dark_oak_fence_gate[facing=north,in_wall=false,open=true,powered=false] + minecraft:dark_oak_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:dark_oak_fence_gate[facing=north,in_wall=true,open=false,powered=false] + minecraft:dark_oak_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:dark_oak_fence_gate[facing=north,in_wall=true,open=true,powered=false] + minecraft:mangrove_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:mangrove_fence_gate[facing=east,in_wall=false,open=false,powered=false] + minecraft:mangrove_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:mangrove_fence_gate[facing=east,in_wall=false,open=true,powered=false] + minecraft:mangrove_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:mangrove_fence_gate[facing=east,in_wall=true,open=false,powered=false] + minecraft:mangrove_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:mangrove_fence_gate[facing=east,in_wall=true,open=true,powered=false] + minecraft:mangrove_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:mangrove_fence_gate[facing=south,in_wall=false,open=false,powered=false] + minecraft:mangrove_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:mangrove_fence_gate[facing=south,in_wall=false,open=true,powered=false] + minecraft:mangrove_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:mangrove_fence_gate[facing=south,in_wall=true,open=false,powered=false] + minecraft:mangrove_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:mangrove_fence_gate[facing=south,in_wall=true,open=true,powered=false] + minecraft:mangrove_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:mangrove_fence_gate[facing=west,in_wall=false,open=false,powered=false] + minecraft:mangrove_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:mangrove_fence_gate[facing=west,in_wall=false,open=true,powered=false] + minecraft:mangrove_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:mangrove_fence_gate[facing=west,in_wall=true,open=false,powered=false] + minecraft:mangrove_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:mangrove_fence_gate[facing=west,in_wall=true,open=true,powered=false] + minecraft:mangrove_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:mangrove_fence_gate[facing=north,in_wall=false,open=false,powered=false] + minecraft:mangrove_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:mangrove_fence_gate[facing=north,in_wall=false,open=true,powered=false] + minecraft:mangrove_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:mangrove_fence_gate[facing=north,in_wall=true,open=false,powered=false] + minecraft:mangrove_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:mangrove_fence_gate[facing=north,in_wall=true,open=true,powered=false] + minecraft:cherry_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:cherry_fence_gate[facing=east,in_wall=false,open=false,powered=false] + minecraft:cherry_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:cherry_fence_gate[facing=east,in_wall=false,open=true,powered=false] + minecraft:cherry_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:cherry_fence_gate[facing=east,in_wall=true,open=false,powered=false] + minecraft:cherry_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:cherry_fence_gate[facing=east,in_wall=true,open=true,powered=false] + minecraft:cherry_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:cherry_fence_gate[facing=south,in_wall=false,open=false,powered=false] + minecraft:cherry_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:cherry_fence_gate[facing=south,in_wall=false,open=true,powered=false] + minecraft:cherry_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:cherry_fence_gate[facing=south,in_wall=true,open=false,powered=false] + minecraft:cherry_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:cherry_fence_gate[facing=south,in_wall=true,open=true,powered=false] + minecraft:cherry_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:cherry_fence_gate[facing=west,in_wall=false,open=false,powered=false] + minecraft:cherry_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:cherry_fence_gate[facing=west,in_wall=false,open=true,powered=false] + minecraft:cherry_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:cherry_fence_gate[facing=west,in_wall=true,open=false,powered=false] + minecraft:cherry_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:cherry_fence_gate[facing=west,in_wall=true,open=true,powered=false] + minecraft:cherry_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:cherry_fence_gate[facing=north,in_wall=false,open=false,powered=false] + minecraft:cherry_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:cherry_fence_gate[facing=north,in_wall=false,open=true,powered=false] + minecraft:cherry_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:cherry_fence_gate[facing=north,in_wall=true,open=false,powered=false] + minecraft:cherry_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:cherry_fence_gate[facing=north,in_wall=true,open=true,powered=false] + minecraft:bamboo_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:bamboo_fence_gate[facing=east,in_wall=false,open=false,powered=false] + minecraft:bamboo_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:bamboo_fence_gate[facing=east,in_wall=false,open=true,powered=false] + minecraft:bamboo_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:bamboo_fence_gate[facing=east,in_wall=true,open=false,powered=false] + minecraft:bamboo_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:bamboo_fence_gate[facing=east,in_wall=true,open=true,powered=false] + minecraft:bamboo_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:bamboo_fence_gate[facing=south,in_wall=false,open=false,powered=false] + minecraft:bamboo_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:bamboo_fence_gate[facing=south,in_wall=false,open=true,powered=false] + minecraft:bamboo_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:bamboo_fence_gate[facing=south,in_wall=true,open=false,powered=false] + minecraft:bamboo_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:bamboo_fence_gate[facing=south,in_wall=true,open=true,powered=false] + minecraft:bamboo_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:bamboo_fence_gate[facing=west,in_wall=false,open=false,powered=false] + minecraft:bamboo_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:bamboo_fence_gate[facing=west,in_wall=false,open=true,powered=false] + minecraft:bamboo_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:bamboo_fence_gate[facing=west,in_wall=true,open=false,powered=false] + minecraft:bamboo_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:bamboo_fence_gate[facing=west,in_wall=true,open=true,powered=false] + minecraft:bamboo_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:bamboo_fence_gate[facing=north,in_wall=false,open=false,powered=false] + minecraft:bamboo_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:bamboo_fence_gate[facing=north,in_wall=false,open=true,powered=false] + minecraft:bamboo_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:bamboo_fence_gate[facing=north,in_wall=true,open=false,powered=false] + minecraft:bamboo_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:bamboo_fence_gate[facing=north,in_wall=true,open=true,powered=false] + minecraft:crimson_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:crimson_fence_gate[facing=east,in_wall=false,open=false,powered=false] + minecraft:crimson_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:crimson_fence_gate[facing=east,in_wall=false,open=true,powered=false] + minecraft:crimson_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:crimson_fence_gate[facing=east,in_wall=true,open=false,powered=false] + minecraft:crimson_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:crimson_fence_gate[facing=east,in_wall=true,open=true,powered=false] + minecraft:crimson_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:crimson_fence_gate[facing=south,in_wall=false,open=false,powered=false] + minecraft:crimson_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:crimson_fence_gate[facing=south,in_wall=false,open=true,powered=false] + minecraft:crimson_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:crimson_fence_gate[facing=south,in_wall=true,open=false,powered=false] + minecraft:crimson_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:crimson_fence_gate[facing=south,in_wall=true,open=true,powered=false] + minecraft:crimson_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:crimson_fence_gate[facing=west,in_wall=false,open=false,powered=false] + minecraft:crimson_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:crimson_fence_gate[facing=west,in_wall=false,open=true,powered=false] + minecraft:crimson_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:crimson_fence_gate[facing=west,in_wall=true,open=false,powered=false] + minecraft:crimson_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:crimson_fence_gate[facing=west,in_wall=true,open=true,powered=false] + minecraft:crimson_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:crimson_fence_gate[facing=north,in_wall=false,open=false,powered=false] + minecraft:crimson_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:crimson_fence_gate[facing=north,in_wall=false,open=true,powered=false] + minecraft:crimson_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:crimson_fence_gate[facing=north,in_wall=true,open=false,powered=false] + minecraft:crimson_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:crimson_fence_gate[facing=north,in_wall=true,open=true,powered=false] + minecraft:warped_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:warped_fence_gate[facing=east,in_wall=false,open=false,powered=false] + minecraft:warped_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:warped_fence_gate[facing=east,in_wall=false,open=true,powered=false] + minecraft:warped_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:warped_fence_gate[facing=east,in_wall=true,open=false,powered=false] + minecraft:warped_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:warped_fence_gate[facing=east,in_wall=true,open=true,powered=false] + minecraft:warped_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:warped_fence_gate[facing=south,in_wall=false,open=false,powered=false] + minecraft:warped_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:warped_fence_gate[facing=south,in_wall=false,open=true,powered=false] + minecraft:warped_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:warped_fence_gate[facing=south,in_wall=true,open=false,powered=false] + minecraft:warped_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:warped_fence_gate[facing=south,in_wall=true,open=true,powered=false] + minecraft:warped_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:warped_fence_gate[facing=west,in_wall=false,open=false,powered=false] + minecraft:warped_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:warped_fence_gate[facing=west,in_wall=false,open=true,powered=false] + minecraft:warped_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:warped_fence_gate[facing=west,in_wall=true,open=false,powered=false] + minecraft:warped_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:warped_fence_gate[facing=west,in_wall=true,open=true,powered=false] + minecraft:warped_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:warped_fence_gate[facing=north,in_wall=false,open=false,powered=false] + minecraft:warped_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:warped_fence_gate[facing=north,in_wall=false,open=true,powered=false] + minecraft:warped_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:warped_fence_gate[facing=north,in_wall=true,open=false,powered=false] + minecraft:warped_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:warped_fence_gate[facing=north,in_wall=true,open=true,powered=false] + + $$>=1.21.4#fence_gate: + minecraft:pale_oak_fence_gate[facing=east,in_wall=false,open=false,powered=true]: minecraft:pale_oak_fence_gate[facing=east,in_wall=false,open=false,powered=false] + minecraft:pale_oak_fence_gate[facing=east,in_wall=false,open=true,powered=true]: minecraft:pale_oak_fence_gate[facing=east,in_wall=false,open=true,powered=false] + minecraft:pale_oak_fence_gate[facing=east,in_wall=true,open=false,powered=true]: minecraft:pale_oak_fence_gate[facing=east,in_wall=true,open=false,powered=false] + minecraft:pale_oak_fence_gate[facing=east,in_wall=true,open=true,powered=true]: minecraft:pale_oak_fence_gate[facing=east,in_wall=true,open=true,powered=false] + minecraft:pale_oak_fence_gate[facing=south,in_wall=false,open=false,powered=true]: minecraft:pale_oak_fence_gate[facing=south,in_wall=false,open=false,powered=false] + minecraft:pale_oak_fence_gate[facing=south,in_wall=false,open=true,powered=true]: minecraft:pale_oak_fence_gate[facing=south,in_wall=false,open=true,powered=false] + minecraft:pale_oak_fence_gate[facing=south,in_wall=true,open=false,powered=true]: minecraft:pale_oak_fence_gate[facing=south,in_wall=true,open=false,powered=false] + minecraft:pale_oak_fence_gate[facing=south,in_wall=true,open=true,powered=true]: minecraft:pale_oak_fence_gate[facing=south,in_wall=true,open=true,powered=false] + minecraft:pale_oak_fence_gate[facing=west,in_wall=false,open=false,powered=true]: minecraft:pale_oak_fence_gate[facing=west,in_wall=false,open=false,powered=false] + minecraft:pale_oak_fence_gate[facing=west,in_wall=false,open=true,powered=true]: minecraft:pale_oak_fence_gate[facing=west,in_wall=false,open=true,powered=false] + minecraft:pale_oak_fence_gate[facing=west,in_wall=true,open=false,powered=true]: minecraft:pale_oak_fence_gate[facing=west,in_wall=true,open=false,powered=false] + minecraft:pale_oak_fence_gate[facing=west,in_wall=true,open=true,powered=true]: minecraft:pale_oak_fence_gate[facing=west,in_wall=true,open=true,powered=false] + minecraft:pale_oak_fence_gate[facing=north,in_wall=false,open=false,powered=true]: minecraft:pale_oak_fence_gate[facing=north,in_wall=false,open=false,powered=false] + minecraft:pale_oak_fence_gate[facing=north,in_wall=false,open=true,powered=true]: minecraft:pale_oak_fence_gate[facing=north,in_wall=false,open=true,powered=false] + minecraft:pale_oak_fence_gate[facing=north,in_wall=true,open=false,powered=true]: minecraft:pale_oak_fence_gate[facing=north,in_wall=true,open=false,powered=false] + minecraft:pale_oak_fence_gate[facing=north,in_wall=true,open=true,powered=true]: minecraft:pale_oak_fence_gate[facing=north,in_wall=true,open=true,powered=false] + + #### Slab #### + minecraft:petrified_oak_slab[type=bottom,waterlogged=false]: minecraft:oak_slab[type=bottom,waterlogged=false] + minecraft:petrified_oak_slab[type=top,waterlogged=false]: minecraft:oak_slab[type=top,waterlogged=false] + minecraft:petrified_oak_slab[type=double,waterlogged=false]: minecraft:oak_slab[type=double,waterlogged=false] + minecraft:petrified_oak_slab[type=bottom,waterlogged=true]: minecraft:oak_slab[type=bottom,waterlogged=true] + minecraft:petrified_oak_slab[type=top,waterlogged=true]: minecraft:oak_slab[type=top,waterlogged=true] + minecraft:petrified_oak_slab[type=double,waterlogged=true]: minecraft:oak_slab[type=double,waterlogged=true] + minecraft:cut_copper_slab[type=bottom,waterlogged=false]: minecraft:waxed_cut_copper_slab[type=bottom,waterlogged=false] + minecraft:cut_copper_slab[type=top,waterlogged=false]: minecraft:waxed_cut_copper_slab[type=top,waterlogged=false] + minecraft:cut_copper_slab[type=double,waterlogged=false]: minecraft:waxed_cut_copper_slab[type=double,waterlogged=false] + minecraft:cut_copper_slab[type=bottom,waterlogged=true]: minecraft:waxed_cut_copper_slab[type=bottom,waterlogged=true] + minecraft:cut_copper_slab[type=top,waterlogged=true]: minecraft:waxed_cut_copper_slab[type=top,waterlogged=true] + minecraft:cut_copper_slab[type=double,waterlogged=true]: minecraft:waxed_cut_copper_slab[type=double,waterlogged=true] + minecraft:exposed_cut_copper_slab[type=bottom,waterlogged=false]: minecraft:waxed_exposed_cut_copper_slab[type=bottom,waterlogged=false] + minecraft:exposed_cut_copper_slab[type=top,waterlogged=false]: minecraft:waxed_exposed_cut_copper_slab[type=top,waterlogged=false] + minecraft:exposed_cut_copper_slab[type=double,waterlogged=false]: minecraft:waxed_exposed_cut_copper_slab[type=double,waterlogged=false] + minecraft:exposed_cut_copper_slab[type=bottom,waterlogged=true]: minecraft:waxed_exposed_cut_copper_slab[type=bottom,waterlogged=true] + minecraft:exposed_cut_copper_slab[type=top,waterlogged=true]: minecraft:waxed_exposed_cut_copper_slab[type=top,waterlogged=true] + minecraft:exposed_cut_copper_slab[type=double,waterlogged=true]: minecraft:waxed_exposed_cut_copper_slab[type=double,waterlogged=true] + minecraft:weathered_cut_copper_slab[type=bottom,waterlogged=false]: minecraft:waxed_weathered_cut_copper_slab[type=bottom,waterlogged=false] + minecraft:weathered_cut_copper_slab[type=top,waterlogged=false]: minecraft:waxed_weathered_cut_copper_slab[type=top,waterlogged=false] + minecraft:weathered_cut_copper_slab[type=double,waterlogged=false]: minecraft:waxed_weathered_cut_copper_slab[type=double,waterlogged=false] + minecraft:weathered_cut_copper_slab[type=bottom,waterlogged=true]: minecraft:waxed_weathered_cut_copper_slab[type=bottom,waterlogged=true] + minecraft:weathered_cut_copper_slab[type=top,waterlogged=true]: minecraft:waxed_weathered_cut_copper_slab[type=top,waterlogged=true] + minecraft:weathered_cut_copper_slab[type=double,waterlogged=true]: minecraft:waxed_weathered_cut_copper_slab[type=double,waterlogged=true] + minecraft:oxidized_cut_copper_slab[type=bottom,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_slab[type=bottom,waterlogged=false] + minecraft:oxidized_cut_copper_slab[type=top,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_slab[type=top,waterlogged=false] + minecraft:oxidized_cut_copper_slab[type=double,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_slab[type=double,waterlogged=false] + minecraft:oxidized_cut_copper_slab[type=bottom,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_slab[type=bottom,waterlogged=true] + minecraft:oxidized_cut_copper_slab[type=top,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_slab[type=top,waterlogged=true] + minecraft:oxidized_cut_copper_slab[type=double,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_slab[type=double,waterlogged=true] + + #### Stairs #### + minecraft:cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=false] + minecraft:cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=true] + minecraft:cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=false] + minecraft:cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=true] + minecraft:cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=false] + minecraft:cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=true] + minecraft:cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=false] + minecraft:cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=true] + minecraft:cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=false] + minecraft:cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=true] + minecraft:cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=false] + minecraft:cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=true] + minecraft:cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=false] + minecraft:cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=true] + minecraft:cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=false] + minecraft:cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=true] + minecraft:cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=false] + minecraft:cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=true] + minecraft:cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=false] + minecraft:cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=true] + minecraft:cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=false] + minecraft:cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=true] + minecraft:cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=false] + minecraft:cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=true] + minecraft:cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=false] + minecraft:cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=true] + minecraft:cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=false] + minecraft:cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=true] + minecraft:cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=false] + minecraft:cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=true] + minecraft:cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=false] + minecraft:cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=true] + minecraft:cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=false] + minecraft:cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=true] + minecraft:cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=false] + minecraft:cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=true] + minecraft:cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=false] + minecraft:cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=true] + minecraft:cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=false] + minecraft:cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=true] + minecraft:cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=false] + minecraft:cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=true] + minecraft:cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=false] + minecraft:cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=true] + minecraft:cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=false] + minecraft:cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=true] + minecraft:cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=false] + minecraft:cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=true] + minecraft:cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=false] + minecraft:cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=true] + minecraft:cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=false] + minecraft:cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=true] + minecraft:cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=false] + minecraft:cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=true] + minecraft:cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=false] + minecraft:cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=true] + minecraft:cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=false] + minecraft:cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=true] + minecraft:cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=false] + minecraft:cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=true] + minecraft:cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=false] + minecraft:cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=true] + minecraft:cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=false] + minecraft:cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=true] + minecraft:cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=false] + minecraft:cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=true] + minecraft:cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=false] + minecraft:cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=true] + minecraft:cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=false] + minecraft:cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=true] + minecraft:cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=false] + minecraft:cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=true] + minecraft:cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=false] + minecraft:cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=true] + minecraft:cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=false] + minecraft:cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=true] + minecraft:cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=false] + minecraft:cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=true] + minecraft:cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=false] + minecraft:cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=true] + minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=false] + minecraft:exposed_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_exposed_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=true] + minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=false] + minecraft:weathered_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_weathered_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=straight,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=inner_left,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=inner_right,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=outer_left,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=bottom,shape=outer_right,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=straight,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=inner_left,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=inner_right,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=outer_left,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=east,half=top,shape=outer_right,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=straight,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=inner_left,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=inner_right,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=outer_left,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=bottom,shape=outer_right,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=straight,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=inner_left,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=inner_right,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=outer_left,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=south,half=top,shape=outer_right,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=straight,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=inner_left,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=inner_right,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=outer_left,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=bottom,shape=outer_right,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=straight,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=inner_left,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=inner_right,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=outer_left,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=west,half=top,shape=outer_right,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=straight,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=inner_left,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=inner_right,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=outer_left,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=bottom,shape=outer_right,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=straight,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=inner_left,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=inner_right,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=outer_left,waterlogged=true] + minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=false]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=false] + minecraft:oxidized_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=true]: minecraft:waxed_oxidized_cut_copper_stairs[facing=north,half=top,shape=outer_right,waterlogged=true] + + #### Grate #### + # Suitable for making glass because it is completely transparent + $$>=1.20.3#grate: + minecraft:copper_grate[waterlogged=false]: minecraft:waxed_copper_grate[waterlogged=false] + minecraft:copper_grate[waterlogged=true]: minecraft:waxed_copper_grate[waterlogged=true] + minecraft:weathered_copper_grate[waterlogged=false]: minecraft:waxed_weathered_copper_grate[waterlogged=false] + minecraft:weathered_copper_grate[waterlogged=true]: minecraft:waxed_weathered_copper_grate[waterlogged=true] + minecraft:exposed_copper_grate[waterlogged=false]: minecraft:waxed_exposed_copper_grate[waterlogged=false] + minecraft:exposed_copper_grate[waterlogged=true]: minecraft:waxed_exposed_copper_grate[waterlogged=true] + minecraft:oxidized_copper_grate[waterlogged=false]: minecraft:waxed_oxidized_copper_grate[waterlogged=false] + minecraft:oxidized_copper_grate[waterlogged=true]: minecraft:waxed_oxidized_copper_grate[waterlogged=true] + + #### Pressure Plate #### + # Triggered pressure plates appear identical, even though they output different signal strengths. + minecraft:light_weighted_pressure_plate[power=2]: minecraft:light_weighted_pressure_plate[power=1] + minecraft:light_weighted_pressure_plate[power=3]: minecraft:light_weighted_pressure_plate[power=1] + minecraft:light_weighted_pressure_plate[power=4]: minecraft:light_weighted_pressure_plate[power=1] + minecraft:light_weighted_pressure_plate[power=5]: minecraft:light_weighted_pressure_plate[power=1] + minecraft:light_weighted_pressure_plate[power=6]: minecraft:light_weighted_pressure_plate[power=1] + minecraft:light_weighted_pressure_plate[power=7]: minecraft:light_weighted_pressure_plate[power=1] + minecraft:light_weighted_pressure_plate[power=8]: minecraft:light_weighted_pressure_plate[power=1] + minecraft:light_weighted_pressure_plate[power=9]: minecraft:light_weighted_pressure_plate[power=1] + minecraft:light_weighted_pressure_plate[power=10]: minecraft:light_weighted_pressure_plate[power=1] + minecraft:light_weighted_pressure_plate[power=11]: minecraft:light_weighted_pressure_plate[power=1] + minecraft:light_weighted_pressure_plate[power=12]: minecraft:light_weighted_pressure_plate[power=1] + minecraft:light_weighted_pressure_plate[power=13]: minecraft:light_weighted_pressure_plate[power=1] + minecraft:light_weighted_pressure_plate[power=14]: minecraft:light_weighted_pressure_plate[power=1] + minecraft:light_weighted_pressure_plate[power=15]: minecraft:light_weighted_pressure_plate[power=1] + minecraft:heavy_weighted_pressure_plate[power=2]: minecraft:heavy_weighted_pressure_plate[power=1] + minecraft:heavy_weighted_pressure_plate[power=3]: minecraft:heavy_weighted_pressure_plate[power=1] + minecraft:heavy_weighted_pressure_plate[power=4]: minecraft:heavy_weighted_pressure_plate[power=1] + minecraft:heavy_weighted_pressure_plate[power=5]: minecraft:heavy_weighted_pressure_plate[power=1] + minecraft:heavy_weighted_pressure_plate[power=6]: minecraft:heavy_weighted_pressure_plate[power=1] + minecraft:heavy_weighted_pressure_plate[power=7]: minecraft:heavy_weighted_pressure_plate[power=1] + minecraft:heavy_weighted_pressure_plate[power=8]: minecraft:heavy_weighted_pressure_plate[power=1] + minecraft:heavy_weighted_pressure_plate[power=9]: minecraft:heavy_weighted_pressure_plate[power=1] + minecraft:heavy_weighted_pressure_plate[power=10]: minecraft:heavy_weighted_pressure_plate[power=1] + minecraft:heavy_weighted_pressure_plate[power=11]: minecraft:heavy_weighted_pressure_plate[power=1] + minecraft:heavy_weighted_pressure_plate[power=12]: minecraft:heavy_weighted_pressure_plate[power=1] + minecraft:heavy_weighted_pressure_plate[power=13]: minecraft:heavy_weighted_pressure_plate[power=1] + minecraft:heavy_weighted_pressure_plate[power=14]: minecraft:heavy_weighted_pressure_plate[power=1] + minecraft:heavy_weighted_pressure_plate[power=15]: minecraft:heavy_weighted_pressure_plate[power=1] + + #### Corals #### + # Coral blocks are ideal for creating water blocks or wall-mounted blocks. But you have to sacrifice its dry appearance. + # minecraft:dead_brain_coral[waterlogged=false]: minecraft:brain_coral[waterlogged=false] + # minecraft:dead_brain_coral[waterlogged=true]: minecraft:brain_coral[waterlogged=true] + # minecraft:dead_brain_coral_fan[waterlogged=false]: minecraft:brain_coral_fan[waterlogged=false] + # minecraft:dead_brain_coral_fan[waterlogged=true]: minecraft:brain_coral_fan[waterlogged=true] + # minecraft:dead_brain_coral_wall_fan[waterlogged=false,facing=east]: minecraft:brain_coral_wall_fan[waterlogged=false,facing=east] + # minecraft:dead_brain_coral_wall_fan[waterlogged=false,facing=north]: minecraft:brain_coral_wall_fan[waterlogged=false,facing=north] + # minecraft:dead_brain_coral_wall_fan[waterlogged=false,facing=south]: minecraft:brain_coral_wall_fan[waterlogged=false,facing=south] + # minecraft:dead_brain_coral_wall_fan[waterlogged=false,facing=west]: minecraft:brain_coral_wall_fan[waterlogged=false,facing=west] + # minecraft:dead_brain_coral_wall_fan[waterlogged=true,facing=east]: minecraft:brain_coral_wall_fan[waterlogged=true,facing=east] + # minecraft:dead_brain_coral_wall_fan[waterlogged=true,facing=north]: minecraft:brain_coral_wall_fan[waterlogged=true,facing=north] + # minecraft:dead_brain_coral_wall_fan[waterlogged=true,facing=south]: minecraft:brain_coral_wall_fan[waterlogged=true,facing=south] + # minecraft:dead_brain_coral_wall_fan[waterlogged=true,facing=west]: minecraft:brain_coral_wall_fan[waterlogged=true,facing=west] + # minecraft:dead_bubble_coral[waterlogged=false]: minecraft:bubble_coral[waterlogged=false] + # minecraft:dead_bubble_coral[waterlogged=true]: minecraft:bubble_coral[waterlogged=true] + # minecraft:dead_bubble_coral_fan[waterlogged=false]: minecraft:bubble_coral_fan[waterlogged=false] + # minecraft:dead_bubble_coral_fan[waterlogged=true]: minecraft:bubble_coral_fan[waterlogged=true] + # minecraft:dead_bubble_coral_wall_fan[waterlogged=false,facing=east]: minecraft:bubble_coral_wall_fan[waterlogged=false,facing=east] + # minecraft:dead_bubble_coral_wall_fan[waterlogged=false,facing=north]: minecraft:bubble_coral_wall_fan[waterlogged=false,facing=north] + # minecraft:dead_bubble_coral_wall_fan[waterlogged=false,facing=south]: minecraft:bubble_coral_wall_fan[waterlogged=false,facing=south] + # minecraft:dead_bubble_coral_wall_fan[waterlogged=false,facing=west]: minecraft:bubble_coral_wall_fan[waterlogged=false,facing=west] + # minecraft:dead_bubble_coral_wall_fan[waterlogged=true,facing=east]: minecraft:bubble_coral_wall_fan[waterlogged=true,facing=east] + # minecraft:dead_bubble_coral_wall_fan[waterlogged=true,facing=north]: minecraft:bubble_coral_wall_fan[waterlogged=true,facing=north] + # minecraft:dead_bubble_coral_wall_fan[waterlogged=true,facing=south]: minecraft:bubble_coral_wall_fan[waterlogged=true,facing=south] + # minecraft:dead_bubble_coral_wall_fan[waterlogged=true,facing=west]: minecraft:bubble_coral_wall_fan[waterlogged=true,facing=west] + # minecraft:dead_fire_coral[waterlogged=false]: minecraft:fire_coral[waterlogged=false] + # minecraft:dead_fire_coral[waterlogged=true]: minecraft:fire_coral[waterlogged=true] + # minecraft:dead_fire_coral_fan[waterlogged=false]: minecraft:fire_coral_fan[waterlogged=false] + # minecraft:dead_fire_coral_fan[waterlogged=true]: minecraft:fire_coral_fan[waterlogged=true] + # minecraft:dead_fire_coral_wall_fan[waterlogged=false,facing=east]: minecraft:fire_coral_wall_fan[waterlogged=false,facing=east] + # minecraft:dead_fire_coral_wall_fan[waterlogged=false,facing=north]: minecraft:fire_coral_wall_fan[waterlogged=false,facing=north] + # minecraft:dead_fire_coral_wall_fan[waterlogged=false,facing=south]: minecraft:fire_coral_wall_fan[waterlogged=false,facing=south] + # minecraft:dead_fire_coral_wall_fan[waterlogged=false,facing=west]: minecraft:fire_coral_wall_fan[waterlogged=false,facing=west] + # minecraft:dead_fire_coral_wall_fan[waterlogged=true,facing=east]: minecraft:fire_coral_wall_fan[waterlogged=true,facing=east] + # minecraft:dead_fire_coral_wall_fan[waterlogged=true,facing=north]: minecraft:fire_coral_wall_fan[waterlogged=true,facing=north] + # minecraft:dead_fire_coral_wall_fan[waterlogged=true,facing=south]: minecraft:fire_coral_wall_fan[waterlogged=true,facing=south] + # minecraft:dead_fire_coral_wall_fan[waterlogged=true,facing=west]: minecraft:fire_coral_wall_fan[waterlogged=true,facing=west] + # minecraft:dead_horn_coral[waterlogged=false]: minecraft:horn_coral[waterlogged=false] + # minecraft:dead_horn_coral[waterlogged=true]: minecraft:horn_coral[waterlogged=true] + # minecraft:dead_horn_coral_fan[waterlogged=false]: minecraft:horn_coral_fan[waterlogged=false] + # minecraft:dead_horn_coral_fan[waterlogged=true]: minecraft:horn_coral_fan[waterlogged=true] + # minecraft:dead_horn_coral_wall_fan[waterlogged=false,facing=east]: minecraft:horn_coral_wall_fan[waterlogged=false,facing=east] + # minecraft:dead_horn_coral_wall_fan[waterlogged=false,facing=north]: minecraft:horn_coral_wall_fan[waterlogged=false,facing=north] + # minecraft:dead_horn_coral_wall_fan[waterlogged=false,facing=south]: minecraft:horn_coral_wall_fan[waterlogged=false,facing=south] + # minecraft:dead_horn_coral_wall_fan[waterlogged=false,facing=west]: minecraft:horn_coral_wall_fan[waterlogged=false,facing=west] + # minecraft:dead_horn_coral_wall_fan[waterlogged=true,facing=east]: minecraft:horn_coral_wall_fan[waterlogged=true,facing=east] + # minecraft:dead_horn_coral_wall_fan[waterlogged=true,facing=north]: minecraft:horn_coral_wall_fan[waterlogged=true,facing=north] + # minecraft:dead_horn_coral_wall_fan[waterlogged=true,facing=south]: minecraft:horn_coral_wall_fan[waterlogged=true,facing=south] + # minecraft:dead_horn_coral_wall_fan[waterlogged=true,facing=west]: minecraft:horn_coral_wall_fan[waterlogged=true,facing=west] + # minecraft:dead_tube_coral[waterlogged=false]: minecraft:tube_coral[waterlogged=false] + # minecraft:dead_tube_coral[waterlogged=true]: minecraft:tube_coral[waterlogged=true] + # minecraft:dead_tube_coral_fan[waterlogged=false]: minecraft:tube_coral_fan[waterlogged=false] + # minecraft:dead_tube_coral_fan[waterlogged=true]: minecraft:tube_coral_fan[waterlogged=true] + # minecraft:dead_tube_coral_wall_fan[waterlogged=false,facing=east]: minecraft:tube_coral_wall_fan[waterlogged=false,facing=east] + # minecraft:dead_tube_coral_wall_fan[waterlogged=false,facing=north]: minecraft:tube_coral_wall_fan[waterlogged=false,facing=north] + # minecraft:dead_tube_coral_wall_fan[waterlogged=false,facing=south]: minecraft:tube_coral_wall_fan[waterlogged=false,facing=south] + # minecraft:dead_tube_coral_wall_fan[waterlogged=false,facing=west]: minecraft:tube_coral_wall_fan[waterlogged=false,facing=west] + # minecraft:dead_tube_coral_wall_fan[waterlogged=true,facing=east]: minecraft:tube_coral_wall_fan[waterlogged=true,facing=east] + # minecraft:dead_tube_coral_wall_fan[waterlogged=true,facing=north]: minecraft:tube_coral_wall_fan[waterlogged=true,facing=north] + # minecraft:dead_tube_coral_wall_fan[waterlogged=true,facing=south]: minecraft:tube_coral_wall_fan[waterlogged=true,facing=south] + # minecraft:dead_tube_coral_wall_fan[waterlogged=true,facing=west]: minecraft:tube_coral_wall_fan[waterlogged=true,facing=west] + + #### Chorus Plant #### + # Chorus Plant does support transparent textures, but man... its hitbox is super weird. You're probably better off using leaves. + # minecraft:chorus_plant[down=false,east=false,north=false,south=false,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=false,north=false,south=false,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=true,north=false,south=false,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=true,north=false,south=false,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=false,north=true,south=false,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=false,north=true,south=false,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=true,north=true,south=false,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=true,north=true,south=false,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=false,north=false,south=true,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=false,north=false,south=true,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=true,north=false,south=true,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=true,north=false,south=true,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=false,north=true,south=true,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=false,north=true,south=true,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=true,north=true,south=true,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=false,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=false,north=false,south=false,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=false,north=false,south=false,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=true,north=false,south=false,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=true,north=false,south=false,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=false,north=true,south=false,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=false,north=true,south=false,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=true,north=true,south=false,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=true,north=true,south=false,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=false,north=false,south=true,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=false,north=false,south=true,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=true,north=false,south=true,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=true,north=false,south=true,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=false,north=true,south=true,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=false,north=true,south=true,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=true,north=true,south=true,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=false]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=false,north=false,south=false,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=false,north=false,south=false,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=true,north=false,south=false,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=true,north=false,south=false,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=false,north=true,south=false,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=false,north=true,south=false,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=true,north=true,south=false,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=true,north=true,south=false,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=false,north=false,south=true,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=false,north=false,south=true,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=true,north=false,south=true,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=true,north=false,south=true,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=false,north=true,south=true,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=false,north=true,south=true,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=true,north=true,south=true,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=false,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=false,north=false,south=false,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=false,north=false,south=false,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=true,north=false,south=false,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=true,north=false,south=false,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=false,north=true,south=false,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=false,north=true,south=false,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=true,north=true,south=false,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=true,north=true,south=false,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=false,north=false,south=true,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=false,north=false,south=true,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=true,north=false,south=true,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=true,north=false,south=true,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=false,north=true,south=true,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=true,east=false,north=true,south=true,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] + # minecraft:chorus_plant[down=false,east=true,north=true,south=true,up=true,west=true]: minecraft:chorus_plant[down=true,east=true,north=true,south=true,up=true,west=true] \ No newline at end of file diff --git a/common-files/src/main/resources/resources/internal/configuration/offset_chars.yml b/common-files/src/main/resources/resources/internal/configuration/offset_chars.yml index f315f2571..4857eb238 100644 --- a/common-files/src/main/resources/resources/internal/configuration/offset_chars.yml +++ b/common-files/src/main/resources/resources/internal/configuration/offset_chars.yml @@ -2,264 +2,264 @@ images: internal:neg_1: height: -3 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf800 internal:neg_2: height: -4 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf801 internal:neg_3: height: -5 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf802 internal:neg_4: height: -6 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf803 internal:neg_5: height: -7 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf804 internal:neg_6: height: -8 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf805 internal:neg_7: height: -9 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf806 internal:neg_8: height: -10 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf807 internal:neg_9: height: -11 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf808 internal:neg_10: height: -12 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf809 internal:neg_11: height: -13 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf80a internal:neg_12: height: -14 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf80b internal:neg_13: height: -15 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf80c internal:neg_14: height: -16 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf80d internal:neg_15: height: -17 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf80e internal:neg_16: height: -18 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf80f internal:neg_24: height: -26 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf810 internal:neg_32: height: -34 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf811 internal:neg_48: height: -50 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf812 internal:neg_64: height: -66 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf813 internal:neg_128: height: -130 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf814 internal:neg_256: height: -258 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf815 internal:pos_1: height: -1 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf830 internal:pos_2: height: 1 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf831 internal:pos_3: height: 2 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf832 internal:pos_4: height: 3 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf833 internal:pos_5: height: 4 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf834 internal:pos_6: height: 5 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf835 internal:pos_7: height: 6 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf836 internal:pos_8: height: 7 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf837 internal:pos_9: height: 8 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf838 internal:pos_10: height: 9 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf839 internal:pos_11: height: 10 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf83a internal:pos_12: height: 11 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf83b internal:pos_13: height: 12 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf83c internal:pos_14: height: 13 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf83d internal:pos_15: height: 14 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf83e internal:pos_16: height: 15 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf83f internal:pos_24: height: 23 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf840 internal:pos_32: height: 31 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf841 internal:pos_48: height: 47 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf842 internal:pos_64: height: 63 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf843 internal:pos_128: height: 127 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf844 internal:pos_256: height: 255 ascent: -5000 - font: minecraft:offset_chars + font: minecraft:default file: minecraft:font/offset/space_split.png char: \uf845 \ No newline at end of file diff --git a/common-files/src/main/resources/translations/de.yml b/common-files/src/main/resources/translations/de.yml index 3e3ffe8b1..b3e57e754 100644 --- a/common-files/src/main/resources/translations/de.yml +++ b/common-files/src/main/resources/translations/de.yml @@ -115,7 +115,7 @@ warning.config.image.missing_file: "Problem in Datei gefunden - warning.config.image.invalid_file_chars: "Problem in Datei gefunden - Das Image '' hat ein 'file'-Argument '', das ungültige Zeichen enthält. Bitte lies https://minecraft.wiki/w/Resource_location#Legal_characters." warning.config.image.invalid_font_chars: "Problem in Datei gefunden - Das Image '' hat ein 'font'-Argument '', das ungültige Zeichen enthält. Bitte lies https://minecraft.wiki/w/Resource_location#Legal_characters." warning.config.image.missing_char: "Problem in Datei gefunden - Beim Image '' fehlt das erforderliche 'char'-Argument." -warning.config.image.codepoint_conflict: "Problem in Datei gefunden - Das Image '' verwendet ein Zeichen '()' im Font , das bereits von einem anderen Image '' verwendet wird." +warning.config.image.codepoint.conflict: "Problem in Datei gefunden - Das Image '' verwendet ein Zeichen '()' im Font , das bereits von einem anderen Image '' verwendet wird." warning.config.image.invalid_codepoint_grid: "Problem in Datei gefunden - Image '' hat ein ungültiges 'chars' Codepoint-Grid." warning.config.image.invalid_char: "Problem in Datei gefunden - Image '' hat einen char-Parameter, der kombinierende Zeichen enthält, was zur Aufteilung des Bildes führen kann." warning.config.image.invalid_hex_value: "Problem in Datei gefunden - Das Image '' verwendet ein Unicode-Zeichen '', das kein gültiger hexadezimaler (Basis 16) Wert ist." @@ -184,7 +184,7 @@ warning.config.item.invalid_material: "Problem in Datei gefunden warning.config.item.invalid_custom_model_data: "Problem in Datei gefunden - Das Item '' verwendet eine negative Custom-Model-Data '', was ungültig ist." warning.config.item.bad_custom_model_data: "Problem in Datei gefunden - Das Item '' verwendet eine Custom-Model-Data '', die zu groß ist. Es wird empfohlen, einen Wert unter 16.777.216 zu verwenden." warning.config.item.item_model.conflict: "Problem in Datei gefunden - Das Item '' verwendet eine ungültige 'item-model'-Option, da dieses Item-Model bereits von einem Vanilla-Item belegt ist." -warning.config.item.custom_model_data_conflict: "Problem in Datei gefunden - Das Item '' verwendet eine Custom-Model-Data '', die bereits von Item '' belegt ist." +warning.config.item.custom_model_data.conflict: "Problem in Datei gefunden - Das Item '' verwendet eine Custom-Model-Data '', die bereits von Item '' belegt ist." warning.config.item.invalid_component: "Problem in Datei gefunden - Das Item '' verwendet einen nicht existierenden Component-Typ ''." warning.config.item.missing_model_id: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'custom-model-data'- oder 'item-model'-Argument." warning.config.item.missing_model: "Problem in Datei gefunden - Beim Item '' fehlt der erforderliche 'model'-Abschnitt für die Unterstützung von Resource Packs ab 1.21.4+." @@ -252,19 +252,14 @@ warning.config.block.state.property.missing_type: "Problem in Datei Problem in Datei gefunden - Der Block '' verwendet das ungültige Typ-Argument '' für die Property ''." warning.config.block.state.property.integer.invalid_range: "Problem in Datei gefunden - Der Block '' verwendet das ungültige 'range'-Argument '' für die Integer-Property ''. Korrekte Syntax: 1~2." warning.config.block.state.property.invalid_format: "Problem in Datei gefunden - Der Block '' verwendet ein ungültiges Block-State-Property-Format ''." -warning.config.block.state.missing_real_id: "Problem in Datei gefunden - Beim Block '' fehlt das erforderliche 'id'-Argument für 'state'. 'id' ist die serverseitige Block-ID, die für jeden Block-State-Typ eindeutig ist. Wenn du einen serverseitigen Block mit 'note_block' und ID 30 erstellst, wäre die echte Block-ID 'craftengine:note_block_30'." warning.config.block.state.missing_state: "Problem in Datei gefunden - Beim Block '' fehlt das erforderliche 'state'-Argument für 'state'." warning.config.block.state.missing_properties: "Problem in Datei gefunden - Beim Block '' fehlt der erforderliche 'properties'-Abschnitt für 'states'." warning.config.block.state.missing_appearances: "Problem in Datei gefunden - Beim Block '' fehlt der erforderliche 'appearances'-Abschnitt für 'states'." -warning.config.block.state.missing_variants: "Problem in Datei gefunden - Beim Block '' fehlt der erforderliche 'variants'-Abschnitt für 'states'." -warning.config.block.state.variant.missing_appearance: "Problem in Datei gefunden - Beim Block '' fehlt das erforderliche 'appearance'-Argument für die Variante ''." warning.config.block.state.variant.invalid_appearance: "Problem in Datei gefunden - Der Block '' hat einen Fehler, dass die Variante '' eine nicht existierende Appearance '' verwendet." warning.config.block.state.invalid_vanilla: "Problem in Datei gefunden - Der Block '' verwendet einen ungültigen Vanilla-Block-State ''." -warning.config.block.state.unavailable_vanilla: "Problem in Datei gefunden - Der Block '' verwendet einen nicht verfügbaren Vanilla-Block-State ''. Bitte gib diesen State in der mappings.yml frei." +warning.config.block.state.unavailable_vanilla: "Problem in Datei gefunden - Der Block '' verwendet einen nicht verfügbaren Vanilla-Block-State ''. Bitte gib diesen State in der block-state-mappings frei." warning.config.block.state.invalid_vanilla_id: "Problem in Datei gefunden - Der Block '' verwendet einen Vanilla-Block-State '', der den verfügbaren Slot-Bereich '0~' überschreitet." -warning.config.block.state.conflict: "Problem in Datei gefunden - Der Block '' verwendet einen Vanilla-Block-State '', der bereits von '' belegt ist." -warning.config.block.state.bind_failed: "Problem in Datei gefunden - Der Block '' konnte den echten Block-State für '' nicht binden, da der State bereits von '' belegt ist." -warning.config.block.state.invalid_real_id: "Problem in Datei gefunden - Der Block '' verwendet einen echten Block-State '', der den verfügbaren Slot-Bereich '0~' überschreitet. Erwäge, weitere echte States in 'additional-real-blocks.yml' hinzuzufügen, wenn die Slots aufgebraucht sind." +warning.config.block.state.id.conflict: "Problem in Datei gefunden - Der Block '' konnte den echten Block-State für '' nicht binden, da der State bereits von '' belegt ist." warning.config.block.state.model.missing_path: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'path'-Option für 'model'." warning.config.block.state.model.invalid_path: "Problem in Datei gefunden - Der Block '' hat ein 'path'-Argument '', das ungültige Zeichen enthält. Bitte lies https://minecraft.wiki/w/Resource_location#Legal_characters." warning.config.block.state.model.conflict: "Problem in Datei gefunden - Der Block '' versucht, das Model '' an den Block-State '' zu binden, der bereits an das Model '' gebunden ist." @@ -273,7 +268,7 @@ warning.config.block.behavior.missing_type: "Problem in Datei ge warning.config.block.behavior.invalid_type: "Problem in Datei gefunden - Der Block '' verwendet einen ungültigen Block-Behavior-Typ ''." warning.config.block.behavior.concrete.missing_solid: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'solid-block'-Option für das 'concrete_block'-Behavior." warning.config.block.behavior.crop.missing_age: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'age'-Property für das 'crop_block'-Behavior." -warning.config.block.behavior.sugar_cane.missing_age: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'age'-Property für das 'sugar_cane_block'-Behavior." +warning.config.block.behavior.vertical_crop.missing_age: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'age'-Property für das 'vertical_crop_block'-Behavior." warning.config.block.behavior.leaves.missing_persistent: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'persistent'-Property für das 'leaves_block'-Behavior." warning.config.block.behavior.leaves.missing_distance: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'distance'-Property für das 'leaves_block'-Behavior." warning.config.block.behavior.lamp.missing_lit: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'lit'-Property für das 'lamp_block'-Behavior." @@ -412,7 +407,7 @@ warning.config.selector.invalid_target: "Problem in Datei gefund warning.config.resource_pack.item_model.already_exist: "Generierung des Item-Models für '' fehlgeschlagen, da die Datei '' bereits existiert." warning.config.resource_pack.model.generation.already_exist: "Generierung des Models fehlgeschlagen, da die Model-Datei '' bereits existiert." warning.config.resource_pack.generation.missing_font_texture: "Beim Font '' fehlt die erforderliche Textur: ''" -warning.config.resource_pack.generation.texture_not_in_atlas: "Textur '' ist nicht im Atlas aufgeführt. Du musst den Texturpfad zum Atlas hinzufügen oder die 'obfuscation'-Option in der config.yml aktivieren." +warning.config.resource_pack.generation.texture_not_in_atlas: "Textur '' ist nicht im Atlas aufgeführt. Du musst den Texturpfad zum Atlas hinzufügen oder die 'fix-atlas'-Option in der config.yml aktivieren." warning.config.resource_pack.generation.missing_model_texture: "Beim Model '' fehlt die Textur ''" warning.config.resource_pack.generation.missing_item_model: "Beim Item '' fehlt die Model-Datei: ''" warning.config.resource_pack.generation.missing_block_model: "Beim Block-State '' fehlt die Model-Datei: ''" diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index efd506c7d..2c0551596 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -63,7 +63,8 @@ command.upload.failure.not_supported: "Current hosting method '' doe command.upload.on_progress: "Started uploading progress. Check the console for more information." command.send_resource_pack.success.single: "Sent resource pack to ." command.send_resource_pack.success.multiple: "Send resource packs to players." -warning.config.pack.duplicated_files: "Duplicated files Found. Please resolve them through config.yml 'resource-pack.duplicated-files-handler' section." +warning.network.resource_pack.unverified_uuid: "Player is attempting to request a resource pack using a UUID () that is not authenticated by the server." +warning.config.pack.duplicated_files: "Duplicated files Found. Please resolve them through config.yml 'resource-pack.duplicated-files-handler' section." warning.config.yaml.duplicated_key: "Issue found in file - Found duplicated key '' at line , this might cause unexpected results." warning.config.yaml.inconsistent_value_type: "Issue found in file - Found duplicated key '' at line with different value types, this might cause unexpected results." warning.config.type.int: "Issue found in file - Failed to load '': Cannot cast '' to integer type for option ''." @@ -72,6 +73,7 @@ warning.config.type.float: "Issue found in file - Failed to load warning.config.type.double: "Issue found in file - Failed to load '': Cannot cast '' to double type for option ''." warning.config.type.quaternionf: "Issue found in file - Failed to load '': Cannot cast '' to Quaternionf type for option ''." warning.config.type.vector3f: "Issue found in file - Failed to load '': Cannot cast '' to Vector3f type for option ''." +warning.config.type.vec3d: "Issue found in file - Failed to load '': Cannot cast '' to Vec3d type for option ''." warning.config.type.map: "Issue found in file - Failed to load '': Cannot cast '' to Map type for option ''." warning.config.type.snbt.invalid_syntax: "Issue found in file - Failed to load '': Invalid snbt syntax ''." warning.config.number.missing_type: "Issue found in file - The config '' is missing the required 'type' argument for number argument." @@ -85,6 +87,8 @@ warning.config.number.uniform.missing_min: "Issue found in file warning.config.number.uniform.missing_max: "Issue found in file - The config '' is missing the required 'max' argument for 'uniform' number." warning.config.number.gaussian.missing_min: "Issue found in file - The config '' is missing the required 'min' argument for 'gaussian' number." warning.config.number.gaussian.missing_max: "Issue found in file - The config '' is missing the required 'max' argument for 'gaussian' number." +warning.config.number.binomial.missing_extra: "Issue found in file - The config '' is missing the required 'extra' argument for 'binomial' number." +warning.config.number.binomial.missing_probability: "Issue found in file - The config '' is missing the required 'probability' argument for 'binomial' number." warning.config.condition.all_of.missing_terms: "Issue found in file - The config '' is missing the required 'terms' argument for 'all_of' condition." warning.config.condition.all_of.invalid_terms_type: "Issue found in file - The config '' has a misconfigured 'all_of' condition, 'terms' should be a map list, current type: ''." warning.config.condition.any_of.missing_terms: "Issue found in file - The config '' is missing the required 'terms' argument for 'any_of' condition." @@ -118,11 +122,14 @@ warning.config.image.height_ascent_conflict: "Issue found in file Issue found in file - The image '' is missing the required 'file' argument." warning.config.image.invalid_file_chars: "Issue found in file - The image '' has a 'file' argument '' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." warning.config.image.invalid_font_chars: "Issue found in file - The image '' has a 'font' argument '' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.image.invalid_grid_size: "Issue found in file - The image '' is using an incorrect grid size format ''. Correct example: '3,5'" warning.config.image.missing_char: "Issue found in file - The image '' is missing the required 'char' argument." -warning.config.image.codepoint_conflict: "Issue found in file - The image '' is using a character '()' in font that has been used by another image ''." +warning.config.image.codepoint.conflict: "Issue found in file - The image '' is using a character '()' in font that has been used by another image ''." +warning.config.image.codepoint.exhausted: "Issue found in file - Cannot allocate codepoint for image '' as the codepoints have already been exhausted for font ''." warning.config.image.invalid_codepoint_grid: "Issue found in file - Image '' has an invalid 'chars' codepoint grid." warning.config.image.invalid_char: "Issue found in file - Image '' has a char parameter containing combining characters, which may result in image splitting." warning.config.image.invalid_hex_value: "Issue found in file - The image '' is using a unicode character '' that is not a valid hexadecimal (radix 16) value." +warning.config.image.invalid_unicode_string: "Issue found in file - The image '' is using an incorrect unicode string ''." warning.config.recipe.duplicate: "Issue found in file - Duplicated recipe ''. Please check if there is the same configuration in other files." warning.config.recipe.missing_type: "Issue found in file - The recipe '' is missing the required 'type' argument." warning.config.recipe.invalid_type: "Issue found in file - The recipe '' is using an invalid recipe type ''." @@ -188,13 +195,15 @@ warning.config.item.invalid_material: "Issue found in file - The warning.config.item.invalid_custom_model_data: "Issue found in file - The item '' is using a negative custom model data '' which is invalid." warning.config.item.bad_custom_model_data: "Issue found in file - The item '' is using a custom model data '' that is too large. It's recommended to use a value lower than 16,777,216." warning.config.item.item_model.conflict: "Issue found in file - The item '' is using an invalid 'item-model' option because this item model has been occupied by a vanilla item." -warning.config.item.custom_model_data_conflict: "Issue found in file - The item '' is using a custom model data '' that has been occupied by item ''." +warning.config.item.custom_model_data.conflict: "Issue found in file - The item '' is using a custom model data '' that has been occupied by item ''." +warning.config.item.custom_model_data.exhausted: "Issue found in file - Cannot allocate custom model data for item '' as the custom model data has already been exhausted for material ''." warning.config.item.invalid_component: "Issue found in file - The item '' is using a non-existing component type ''." warning.config.item.missing_model_id: "Issue found in file - The item '' is missing the required 'custom-model-data' or 'item-model' argument." warning.config.item.missing_model: "Issue found in file - The item '' is missing the required 'model' section for 1.21.4+ resource pack support." warning.config.item.behavior.missing_type: "Issue found in file - The item '' is missing the required 'type' argument for its item behavior." warning.config.item.behavior.invalid_type: "Issue found in file - The item '' is using an invalid item behavior type ''." warning.config.item.behavior.block.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'block_item' behavior." +warning.config.item.behavior.wall_block.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'wall_block_item' behavior." warning.config.item.behavior.furniture.missing_furniture: "Issue found in file - The item '' is missing the required 'furniture' argument for 'furniture_item' behavior." warning.config.item.behavior.liquid_collision.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'liquid_collision_block_item' behavior." warning.config.item.behavior.double_high.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'double_high_block_item' behavior." @@ -250,25 +259,31 @@ warning.config.item.model.special.head.missing_kind: "Issue found in fil warning.config.item.updater.missing_type: "Issue found in file - The item '' is missing the required 'type' argument for item updater." warning.config.item.updater.invalid_type: "Issue found in file - The item '' is using an invalid 'type' argument '' for item updater." warning.config.item.updater.transmute.missing_material: "Issue found in file - The item '' is missing the required 'material' argument for 'transmute' item updater." +warning.config.block_state_mapping.invalid_state: "Issue found in file - The config '' is using an invalid block state ''." +warning.config.block_state_mapping.conflict: "Issue found in file - The config '' is unable to map block state to block state because the state has already been mapped to ." warning.config.block.duplicate: "Issue found in file - Duplicated block ''. Please check if there is the same configuration in other files." warning.config.block.missing_state: "Issue found in file - The block '' is missing the required 'state' argument." warning.config.block.state.property.missing_type: "Issue found in file - The block '' is missing the required 'type' argument for property ''." warning.config.block.state.property.invalid_type: "Issue found in file - The block '' is using the invalid type argument '' for property ''." warning.config.block.state.property.integer.invalid_range: "Issue found in file - The block '' is using the invalid 'range' argument '' for integer property ''. Correct syntax: 1~2." warning.config.block.state.property.invalid_format: "Issue found in file - The block '' is using an invalid block state property format ''." -warning.config.block.state.missing_real_id: "Issue found in file - The block '' is missing the required 'id' argument for 'state'. 'id' is the serverside block id which is unique for each type of block state. If you create a serverside side block with 'note_block' and id 30, then the real block id would be 'craftengine:note_block_30'." warning.config.block.state.missing_state: "Issue found in file - The block '' is missing the required 'state' argument for 'state'." warning.config.block.state.missing_properties: "Issue found in file - The block '' is missing the required 'properties' section for 'states'." warning.config.block.state.missing_appearances: "Issue found in file - The block '' is missing the required 'appearances' section for 'states'." -warning.config.block.state.missing_variants: "Issue found in file - The block '' is missing the required 'variants' section for 'states'." -warning.config.block.state.variant.missing_appearance: "Issue found in file - The block '' is missing the required 'appearance' argument for variant ''." +warning.config.block.state.entity_renderer.invalid_type: "Issue found in file - The block '' is using an invalid entity renderer type ''." +warning.config.block.state.entity_renderer.item_display.missing_item: "Issue found in file - The block '' is missing the required 'item' argument for 'item_display' entity renderer." +warning.config.block.state.entity_renderer.text_display.missing_text: "Issue found in file - The block '' is missing the required 'text' argument for 'text_display' entity renderer." +warning.config.block.state.entity_renderer.better_model.missing_model: "Issue found in file - The block '' is missing the required 'model' argument for 'better_model' entity renderer." +warning.config.block.state.entity_renderer.model_engine.missing_model: "Issue found in file - The block '' is missing the required 'model' argument for 'model_engine' entity renderer." warning.config.block.state.variant.invalid_appearance: "Issue found in file - The block '' has an error that the variant '' is using a non-existing appearance ''." warning.config.block.state.invalid_vanilla: "Issue found in file - The block '' is using an invalid vanilla block state ''." -warning.config.block.state.unavailable_vanilla: "Issue found in file - The block '' is using an unavailable vanilla block state ''. Please free that state in mappings.yml." +warning.config.block.state.invalid_auto_state: "Issue found in file - The block '' is using an invalid auto-state ''. Allowed values: []." +warning.config.block.state.auto_state.exhausted: "Issue found in file - The visual state group '' has reached its maximum capacity of '' slots and cannot allocate a state for block ''." +warning.config.block.state.unavailable_vanilla: "Issue found in file - The block '' is using an unavailable vanilla block state ''. Please free that state in block-state-mappings." warning.config.block.state.invalid_vanilla_id: "Issue found in file - The block '' is using a vanilla block state '' that exceeds the available slot range '0~'." -warning.config.block.state.conflict: "Issue found in file - The block '' is using a vanilla block state '' that has been occupied by ''." -warning.config.block.state.bind_failed: "Issue found in file - The block '' failed to bind real block state for '' as the state has been occupied by ''." -warning.config.block.state.invalid_real_id: "Issue found in file - The block '' is using a real block state '' that exceeds the available slot range '0~'. Consider adding more real states in 'additional-real-blocks.yml' if the slots are used up." +warning.config.block.state.invalid_id: "Issue found in file - The block state ID range () used by block '' is outside the valid range of 0 to . Please add more server-side blocks in 'config.yml' if the current slots are exhausted." +warning.config.block.state.id.conflict: "Issue found in file - The block '' failed to bind real block state '' for '' as the state has been occupied by ''." +warning.config.block.state.id.exhausted: "Issue found in file - Cannot allocate enough real block state for block ''. Please add more server-side blocks in 'config.yml' and restart if the current slots are exhausted." warning.config.block.state.model.missing_path: "Issue found in file - The block '' is missing the required 'path' option for 'model'." warning.config.block.state.model.invalid_path: "Issue found in file - The block '' has a 'path' argument '' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." warning.config.block.state.model.conflict: "Issue found in file - The block '' is trying to bind model '' to block state '' which has already been bound to model ''" @@ -277,10 +292,12 @@ warning.config.block.behavior.missing_type: "Issue found in file warning.config.block.behavior.invalid_type: "Issue found in file - The block '' is using an invalid block behavior type ''." warning.config.block.behavior.concrete.missing_solid: "Issue found in file - The block '' is missing the required 'solid-block' option for 'concrete_block' behavior." warning.config.block.behavior.crop.missing_age: "Issue found in file - The block '' is missing the required 'age' property for 'crop_block' behavior." -warning.config.block.behavior.sugar_cane.missing_age: "Issue found in file - The block '' is missing the required 'age' property for 'sugar_cane_block' behavior." +warning.config.block.behavior.vertical_crop.missing_age: "Issue found in file - The block '' is missing the required 'age' property for 'vertical_crop_block' behavior." warning.config.block.behavior.leaves.missing_persistent: "Issue found in file - The block '' is missing the required 'persistent' property for 'leaves_block' behavior." warning.config.block.behavior.leaves.missing_distance: "Issue found in file - The block '' is missing the required 'distance' property for 'leaves_block' behavior." warning.config.block.behavior.lamp.missing_lit: "Issue found in file - The block '' is missing the required 'lit' property for 'lamp_block' behavior." +warning.config.block.behavior.toggleable_lamp.missing_lit: "Issue found in file - The block '' is missing the required 'lit' property for 'toggleable_lamp_block' behavior." +warning.config.block.behavior.toggleable_lamp.missing_powered: "Issue found in file - The block '' is missing the required 'powered' property for 'toggleable_lamp_block' behavior." warning.config.block.behavior.sapling.missing_stage: "Issue found in file - The block '' is missing the required 'stage' property for 'sapling_block' behavior." warning.config.block.behavior.sapling.missing_feature: "Issue found in file - The block '' is missing the required 'feature' argument for 'sapling_block' behavior." warning.config.block.behavior.strippable.missing_stripped: "Issue found in file - The block '' is missing the required 'stripped' argument for 'strippable_block' behavior." @@ -295,18 +312,36 @@ warning.config.block.behavior.trapdoor.missing_open: "Issue found in fil warning.config.block.behavior.trapdoor.missing_powered: "Issue found in file - The block '' is missing the required 'powered' property for 'trapdoor_block' behavior." warning.config.block.behavior.stackable.missing_property: "Issue found in file - The block '' is missing the required '' property for 'stackable_block' behavior." warning.config.block.behavior.stackable.missing_items: "Issue found in file - The block '' is missing the required 'items' argument for 'stackable_block' behavior." -warning.config.block.behavior.fence_gate.missing_facing: "Issue found in file - The block '' is missing the required 'facing' argument for 'fence_gate_block' behavior." -warning.config.block.behavior.fence_gate.missing_in_wall: "Issue found in file - The block '' is missing the required 'in_wall' argument for 'fence_gate_block' behavior." -warning.config.block.behavior.fence_gate.missing_open: "Issue found in file - The block '' is missing the required 'powered' argument for 'fence_gate_block' behavior." -warning.config.block.behavior.fence_gate.missing_powered: "Issue found in file - The block '' is missing the required 'open' argument for 'fence_gate_block' behavior." +warning.config.block.behavior.fence_gate.missing_facing: "Issue found in file - The block '' is missing the required 'facing' property for 'fence_gate_block' behavior." +warning.config.block.behavior.fence_gate.missing_in_wall: "Issue found in file - The block '' is missing the required 'in_wall' property for 'fence_gate_block' behavior." +warning.config.block.behavior.fence_gate.missing_open: "Issue found in file - The block '' is missing the required 'powered' property for 'fence_gate_block' behavior." +warning.config.block.behavior.fence_gate.missing_powered: "Issue found in file - The block '' is missing the required 'open' property for 'fence_gate_block' behavior." warning.config.block.behavior.slab.missing_type: "Issue found in file - The block '' is missing the required 'type' property for 'slab_block' behavior." warning.config.block.behavior.stairs.missing_facing: "Issue found in file - The block '' is missing the required 'facing' property for 'stairs_block' behavior." warning.config.block.behavior.stairs.missing_half: "Issue found in file - The block '' is missing the required 'half' property for 'stairs_block' behavior." warning.config.block.behavior.stairs.missing_shape: "Issue found in file - The block '' is missing the required 'shape' property for 'stairs_block' behavior." +warning.config.block.behavior.sofa.missing_facing: "Issue found in file - The block '' is missing the required 'facing' property for 'sofa_block' behavior." +warning.config.block.behavior.sofa.missing_shape: "Issue found in file - The block '' is missing the required 'shape' property for 'sofa_block' behavior." warning.config.block.behavior.pressure_plate.missing_powered: "Issue found in file - The block '' is missing the required 'powered' property for 'pressure_plate_block' behavior." warning.config.block.behavior.grass.missing_feature: "Issue found in file - The block '' is missing the required 'feature' argument for 'grass_block' behavior." warning.config.block.behavior.double_high.missing_half: "Issue found in file - The block '' is missing the required 'half' property for 'double_block' behavior." -warning.config.block.behavior.change_over_time.missing_next_block: "Issue found in file - The block '' is missing the required 'next_block' property for 'change_over_time_block' behavior." +warning.config.block.behavior.change_over_time.missing_next_block: "Issue found in file - The block '' is missing the required 'next_block' argument for 'change_over_time_block' behavior." +warning.config.block.behavior.directional_attached.missing_facing: "Issue found in file - The block '' is missing the required 'facing' property for 'directional_attached_block' behavior." +warning.config.block.behavior.wall_torch_particle.missing_facing: "Issue found in file - The block '' is missing the required 'facing' property for 'wall_torch_particle_block' behavior." +warning.config.block.behavior.fence.missing_north: "Issue found in file - The block '' is missing the required 'north' property for 'fence_block' behavior." +warning.config.block.behavior.fence.missing_east: "Issue found in file - The block '' is missing the required 'east' property for 'fence_block' behavior." +warning.config.block.behavior.fence.missing_south: "Issue found in file - The block '' is missing the required 'south' property for 'fence_block' behavior." +warning.config.block.behavior.fence.missing_west: "Issue found in file - The block '' is missing the required 'west' property for 'fence_block' behavior." +warning.config.block.behavior.face_attached_horizontal_directional.missing_face: "Issue found in file - The block '' is missing the required 'face' property for 'face_attached_horizontal_directional_block' behavior." +warning.config.block.behavior.face_attached_horizontal_directional.missing_facing: "Issue found in file - The block '' is missing the required 'facing' property for 'face_attached_horizontal_directional_block' behavior." +warning.config.block.behavior.button.missing_powered: "Issue found in file - The block '' is missing the required 'powered' property for 'button_block' behavior." +warning.config.block.behavior.stem.missing_age: "Issue found in file - The block '' is missing the required 'age' property for 'stem_block' behavior." +warning.config.block.behavior.stem.missing_fruit: "Issue found in file - The block '' is missing the required 'fruit' argument for 'stem_block' behavior." +warning.config.block.behavior.stem.missing_attached_stem: "Issue found in file - The block '' is missing the required 'attached_stem' argument for 'stem_block' behavior." +warning.config.block.behavior.attached_stem.missing_facing: "Issue found in file - The block '' is missing the required 'facing' property for 'attached_stem_block' behavior." +warning.config.block.behavior.attached_stem.missing_fruit: "Issue found in file - The block '' is missing the required 'fruit' argument for 'attached_stem_block' behavior." +warning.config.block.behavior.attached_stem.missing_stem: "Issue found in file - The block '' is missing the required 'stem' argument for 'attached_stem_block' behavior." +warning.config.block.behavior.chime.missing_sounds_projectile_hit: "Issue found in file - The block '' is missing the required 'sounds.projectile-hit' argument for 'chime_block' behavior." warning.config.model.generation.missing_parent: "Issue found in file - The config '' is missing the required 'parent' argument in 'generation' section." warning.config.model.generation.conflict: "Issue found in file - Failed to generate model for '' as two or more configurations attempt to generate different json models with the same path: ''." warning.config.model.generation.invalid_display_position: "Issue found in file - The config '' is using an invalid display position '' in 'generation.display' section. Allowed display positions: []" @@ -388,7 +423,7 @@ warning.config.function.command.missing_command: "Issue found in file Issue found in file - The config '' is missing the required 'actionbar' argument for 'actionbar' function." warning.config.function.message.missing_message: "Issue found in file - The config '' is missing the required 'message' argument for 'message' function." warning.config.function.open_window.missing_gui_type: "Issue found in file - The config '' is missing the required 'gui-type' argument for 'open_window' function." -warning.config.function.open_window.invalid_gui_type: "Issue found in file - The config '' is using an invalid gui type for 'open_window' function. Allowed types: []." +warning.config.function.open_window.invalid_gui_type: "Issue found in file - The config '' is using an invalid gui type '' for 'open_window' function. Allowed types: []." warning.config.function.run.missing_functions: "Issue found in file - The config '' is missing the required 'functions' argument for 'run' function." warning.config.function.place_block.missing_block_state: "Issue found in file - The config '' is missing the required 'block-state' argument for 'place_block' function." warning.config.function.set_food.missing_food: "Issue found in file - The config '' is missing the required 'food' argument for 'set_food' function." @@ -411,6 +446,14 @@ warning.config.function.remove_cooldown.missing_id: "Issue found in file warning.config.function.mythic_mobs_skill.missing_skill: "Issue found in file - The config '' is missing the required 'skill' argument for 'mythic_mobs_skill' function." warning.config.function.spawn_furniture.missing_furniture_id: "Issue found in file - The config '' is missing the required 'furniture-id' argument for 'spawn_furniture' function." warning.config.function.replace_furniture.missing_furniture_id: "Issue found in file - The config '' is missing the required 'furniture-id' argument for 'replace_furniture' function." +warning.config.function.teleport.missing_x: "Issue found in file - The config '' is missing the required 'x' argument for 'teleport' function." +warning.config.function.teleport.missing_y: "Issue found in file - The config '' is missing the required 'y' argument for 'teleport' function." +warning.config.function.teleport.missing_z: "Issue found in file - The config '' is missing the required 'z' argument for 'teleport' function." +warning.config.function.set_variable.missing_name: "Issue found in file - The config '' is missing the required 'name' argument for 'set_variable' function." +warning.config.function.set_variable.missing_value: "Issue found in file - The config '' is missing the required 'number' or 'text' argument for 'set_variable' function." +warning.config.function.toast.missing_toast: "Issue found in file - The config '' is missing the required 'toast' argument for 'toast' function." +warning.config.function.toast.missing_icon: "Issue found in file - The config '' is missing the required 'icon' argument for 'toast' function." +warning.config.function.toast.invalid_advancement_type: "Issue found in file - The config '' is using an invalid advancement type '' for 'toast' function. Allowed types: []." warning.config.selector.missing_type: "Issue found in file - The config '' is missing the required 'type' argument for selector." warning.config.selector.invalid_type: "Issue found in file - The config '' is using an invalid selector type ''." warning.config.selector.invalid_target: "Issue found in file - The config '' is using an invalid selector target ''." @@ -423,7 +466,7 @@ warning.config.resource_pack.generation.missing_item_model: "Item 'Block state '' is missing model file: ''" warning.config.resource_pack.generation.missing_parent_model: "Model '' cannot find parent model: ''" warning.config.resource_pack.generation.missing_equipment_texture: "Equipment '' is missing texture ''" -warning.config.resource_pack.generation.texture_not_in_atlas: "Texture '' is not listed in the atlas. You need to add the texture path to the atlas or enable 'obfuscation' option in config.yml." +warning.config.resource_pack.generation.texture_not_in_atlas: "Texture '' is not listed in the atlas. You need to add the texture path to the atlas or enable 'fix-atlas' option in config.yml." warning.config.resource_pack.invalid_overlay_format: "Issue found in config.yml at 'resource-pack.overlay-format' - Invalid overlay format ''. Overlay format must contain the placeholder '{version}'." warning.config.equipment.duplicate: "Issue found in file - Duplicated equipment ''. Please check if there is the same configuration in other files." warning.config.equipment.missing_type: "Issue found in file - The equipment '' is missing the required 'type' argument." diff --git a/common-files/src/main/resources/translations/es.yml b/common-files/src/main/resources/translations/es.yml index b26e3f80d..13b0fe348 100644 --- a/common-files/src/main/resources/translations/es.yml +++ b/common-files/src/main/resources/translations/es.yml @@ -77,7 +77,7 @@ warning.config.image.missing_file: "Problema encontrado en el archivo Problema encontrado en el archivo - La imagen '' tiene un argumento 'file' '' que contiene caracteres prohibidos. Por favor lee https://minecraft.wiki/w/Resource_location#Legal_characters" warning.config.image.invalid_font_chars: "Problema encontrado en el archivo - La imagen '' tiene un argumento 'font' '' que contiene caracteres prohibidos. Por favor lee https://minecraft.wiki/w/Resource_location#Legal_characters" warning.config.image.missing_char: "Problema encontrado en el archivo - La imagen '' carece del argumento requerido 'char'." -warning.config.image.codepoint_conflict: "Problema encontrado en el archivo - La imagen '' está usando el carácter '()' que ya ha sido usado por otra imagen '' en la fuente ." +warning.config.image.codepoint.conflict: "Problema encontrado en el archivo - La imagen '' está usando el carácter '()' que ya ha sido usado por otra imagen '' en la fuente ." warning.config.image.invalid_codepoint_grid: "Problema encontrado en el archivo - La imagen '' tiene una cuadrícula de puntos de código 'chars' inválida." warning.config.image.file_not_found: "Problema encontrado en el archivo - Archivo PNG '' no encontrado para la imagen ''." warning.config.image.invalid_hex_value: "Problema encontrado en el archivo - La imagen '' está usando el carácter unicode '' que no es un valor hexadecimal válido." @@ -121,7 +121,7 @@ warning.config.item.settings.unknown: "Problema encontrado en el archivo warning.config.item.missing_material: "Problema encontrado en el archivo - El objeto '' carece del argumento requerido 'material'." warning.config.item.invalid_material: "Problema encontrado en el archivo - El objeto '' está usando un tipo de material inválido ''." warning.config.item.bad_custom_model_data: "Problema encontrado en el archivo - El objeto '' está usando un dato de modelo personalizado demasiado grande ''. Se recomienda usar un valor menor a 16.777.216." -warning.config.item.custom_model_data_conflict: "Problema encontrado en el archivo - El objeto '' está usando un dato de modelo personalizado '' que está ocupado por el objeto ''." +warning.config.item.custom_model_data.conflict: "Problema encontrado en el archivo - El objeto '' está usando un dato de modelo personalizado '' que está ocupado por el objeto ''." warning.config.item.missing_model_id: "Problema encontrado en el archivo - El objeto '' carece del argumento requerido 'custom-model-data' o 'item-model'." warning.config.item.behavior.missing_type: "Problema encontrado en el archivo - El objeto '' carece del argumento requerido 'type' para el comportamiento del objeto." warning.config.item.behavior.invalid_type: "Problema encontrado en el archivo - El objeto '' está usando un tipo de comportamiento de objeto inválido ''." @@ -175,19 +175,14 @@ warning.config.block.state.property.missing_type: "Problema encontrado e warning.config.block.state.property.invalid_type: "Problema encontrado en el archivo - El bloque '' está usando un argumento 'type' inválido '' para la propiedad ''." warning.config.block.state.property.integer.invalid_range: "Problema encontrado en el archivo - El bloque '' está usando un argumento 'range' inválido '' para la propiedad entero ''. Sintaxis correcta: 1~2." warning.config.block.state.property.invalid_format: "Problema encontrado en el archivo - El bloque '' está usando un formato de propiedad de estado de bloque inválido ''." -warning.config.block.state.missing_real_id: "Problema encontrado en el archivo - El bloque '' carece del argumento requerido 'id' para 'state'. 'id' es el id de bloque del lado del servidor que es único para cada tipo de estado de bloque. Si creas un bloque del lado del servidor con 'note_block' e id 30, el id de bloque real será 'craftengine:note_block_30'." warning.config.block.state.missing_state: "Problema encontrado en el archivo - El bloque '' carece del argumento requerido 'state' para 'state'." warning.config.block.state.missing_properties: "Problema encontrado en el archivo - El bloque '' carece de la sección requerida 'properties' para 'states'." warning.config.block.state.missing_appearances: "Problema encontrado en el archivo - El bloque '' carece de la sección requerida 'appearances' para 'states'." -warning.config.block.state.missing_variants: "Problema encontrado en el archivo - El bloque '' carece de la sección requerida 'variants' para 'states'." -warning.config.block.state.variant.missing_appearance: "Problema encontrado en el archivo - El bloque '' carece del argumento requerido 'appearance' para la variante ''." warning.config.block.state.variant.invalid_appearance: "Problema encontrado en el archivo - Hay un error en el bloque '' donde la variante '' está usando una apariencia inexistente ''." warning.config.block.state.invalid_vanilla: "Problema encontrado en el archivo - El bloque '' está usando un estado de bloque vanilla inválido ''." -warning.config.block.state.unavailable_vanilla: "Problema encontrado en el archivo - El bloque '' está usando un estado de bloque vanilla no disponible ''. Por favor libera este estado en el archivo mappings.yml." +warning.config.block.state.unavailable_vanilla: "Problema encontrado en el archivo - El bloque '' está usando un estado de bloque vanilla no disponible ''. Por favor libera este estado en el archivo block-state-mappings." warning.config.block.state.invalid_vanilla_id: "Problema encontrado en el archivo - El bloque '' está usando un estado de bloque vanilla '' que excede el rango de slots disponible '0~'." -warning.config.block.state.conflict: "Problema encontrado en el archivo - El bloque '' está usando un estado de bloque vanilla '' que está ocupado por ''." -warning.config.block.state.bind_failed: "Problema encontrado en el archivo - El bloque '' falló al vincular el estado de bloque real para '' porque está ocupado por el estado ''." -warning.config.block.state.invalid_real_id: "Problema encontrado en el archivo - El bloque '' está usando un estado de bloque real '' que excede el rango de slots disponible '0~'. Si los slots están usados, considera agregar más estados reales a 'additional-real-blocks.yml'." +warning.config.block.state.id.conflict: "Problema encontrado en el archivo - El bloque '' falló al vincular el estado de bloque real para '' porque está ocupado por el estado ''." warning.config.block.state.model.missing_path: "Problema encontrado en el archivo - El bloque '' carece de la opción requerida 'path' para 'model'." warning.config.block.state.model.invalid_path: "Problema encontrado en el archivo - El bloque '' tiene un argumento 'path' '' que contiene caracteres prohibidos. Por favor lee https://minecraft.wiki/w/Resource_location#Legal_characters" warning.config.block.settings.unknown: "Problema encontrado en el archivo - El bloque '' está usando un tipo de configuración desconocido ''." @@ -195,7 +190,7 @@ warning.config.block.behavior.missing_type: "Problema encontrado en el a warning.config.block.behavior.invalid_type: "Problema encontrado en el archivo - El bloque '' está usando un tipo de comportamiento de bloque inválido ''." warning.config.block.behavior.concrete.missing_solid: "Problema encontrado en el archivo - El bloque '' carece de la opción requerida 'solid-block' para el comportamiento 'concrete_block'." warning.config.block.behavior.crop.missing_age: "Problema encontrado en el archivo - El bloque '' carece de la propiedad requerida 'age' para el comportamiento 'crop_block'." -warning.config.block.behavior.sugar_cane.missing_age: "Problema encontrado en el archivo - El bloque '' carece de la propiedad requerida 'age' para el comportamiento 'sugar_cane_block'." +warning.config.block.behavior.vertical_crop.missing_age: "Problema encontrado en el archivo - El bloque '' carece de la propiedad requerida 'age' para el comportamiento 'vertical_crop_block'." warning.config.block.behavior.leaves.missing_persistent: "Problema encontrado en el archivo - El bloque '' carece de la propiedad requerida 'persistent' para el comportamiento 'leaves_block'." warning.config.block.behavior.leaves.missing_distance: "Problema encontrado en el archivo - El bloque '' carece de la propiedad requerida 'distance' para el comportamiento 'leaves_block'." warning.config.block.behavior.sapling.missing_stage: "Problema encontrado en el archivo - El bloque '' carece de la propiedad requerida 'stage' para el comportamiento 'sapling_block'." diff --git a/common-files/src/main/resources/translations/ru_ru.yml b/common-files/src/main/resources/translations/ru_ru.yml index 60036cb99..5ae93dfe1 100644 --- a/common-files/src/main/resources/translations/ru_ru.yml +++ b/common-files/src/main/resources/translations/ru_ru.yml @@ -113,7 +113,7 @@ warning.config.image.missing_file: "Проблема найдена в warning.config.image.invalid_file_chars: "Проблема найдена в файле - Изображение '' имеет 'file' аргумент '', который содержит недопустимые символы. Пожалуйста, прочтите https://minecraft.wiki/w/Resource_location#Legal_characters." warning.config.image.invalid_font_chars: "Проблема найдена в файле - Изображение'' имеет 'font' аргумент '', который содержит недопустимые символы. Пожалуйста, прочтите https://minecraft.wiki/w/Resource_location#Legal_characters." warning.config.image.missing_char: "Проблема найдена в файле - В изображении '' отсутствует необходимый 'char' аргумент." -warning.config.image.codepoint_conflict: "Проблема найдена в файле - Изображение '' использует символ '()' в шрифте который был использован другим изображением ''." +warning.config.image.codepoint.conflict: "Проблема найдена в файле - Изображение '' использует символ '()' в шрифте который был использован другим изображением ''." warning.config.image.invalid_codepoint_grid: "Проблема найдена в файле - Изображение '' имеет недействительную 'chars' сетку кодовых точек." warning.config.image.invalid_char: "Проблема найдена в файле - Изображение '' имеет параметр char, содержащий комбинированные символы, что может привести к разделению изображения." warning.config.image.invalid_hex_value: "Проблема найдена в файле - Изображение '' использует символ юникода '' это недопустимое шестнадцатеричное (radix 16) значение." @@ -161,7 +161,7 @@ warning.config.item.missing_material: "Проблема найдена warning.config.item.invalid_material: "Проблема найдена в файле - Предмет '' использует недопустимый тип материала ''." warning.config.item.invalid_custom_model_data: "Проблема найдена в файле - Предмет '' использует отрицательные данные пользовательской модели '', что недействительно." warning.config.item.bad_custom_model_data: "Проблема найдена в файле - Предмет '' использует пользовательскую модель данных '', которая имеет слишком большое значение. Рекомендуется использовать значение ниже 16,777,216." -warning.config.item.custom_model_data_conflict: "Проблема найдена в файле - Предмет '' использует пользовательскую модель данных '', которая занята элементом ''." +warning.config.item.custom_model_data.conflict: "Проблема найдена в файле - Предмет '' использует пользовательскую модель данных '', которая занята элементом ''." warning.config.item.invalid_component: "Проблема найдена в файле - Предмет '' использует несуществующий тип компонента ''." warning.config.item.missing_model_id: "Проблема найдена в файле - В предмете '' отсутствует необходимый 'custom-model-data' или 'item-model' аргумент." warning.config.item.missing_model: "Проблема найдена в файле - В предмете '' отсутствует необходимый 'model' раздел для поддержки пакета ресурсов 1.21.4+." @@ -225,19 +225,14 @@ warning.config.block.state.property.missing_type: "Проблема на warning.config.block.state.property.invalid_type: "Проблема найдена в файле - Блок '' использует недопустимый аргумент типа '' для свойства ''." warning.config.block.state.property.integer.invalid_range: "Проблема найдена в файле - Блок '' использует недействительный 'range' аргумент '' для integer свойства ''. Правильный синтаксис: 1~2." warning.config.block.state.property.invalid_format: "Проблема найдена в файле - Блок '' имеет недействительный формат свойства состояния блока ''." -warning.config.block.state.missing_real_id: "Проблема найдена в файле - В блоке '' отсутствует необходимый 'id' аргумент для 'state'. 'id' это идентификатор блока на стороне сервера, который уникален для каждого типа состояния блока. Если вы создаете блок на стороне сервера с 'note_block' и идентификатор 30, тогда реальный идентификатор блока будет 'craftengine:note_block_30'." warning.config.block.state.missing_state: "Проблема найдена в файле - В блоке '' отсутствует необходимый 'state' аргумент для 'state'." warning.config.block.state.missing_properties: "Проблема найдена в файле - В блоке '' отсутствует необходимый 'properties' раздел для 'states'." warning.config.block.state.missing_appearances: "Проблема найдена в файле - В блоке '' отсутствует необходимый 'appearances' раздел для 'states'." -warning.config.block.state.missing_variants: "Проблема найдена в файле - В блоке '' отсутствует необходимый 'variants' раздел для 'states'." -warning.config.block.state.variant.missing_appearance: "Проблема найдена в файле - В блоке '' отсутствует необходимый 'appearance' аргумент для варианта ''." warning.config.block.state.variant.invalid_appearance: "Проблема найдена в файле - Блок '' имеет ошибку, что вариант '' использует несуществующий внешний вид ''." warning.config.block.state.invalid_vanilla: "Проблема найдена в файле - Блок '' имеет недействительное состояние ванильного блока ''." -warning.config.block.state.unavailable_vanilla: "Проблема найдена в файле - Блок '' использует недоступное состояние ванильного блока ''. Пожалуйста, освободите это состояние в mappings.yml." +warning.config.block.state.unavailable_vanilla: "Проблема найдена в файле - Блок '' использует недоступное состояние ванильного блока ''. Пожалуйста, освободите это состояние в block-state-mappings." warning.config.block.state.invalid_vanilla_id: "Проблема найдена в файле - Блок '' использует состояние ванильного блока '', что превышает доступный диапазон слотов '0~'." -warning.config.block.state.conflict: "Проблема найдена в файле - Блок '' использует состояние ванильного блока '' которое занято ''." -warning.config.block.state.bind_failed: "Проблема найдена в файле - Блоку '' не удалось привязать реальное состояние блока для '', так как состояние занято ''." -warning.config.block.state.invalid_real_id: "Проблема найдена в файле - Блок '' использует реальное состояние блока '', которое превышает доступный диапазон слотов '0~'. Рассмотрите возможность добавления большего количества реальных состояний в 'additional-real-blocks.yml' если слоты израсходованы." +warning.config.block.state.id.conflict: "Проблема найдена в файле - Блоку '' не удалось привязать реальное состояние блока для '', так как состояние занято ''." warning.config.block.state.model.missing_path: "Проблема найдена в файле - В блоке '' отсутствует необходимый 'path' опция для 'model'." warning.config.block.state.model.invalid_path: "Проблема найдена в файле - Блок '' имеет 'path' аргумент '' содержит недопустимые символы. Пожалуйста, прочтите https://minecraft.wiki/w/Resource_location#Legal_characters." warning.config.block.settings.unknown: "Проблема найдена в файле - Блок '' использует неизвестный тип настройки ''." @@ -245,7 +240,7 @@ warning.config.block.behavior.missing_type: "Проблема найде warning.config.block.behavior.invalid_type: "Проблема найдена в файле - Блок '' имеет недействительный блочный behavior тип ''." warning.config.block.behavior.concrete.missing_solid: "Проблема найдена в файле - В блоке '' отсутствует необходимый 'solid-block' вариант для 'concrete_block' behavior." warning.config.block.behavior.crop.missing_age: "Проблема найдена в файле - В блоке '' отсутствует необходимый 'age' свойство для 'crop_block' behavior." -warning.config.block.behavior.sugar_cane.missing_age: "Проблема найдена в файле - В блоке '' отсутствует необходимый 'age' свойство для 'sugar_cane_block' behavior." +warning.config.block.behavior.vertical_crop.missing_age: "Проблема найдена в файле - В блоке '' отсутствует необходимый 'age' свойство для 'vertical_crop_block' behavior." warning.config.block.behavior.leaves.missing_persistent: "Проблема найдена в файле - В блоке '' отсутствует необходимый 'persistent' свойство для 'leaves_block' behavior." warning.config.block.behavior.leaves.missing_distance: "Проблема найдена в файле - В блоке '' отсутствует необходимый 'distance' свойство для 'leaves_block' behavior." warning.config.block.behavior.lamp.missing_lit: "Проблема найдена в файле - В блоке '' отсутствует необходимый 'lit' свойство для 'lamp_block' behavior." @@ -381,7 +376,7 @@ warning.config.resource_pack.item_model.conflict.vanilla: "Не удал warning.config.resource_pack.item_model.already_exist: "Не удалось создать модель элемента для '', потому что файл '' уже существует." warning.config.resource_pack.model.generation.already_exist: "Не удалось создать модель, так как файл модели '' уже существует." warning.config.resource_pack.generation.missing_font_texture: "В шрифте '' отсутствует обязательная текстура: ''" -warning.config.resource_pack.generation.texture_not_in_atlas: "Текстура '' не указана в атласе. Вам нужно добавить путь к текстуре в атлас или включить 'obfuscation' опцию в config.yml." +warning.config.resource_pack.generation.texture_not_in_atlas: "Текстура '' не указана в атласе. Вам нужно добавить путь к текстуре в атлас или включить 'fix-atlas' опцию в config.yml." warning.config.resource_pack.generation.missing_model_texture: "В модели '' отсутствует текстура ''" warning.config.resource_pack.generation.missing_item_model: "В предмете '' отсутствует файл модели: ''" warning.config.resource_pack.generation.missing_block_model: "В блоке '' отсутствует файл модели: ''" diff --git a/common-files/src/main/resources/translations/tr.yml b/common-files/src/main/resources/translations/tr.yml index 0704fb7f2..8507154a8 100644 --- a/common-files/src/main/resources/translations/tr.yml +++ b/common-files/src/main/resources/translations/tr.yml @@ -77,7 +77,7 @@ warning.config.image.missing_file: " dosyasında sorun bulundu - warning.config.image.invalid_file_chars: " dosyasında sorun bulundu - '' resmi, yasak karakterler içeren '' 'file' argümanına sahip. Lütfen https://minecraft.wiki/w/Resource_location#Legal_characters sayfasını okuyun." warning.config.image.invalid_font_chars: " dosyasında sorun bulundu - '' resmi, yasak karakterler içeren '' 'font' argümanına sahip. Lütfen https://minecraft.wiki/w/Resource_location#Legal_characters sayfasını okuyun." warning.config.image.missing_char: " dosyasında sorun bulundu - '' resmi gerekli 'char' argümanı eksik." -warning.config.image.codepoint_conflict: " dosyasında sorun bulundu - '' resmi, yazı tipinde başka bir resim '' tarafından kullanılmış olan '()' karakterini kullanıyor." +warning.config.image.codepoint.conflict: " dosyasında sorun bulundu - '' resmi, yazı tipinde başka bir resim '' tarafından kullanılmış olan '()' karakterini kullanıyor." warning.config.image.invalid_codepoint_grid: " dosyasında sorun bulundu - '' resminin geçersiz bir 'chars' kod noktası ızgarası var." warning.config.image.invalid_hex_value: " dosyasında sorun bulundu - '' resmi, geçerli bir onaltılık (16 tabanlı) değer olmayan '' unicode karakterini kullanıyor." warning.config.recipe.duplicate: " dosyasında sorun bulundu - Yinelenen tarif ''. Diğer dosyalarda aynı yapılandırmanın olup olmadığını kontrol edin." @@ -120,7 +120,7 @@ warning.config.item.settings.unknown: " dosyasında sorun bulundu warning.config.item.missing_material: " dosyasında sorun bulundu - '' eşyası gerekli 'material' argümanı eksik." warning.config.item.invalid_material: " dosyasında sorun bulundu - '' eşyası geçersiz bir malzeme türü '' kullanıyor." warning.config.item.bad_custom_model_data: " dosyasında sorun bulundu - '' eşyası çok büyük bir özel model verisi '' kullanıyor. 16.777.216'dan düşük bir değer kullanmanız önerilir." -warning.config.item.custom_model_data_conflict: " dosyasında sorun bulundu - '' eşyası, '' eşyası tarafından işgal edilmiş bir özel model verisi '' kullanıyor." +warning.config.item.custom_model_data.conflict: " dosyasında sorun bulundu - '' eşyası, '' eşyası tarafından işgal edilmiş bir özel model verisi '' kullanıyor." warning.config.item.missing_model_id: " dosyasında sorun bulundu - '' eşyası gerekli 'custom-model-data' veya 'item-model' argümanı eksik." warning.config.item.behavior.missing_type: " dosyasında sorun bulundu - '' eşyası, eşya davranışı için gerekli 'type' argümanı eksik." warning.config.item.behavior.invalid_type: " dosyasında sorun bulundu - '' eşyası geçersiz bir eşya davranış türü '' kullanıyor." @@ -173,19 +173,14 @@ warning.config.block.state.property.missing_type: " dosyasında s warning.config.block.state.property.invalid_type: " dosyasında sorun bulundu - '' bloğu, '' özelliği için geçersiz bir 'type' argümanı '' kullanıyor." warning.config.block.state.property.integer.invalid_range: " dosyasında sorun bulundu - '' bloğu, '' tamsayı özelliği için geçersiz bir 'range' argümanı '' kullanıyor. Doğru sözdizimi: 1~2." warning.config.block.state.property.invalid_format: " dosyasında sorun bulundu - '' bloğu, geçersiz bir blok durum özelliği formatı '' kullanıyor." -warning.config.block.state.missing_real_id: " dosyasında sorun bulundu - '' bloğu, 'state' için gerekli 'id' argümanı eksik. 'id', her blok durumu türü için benzersiz olan sunucu tarafı blok kimliğidir. 'note_block' ve id 30 ile bir sunucu tarafı blok oluşturursanız, gerçek blok kimliği 'craftengine:note_block_30' olur." warning.config.block.state.missing_state: " dosyasında sorun bulundu - '' bloğu, 'state' için gerekli 'state' argümanı eksik." warning.config.block.state.missing_properties: " dosyasında sorun bulundu - '' bloğu, 'states' için gerekli 'properties' bölümü eksik." warning.config.block.state.missing_appearances: " dosyasında sorun bulundu - '' bloğu, 'states' için gerekli 'appearances' bölümü eksik." -warning.config.block.state.missing_variants: " dosyasında sorun bulundu - '' bloğu, 'states' için gerekli 'variants' bölümü eksik." -warning.config.block.state.variant.missing_appearance: " dosyasında sorun bulundu - '' bloğu, '' varyantı için gerekli 'appearance' argümanı eksik." warning.config.block.state.variant.invalid_appearance: " dosyasında sorun bulundu - '' bloğunda, '' varyantının var olmayan bir görünüm '' kullandığı bir hata var." warning.config.block.state.invalid_vanilla: " dosyasında sorun bulundu - '' bloğu geçersiz bir vanilya blok durumu '' kullanıyor." -warning.config.block.state.unavailable_vanilla: " dosyasında sorun bulundu - '' bloğu kullanılamayan bir vanilya blok durumu '' kullanıyor. Lütfen bu durumu mappings.yml dosyasında serbest bırakın." +warning.config.block.state.unavailable_vanilla: " dosyasında sorun bulundu - '' bloğu kullanılamayan bir vanilya blok durumu '' kullanıyor. Lütfen bu durumu block-state-mappings dosyasında serbest bırakın." warning.config.block.state.invalid_vanilla_id: " dosyasında sorun bulundu - '' bloğu, mevcut yuva aralığı '0~' aşan bir vanilya blok durumu '' kullanıyor." -warning.config.block.state.conflict: " dosyasında sorun bulundu - '' bloğu, '' tarafından işgal edilmiş bir vanilya blok durumu '' kullanıyor." -warning.config.block.state.bind_failed: " dosyasında sorun bulundu - '' bloğu, durum '' tarafından işgal edildiği için '' için gerçek blok durumu bağlamada başarısız oldu." -warning.config.block.state.invalid_real_id: " dosyasında sorun bulundu - '' bloğu, mevcut yuva aralığı '0~' aşan bir gerçek blok durumu '' kullanıyor. Yuvalar kullanılmışsa, 'additional-real-blocks.yml' dosyasına daha fazla gerçek durum eklemeyi düşünün." +warning.config.block.state.id.conflict: " dosyasında sorun bulundu - '' bloğu, durum '' tarafından işgal edildiği için '' için gerçek blok durumu bağlamada başarısız oldu." warning.config.block.state.model.missing_path: " dosyasında sorun bulundu - '' bloğu, 'model' için gerekli 'path' seçeneği eksik." warning.config.block.state.model.invalid_path: " dosyasında sorun bulundu - '' bloğunun, yasak karakterler içeren bir 'path' argümanı '' var. Lütfen https://minecraft.wiki/w/Resource_location#Legal_characters sayfasını okuyun." warning.config.block.settings.unknown: " dosyasında sorun bulundu - '' bloğu bilinmeyen bir ayar türü '' kullanıyor." @@ -193,7 +188,7 @@ warning.config.block.behavior.missing_type: " dosyasında sorun b warning.config.block.behavior.invalid_type: " dosyasında sorun bulundu - '' bloğu geçersiz bir blok davranış türü '' kullanıyor." warning.config.block.behavior.concrete.missing_solid: " dosyasında sorun bulundu - '' bloğu, 'concrete_block' davranışı için gerekli 'solid-block' seçeneği eksik." warning.config.block.behavior.crop.missing_age: " dosyasında sorun bulundu - '' bloğu, 'crop_block' davranışı için gerekli 'age' özelliği eksik." -warning.config.block.behavior.sugar_cane.missing_age: " dosyasında sorun bulundu - '' bloğu, 'sugar_cane_block' davranışı için gerekli 'age' özelliği eksik." +warning.config.block.behavior.vertical_crop.missing_age: " dosyasında sorun bulundu - '' bloğu, 'vertical_crop_block' davranışı için gerekli 'age' özelliği eksik." warning.config.block.behavior.leaves.missing_persistent: " dosyasında sorun bulundu - '' bloğu, 'leaves_block' davranışı için gerekli 'persistent' özelliği eksik." warning.config.block.behavior.leaves.missing_distance: " dosyasında sorun bulundu - '' bloğu, 'leaves_block' davranışı için gerekli 'distance' özelliği eksik." warning.config.block.behavior.sapling.missing_stage: " dosyasında sorun bulundu - '' bloğu, 'sapling_block' davranışı için gerekli 'stage' özelliği eksik." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index 239100483..97c6d2d43 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -63,7 +63,8 @@ command.upload.failure.not_supported: "当前托管模式 '' 不支 command.upload.on_progress: "已开始上传进程. 检查控制台以获取详细信息" command.send_resource_pack.success.single: "发送资源包给 " command.send_resource_pack.success.multiple: "发送资源包给 个玩家" -warning.config.pack.duplicated_files: "发现重复文件 请通过 config.yml 的 'resource-pack.duplicated-files-handler' 部分解决" +warning.network.resource_pack.unverified_uuid: "玩家 使用未经服务器验证的 UUID () 尝试请求获取资源包" +warning.config.pack.duplicated_files: "发现重复文件 请通过 config.yml 的 'resource-pack.duplicated-files-handler' 部分解决" warning.config.yaml.duplicated_key: "在文件 发现问题 - 在第行发现重复的键 '', 这可能会导致一些意料之外的问题" warning.config.yaml.inconsistent_value_type: "在文件 发现问题 - 在第行发现重复且值类型不同的键 '', 这可能会导致一些意料之外的问题" warning.config.type.int: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为整数类型 (选项 '')" @@ -72,6 +73,7 @@ warning.config.type.float: "在文件 发现问题 - 无法加 warning.config.type.double: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为双精度类型 (选项 '')" warning.config.type.quaternionf: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为四元数类型 (选项 '')" warning.config.type.vector3f: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为三维向量类型 (选项 '')" +warning.config.type.vec3d: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为双精度浮点数三维向量类型 (选项 '')" warning.config.type.map: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为映射类型 (选项 '')" warning.config.type.snbt.invalid_syntax: "在文件 发现问题 - 无法加载 '': 无效的 SNBT 语法 ''" warning.config.number.missing_type: "在文件 发现问题 - 配置项 '' 缺少数字类型所需的 'type' 参数" @@ -85,6 +87,8 @@ warning.config.number.uniform.missing_min: "在文件 发现问 warning.config.number.uniform.missing_max: "在文件 发现问题 - 配置项 '' 缺少 'uniform' 数字类型所需的 'max' 参数" warning.config.number.gaussian.missing_min: "在文件 发现问题 - 配置项 '' 缺少 'gaussian' 数字类型所需的 'min' 参数" warning.config.number.gaussian.missing_max: "在文件 发现问题 - 配置项 '' 缺少 'gaussian' 数字类型所需的 'max' 参数" +warning.config.number.binomial.missing_extra: "在文件 发现问题 - 配置项 '' 缺少 'binomial' 数字类型所需的 'extra' 参数" +warning.config.number.binomial.missing_probability: "在文件 发现问题 - 配置项 '' 缺少 'binomial' 数字类型所需的 'probability' 参数" warning.config.condition.all_of.missing_terms: "在文件 发现问题 - 配置项 '' 缺少 'all_of' 条件所需的 'terms' 参数" warning.config.condition.all_of.invalid_terms_type: "在文件 发现问题 - 配置项 '' 的 'all_of' 条件配置错误, 'terms' 应为映射列表, 当前类型: ''" warning.config.condition.any_of.missing_terms: "在文件 发现问题 - 配置项 '' 缺少 'any_of' 条件所需的 'terms' 参数" @@ -118,8 +122,9 @@ warning.config.image.height_ascent_conflict: "在文件 发现 warning.config.image.missing_file: "在文件 发现问题 - 图片 '' 缺少必需的 'file' 参数" warning.config.image.invalid_file_chars: "在文件 发现问题 - 图片 '' 的 'file' 参数 '' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" warning.config.image.invalid_font_chars: "在文件 发现问题 - 图片 '' 的 'font' 参数 '' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" +warning.config.image.invalid_grid_size: "在文件 发现问题 - 图片 '' 使用了无效的网格尺寸 ''. 正确的格式 '3,5'" warning.config.image.missing_char: "在文件 发现问题 - 图片 '' 缺少必需的 'char' 参数" -warning.config.image.codepoint_conflict: "在文件 发现问题 - 图片 '' 在字体 中使用的字符 '()' 已被其他图片 '' 占用" +warning.config.image.codepoint.conflict: "在文件 发现问题 - 图片 '' 在字体 中使用的字符 '()' 已被其他图片 '' 占用" warning.config.image.invalid_codepoint_grid: "在文件 发现问题 - 图片 '' 的 'chars' 码位网格无效" warning.config.image.invalid_char: "在文件 发现问题 - 图片 '' 的 'char' 参数包含组合字符可能导致图片分裂" warning.config.image.invalid_hex_value: "在文件 发现问题 - 图片 '' 使用的 Unicode 字符 '' 不是有效的十六进制值" @@ -188,7 +193,7 @@ warning.config.item.invalid_material: "在文件 发现问题 - warning.config.item.invalid_custom_model_data: "在文件 发现问题 - 物品 '' 使用了无效的负数模型值 ''" warning.config.item.bad_custom_model_data: "在文件 发现问题 - 物品 '' 使用的自定义模型数据 '' 数值过大 建议使用小于 16,777,216 的值" warning.config.item.item_model.conflict: "在文件 发现问题 - 物品 '' 使用了无效的 'item-model' 选项. 这个 item-model 已经存在对应的原版物品" -warning.config.item.custom_model_data_conflict: "在文件 发现问题 - 物品 '' 使用的自定义模型数据 '' 已被物品 '' 占用" +warning.config.item.custom_model_data.conflict: "在文件 发现问题 - 物品 '' 使用的自定义模型数据 '' 已被物品 '' 占用" warning.config.item.invalid_component: "在文件 发现问题 - 物品 '' 使用了未知的数据组件 ''" warning.config.item.missing_model_id: "在文件 发现问题 - 物品 '' 缺少必需的 'custom-model-data' 或 'item-model' 参数" warning.config.item.missing_model: "在文件 中发现问题 - 物品 '' 缺少支持 1.21.4+ 资源包必需的 'model' 配置项" @@ -256,19 +261,15 @@ warning.config.block.state.property.missing_type: "在文件 发 warning.config.block.state.property.invalid_type: "在文件 发现问题 - 方块 '' 的属性 '' 使用了无效的类型参数 ''" warning.config.block.state.property.integer.invalid_range: "在文件 发现问题 - 方块 '' 的整数属性 '' 使用了无效的范围参数 '' 正确语法: 1~2" warning.config.block.state.property.invalid_format: "在文件 发现问题 - 方块 '' 使用了无效的方块状态属性格式 ''" -warning.config.block.state.missing_real_id: "在文件 发现问题 - 方块 '' 的 'state' 缺少必需的 'id' 参数 该 ID 是服务端方块 ID 用于唯一标识每种方块状态类型" warning.config.block.state.missing_state: "在文件 发现问题 - 方块 '' 的 'state' 缺少必需的 'state' 参数" warning.config.block.state.missing_properties: "在文件 发现问题 - 方块 '' 的 'states' 缺少必需的 'properties' 段落" warning.config.block.state.missing_appearances: "在文件 发现问题 - 方块 '' 的 'states' 缺少必需的 'appearances' 段落" -warning.config.block.state.missing_variants: "在文件 发现问题 - 方块 '' 的 'states' 缺少必需的 'variants' 段落" -warning.config.block.state.variant.missing_appearance: "在文件 发现问题 - 方块 '' 的变体 '' 缺少必需的 'appearance' 参数" warning.config.block.state.variant.invalid_appearance: "在文件 发现问题 - 方块 '' 的变体 '' 使用了不存在的 appearance ''" warning.config.block.state.invalid_vanilla: "在文件 发现问题 - 方块 '' 使用了无效的原版方块状态 ''" -warning.config.block.state.unavailable_vanilla: "在文件 发现问题 - 方块 '' 使用了不可用的原版方块状态 '' 请在 mappings.yml 中释放该状态" +warning.config.block.state.unavailable_vanilla: "在文件 发现问题 - 方块 '' 使用了不可用的原版方块状态 '' 请在 block-state-mappings 中释放该状态" warning.config.block.state.invalid_vanilla_id: "在文件 发现问题 - 方块 '' 使用的原版方块状态 '' 超出可用槽位范围 '0~'" -warning.config.block.state.conflict: "在文件 发现问题 - 方块 '' 使用的原版方块状态 '' 已被 '' 占用" -warning.config.block.state.bind_failed: "在文件 发现问题 - 方块 '' 无法为 '' 绑定真实方块状态 因该状态已被 '' 占用" -warning.config.block.state.invalid_real_id: "在文件 发现问题 - 方块 '' 使用的真实方块状态 '' 超出可用槽位范围 '0~' 如果槽位已用尽 请在 additional-real-blocks.yml 中添加更多真实状态" +warning.config.block.state.id.conflict: "在文件 发现问题 - 方块 '' 无法为 '' 绑定真实方块状态 '' 因该状态已被 '' 占用" +warning.config.block.state.invalid_id: "在文件 发现问题 - 方块 '' 使用的真实方块状态 '' 超出可用槽位范围 '0~' 如果槽位已用尽 请在 config.yml 中添加更多服务端侧方块" warning.config.block.state.model.missing_path: "在文件 发现问题 - 方块 '' 的 'model' 缺少必需的 'path' 选项" warning.config.block.state.model.invalid_path: "在文件 发现问题 - 方块 '' 的 'path' 参数 '' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" warning.config.block.state.model.conflict: "在文件 发现问题 - 方块 '' 正尝试将模型 '' 绑定到方块状态 '' 上, 但是此状态已绑定了另一个模型 ''" @@ -277,10 +278,12 @@ warning.config.block.behavior.missing_type: "在文件 发现问 warning.config.block.behavior.invalid_type: "在文件 发现问题 - 方块 '' 使用了无效的行为类型 ''" warning.config.block.behavior.concrete.missing_solid: "在文件 发现问题 - 方块 '' 的 'concrete_block' 行为缺少必需的 'solid-block' 选项" warning.config.block.behavior.crop.missing_age: "在文件 发现问题 - 方块 '' 的 'crop_block' 行为缺少必需的 'age' 属性" -warning.config.block.behavior.sugar_cane.missing_age: "在文件 发现问题 - 方块 '' 的 'sugar_cane_block' 行为缺少必需的 'age' 属性" +warning.config.block.behavior.vertical_crop.missing_age: "在文件 发现问题 - 方块 '' 的 'vertical_crop_block' 行为缺少必需的 'age' 属性" warning.config.block.behavior.leaves.missing_persistent: "在文件 发现问题 - 方块 '' 的 'leaves_block' 行为缺少必需的 'persistent' 属性" warning.config.block.behavior.leaves.missing_distance: "在文件 发现问题 - 方块 '' 的 'leaves_block' 行为缺少必需的 'distance' 属性" warning.config.block.behavior.lamp.missing_lit: "在文件 发现问题 - 方块 '' 的 'lamp_block' 行为缺少必需的 'lit' 属性" +warning.config.block.behavior.toggleable_lamp.missing_lit: "在文件 发现问题 - 方块 '' 的 'toggleable_lamp_block' 行为缺少必需的 'lit' 属性" +warning.config.block.behavior.toggleable_lamp.missing_powered: "在文件 发现问题 - 方块 '' 的 'toggleable_lamp_block' 行为缺少必需的 'powered' 属性" warning.config.block.behavior.sapling.missing_stage: "在文件 发现问题 - 方块 '' 的 'sapling_block' 行为缺少必需的 'stage' 属性" warning.config.block.behavior.sapling.missing_feature: "在文件 发现问题 - 方块 '' 的 'sapling_block' 行为缺少必需的 'feature' 参数" warning.config.block.behavior.strippable.missing_stripped: "在文件 发现问题 - 方块 '' 的 'strippable_block' 行为缺少必需的 'stripped' 参数" @@ -303,10 +306,28 @@ warning.config.block.behavior.slab.missing_type: "在文件 发 warning.config.block.behavior.stairs.missing_facing: "在文件 发现问题 - 方块 '' 的 'stairs_block' 行为缺少必需的 'facing' 属性" warning.config.block.behavior.stairs.missing_half: "在文件 发现问题 - 方块 '' 的 'stairs_block' 行为缺少必需的 'half' 属性" warning.config.block.behavior.stairs.missing_shape: "在文件 发现问题 - 方块 '' 的 'stairs_block' 行为缺少必需的 'shape' 属性" +warning.config.block.behavior.sofa.missing_facing: "在文件 发现问题 - 方块 '' 的 'sofa_block' 行为缺少必需的 'facing' 属性" +warning.config.block.behavior.sofa.missing_shape: "在文件 发现问题 - 方块 '' 的 'sofa_block' 行为缺少必需的 'shape' 属性" warning.config.block.behavior.pressure_plate.missing_powered: "在文件 发现问题 - 方块 '' 的 'pressure_plate_block' 行为缺少必需的 'powered' 属性" warning.config.block.behavior.grass.missing_feature: "在文件 发现问题 - 方块 '' 的 'grass_block' 行为缺少必需的 'feature' 参数" warning.config.block.behavior.double_high.missing_half: "在文件 发现问题 - 方块 '' 的 'double_block' 行为缺少必需的 'half' 属性" -warning.config.block.behavior.change_over_time.missing_next_block: "在文件 发现问题 - 方块 '' 的 'change_over_time_block' 行为缺少必需的 'next-block' 配置项" +warning.config.block.behavior.change_over_time.missing_next_block: "在文件 发现问题 - 方块 '' 的 'change_over_time_block' 行为缺少必需的 'next-block' 参数" +warning.config.block.behavior.directional_attached.missing_facing: "在文件 发现问题 - 方块 '' 的 'directional_attached_block' 行为缺少必需的 'facing' 属性" +warning.config.block.behavior.wall_torch_particle.missing_facing: "在文件 发现问题 - 配置项 '' 的 'wall_torch_particle_block' 行为缺少必需的 'facing' 属性" +warning.config.block.behavior.fence.missing_north: "在文件 发现问题 - 方块 '' 的 'fence_block' 行为缺少必需的 'north' 属性" +warning.config.block.behavior.fence.missing_east: "在文件 发现问题 - 方块 '' 的 'fence_block' 行为缺少必需的 'east' 属性" +warning.config.block.behavior.fence.missing_south: "在文件 发现问题 - 方块 '' 的 'fence_block' 行为缺少必需的 'south' 属性" +warning.config.block.behavior.fence.missing_west: "在文件 发现问题 - 方块 '' 的 'fence_block' 行为缺少必需的 'west' 属性" +warning.config.block.behavior.face_attached_horizontal_directional.missing_face: "在文件 发现问题 - 方块 '' 的 'face_attached_horizontal_directional_block' 行为缺少必需的 'face' 属性" +warning.config.block.behavior.face_attached_horizontal_directional.missing_facing: "在文件 发现问题 - 方块 '' 的 'face_attached_horizontal_directional_block' 行为缺少必需的 'facing' 属性" +warning.config.block.behavior.button.missing_powered: "在文件 发现问题 - 方块 '' 的 'button_block' 行为缺少必需的 'powered' 属性" +warning.config.block.behavior.stem.missing_age: "在文件 发现问题 - 方块 '' 的 'stem_block' 行为缺少必需的 'age' 属性" +warning.config.block.behavior.stem.missing_fruit: "在文件 发现问题 - 方块 '' 的 'stem_block' 行为缺少必需的 'fruit' 选项" +warning.config.block.behavior.stem.missing_attached_stem: "在文件 发现问题 - 方块 '' 的 'stem_block' 行为缺少必需的 'attached_stem' 选项" +warning.config.block.behavior.attached_stem.missing_facing: "在文件 发现问题 - 方块 '' 的 'attached_stem_block' 行为缺少必需的 'facing' 属性" +warning.config.block.behavior.attached_stem.missing_fruit: "在文件 发现问题 - 方块 '' 的 'attached_stem_block' 行为缺少必需的 'fruit' 选项" +warning.config.block.behavior.attached_stem.missing_stem: "在文件 发现问题 - 方块 '' 的 'attached_stem_block' 行为缺少必需的 'stem' 选项" +warning.config.block.behavior.chime.missing_sounds_projectile_hit: "在文件 发现问题 - 方块 '' 的 'chime_block' 行为缺少必需的 'sounds.projectile-hit' 选项" warning.config.model.generation.missing_parent: "在文件 发现问题 - 配置项 '' 的 'generation' 段落缺少必需的 'parent' 参数" warning.config.model.generation.conflict: "在文件 发现问题 - 无法为 '' 生成模型 存在多个配置尝试使用相同路径 '' 生成不同的 JSON 模型" warning.config.model.generation.invalid_display_position: "在文件 发现问题 - 配置项 '' 在 'generation.display' 区域使用了无效的 display 位置类型 ''. 可用展示类型: []" @@ -423,7 +444,7 @@ warning.config.resource_pack.generation.missing_item_model: "物品'方块状态''缺少模型文件: ''" warning.config.resource_pack.generation.missing_parent_model: "模型''找不到父级模型文件: ''" warning.config.resource_pack.generation.missing_equipment_texture: "装备 '' 缺少纹理 ''" -warning.config.resource_pack.generation.texture_not_in_atlas: "纹理''不在图集内. 你需要将纹理路径或文件夹前缀添加到图集内,或者启用 config.yml 中的 'obfuscation' 选项" +warning.config.resource_pack.generation.texture_not_in_atlas: "纹理''不在图集内. 你需要将纹理路径或文件夹前缀添加到图集内,或者启用 config.yml 中的 'fix-atlas' 选项" warning.config.resource_pack.invalid_overlay_format: "在 config.yml 的 'resource-pack.overlay-format' 处发现问题 - 无效的overlay格式 ''. Overlay格式必须包含占位符 '{version}'" warning.config.equipment.duplicate: "在文件 发现问题 - 重复的装备配置 ''。请检查其他文件中是否存在相同配置" warning.config.equipment.missing_type: "在文件 发现问题 - 装备 '' 缺少必需的 'type' 参数" diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 6a172d1c3..352ad960b 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -21,7 +21,7 @@ dependencies { implementation("net.momirealms:sparrow-nbt-codec:${rootProject.properties["sparrow_nbt_version"]}") implementation("net.momirealms:sparrow-nbt-legacy-codec:${rootProject.properties["sparrow_nbt_version"]}") // S3 - implementation("net.momirealms:craft-engine-s3:0.4") + implementation("net.momirealms:craft-engine-s3:0.5") // Util compileOnly("net.momirealms:sparrow-util:${rootProject.properties["sparrow_util_version"]}") // Adventure @@ -30,6 +30,8 @@ dependencies { compileOnly("net.kyori:adventure-text-serializer-gson:${rootProject.properties["adventure_bundle_version"]}") { exclude("com.google.code.gson", "gson") } + compileOnly("net.kyori:adventure-text-serializer-legacy:${rootProject.properties["adventure_bundle_version"]}") + compileOnly("net.kyori:adventure-text-serializer-json-legacy-impl:${rootProject.properties["adventure_bundle_version"]}") // Command compileOnly("org.incendo:cloud-core:${rootProject.properties["cloud_core_version"]}") @@ -101,6 +103,10 @@ tasks { relocate("com.google.common.jimfs", "net.momirealms.craftengine.libraries.jimfs") relocate("org.apache.commons", "net.momirealms.craftengine.libraries.commons") relocate("io.leangen.geantyref", "net.momirealms.craftengine.libraries.geantyref") + relocate("io.netty.handler.codec.http", "net.momirealms.craftengine.libraries.netty.handler.codec.http") + relocate("io.netty.handler.codec.rtsp", "net.momirealms.craftengine.libraries.netty.handler.codec.rtsp") + relocate("io.netty.handler.codec.spdy", "net.momirealms.craftengine.libraries.netty.handler.codec.spdy") + relocate("io.netty.handler.codec.http2", "net.momirealms.craftengine.libraries.netty.handler.codec.http2") } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/advancement/AbstractAdvancementManager.java b/core/src/main/java/net/momirealms/craftengine/core/advancement/AbstractAdvancementManager.java index 8af17d774..43711d2f9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/advancement/AbstractAdvancementManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/advancement/AbstractAdvancementManager.java @@ -8,4 +8,6 @@ public abstract class AbstractAdvancementManager implements AdvancementManager { public AbstractAdvancementManager(CraftEngine plugin) { this.plugin = plugin; } + + } diff --git a/core/src/main/java/net/momirealms/craftengine/core/advancement/AdvancementManager.java b/core/src/main/java/net/momirealms/craftengine/core/advancement/AdvancementManager.java index b4a13703b..f7d664bdf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/advancement/AdvancementManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/advancement/AdvancementManager.java @@ -1,9 +1,14 @@ package net.momirealms.craftengine.core.advancement; +import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.Manageable; import net.momirealms.craftengine.core.plugin.config.ConfigParser; public interface AdvancementManager extends Manageable { ConfigParser parser(); + + void sendToast(Player player, Item icon, Component message, AdvancementType type); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java index ba6327f9d..86719197c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java @@ -1,66 +1,120 @@ package net.momirealms.craftengine.core.block; +import com.google.common.collect.ImmutableList; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigs; +import net.momirealms.craftengine.core.block.parser.BlockNbtParser; import net.momirealms.craftengine.core.block.properties.Properties; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.pack.PendingConfigSection; import net.momirealms.craftengine.core.pack.ResourceLocation; +import net.momirealms.craftengine.core.pack.allocator.BlockStateCandidate; +import net.momirealms.craftengine.core.pack.allocator.IdAllocator; +import net.momirealms.craftengine.core.pack.allocator.VisualBlockStateAllocator; import net.momirealms.craftengine.core.pack.model.generation.AbstractModelGenerator; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.config.IdSectionConfigParser; +import net.momirealms.craftengine.core.plugin.config.SectionConfigParser; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.event.EventFunctions; +import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; +import net.momirealms.craftengine.core.plugin.context.function.Function; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.GsonHelper; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; -import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.plugin.logger.Debugger; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.registry.Holder; +import net.momirealms.craftengine.core.registry.WritableRegistry; +import net.momirealms.craftengine.core.util.*; +import net.momirealms.sparrow.nbt.CompoundTag; import org.incendo.cloud.suggestion.Suggestion; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.io.IOException; import java.nio.file.Path; import java.util.*; -import java.util.function.Function; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; public abstract class AbstractBlockManager extends AbstractModelGenerator implements BlockManager { protected final BlockParser blockParser; - // CraftEngine objects + protected final BlockStateMappingParser blockStateMappingParser; + // 根据id获取自定义方块 protected final Map byId = new HashMap<>(); - // Cached command suggestions + // 缓存的指令建议 protected final List cachedSuggestions = new ArrayList<>(); - // Cached Namespace + // 缓存的使用中的命名空间 protected final Set namespacesInUse = new HashSet<>(); - // for mod, real block id -> state models - protected final Map modBlockStates = new HashMap<>(); - // A temporary map that stores the model path of a certain vanilla block state - protected final Map tempVanillaBlockStateModels = new Int2ObjectOpenHashMap<>(); - // A temporary map used to detect whether the same block state corresponds to multiple models. - protected final Map tempRegistryIdConflictMap = new Int2ObjectOpenHashMap<>(); - // A temporary map that converts the custom block registered on the server to the vanilla block ID. - protected final Map tempBlockAppearanceConvertor = new Int2IntOpenHashMap(); - // Used to store override information of json files + // Map<方块类型, Map<方块状态NBT,模型>>,用于生成block state json protected final Map> blockStateOverrides = new HashMap<>(); - // a reverted mapper + // 用于生成mod使用的block state json + protected final Map modBlockStateOverrides = new HashMap<>(); + // 根据外观查找真实状态,用于debug指令 protected final Map> appearanceToRealState = new Int2ObjectOpenHashMap<>(); + // 用于note_block:0这样格式的自动分配 + protected final Map> blockStateArranger = new HashMap<>(); + // 全方块状态映射文件,用于网络包映射 + protected final int[] blockStateMappings; + // 原版方块状态数量 + protected final int vanillaBlockStateCount; + // 注册的大宝贝 + protected final DelegatingBlock[] customBlocks; + protected final DelegatingBlockState[] customBlockStates; + protected final Object[] customBlockHolders; + // 自定义状态列表,会随着重载变化 + protected final ImmutableBlockState[] immutableBlockStates; + // 倒推缓存 + protected final BlockStateCandidate[] autoVisualBlockStateCandidates; + // 用于检测单个外观方块状态是否被绑定了不同模型 + protected final JsonElement[] tempVanillaBlockStateModels; + // 临时存储哪些视觉方块被使用了 + protected final Set tempVisualBlockStatesInUse = new HashSet<>(); + protected final Set tempVisualBlocksInUse = new HashSet<>(); + // 声音映射表,和使用了哪些视觉方块有关 + protected Map soundReplacements = Map.of(); - // client side block tags - protected Map> clientBoundTags = Map.of(); - protected Map> previousClientBoundTags = Map.of(); - // Used to automatically arrange block states for strings such as minecraft:note_block:0 - protected Map> blockAppearanceArranger; - protected Map> realBlockArranger; - protected Map internalId2StateId; - protected Map registeredBlocks; - - protected AbstractBlockManager(CraftEngine plugin) { + protected AbstractBlockManager(CraftEngine plugin, int vanillaBlockStateCount, int customBlockCount) { super(plugin); - this.blockParser = new BlockParser(); + this.vanillaBlockStateCount = vanillaBlockStateCount; + this.customBlocks = new DelegatingBlock[customBlockCount]; + this.customBlockHolders = new Object[customBlockCount]; + this.customBlockStates = new DelegatingBlockState[customBlockCount]; + this.immutableBlockStates = new ImmutableBlockState[customBlockCount]; + this.blockStateMappings = new int[customBlockCount + vanillaBlockStateCount]; + this.autoVisualBlockStateCandidates = new BlockStateCandidate[vanillaBlockStateCount]; + this.tempVanillaBlockStateModels = new JsonElement[vanillaBlockStateCount]; + this.blockParser = new BlockParser(this.autoVisualBlockStateCandidates); + this.blockStateMappingParser = new BlockStateMappingParser(); + Arrays.fill(this.blockStateMappings, -1); + } + + @NotNull + @Override + public ImmutableBlockState getImmutableBlockStateUnsafe(int stateId) { + return this.immutableBlockStates[stateId - this.vanillaBlockStateCount]; + } + + @Nullable + @Override + public ImmutableBlockState getImmutableBlockState(int stateId) { + if (!isVanillaBlockState(stateId)) { + return this.immutableBlockStates[stateId - this.vanillaBlockStateCount]; + } + return null; } @Override @@ -68,23 +122,30 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem super.clearModelsToGenerate(); this.clearCache(); this.cachedSuggestions.clear(); + this.namespacesInUse.clear(); this.blockStateOverrides.clear(); - this.modBlockStates.clear(); + this.modBlockStateOverrides.clear(); this.byId.clear(); - this.previousClientBoundTags = this.clientBoundTags; - this.clientBoundTags = new HashMap<>(); + this.blockStateArranger.clear(); this.appearanceToRealState.clear(); + Arrays.fill(this.blockStateMappings, -1); + Arrays.fill(this.immutableBlockStates, EmptyBlock.STATE); + Arrays.fill(this.autoVisualBlockStateCandidates, null); + for (AutoStateGroup autoStateGroup : AutoStateGroup.values()) { + autoStateGroup.reset(); + } } @Override public void delayedLoad() { this.initSuggestions(); - this.clearCache(); this.resendTags(); + this.processSounds(); + this.clearCache(); } @Override - public Map blocks() { + public Map loadedBlocks() { return Collections.unmodifiableMap(this.byId); } @@ -93,24 +154,20 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem return Optional.ofNullable(this.byId.get(id)); } - protected void addBlockInternal(Key id, CustomBlock customBlock) { - this.byId.put(id, customBlock); - // generate mod assets - if (Config.generateModAssets()) { - for (ImmutableBlockState state : customBlock.variantProvider().states()) { - this.modBlockStates.put(getBlockOwnerId(state.customBlockState()), this.tempVanillaBlockStateModels.get(state.vanillaBlockState().registryId())); - } - } + public Map> blockStateArranger() { + return this.blockStateArranger; } + protected abstract void applyPlatformSettings(ImmutableBlockState state); + @Override - public ConfigParser parser() { - return this.blockParser; + public ConfigParser[] parsers() { + return new ConfigParser[]{this.blockParser, this.blockStateMappingParser}; } @Override public Map modBlockStates() { - return Collections.unmodifiableMap(this.modBlockStates); + return Collections.unmodifiableMap(this.modBlockStateOverrides); } @Override @@ -123,14 +180,24 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem return Collections.unmodifiableCollection(this.cachedSuggestions); } + @Nullable + public Key replaceSoundIfExist(Key id) { + return this.soundReplacements.get(id); + } + + @Override + public Map soundReplacements() { + return Collections.unmodifiableMap(this.soundReplacements); + } + public Set namespacesInUse() { return Collections.unmodifiableSet(this.namespacesInUse); } protected void clearCache() { - this.tempRegistryIdConflictMap.clear(); - this.tempBlockAppearanceConvertor.clear(); - this.tempVanillaBlockStateModels.clear(); + Arrays.fill(this.tempVanillaBlockStateModels, null); + this.tempVisualBlockStatesInUse.clear(); + this.tempVisualBlocksInUse.clear(); } protected void initSuggestions() { @@ -154,20 +221,149 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem return Optional.ofNullable(this.appearanceToRealState.get(appearanceStateId)).orElse(List.of()); } + public abstract BlockBehavior createBlockBehavior(CustomBlock customBlock, List> behaviorConfig); + protected abstract void resendTags(); protected abstract boolean isVanillaBlock(Key id); - protected abstract int getBlockRegistryId(Key id); - - protected abstract String stateRegistryIdToStateSNBT(int id); - protected abstract Key getBlockOwnerId(int id); - protected abstract CustomBlock.Builder platformBuilder(Key id); + protected abstract void setVanillaBlockTags(Key id, List tags); - public class BlockParser implements ConfigParser { + protected abstract int vanillaBlockStateCount(); + + protected abstract void processSounds(); + + protected abstract CustomBlock createCustomBlock(@NotNull Holder.Reference holder, + @NotNull BlockStateVariantProvider variantProvider, + @NotNull Map>> events, + @Nullable LootTable lootTable); + + public class BlockStateMappingParser extends SectionConfigParser { + public static final String[] CONFIG_SECTION_NAME = new String[]{"block-state-mappings", "block-state-mapping"}; + + @Override + public String[] sectionId() { + return CONFIG_SECTION_NAME; + } + + @Override + public int loadingSequence() { + return LoadingSequence.BLOCK_STATE_MAPPING; + } + + @Override + public void parseSection(Pack pack, Path path, Map section) throws LocalizedException { + ExceptionCollector exceptionCollector = new ExceptionCollector<>(); + for (Map.Entry entry : section.entrySet()) { + String before = entry.getKey(); + String after = entry.getValue().toString(); + // 先解析为唯一的wrapper + BlockStateWrapper beforeState = createVanillaBlockState(before); + BlockStateWrapper afterState = createVanillaBlockState(after); + if (beforeState == null) { + exceptionCollector.add(new LocalizedResourceConfigException("warning.config.block_state_mapping.invalid_state", before)); + continue; + } + if (afterState == null) { + exceptionCollector.add(new LocalizedResourceConfigException("warning.config.block_state_mapping.invalid_state", after)); + continue; + } + int previous = AbstractBlockManager.this.blockStateMappings[beforeState.registryId()]; + if (previous != -1 && previous != afterState.registryId()) { + exceptionCollector.add(new LocalizedResourceConfigException("warning.config.block_state_mapping.conflict", + beforeState.toString(), + afterState.toString(), + BlockRegistryMirror.byId(previous).toString())); + continue; + } + AbstractBlockManager.this.blockStateMappings[beforeState.registryId()] = afterState.registryId(); + Key blockOwnerId = getBlockOwnerId(beforeState); + List blockStateWrappers = AbstractBlockManager.this.blockStateArranger.computeIfAbsent(blockOwnerId, k -> new ArrayList<>()); + blockStateWrappers.add(beforeState); + AbstractBlockManager.this.autoVisualBlockStateCandidates[beforeState.registryId()] = createVisualBlockCandidate(beforeState); + } + exceptionCollector.throwIfPresent(); + } + + @Nullable + public BlockStateCandidate createVisualBlockCandidate(BlockStateWrapper blockState) { + List groups = AutoStateGroup.findGroups(blockState); + if (!groups.isEmpty()) { + BlockStateCandidate candidate = new BlockStateCandidate(blockState); + for (AutoStateGroup group : groups) { + group.addCandidate(candidate); + } + return candidate; + } + return null; + } + } + + public class BlockParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[]{"blocks", "block"}; + private final IdAllocator internalIdAllocator; + private final VisualBlockStateAllocator visualBlockStateAllocator; + private final List pendingConfigSections = new ArrayList<>(); + + public BlockParser(BlockStateCandidate[] candidates) { + this.internalIdAllocator = new IdAllocator(AbstractBlockManager.this.plugin.dataFolderPath().resolve("cache").resolve("custom-block-states.json")); + this.visualBlockStateAllocator = new VisualBlockStateAllocator(AbstractBlockManager.this.plugin.dataFolderPath().resolve("cache").resolve("visual-block-states.json"), candidates, AbstractBlockManager.this::createVanillaBlockState); + } + + public void addPendingConfigSection(PendingConfigSection section) { + this.pendingConfigSections.add(section); + } + + public IdAllocator internalIdAllocator() { + return internalIdAllocator; + } + + public VisualBlockStateAllocator visualBlockStateAllocator() { + return visualBlockStateAllocator; + } + + @Override + public void postProcess() { + this.internalIdAllocator.processPendingAllocations(); + try { + this.internalIdAllocator.saveToCache(); + } catch (IOException e) { + AbstractBlockManager.this.plugin.logger().warn("Error while saving custom block states allocation", e); + } + this.visualBlockStateAllocator.processPendingAllocations(); + try { + this.visualBlockStateAllocator.saveToCache(); + } catch (IOException e) { + AbstractBlockManager.this.plugin.logger().warn("Error while saving visual block states allocation", e); + } + } + + @Override + public void preProcess() { + this.internalIdAllocator.reset(0, Config.serverSideBlocks() - 1); + this.visualBlockStateAllocator.reset(); + try { + this.visualBlockStateAllocator.loadFromCache(); + } catch (IOException e) { + AbstractBlockManager.this.plugin.logger().warn("Error while loading visual block states allocation cache", e); + } + try { + this.internalIdAllocator.loadFromCache(); + } catch (IOException e) { + AbstractBlockManager.this.plugin.logger().warn("Error while loading custom block states allocation cache", e); + } + for (PendingConfigSection section : this.pendingConfigSections) { + ResourceConfigUtils.runCatching( + section.path(), + section.node(), + () -> parseSection(section.pack(), section.path(), section.node(), section.id(), section.config()), + () -> GsonHelper.get().toJson(section.config()) + ); + } + this.pendingConfigSections.clear(); + } @Override public String[] sectionId() { @@ -180,114 +376,286 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem } @Override - public void parseSection(Pack pack, Path path, Key id, Map section) { + public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (isVanillaBlock(id)) { - parseVanillaBlock(pack, path, id, section); + parseVanillaBlock(id, section); } else { // check duplicated config if (AbstractBlockManager.this.byId.containsKey(id)) { throw new LocalizedResourceConfigException("warning.config.block.duplicate"); } - parseCustomBlock(pack, path, id, section); + parseCustomBlock(path, node, id, section); } } - private void parseVanillaBlock(Pack pack, Path path, Key id, Map section) { + private void parseVanillaBlock(Key id, Map section) { Map settings = MiscUtils.castToMap(section.get("settings"), true); if (settings != null) { Object clientBoundTags = settings.get("client-bound-tags"); if (clientBoundTags instanceof List list) { List clientSideTags = MiscUtils.getAsStringList(list).stream().filter(ResourceLocation::isValid).toList(); - AbstractBlockManager.this.clientBoundTags.put(getBlockRegistryId(id), clientSideTags); + AbstractBlockManager.this.setVanillaBlockTags(id, clientSideTags); } } } - private void parseCustomBlock(Pack pack, Path path, Key id, Map section) { - // 获取方块设置 + private void parseCustomBlock(Path path, String node, Key id, Map section) { + // 获取共享方块设置 BlockSettings settings = BlockSettings.fromMap(id, MiscUtils.castToMap(section.get("settings"), true)); - // 读取基础外观配置 - Map> properties; - Map appearances; - Map variants; // 读取states区域 - Map stateSection = MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow( - ResourceConfigUtils.get(section, "state", "states"), "warning.config.block.missing_state"), true); + Map stateSection = MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(section, "state", "states"), "warning.config.block.missing_state"), true); boolean singleState = !stateSection.containsKey("properties"); - // 单方块状态 - if (singleState) { - int internalId = ResourceConfigUtils.getAsInt(ResourceConfigUtils.requireNonNullOrThrow( - stateSection.get("id"), "warning.config.block.state.missing_real_id"), "id"); - // 获取原版外观的注册表id - int appearanceId = pluginFormattedBlockStateToRegistryId(ResourceConfigUtils.requireNonEmptyStringOrThrow( - stateSection.get("state"), "warning.config.block.state.missing_state")); - // 为原版外观赋予外观模型并检查模型冲突 - this.arrangeModelForStateAndVerify(appearanceId, ResourceConfigUtils.get(stateSection, "model", "models")); - // 设置参数 - properties = Map.of(); - appearances = Map.of("", appearanceId); - variants = Map.of("", new BlockStateVariant("", settings, getInternalBlockId(internalId, appearanceId))); - } - // 多方块状态 - else { - properties = parseBlockProperties(ResourceConfigUtils.getAsMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("properties"), "warning.config.block.state.missing_properties"), "properties")); - appearances = parseBlockAppearances(ResourceConfigUtils.getAsMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("appearances"), "warning.config.block.state.missing_appearances"), "appearances")); - variants = parseBlockVariants( - ResourceConfigUtils.getAsMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("variants"), "warning.config.block.state.missing_variants"), "variants"), - it -> appearances.getOrDefault(it, -1), settings - ); - } + // 读取方块的property,通过property决定 + Map> properties = singleState ? Map.of() : parseBlockProperties(ResourceConfigUtils.getAsMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("properties"), "warning.config.block.state.missing_properties"), "properties")); + // 注册方块容器 + Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.BLOCK).getOrRegisterForHolder(ResourceKey.create(BuiltInRegistries.BLOCK.key().location(), id)); + // 先绑定无效方块 + holder.bindValue(new InactiveCustomBlock(holder)); - addBlockInternal(id, platformBuilder(id) - .appearances(appearances) - .variantMapper(variants) - .properties(properties) - .settings(settings) - .lootTable(LootTable.fromMap(ResourceConfigUtils.getAsMapOrNull(section.get("loot"), "loot"))) - .behavior(MiscUtils.getAsMapList(ResourceConfigUtils.get(section, "behavior", "behaviors"))) - .events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event"))) - .build()); - } + // 根据properties生成variant provider + BlockStateVariantProvider variantProvider = new BlockStateVariantProvider(holder, (owner, propertyMap) -> { + ImmutableBlockState blockState = new ImmutableBlockState(owner, propertyMap); + blockState.setSettings(settings); + return blockState; + }, properties); - private Map parseBlockVariants(Map variantsSection, - Function appearanceValidator, - BlockSettings parentSettings) { - Map variants = new HashMap<>(); - for (Map.Entry entry : variantsSection.entrySet()) { - Map variantSection = ResourceConfigUtils.getAsMap(entry.getValue(), entry.getKey()); - String variantNBT = entry.getKey(); - String appearance = ResourceConfigUtils.requireNonEmptyStringOrThrow(variantSection.get("appearance"), "warning.config.block.state.variant.missing_appearance"); - int appearanceId = appearanceValidator.apply(appearance); - if (appearanceId == -1) { - throw new LocalizedResourceConfigException("warning.config.block.state.variant.invalid_appearance", variantNBT, appearance); + ImmutableList states = variantProvider.states(); + List> internalIdAllocators = new ArrayList<>(states.size()); + + // 如果用户指定了起始id + if (stateSection.containsKey("id")) { + int startingId = ResourceConfigUtils.getAsInt(stateSection.get("id"), "id"); + int endingId = startingId + states.size() - 1; + if (startingId < 0 || endingId >= Config.serverSideBlocks()) { + throw new LocalizedResourceConfigException("warning.config.block.state.invalid_id", startingId + "~" + endingId, String.valueOf(Config.serverSideBlocks() - 1)); + } + // 先检测范围冲突 + List> conflicts = this.internalIdAllocator.getFixedIdsBetween(startingId, endingId); + if (!conflicts.isEmpty()) { + ExceptionCollector exceptionCollector = new ExceptionCollector<>(); + for (Pair conflict : conflicts) { + int internalId = conflict.right(); + int index = internalId - startingId; + exceptionCollector.add(new LocalizedResourceConfigException("warning.config.block.state.id.conflict", states.get(index).toString(), conflict.left(), BlockManager.createCustomBlockKey(internalId).toString())); + } + exceptionCollector.throwIfPresent(); + } + // 强行分配id + for (ImmutableBlockState blockState : states) { + String blockStateId = blockState.toString(); + internalIdAllocators.add(this.internalIdAllocator.assignFixedId(blockStateId, startingId++)); } - int internalId = getInternalBlockId(ResourceConfigUtils.getAsInt(ResourceConfigUtils.requireNonNullOrThrow(variantSection.get("id"), "warning.config.block.state.missing_real_id"), "id"), appearanceId); - Map anotherSetting = ResourceConfigUtils.getAsMapOrNull(variantSection.get("settings"), "settings"); - variants.put(variantNBT, new BlockStateVariant(appearance, anotherSetting == null ? parentSettings : BlockSettings.ofFullCopy(parentSettings, anotherSetting), internalId)); } - return variants; + // 未指定,则使用自动分配 + else { + for (ImmutableBlockState blockState : states) { + String blockStateId = blockState.toString(); + internalIdAllocators.add(this.internalIdAllocator.requestAutoId(blockStateId)); + } + } + + CompletableFutures.allOf(internalIdAllocators).whenComplete((v1, t1) -> ResourceConfigUtils.runCatching(path, node, () -> { + if (t1 != null) { + if (t1 instanceof CompletionException e) { + Throwable cause = e.getCause(); + // 这里不会有conflict了,因为之前已经判断过了 + if (cause instanceof IdAllocator.IdExhaustedException) { + throw new LocalizedResourceConfigException("warning.config.block.state.id.exhausted"); + } else { + Debugger.BLOCK.warn(() -> "Unknown error while allocating internal block state id.", cause); + return; + } + } + throw new RuntimeException("Unknown error occurred", t1); + } + + for (int i = 0; i < internalIdAllocators.size(); i++) { + CompletableFuture future = internalIdAllocators.get(i); + try { + int internalId = future.get(); + states.get(i).setCustomBlockState(BlockRegistryMirror.byId(internalId + AbstractBlockManager.this.vanillaBlockStateCount)); + } catch (InterruptedException | ExecutionException e) { + AbstractBlockManager.this.plugin.logger().warn("Interrupted while allocating internal block state for block " + id.asString(), e); + return; + } + } + + // 创建自定义方块 + AbstractCustomBlock customBlock = (AbstractCustomBlock) createCustomBlock( + holder, + variantProvider, + EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event")), + LootTable.fromMap(ResourceConfigUtils.getAsMapOrNull(section.get("loot"), "loot")) + ); + BlockBehavior blockBehavior = createBlockBehavior(customBlock, MiscUtils.getAsMapList(ResourceConfigUtils.get(section, "behavior", "behaviors"))); + + Map> appearanceConfigs; + Map> futureVisualStates = new HashMap<>(); + if (singleState) { + appearanceConfigs = Map.of("", stateSection); + } else { + Map rawAppearancesSection = ResourceConfigUtils.getAsMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("appearances"), "warning.config.block.state.missing_appearances"), "appearances"); + appearanceConfigs = new LinkedHashMap<>(4); + for (Map.Entry entry : rawAppearancesSection.entrySet()) { + appearanceConfigs.put(entry.getKey(), ResourceConfigUtils.getAsMap(entry.getValue(), entry.getKey())); + } + } + + for (Map.Entry> entry : appearanceConfigs.entrySet()) { + Map appearanceSection = entry.getValue(); + if (appearanceSection.containsKey("state")) { + String appearanceName = entry.getKey(); + futureVisualStates.put( + appearanceName, + this.visualBlockStateAllocator.assignFixedBlockState(appearanceName.isEmpty() ? id.asString() : id.asString() + ":" + appearanceName, parsePluginFormattedBlockState(appearanceSection.get("state").toString())) + ); + } else if (appearanceSection.containsKey("auto-state")) { + String autoStateId = appearanceSection.get("auto-state").toString(); + AutoStateGroup group = AutoStateGroup.byId(autoStateId); + if (group == null) { + throw new LocalizedResourceConfigException("warning.config.block.state.invalid_auto_state", autoStateId, EnumUtils.toString(AutoStateGroup.values())); + } + String appearanceName = entry.getKey(); + futureVisualStates.put( + appearanceName, + this.visualBlockStateAllocator.requestAutoState(appearanceName.isEmpty() ? id.asString() : id.asString() + ":" + appearanceName, group) + ); + } else { + throw new LocalizedResourceConfigException("warning.config.block.state.missing_state"); + } + } + + CompletableFutures.allOf(futureVisualStates.values()).whenComplete((v2, t2) -> ResourceConfigUtils.runCatching(path, node, () -> { + if (t2 != null) { + if (t2 instanceof CompletionException e) { + Throwable cause = e.getCause(); + if (cause instanceof VisualBlockStateAllocator.StateExhaustedException exhausted) { + throw new LocalizedResourceConfigException("warning.config.block.state.auto_state.exhausted", exhausted.group().id(), String.valueOf(exhausted.group().candidateCount())); + } else { + Debugger.BLOCK.warn(() -> "Unknown error while allocating visual block state.", cause); + return; + } + } + throw new RuntimeException("Unknown error occurred", t2); + } + + BlockStateAppearance anyAppearance = null; + Map appearances = new HashMap<>(); + for (Map.Entry> entry : appearanceConfigs.entrySet()) { + String appearanceName = entry.getKey(); + Map appearanceSection = entry.getValue(); + BlockStateWrapper visualBlockState; + try { + visualBlockState = futureVisualStates.get(appearanceName).get(); + } catch (InterruptedException | ExecutionException e) { + AbstractBlockManager.this.plugin.logger().warn("Interrupted while allocating visual block state for block " + id.asString(), e); + return; + } + this.arrangeModelForStateAndVerify(visualBlockState, ResourceConfigUtils.get(appearanceSection, "model", "models")); + BlockStateAppearance blockStateAppearance = new BlockStateAppearance(visualBlockState, parseBlockEntityRender(appearanceSection.get("entity-renderer"))); + appearances.put(appearanceName, blockStateAppearance); + if (anyAppearance == null) { + anyAppearance = blockStateAppearance; + } + } + + // 至少有一个外观吧 + Objects.requireNonNull(anyAppearance, "any appearance should not be null"); + + ExceptionCollector exceptionCollector = new ExceptionCollector<>(); + if (!singleState) { + Map variantsSection = ResourceConfigUtils.getAsMapOrNull(stateSection.get("variants"), "variants"); + if (variantsSection != null) { + for (Map.Entry entry : variantsSection.entrySet()) { + Map variantSection = ResourceConfigUtils.getAsMap(entry.getValue(), entry.getKey()); + String variantNBT = entry.getKey(); + // 先解析nbt,找到需要修改的方块状态 + CompoundTag tag = BlockNbtParser.deserialize(variantProvider, variantNBT); + if (tag == null) { + exceptionCollector.add(new LocalizedResourceConfigException("warning.config.block.state.property.invalid_format", variantNBT)); + continue; + } + List possibleStates = variantProvider.getPossibleStates(tag); + Map anotherSetting = ResourceConfigUtils.getAsMapOrNull(variantSection.get("settings"), "settings"); + if (anotherSetting != null) { + for (ImmutableBlockState possibleState : possibleStates) { + possibleState.setSettings(BlockSettings.ofFullCopy(possibleState.settings(), anotherSetting)); + } + } + String appearanceName = ResourceConfigUtils.getAsString(variantSection.get("appearance")); + if (appearanceName != null) { + BlockStateAppearance appearance = appearances.get(appearanceName); + if (appearance == null) { + exceptionCollector.add(new LocalizedResourceConfigException("warning.config.block.state.variant.invalid_appearance", variantNBT, appearanceName)); + continue; + } + for (ImmutableBlockState possibleState : possibleStates) { + possibleState.setVanillaBlockState(appearance.blockState()); + appearance.blockEntityRenderer().ifPresent(possibleState::setConstantRenderers); + } + } + } + } + } + + // 获取方块实体行为 + EntityBlockBehavior entityBlockBehavior = blockBehavior.getEntityBehavior(); + boolean isEntityBlock = entityBlockBehavior != null; + + // 绑定行为 + for (ImmutableBlockState state : states) { + if (isEntityBlock) { + state.setBlockEntityType(entityBlockBehavior.blockEntityType()); + } + state.setBehavior(blockBehavior); + int internalId = state.customBlockState().registryId(); + BlockStateWrapper visualState = state.vanillaBlockState(); + // 校验,为未绑定外观的强行添加外观 + if (visualState == null) { + visualState = anyAppearance.blockState(); + state.setVanillaBlockState(visualState); + anyAppearance.blockEntityRenderer().ifPresent(state::setConstantRenderers); + } + int appearanceId = visualState.registryId(); + int index = internalId - AbstractBlockManager.this.vanillaBlockStateCount; + AbstractBlockManager.this.immutableBlockStates[index] = state; + AbstractBlockManager.this.blockStateMappings[internalId] = appearanceId; + AbstractBlockManager.this.appearanceToRealState.computeIfAbsent(appearanceId, k -> new IntArrayList()).add(internalId); + AbstractBlockManager.this.tempVisualBlockStatesInUse.add(visualState); + AbstractBlockManager.this.tempVisualBlocksInUse.add(getBlockOwnerId(visualState)); + AbstractBlockManager.this.applyPlatformSettings(state); + // generate mod assets + if (Config.generateModAssets()) { + AbstractBlockManager.this.modBlockStateOverrides.put(BlockManager.createCustomBlockKey(index), Optional.ofNullable(AbstractBlockManager.this.tempVanillaBlockStateModels[appearanceId]) + .orElseGet(() -> { + // 如果未指定模型,说明复用原版模型?但是部分模型是多部位模型,无法使用变体解决问题 + // 未来需要靠mod重构彻底解决问题 + JsonObject json = new JsonObject(); + json.addProperty("model", "minecraft:block/air"); + return json; + })); + } + } + + // 一定要到最后再绑定 + customBlock.setBehavior(blockBehavior); + holder.bindValue(customBlock); + + // 添加方块 + AbstractBlockManager.this.byId.put(customBlock.id(), customBlock); + + // 抛出次要警告 + exceptionCollector.throwIfPresent(); + }, () -> GsonHelper.get().toJson(section))); + }, () -> GsonHelper.get().toJson(section))); } - private int getInternalBlockId(int internalId, int appearanceId) { - Key baseBlock = getBlockOwnerId(appearanceId); - Key internalBlockId = Key.of(Key.DEFAULT_NAMESPACE, baseBlock.value() + "_" + internalId); - int internalBlockRegistryId = Optional.ofNullable(AbstractBlockManager.this.internalId2StateId.get(internalBlockId)).orElse(-1); - if (internalBlockRegistryId == -1) { - throw new LocalizedResourceConfigException("warning.config.block.state.invalid_real_id", internalBlockId.toString(), String.valueOf(availableAppearances(baseBlock) - 1)); - } - return internalBlockRegistryId; - } - - private Map parseBlockAppearances(Map appearancesSection) { - Map appearances = new HashMap<>(); - for (Map.Entry entry : appearancesSection.entrySet()) { - Map appearanceSection = ResourceConfigUtils.getAsMap(entry.getValue(), entry.getKey()); - int appearanceId = pluginFormattedBlockStateToRegistryId(ResourceConfigUtils.requireNonEmptyStringOrThrow( - appearanceSection.get("state"), "warning.config.block.state.missing_state")); - this.arrangeModelForStateAndVerify(appearanceId, ResourceConfigUtils.get(appearanceSection, "model", "models")); - appearances.put(entry.getKey(), appearanceId); - } - return appearances; + @SuppressWarnings("unchecked") + private Optional[]> parseBlockEntityRender(Object arguments) { + if (arguments == null) return Optional.empty(); + List> blockEntityElementConfigs = ResourceConfigUtils.parseConfigAsList(arguments, BlockEntityElementConfigs::fromMap); + if (blockEntityElementConfigs.isEmpty()) return Optional.empty(); + return Optional.of(blockEntityElementConfigs.toArray(new BlockEntityElementConfig[0])); } @NotNull @@ -300,7 +668,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem return properties; } - private void arrangeModelForStateAndVerify(int registryId, Object modelOrModels) { + private void arrangeModelForStateAndVerify(BlockStateWrapper blockStateWrapper, Object modelOrModels) { // 如果没有配置models if (modelOrModels == null) { return; @@ -318,9 +686,10 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem } } // 拆分方块id与属性 - String blockState = stateRegistryIdToStateSNBT(registryId); - Key blockId = Key.of(blockState.substring(blockState.indexOf('{') + 1, blockState.lastIndexOf('}'))); - String propertyNBT = blockState.substring(blockState.indexOf('[') + 1, blockState.lastIndexOf(']')); + String blockState = blockStateWrapper.getAsString(); + int firstIndex = blockState.indexOf('['); + Key blockId = firstIndex == -1 ? Key.of(blockState) : Key.of(blockState.substring(0, firstIndex)); + String propertyNBT = firstIndex == -1 ? "" : blockState.substring(firstIndex + 1, blockState.lastIndexOf(']')); // 结合variants JsonElement combinedVariant = GsonHelper.combine(variants); Map overrideMap = AbstractBlockManager.this.blockStateOverrides.computeIfAbsent(blockId, k -> new HashMap<>()); @@ -329,6 +698,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem throw new LocalizedResourceConfigException("warning.config.block.state.model.conflict", GsonHelper.get().toJson(combinedVariant), blockState, GsonHelper.get().toJson(previous)); } overrideMap.put(propertyNBT, combinedVariant); + AbstractBlockManager.this.tempVanillaBlockStateModels[blockStateWrapper.registryId()] = combinedVariant; } private JsonObject parseAppearanceModelSectionAsJson(Map section) { @@ -353,7 +723,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem } // 从方块外观的state里获取其原版方块的state id - private int pluginFormattedBlockStateToRegistryId(String blockState) { + private BlockStateWrapper parsePluginFormattedBlockState(String blockState) { // 五种合理情况 // minecraft:note_block:10 // note_block:10 @@ -364,7 +734,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem if (split.length >= 4) { throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState); } - int registryId; + BlockStateWrapper wrapper; String stateOrId = split[split.length - 1]; boolean isId = false; int arrangerIndex = 0; @@ -384,14 +754,14 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem // 获取原版方块的id Key block = split.length == 2 ? Key.of(split[0]) : Key.of(split[0], split[1]); try { - List arranger = blockAppearanceArranger.get(block); + List arranger = AbstractBlockManager.this.blockStateArranger.get(block); if (arranger == null) { throw new LocalizedResourceConfigException("warning.config.block.state.unavailable_vanilla", blockState); } if (arrangerIndex >= arranger.size()) { throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla_id", blockState, String.valueOf(arranger.size() - 1)); } - registryId = arranger.get(arrangerIndex); + wrapper = arranger.get(arrangerIndex); } catch (NumberFormatException e) { throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", e, blockState); } @@ -401,9 +771,21 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem if (packedBlockState == null) { throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState); } - registryId = packedBlockState.registryId(); + wrapper = packedBlockState; } - return registryId; + return wrapper; } } + + public boolean isVanillaBlockState(int id) { + return id < this.vanillaBlockStateCount && id >= 0; + } + + public BlockParser blockParser() { + return blockParser; + } + + public BlockStateMappingParser blockStateMappingParser() { + return blockStateMappingParser; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockStateWrapper.java b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockStateWrapper.java new file mode 100644 index 000000000..4589397a0 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockStateWrapper.java @@ -0,0 +1,38 @@ +package net.momirealms.craftengine.core.block; + +public abstract class AbstractBlockStateWrapper implements BlockStateWrapper { + protected final Object blockState; + protected final int registryId; + + protected AbstractBlockStateWrapper(Object blockState, int registryId) { + this.blockState = blockState; + this.registryId = registryId; + } + + @Override + public Object literalObject() { + return this.blockState; + } + + @Override + public int registryId() { + return this.registryId; + } + + @Override + public String toString() { + return this.blockState.toString(); + } + + @Override + public final boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof BlockStateWrapper that)) return false; + return this.registryId == that.registryId(); + } + + @Override + public int hashCode() { + return this.registryId; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractCustomBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractCustomBlock.java index ec7a0ff65..01e818042 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractCustomBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractCustomBlock.java @@ -1,8 +1,6 @@ package net.momirealms.craftengine.core.block; -import com.google.common.collect.ImmutableMap; import net.momirealms.craftengine.core.block.behavior.EmptyBlockBehavior; -import net.momirealms.craftengine.core.block.parser.BlockNbtParser; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.loot.LootTable; @@ -10,7 +8,6 @@ import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.context.function.Function; -import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; import net.momirealms.sparrow.nbt.CompoundTag; @@ -22,76 +19,33 @@ import java.util.*; import java.util.function.BiFunction; public abstract class AbstractCustomBlock implements CustomBlock { - protected final Holder holder; protected final Key id; + protected final Holder.Reference holder; protected final BlockStateVariantProvider variantProvider; - protected final Map> properties; - protected final BlockBehavior behavior; protected final BiFunction placementFunction; protected final ImmutableBlockState defaultState; protected final Map>> events; @Nullable protected final LootTable lootTable; + protected BlockBehavior behavior = EmptyBlockBehavior.INSTANCE; protected AbstractCustomBlock( - @NotNull Key id, @NotNull Holder.Reference holder, - @NotNull Map> properties, - @NotNull Map appearances, - @NotNull Map variantMapper, - @NotNull BlockSettings settings, + @NotNull BlockStateVariantProvider variantProvider, @NotNull Map>> events, - @Nullable List> behaviorConfig, @Nullable LootTable lootTable ) { - holder.bindValue(this); + this.id = holder.key().location(); this.holder = holder; - this.id = id; this.lootTable = lootTable; - this.properties = ImmutableMap.copyOf(properties); this.events = events; - this.variantProvider = new BlockStateVariantProvider(holder, ImmutableBlockState::new, properties); + this.variantProvider = variantProvider; this.defaultState = this.variantProvider.getDefaultState(); - this.behavior = setupBehavior(behaviorConfig); List> placements = new ArrayList<>(4); - for (Map.Entry> propertyEntry : this.properties.entrySet()) { + for (Map.Entry> propertyEntry : this.variantProvider.properties().entrySet()) { placements.add(Property.createStateForPlacement(propertyEntry.getKey(), propertyEntry.getValue())); } this.placementFunction = composite(placements); - for (Map.Entry entry : variantMapper.entrySet()) { - String nbtString = entry.getKey(); - CompoundTag tag = BlockNbtParser.deserialize(this, nbtString); - if (tag == null) { - throw new LocalizedResourceConfigException("warning.config.block.state.property.invalid_format", nbtString); - } - List possibleStates = this.getPossibleStates(tag); - if (possibleStates.size() != 1) { - throw new LocalizedResourceConfigException("warning.config.block.state.property.invalid_format", nbtString); - } - BlockStateVariant blockStateVariant = entry.getValue(); - int vanillaStateRegistryId = appearances.getOrDefault(blockStateVariant.appearance(), -1); - // This should never happen - if (vanillaStateRegistryId == -1) { - vanillaStateRegistryId = appearances.values().iterator().next(); - } - // Late init states - ImmutableBlockState state = possibleStates.getFirst(); - state.setSettings(blockStateVariant.settings()); - state.setVanillaBlockState(BlockRegistryMirror.stateByRegistryId(vanillaStateRegistryId)); - state.setCustomBlockState(BlockRegistryMirror.stateByRegistryId(blockStateVariant.internalRegistryId())); - } - // double check if there's any invalid state - for (ImmutableBlockState state : this.variantProvider().states()) { - state.setBehavior(this.behavior); - if (state.settings() == null) { - state.setSettings(settings); - } - } - this.applyPlatformSettings(); - } - - protected BlockBehavior setupBehavior(List> behaviorConfig) { - return EmptyBlockBehavior.INSTANCE; } private static BiFunction composite(List> placements) { @@ -112,8 +66,6 @@ public abstract class AbstractCustomBlock implements CustomBlock { }; } - protected abstract void applyPlatformSettings(); - @Override public @Nullable LootTable lootTable() { return this.lootTable; @@ -138,25 +90,13 @@ public abstract class AbstractCustomBlock implements CustomBlock { return this.id; } + public void setBehavior(@Nullable BlockBehavior behavior) { + this.behavior = behavior; + } + @Override public List getPossibleStates(CompoundTag nbt) { - List tempStates = new ArrayList<>(); - tempStates.add(defaultState()); - for (Property property : this.variantProvider.getDefaultState().getProperties()) { - Tag value = nbt.get(property.name()); - if (value != null) { - tempStates.replaceAll(immutableBlockState -> ImmutableBlockState.with(immutableBlockState, property, property.unpack(value))); - } else { - List newStates = new ArrayList<>(); - for (ImmutableBlockState state : tempStates) { - for (Object possibleValue : property.possibleValues()) { - newStates.add(ImmutableBlockState.with(state, property, possibleValue)); - } - } - tempStates = newStates; - } - } - return tempStates; + return this.variantProvider.getPossibleStates(nbt); } @Override @@ -177,12 +117,12 @@ public abstract class AbstractCustomBlock implements CustomBlock { @Override public @Nullable Property getProperty(String name) { - return this.properties.get(name); + return this.variantProvider.getProperty(name); } @Override public @NotNull Collection> properties() { - return this.properties.values(); + return this.variantProvider.properties().values(); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/AutoStateGroup.java b/core/src/main/java/net/momirealms/craftengine/core/block/AutoStateGroup.java new file mode 100644 index 000000000..f917f72b4 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/AutoStateGroup.java @@ -0,0 +1,117 @@ +package net.momirealms.craftengine.core.block; + +import net.momirealms.craftengine.core.pack.allocator.BlockStateCandidate; +import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.Predicate; + +public enum AutoStateGroup { + LEAVES("leaves", + Set.of(BlockKeys.OAK_LEAVES, BlockKeys.SPRUCE_LEAVES, BlockKeys.BIRCH_LEAVES, BlockKeys.JUNGLE_LEAVES, BlockKeys.ACACIA_LEAVES, BlockKeys.DARK_OAK_LEAVES, BlockKeys.MANGROVE_LEAVES, BlockKeys.CHERRY_LEAVES, BlockKeys.PALE_OAK_LEAVES, BlockKeys.AZALEA_LEAVES, BlockKeys.FLOWERING_AZALEA_LEAVES), + (w) -> !(boolean) w.getProperty("waterlogged") + ), + WATERLOGGED_LEAVES( + "waterlogged_leaves", + Set.of(BlockKeys.OAK_LEAVES, BlockKeys.SPRUCE_LEAVES, BlockKeys.BIRCH_LEAVES, BlockKeys.JUNGLE_LEAVES, BlockKeys.ACACIA_LEAVES, BlockKeys.DARK_OAK_LEAVES, BlockKeys.MANGROVE_LEAVES, BlockKeys.CHERRY_LEAVES, BlockKeys.PALE_OAK_LEAVES, BlockKeys.AZALEA_LEAVES, BlockKeys.FLOWERING_AZALEA_LEAVES), + (w) -> w.getProperty("waterlogged") + ), + LOWER_TRIPWIRE("lower_tripwire", Set.of(BlockKeys.TRIPWIRE), (w) -> w.getProperty("attached")), + HIGHER_TRIPWIRE("higher_tripwire", Set.of(BlockKeys.TRIPWIRE), (w) -> !(boolean) w.getProperty("attached")), + NOTE_BLOCK("note_block", Set.of(BlockKeys.NOTE_BLOCK), (w) -> true), + BROWN_MUSHROOM("brown_mushroom", Set.of(BlockKeys.BROWN_MUSHROOM_BLOCK), (w) -> true), + RED_MUSHROOM("red_mushroom", Set.of(BlockKeys.RED_MUSHROOM_BLOCK), (w) -> true), + MUSHROOM_STEM("mushroom_stem", Set.of(BlockKeys.MUSHROOM_STEM), (w) -> true), + TRIPWIRE("tripwire", Set.of(BlockKeys.TRIPWIRE), (w) -> true), + SUGAR_CANE("sugar_cane", Set.of(BlockKeys.SUGAR_CANE), (w) -> true), + CACTUS("cactus", Set.of(BlockKeys.CACTUS), (w) -> true), + SAPLING("sapling", Set.of(BlockKeys.OAK_SAPLING, BlockKeys.SPRUCE_SAPLING, BlockKeys.BIRCH_SAPLING, BlockKeys.JUNGLE_SAPLING, BlockKeys.ACACIA_SAPLING, BlockKeys.DARK_OAK_SAPLING, BlockKeys.CHERRY_SAPLING, BlockKeys.PALE_OAK_SAPLING), (w) -> true), + MUSHROOM("mushroom", Set.of(BlockKeys.BROWN_MUSHROOM_BLOCK, BlockKeys.RED_MUSHROOM_BLOCK, BlockKeys.MUSHROOM_STEM), (w) -> true), + SOLID("solid", Set.of(BlockKeys.BROWN_MUSHROOM_BLOCK, BlockKeys.RED_MUSHROOM_BLOCK, BlockKeys.MUSHROOM_STEM, BlockKeys.NOTE_BLOCK), (w) -> true); + + private final Set blocks; + private final String id; + private final Predicate predicate; + private final List candidates = new ArrayList<>(); + private int pointer; + + AutoStateGroup(String id, Set blocks, Predicate predicate) { + this.id = id; + this.blocks = blocks; + this.predicate = predicate; + } + + public void reset() { + this.pointer = 0; + this.candidates.clear(); + } + + public void addCandidate(@NotNull BlockStateCandidate candidate) { + this.candidates.add(candidate); + } + + public int candidateCount() { + return candidates.size(); + } + + @Nullable + public BlockStateCandidate findNextCandidate() { + while (this.pointer < this.candidates.size()) { + final BlockStateCandidate state = this.candidates.get(this.pointer); + if (!state.isUsed()) { + return state; + } + this.pointer++; + } + return null; + } + + public boolean test(BlockStateWrapper state) { + if (!this.blocks.contains(state.ownerId())) { + return false; + } + return this.predicate.test(state); + } + + public Set blocks() { + return blocks; + } + + public String id() { + return id; + } + + private static final Map BY_ID = new HashMap<>(); + private static final Map> BY_BLOCKS = new HashMap<>(); + + static { + for (AutoStateGroup group : AutoStateGroup.values()) { + BY_ID.put(group.id(), group); + BY_ID.put(group.id().toUpperCase(Locale.ROOT), group); + for (Key key : group.blocks) { + BY_BLOCKS.computeIfAbsent(key, k -> new ArrayList<>(4)).add(group); + } + } + } + + @Nullable + public static AutoStateGroup byId(String id) { + return BY_ID.get(id); + } + + public static List findGroups(BlockStateWrapper wrapper) { + return findGroups(wrapper.ownerId(), wrapper); + } + + public static List findGroups(Key id, BlockStateWrapper wrapper) { + List groups = BY_BLOCKS.get(id); + if (groups == null) return Collections.emptyList(); + List result = new ArrayList<>(groups.size()); + for (AutoStateGroup group : groups) { + if (group.predicate.test(wrapper)) result.add(group); + } + return result; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java index 4af326d66..738dd94ea 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockBehavior.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.block; +import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior; import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.item.CustomItem; import net.momirealms.craftengine.core.item.Item; @@ -10,6 +11,7 @@ import net.momirealms.craftengine.core.item.context.UseOnContext; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; import java.util.Optional; import java.util.concurrent.Callable; @@ -24,6 +26,14 @@ public abstract class BlockBehavior { return Optional.empty(); } + @Nullable + public EntityBlockBehavior getEntityBehavior() { + if (this instanceof EntityBlockBehavior behavior) { + return behavior; + } + return null; + } + // BlockState state, Rotation rotation public Object rotate(Object thisBlock, Object[] args, Callable superMethod) throws Exception { return superMethod.call(); @@ -45,18 +55,18 @@ public abstract class BlockBehavior { superMethod.call(); } - // ServerLevel level, BlockPos pos, RandomSource random + // BlockState state, ServerLevel level, BlockPos pos, RandomSource random public void tick(Object thisBlock, Object[] args, Callable superMethod) throws Exception { superMethod.call(); } - // ServerLevel level, BlockPos pos, RandomSource random + // BlockState state, ServerLevel level, BlockPos pos, RandomSource random public void randomTick(Object thisBlock, Object[] args, Callable superMethod) throws Exception { superMethod.call(); } // 1.20-1.20.4 BlockState state, Level world, BlockPos pos, BlockState oldState, boolean notify, UseOnContext context - // 1.20.5+ Level level, BlockPos pos, BlockState oldState, boolean movedByPiston + // 1.20.5+ BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston public void onPlace(Object thisBlock, Object[] args, Callable superMethod) throws Exception { superMethod.call(); } @@ -85,6 +95,22 @@ public abstract class BlockBehavior { return false; } + // BlockState state + public boolean hasAnalogOutputSignal(Object thisBlock, Object[] args) throws Exception { + return false; + } + + // 1.20.1~1.21.8 BlockState state, Level level, BlockPos pos + // 1.21.9+ BlockState state, Level level, BlockPos pos + public int getAnalogOutputSignal(Object thisBlock, Object[] args) throws Exception { + return 0; + } + + // BlockState state, LevelAccessor level, BlockPos pos + public Object getContainer(Object thisBlock, Object[] args) throws Exception { + return null; + } + // Level level, RandomSource random, BlockPos pos, BlockState state public boolean isBoneMealSuccess(Object thisBlock, Object[] args) throws Exception { return false; @@ -153,6 +179,14 @@ public abstract class BlockBehavior { public void spawnAfterBreak(Object thisBlock, Object[] args, Callable superMethod) throws Exception { } + // Level level, BlockPos pos, BlockState state, Entity entity + public void stepOn(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + } + + // Level level, BlockState state, BlockHitResult hit, Projectile projectile + public void onProjectileHit(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + } + public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) { return state; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockEntityState.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockEntityState.java deleted file mode 100644 index e6ac593f0..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockEntityState.java +++ /dev/null @@ -1,17 +0,0 @@ -package net.momirealms.craftengine.core.block; - -import net.momirealms.sparrow.nbt.CompoundTag; -import org.jetbrains.annotations.ApiStatus; - -@ApiStatus.Experimental -public class BlockEntityState { - private final CompoundTag nbt; - - public BlockEntityState(CompoundTag nbt) { - this.nbt = nbt.deepClone(); - } - - public CompoundTag nbt() { - return this.nbt; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockKeys.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockKeys.java index 39b79afb0..05235e211 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockKeys.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockKeys.java @@ -2,10 +2,15 @@ package net.momirealms.craftengine.core.block; import net.momirealms.craftengine.core.util.Key; +import java.util.List; + public final class BlockKeys { private BlockKeys() {} // 特殊 public static final Key AIR = Key.of("minecraft:air"); + + public static final Key SUGAR_CANE = Key.of("minecraft:sugar_cane"); + public static final Key NOTE_BLOCK = Key.of("minecraft:note_block"); public static final Key TRIPWIRE = Key.of("minecraft:tripwire"); public static final Key CACTUS = Key.of("minecraft:cactus"); public static final Key POWDER_SNOW = Key.of("minecraft:powder_snow"); @@ -299,6 +304,33 @@ public final class BlockKeys { public static final Key BAMBOO_WALL_HANGING_SIGN = Key.of("minecraft:bamboo_wall_hanging_sign"); public static final Key CRIMSON_WALL_HANGING_SIGN = Key.of("minecraft:crimson_wall_hanging_sign"); public static final Key WARPED_WALL_HANGING_SIGN = Key.of("minecraft:warped_wall_hanging_sign"); + + public static final Key CACTUS = Key.of("minecraft:cactus"); + + public static final Key BROWN_MUSHROOM_BLOCK = Key.of("minecraft:brown_mushroom_block"); + public static final Key RED_MUSHROOM_BLOCK = Key.of("minecraft:red_mushroom_block"); + public static final Key MUSHROOM_STEM = Key.of("minecraft:mushroom_stem"); + + public static final Key OAK_LEAVES = Key.of("minecraft:oak_leaves"); + public static final Key SPRUCE_LEAVES = Key.of("minecraft:spruce_leaves"); + public static final Key BIRCH_LEAVES = Key.of("minecraft:birch_leaves"); + public static final Key JUNGLE_LEAVES = Key.of("minecraft:jungle_leaves"); + public static final Key ACACIA_LEAVES = Key.of("minecraft:acacia_leaves"); + public static final Key DARK_OAK_LEAVES = Key.of("minecraft:dark_oak_leaves"); + public static final Key MANGROVE_LEAVES = Key.of("minecraft:mangrove_leaves"); + public static final Key CHERRY_LEAVES = Key.of("minecraft:cherry_leaves"); + public static final Key PALE_OAK_LEAVES = Key.of("minecraft:pale_oak_leaves"); + public static final Key AZALEA_LEAVES = Key.of("minecraft:azalea_leaves"); + public static final Key FLOWERING_AZALEA_LEAVES = Key.of("minecraft:flowering_azalea_leaves"); + + public static final Key OAK_SAPLING = Key.of("minecraft:oak_sapling"); + public static final Key SPRUCE_SAPLING = Key.of("minecraft:spruce_sapling"); + public static final Key BIRCH_SAPLING = Key.of("minecraft:birch_sapling"); + public static final Key JUNGLE_SAPLING = Key.of("minecraft:jungle_sapling"); + public static final Key DARK_OAK_SAPLING = Key.of("minecraft:dark_oak_sapling"); + public static final Key ACACIA_SAPLING = Key.of("minecraft:acacia_sapling"); + public static final Key CHERRY_SAPLING = Key.of("minecraft:cherry_sapling"); + public static final Key PALE_OAK_SAPLING = Key.of("minecraft:pale_oak_sapling"); public static final Key[] BUTTONS = new Key[]{ OAK_BUTTON, SPRUCE_BUTTON, BIRCH_BUTTON, JUNGLE_BUTTON, ACACIA_BUTTON, DARK_OAK_BUTTON, MANGROVE_BUTTON, CHERRY_BUTTON, @@ -316,4 +348,33 @@ public final class BlockKeys { REDSTONE_WIRE, REDSTONE_TORCH, REDSTONE_BLOCK, REPEATER, COMPARATOR, TARGET, LEVER, SCULK_SENSOR, CALIBRATED_SCULK_SENSOR, TRIPWIRE_HOOK, LECTERN, DAYLIGHT_DETECTOR, LIGHTNING_ROD, TRAPPED_CHEST, JUKEBOX, OBSERVER, DETECTOR_RAIL }; + + public static final List WOODEN_TRAPDOORS = List.of(OAK_TRAPDOOR, SPRUCE_TRAPDOOR, BIRCH_TRAPDOOR, + ACACIA_TRAPDOOR, PALE_OAK_TRAPDOOR, DARK_OAK_TRAPDOOR, MANGROVE_TRAPDOOR, JUNGLE_TRAPDOOR); + public static final List CHERRY_TRAPDOORS = List.of(CHERRY_TRAPDOOR); + public static final List BAMBOO_TRAPDOORS = List.of(BAMBOO_TRAPDOOR); + public static final List NETHER_TRAPDOORS = List.of(WARPED_TRAPDOOR, CRIMSON_TRAPDOOR); + public static final List COPPER_TRAPDOORS = List.of(COPPER_TRAPDOOR, EXPOSED_COPPER_TRAPDOOR, WEATHERED_COPPER_TRAPDOOR, OXIDIZED_COPPER_DOOR, + WAXED_COPPER_TRAPDOOR, WAXED_EXPOSED_COPPER_TRAPDOOR, WAXED_WEATHERED_COPPER_TRAPDOOR, WAXED_OXIDIZED_COPPER_TRAPDOOR); + + public static final List WOODEN_DOORS = List.of(OAK_DOOR, SPRUCE_DOOR, BIRCH_DOOR, + ACACIA_DOOR, PALE_OAK_DOOR, DARK_OAK_DOOR, MANGROVE_DOOR, JUNGLE_DOOR); + public static final List CHERRY_DOORS = List.of(CHERRY_DOOR); + public static final List BAMBOO_DOORS = List.of(BAMBOO_DOOR); + public static final List NETHER_DOORS = List.of(WARPED_DOOR, CRIMSON_DOOR); + public static final List COPPER_DOORS = List.of(COPPER_DOOR, EXPOSED_COPPER_DOOR, WEATHERED_COPPER_DOOR, OXIDIZED_COPPER_DOOR, + WAXED_COPPER_DOOR, WAXED_EXPOSED_COPPER_DOOR, WAXED_WEATHERED_COPPER_DOOR, WAXED_OXIDIZED_COPPER_DOOR); + + public static final List WOODEN_FENCE_GATES = List.of(OAK_FENCE_GATE, SPRUCE_FENCE_GATE, BIRCH_FENCE_GATE, + ACACIA_FENCE_GATE, PALE_OAK_FENCE_GATE, DARK_OAK_FENCE_GATE, MANGROVE_FENCE_GATE, JUNGLE_FENCE_GATE); + public static final List CHERRY_FENCE_GATES = List.of(CHERRY_FENCE_GATE); + public static final List BAMBOO_FENCE_GATES = List.of(BAMBOO_FENCE_GATE); + public static final List NETHER_FENCE_GATES = List.of(WARPED_FENCE_GATE, CRIMSON_FENCE_GATE); + + public static final List WOODEN_BUTTONS = List.of(OAK_BUTTON, SPRUCE_BUTTON, BIRCH_BUTTON, JUNGLE_BUTTON, + ACACIA_BUTTON, DARK_OAK_BUTTON, PALE_OAK_BUTTON, MANGROVE_BUTTON); + public static final List CHERRY_BUTTONS = List.of(CHERRY_BUTTON); + public static final List BAMBOO_BUTTONS = List.of(BAMBOO_BUTTON); + public static final List NETHER_BUTTONS = List.of(CRIMSON_BUTTON, WARPED_BUTTON); + public static final List STONE_BUTTONS = List.of(STONE_BUTTON, POLISHED_BLACKSTONE_BUTTON); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockManager.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockManager.java index 1f6568636..1246a2bae 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockManager.java @@ -16,7 +16,7 @@ import java.util.Optional; public interface BlockManager extends Manageable, ModelGenerator { - ConfigParser parser(); + ConfigParser[] parsers(); Collection modelsToGenerate(); @@ -24,15 +24,18 @@ public interface BlockManager extends Manageable, ModelGenerator { Map modBlockStates(); - Map blocks(); + Map loadedBlocks(); + + @Deprecated(forRemoval = true) + default Map blocks() { + return loadedBlocks(); + } Optional blockById(Key key); Collection cachedSuggestions(); - Map soundMapper(); - - int availableAppearances(Key blockType); + Map soundReplacements(); Key getBlockOwnerId(BlockStateWrapper state); @@ -44,4 +47,11 @@ public interface BlockManager extends Manageable, ModelGenerator { @Nullable BlockStateWrapper createBlockState(String blockState); + + @Nullable + BlockStateWrapper createVanillaBlockState(String blockState); + + static Key createCustomBlockKey(int id) { + return Key.of("craftengine", "custom_" + id); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockRegistryMirror.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockRegistryMirror.java index acd70d21e..ccc19d42e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockRegistryMirror.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockRegistryMirror.java @@ -5,14 +5,14 @@ public final class BlockRegistryMirror { private static BlockStateWrapper stoneState; public static void init(BlockStateWrapper[] states, BlockStateWrapper state) { - if (blockStates != null) throw new IllegalStateException("block states are already set"); + if (blockStates != null) throw new IllegalStateException("block states have already been set"); blockStates = states; stoneState = state; } - public static BlockStateWrapper stateByRegistryId(int vanillaId) { - if (vanillaId < 0) return stoneState; - return blockStates[vanillaId]; + public static BlockStateWrapper byId(int stateId) { + if (stateId < 0) return stoneState; + return blockStates[stateId]; } public static int size() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockSounds.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockSounds.java index 88d517857..2997c5dfa 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockSounds.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockSounds.java @@ -1,7 +1,6 @@ package net.momirealms.craftengine.core.block; import net.momirealms.craftengine.core.sound.SoundData; -import net.momirealms.craftengine.core.util.Key; import java.util.Map; @@ -15,25 +14,20 @@ public final class BlockSounds { Land 0.3 1 Destroy 1 1 */ - public static final SoundData EMPTY_SOUND = new SoundData(Key.of("minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_1); - public static final BlockSounds EMPTY = new BlockSounds(EMPTY_SOUND, EMPTY_SOUND, EMPTY_SOUND, EMPTY_SOUND, EMPTY_SOUND, EMPTY_SOUND, EMPTY_SOUND); + public static final BlockSounds EMPTY = new BlockSounds(SoundData.EMPTY, SoundData.EMPTY, SoundData.EMPTY, SoundData.EMPTY, SoundData.EMPTY); private final SoundData breakSound; private final SoundData stepSound; private final SoundData placeSound; private final SoundData hitSound; private final SoundData fallSound; - private final SoundData landSound; - private final SoundData destroySound; - public BlockSounds(SoundData breakSound, SoundData stepSound, SoundData placeSound, SoundData hitSound, SoundData fallSound, SoundData landSound, SoundData destroySound) { + public BlockSounds(SoundData breakSound, SoundData stepSound, SoundData placeSound, SoundData hitSound, SoundData fallSound) { this.breakSound = breakSound; this.stepSound = stepSound; this.placeSound = placeSound; this.hitSound = hitSound; this.fallSound = fallSound; - this.landSound = landSound; - this.destroySound = destroySound; } public static BlockSounds fromMap(Map map) { @@ -43,16 +37,10 @@ public final class BlockSounds { SoundData.create(map.getOrDefault("step", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_0_15, SoundData.SoundValue.FIXED_1), SoundData.create(map.getOrDefault("place", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_0_8), SoundData.create(map.getOrDefault("hit", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_0_5, SoundData.SoundValue.FIXED_0_5), - SoundData.create(map.getOrDefault("fall", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_0_5, SoundData.SoundValue.FIXED_0_75), - SoundData.create(map.getOrDefault("land", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_0_3, SoundData.SoundValue.FIXED_1), - SoundData.create(map.getOrDefault("destroy", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_1) + SoundData.create(map.getOrDefault("fall", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_0_5, SoundData.SoundValue.FIXED_0_75) ); } - public SoundData destroySound() { - return destroySound; - } - public SoundData breakSound() { return breakSound; } @@ -69,10 +57,6 @@ public final class BlockSounds { return hitSound; } - public SoundData landSound() { - return landSound; - } - public SoundData fallSound() { return fallSound; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateAppearance.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateAppearance.java new file mode 100644 index 000000000..75e39df37 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateAppearance.java @@ -0,0 +1,9 @@ +package net.momirealms.craftengine.core.block; + +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; + +import java.util.Optional; + +public record BlockStateAppearance(BlockStateWrapper blockState, Optional[]> blockEntityRenderer) { +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateHolder.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateHolder.java index 445f9c518..eaa50da56 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateHolder.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateHolder.java @@ -9,11 +9,11 @@ import java.util.*; import java.util.stream.Collectors; public class BlockStateHolder { - protected final Holder owner; + protected final Holder.Reference owner; private final Reference2ObjectArrayMap, Comparable> propertyMap; private Map, ImmutableBlockState[]> withMap; - public BlockStateHolder(Holder owner, Reference2ObjectArrayMap, Comparable> propertyMap) { + public BlockStateHolder(Holder.Reference owner, Reference2ObjectArrayMap, Comparable> propertyMap) { this.owner = owner; this.propertyMap = new Reference2ObjectArrayMap<>(propertyMap); } @@ -39,9 +39,9 @@ public class BlockStateHolder { @Override public String toString() { if (this.propertyMap.isEmpty()) { - return this.owner.value().id().toString(); + return this.owner.key().location().toString(); } - return this.owner.value().id() + "[" + getPropertiesAsString() + "]"; + return this.owner.key().location() + "[" + getPropertiesAsString() + "]"; } public String getPropertiesAsString() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateVariant.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateVariant.java deleted file mode 100644 index cfe81345b..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateVariant.java +++ /dev/null @@ -1,25 +0,0 @@ -package net.momirealms.craftengine.core.block; - -public class BlockStateVariant { - private final String appearance; - private final BlockSettings settings; - private final int internalId; - - public BlockStateVariant(String appearance, BlockSettings settings, int internalId) { - this.appearance = appearance; - this.settings = settings; - this.internalId = internalId; - } - - public String appearance() { - return appearance; - } - - public BlockSettings settings() { - return settings; - } - - public int internalRegistryId() { - return internalId; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateVariantProvider.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateVariantProvider.java index 8b131563f..75c6c9080 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateVariantProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateVariantProvider.java @@ -8,6 +8,8 @@ import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Pair; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.Tag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -23,7 +25,7 @@ public final class BlockStateVariantProvider { private final ImmutableList states; private final Holder owner; - public BlockStateVariantProvider(Holder owner, Factory, ImmutableBlockState> factory, Map> propertiesMap) { + public BlockStateVariantProvider(Holder.Reference owner, Factory, ImmutableBlockState> factory, Map> propertiesMap) { this.owner = owner; this.properties = ImmutableSortedMap.copyOf(propertiesMap); @@ -59,6 +61,27 @@ public final class BlockStateVariantProvider { this.states = ImmutableList.copyOf(list); } + public List getPossibleStates(CompoundTag nbt) { + List tempStates = new ArrayList<>(); + ImmutableBlockState defaultState = getDefaultState(); + tempStates.add(defaultState); + for (Property property : defaultState.getProperties()) { + Tag value = nbt.get(property.name()); + if (value != null) { + tempStates.replaceAll(immutableBlockState -> ImmutableBlockState.with(immutableBlockState, property, property.unpack(value))); + } else { + List newStates = new ArrayList<>(); + for (ImmutableBlockState state : tempStates) { + for (Object possibleValue : property.possibleValues()) { + newStates.add(ImmutableBlockState.with(state, property, possibleValue)); + } + } + tempStates = newStates; + } + } + return tempStates; + } + public interface Factory { S create(O owner, Reference2ObjectArrayMap, Comparable> propertyMap); } @@ -80,6 +103,11 @@ public final class BlockStateVariantProvider { return this.owner; } + @NotNull + public ImmutableSortedMap> properties() { + return this.properties; + } + @Nullable public Property getProperty(String name) { return this.properties.get(name); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateWrapper.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateWrapper.java index fb880292e..5ef5223b1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateWrapper.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateWrapper.java @@ -1,8 +1,24 @@ package net.momirealms.craftengine.core.block; -public interface BlockStateWrapper { +import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.NotNull; + +public interface BlockStateWrapper extends Comparable { Object literalObject(); int registryId(); + + Key ownerId(); + + T getProperty(String propertyName); + + boolean hasProperty(String propertyName); + + String getAsString(); + + @Override + default int compareTo(@NotNull BlockStateWrapper o) { + return Integer.compare(registryId(), o.registryId()); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java index c40700c12..83932728d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java @@ -5,7 +5,6 @@ import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; -import net.momirealms.craftengine.core.plugin.context.function.Function; import net.momirealms.craftengine.core.util.Key; import net.momirealms.sparrow.nbt.CompoundTag; import org.jetbrains.annotations.NotNull; @@ -13,17 +12,18 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.List; -import java.util.Map; public interface CustomBlock { Key id(); - @Nullable LootTable lootTable(); + @Nullable + LootTable lootTable(); void execute(PlayerOptionalContext context, EventTrigger trigger); - @NotNull BlockStateVariantProvider variantProvider(); + @NotNull + BlockStateVariantProvider variantProvider(); List getPossibleStates(CompoundTag nbt); @@ -38,23 +38,4 @@ public interface CustomBlock { ImmutableBlockState getStateForPlacement(BlockPlaceContext context); void setPlacedBy(BlockPlaceContext context, ImmutableBlockState state); - - interface Builder { - - Builder events(Map>> events); - - Builder appearances(Map appearances); - - Builder behavior(List> behavior); - - Builder lootTable(LootTable lootTable); - - Builder properties(Map> properties); - - Builder settings(BlockSettings settings); - - Builder variantMapper(Map variantMapper); - - @NotNull CustomBlock build(); - } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/DelegatingBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/DelegatingBlock.java index ee4b7687f..e3bb1ae92 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/DelegatingBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/DelegatingBlock.java @@ -32,9 +32,11 @@ public interface DelegatingBlock { */ ObjectHolder behaviorDelegate(); + // 其实是错误的做法 @Deprecated boolean isNoteBlock(); + // 其实是错误的做法 @Deprecated boolean isTripwire(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/EmptyBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/EmptyBlock.java index 6570e95e8..a095e3dc4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/EmptyBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/EmptyBlock.java @@ -1,22 +1,32 @@ package net.momirealms.craftengine.core.block; +import net.momirealms.craftengine.core.block.behavior.EmptyBlockBehavior; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; +import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceKey; -import java.util.List; import java.util.Map; public final class EmptyBlock extends AbstractCustomBlock { - public static EmptyBlock INSTANCE; - public static ImmutableBlockState STATE; + public static final EmptyBlock INSTANCE; + public static final ImmutableBlockState STATE; - public EmptyBlock(Key id, Holder.Reference holder) { - super(id, holder, Map.of(), Map.of(), Map.of(), BlockSettings.of(), Map.of(), List.of(), null); - INSTANCE = this; - STATE = defaultState(); + static { + Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.BLOCK) + .registerForHolder(ResourceKey.create(BuiltInRegistries.BLOCK.key().location(), Key.withDefaultNamespace("empty"))); + INSTANCE = new EmptyBlock(holder); + holder.bindValue(INSTANCE); + STATE = INSTANCE.defaultState(); + STATE.setSettings(BlockSettings.of()); + STATE.setBehavior(EmptyBlockBehavior.INSTANCE); } - @Override - protected void applyPlatformSettings() { + private EmptyBlock(Holder.Reference holder) { + super(holder, new BlockStateVariantProvider(holder, ImmutableBlockState::new, Map.of()), Map.of(), null); + } + + public static void initialize() { } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java b/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java index fbf649bba..8a68b9527 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java @@ -1,6 +1,12 @@ package net.momirealms.craftengine.core.block; import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap; +import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.entity.BlockEntityType; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; +import net.momirealms.craftengine.core.block.entity.tick.BlockEntityTicker; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; @@ -8,6 +14,7 @@ import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.registry.Holder; +import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.World; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.NBT; @@ -22,11 +29,13 @@ public final class ImmutableBlockState extends BlockStateHolder { private BlockStateWrapper customBlockState; private BlockStateWrapper vanillaBlockState; private BlockBehavior behavior; - private Integer hashCode; private BlockSettings settings; + private BlockEntityType blockEntityType; + @Nullable + private BlockEntityElementConfig[] renderers; ImmutableBlockState( - Holder owner, + Holder.Reference owner, Reference2ObjectArrayMap, Comparable> propertyMap ) { super(owner, propertyMap); @@ -48,23 +57,32 @@ public final class ImmutableBlockState extends BlockStateHolder { this.settings = settings; } + public BlockEntityType blockEntityType() { + return blockEntityType; + } + + public void setBlockEntityType(BlockEntityType blockEntityType) { + this.blockEntityType = blockEntityType; + } + public boolean isEmpty() { return this == EmptyBlock.STATE; } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ImmutableBlockState state)) return false; - return state.owner == this.owner && state.tag.equals(this.tag); + public BlockEntityElementConfig[] constantRenderers() { + return this.renderers; } - @Override - public int hashCode() { - if (this.hashCode == null) { - this.hashCode = getNbtToSave().hashCode(); - } - return this.hashCode; + public void setConstantRenderers(BlockEntityElementConfig[] renderers) { + this.renderers = renderers; + } + + public boolean hasBlockEntity() { + return this.blockEntityType != null; + } + + public boolean hasConstantBlockEntityRenderer() { + return this.renderers != null; } public BlockStateWrapper customBlockState() { @@ -111,7 +129,7 @@ public final class ImmutableBlockState extends BlockStateHolder { public CompoundTag toNbtToSave(CompoundTag properties) { CompoundTag tag = new CompoundTag(); tag.put("properties", properties); - tag.put("id", NBT.createString(this.owner.value().id().asString())); + tag.put("id", NBT.createString(this.owner.key().location().asString())); return tag; } @@ -132,4 +150,18 @@ public final class ImmutableBlockState extends BlockStateHolder { if (lootTable == null) return List.of(); return lootTable.getRandomItems(builder.withParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, this).build(), world, player); } + + @SuppressWarnings("unchecked") + public BlockEntityTicker createSyncBlockEntityTicker(CEWorld world, BlockEntityType type) { + EntityBlockBehavior blockBehavior = this.behavior.getEntityBehavior(); + if (blockBehavior == null) return null; + return (BlockEntityTicker) blockBehavior.createSyncBlockEntityTicker(world, this, type); + } + + @SuppressWarnings("unchecked") + public BlockEntityTicker createAsyncBlockEntityTicker(CEWorld world, BlockEntityType type) { + EntityBlockBehavior blockBehavior = this.behavior.getEntityBehavior(); + if (blockBehavior == null) return null; + return (BlockEntityTicker) blockBehavior.createAsyncBlockEntityTicker(world, this, type); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/InactiveCustomBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/InactiveCustomBlock.java index a68e0c461..524dc7865 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/InactiveCustomBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/InactiveCustomBlock.java @@ -1,29 +1,25 @@ package net.momirealms.craftengine.core.block; import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap; +import net.momirealms.craftengine.core.block.behavior.EmptyBlockBehavior; import net.momirealms.craftengine.core.registry.Holder; -import net.momirealms.craftengine.core.util.Key; import net.momirealms.sparrow.nbt.CompoundTag; import java.util.HashMap; -import java.util.List; import java.util.Map; public final class InactiveCustomBlock extends AbstractCustomBlock { private final Map cachedData = new HashMap<>(); - public InactiveCustomBlock(Key id, Holder.Reference holder) { - super(id, holder, Map.of(), Map.of(), Map.of(), BlockSettings.of(), Map.of(), List.of(), null); - } - - @Override - protected void applyPlatformSettings() { + public InactiveCustomBlock(Holder.Reference holder) { + super(holder, new BlockStateVariantProvider(holder, ImmutableBlockState::new, Map.of()), Map.of(), null); } @Override public ImmutableBlockState getBlockState(CompoundTag nbt) { return this.cachedData.computeIfAbsent(nbt, k -> { ImmutableBlockState state = new ImmutableBlockState(super.holder, new Reference2ObjectArrayMap<>()); + state.setBehavior(EmptyBlockBehavior.INSTANCE); state.setNbtToSave(state.toNbtToSave(nbt)); return state; }); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/PushReaction.java b/core/src/main/java/net/momirealms/craftengine/core/block/PushReaction.java index db1d653be..5a7f65756 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/PushReaction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/PushReaction.java @@ -5,5 +5,7 @@ public enum PushReaction { DESTROY, BLOCK, IGNORE, - PUSH_ONLY + PUSH_ONLY; + + public static final PushReaction[] VALUES = values(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/state/StatePropertyAccessor.java b/core/src/main/java/net/momirealms/craftengine/core/block/StatePropertyAccessor.java similarity index 83% rename from core/src/main/java/net/momirealms/craftengine/core/block/state/StatePropertyAccessor.java rename to core/src/main/java/net/momirealms/craftengine/core/block/StatePropertyAccessor.java index a69d4e24e..3e1a46e3f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/state/StatePropertyAccessor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/StatePropertyAccessor.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.block.state; +package net.momirealms.craftengine.core.block; import java.util.Collection; diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/EntityBlockBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/EntityBlockBehavior.java new file mode 100644 index 000000000..2eb34bae7 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/EntityBlockBehavior.java @@ -0,0 +1,35 @@ +package net.momirealms.craftengine.core.block.behavior; + +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.entity.BlockEntityType; +import net.momirealms.craftengine.core.block.entity.tick.BlockEntityTicker; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.CEWorld; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +public interface EntityBlockBehavior { + + BlockEntityType blockEntityType(); + + BlockEntity createBlockEntity(BlockPos pos, ImmutableBlockState state); + + default BlockEntityTicker createSyncBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType blockEntityType) { + return null; + } + + default BlockEntityTicker createAsyncBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType blockEntityType) { + return null; + } + + @SuppressWarnings("unchecked") + static BlockEntityTicker createTickerHelper(BlockEntityTicker ticker) { + return (BlockEntityTicker) ticker; + } + + @SuppressWarnings("unchecked") + static BlockEntityType blockEntityTypeHelper(BlockEntityType type) { + return (BlockEntityType) type; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/special/FallOnBlockBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/special/FallOnBlockBehavior.java new file mode 100644 index 000000000..5376477a7 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/special/FallOnBlockBehavior.java @@ -0,0 +1,17 @@ +package net.momirealms.craftengine.core.block.behavior.special; + +import java.util.concurrent.Callable; + +public interface FallOnBlockBehavior { + + // 1.20.1~1.21.4 Level world, BlockState state, BlockPos pos, Entity entity, float fallDistance + // 1.21.5+ Level level, BlockState state, BlockPos pos, Entity entity, double fallDistance + default void fallOn(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + superMethod.call(); + } + + // BlockGetter level, Entity entity + default void updateEntityMovementAfterFallOn(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + superMethod.call(); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/special/PlaceLiquidBlockBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/special/PlaceLiquidBlockBehavior.java new file mode 100644 index 000000000..ddab31b4f --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/special/PlaceLiquidBlockBehavior.java @@ -0,0 +1,10 @@ +package net.momirealms.craftengine.core.block.behavior.special; + +import java.util.concurrent.Callable; + +public interface PlaceLiquidBlockBehavior { + + boolean placeLiquid(Object thisBlock, Object[] args, Callable superMethod); + + boolean canPlaceLiquid(Object thisBlock, Object[] args, Callable superMethod); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntity.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntity.java new file mode 100644 index 000000000..78dc7210a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntity.java @@ -0,0 +1,115 @@ +package net.momirealms.craftengine.core.block.entity; + +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.render.DynamicBlockEntityRenderer; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.ChunkPos; +import net.momirealms.craftengine.core.world.SectionPos; +import net.momirealms.sparrow.nbt.CompoundTag; +import org.jetbrains.annotations.Nullable; + +public abstract class BlockEntity { + protected final BlockPos pos; + protected ImmutableBlockState blockState; + protected BlockEntityType type; + public CEWorld world; + protected boolean valid; + @Nullable + protected DynamicBlockEntityRenderer blockEntityRenderer; + + protected BlockEntity(BlockEntityType type, BlockPos pos, ImmutableBlockState blockState) { + this.pos = pos; + this.blockState = blockState; + this.type = type; + } + + public final CompoundTag saveAsTag() { + CompoundTag tag = new CompoundTag(); + this.saveId(tag); + this.savePos(tag); + this.saveCustomData(tag); + return tag; + } + + private void saveId(CompoundTag tag) { + tag.putString("id", this.type.id().asString()); + } + + public void setBlockState(ImmutableBlockState blockState) { + this.blockState = blockState; + } + + public ImmutableBlockState blockState() { + return blockState; + } + + public CEWorld world() { + return world; + } + + public void setWorld(CEWorld world) { + this.world = world; + } + + public boolean isValid() { + return valid; + } + + public void setValid(boolean valid) { + this.valid = valid; + } + + private void savePos(CompoundTag tag) { + tag.putInt("x", this.pos.x()); + tag.putInt("y", this.pos.y()); + tag.putInt("z", this.pos.z()); + } + + protected void saveCustomData(CompoundTag tag) { + } + + public void loadCustomData(CompoundTag tag) { + } + + public void preRemove() { + } + + public static BlockPos readPos(CompoundTag tag) { + return new BlockPos(tag.getInt("x"), tag.getInt("y"), tag.getInt("z")); + } + + public BlockEntityType type() { + return this.type; + } + + public @Nullable DynamicBlockEntityRenderer blockEntityRenderer() { + return blockEntityRenderer; + } + + public static BlockPos readPosAndVerify(CompoundTag tag, ChunkPos chunkPos) { + int x = tag.getInt("x", 0); + int y = tag.getInt("y", 0); + int z = tag.getInt("z", 0); + int sectionX = SectionPos.blockToSectionCoord(x); + int sectionZ = SectionPos.blockToSectionCoord(z); + if (sectionX != chunkPos.x || sectionZ != chunkPos.z) { + x = chunkPos.x * 16 + SectionPos.sectionRelative(x); + z = chunkPos.z * 16 + SectionPos.sectionRelative(z); + } + return new BlockPos(x, y, z); + } + + public BlockPos pos() { + return this.pos; + } + + public boolean isValidBlockState(ImmutableBlockState blockState) { + return this.type == blockState.blockEntityType(); + } + + public interface Factory { + + T create(BlockPos pos, ImmutableBlockState state); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityType.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityType.java new file mode 100644 index 000000000..4291b6de5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityType.java @@ -0,0 +1,6 @@ +package net.momirealms.craftengine.core.block.entity; + +import net.momirealms.craftengine.core.util.Key; + +public record BlockEntityType(Key id, BlockEntity.Factory factory) { +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypeKeys.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypeKeys.java new file mode 100644 index 000000000..720da9e20 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypeKeys.java @@ -0,0 +1,12 @@ +package net.momirealms.craftengine.core.block.entity; + +import net.momirealms.craftengine.core.util.Key; + +public final class BlockEntityTypeKeys { + private BlockEntityTypeKeys() {} + + public static final Key UNSAFE_COMPOSITE = Key.of("craftengine:unsafe_composite"); + public static final Key SIMPLE_STORAGE = Key.of("craftengine:simple_storage"); + public static final Key SIMPLE_PARTICLE = Key.of("craftengine:simple_particle"); + public static final Key WALL_TORCH_PARTICLE = Key.of("craftengine:wall_torch_particle"); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypes.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypes.java new file mode 100644 index 000000000..d3d0c0941 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypes.java @@ -0,0 +1,17 @@ +package net.momirealms.craftengine.core.block.entity; + +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.registry.Registries; +import net.momirealms.craftengine.core.registry.WritableRegistry; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceKey; + +public class BlockEntityTypes { + + public static BlockEntityType register(Key id, BlockEntity.Factory factory) { + BlockEntityType type = new BlockEntityType<>(id, factory); + ((WritableRegistry>) BuiltInRegistries.BLOCK_ENTITY_TYPE) + .register(ResourceKey.create(Registries.BLOCK_ENTITY_TYPE.location(), id), type); + return type; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/ConstantBlockEntityRenderer.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/ConstantBlockEntityRenderer.java new file mode 100644 index 000000000..f28ab579f --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/ConstantBlockEntityRenderer.java @@ -0,0 +1,38 @@ +package net.momirealms.craftengine.core.block.entity.render; + +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; +import net.momirealms.craftengine.core.entity.player.Player; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +public class ConstantBlockEntityRenderer { + private final BlockEntityElement[] elements; + + public ConstantBlockEntityRenderer(BlockEntityElement[] elements) { + this.elements = elements; + } + + public void show(Player player) { + for (BlockEntityElement element : this.elements) { + element.show(player); + } + } + + public void hide(Player player) { + for (BlockEntityElement element : this.elements) { + element.hide(player); + } + } + + public void deactivate() { + for (BlockEntityElement element : this.elements) { + element.deactivate(); + } + } + + public void activate() { + for (BlockEntityElement element : this.elements) { + element.activate(); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/DynamicBlockEntityRenderer.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/DynamicBlockEntityRenderer.java new file mode 100644 index 000000000..bd7fa3c88 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/DynamicBlockEntityRenderer.java @@ -0,0 +1,14 @@ +package net.momirealms.craftengine.core.block.entity.render; + +import net.momirealms.craftengine.core.entity.player.Player; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +public interface DynamicBlockEntityRenderer { + + void show(Player player); + + void hide(Player player); + + void update(Player player); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElement.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElement.java new file mode 100644 index 000000000..eaf5d605c --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElement.java @@ -0,0 +1,16 @@ +package net.momirealms.craftengine.core.block.entity.render.element; + +import net.momirealms.craftengine.core.entity.player.Player; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +public interface BlockEntityElement { + + void show(Player player); + + void hide(Player player); + + default void deactivate() {} + + default void activate() {} +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfig.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfig.java new file mode 100644 index 000000000..a61f0b030 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfig.java @@ -0,0 +1,9 @@ +package net.momirealms.craftengine.core.block.entity.render.element; + +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.World; + +public interface BlockEntityElementConfig { + + E create(World world, BlockPos pos); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfigFactory.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfigFactory.java new file mode 100644 index 000000000..2aba16476 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfigFactory.java @@ -0,0 +1,9 @@ +package net.momirealms.craftengine.core.block.entity.render.element; + +import java.util.Map; + +@FunctionalInterface +public interface BlockEntityElementConfigFactory { + + BlockEntityElementConfig create(Map args); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfigs.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfigs.java new file mode 100644 index 000000000..77841caa8 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfigs.java @@ -0,0 +1,30 @@ +package net.momirealms.craftengine.core.block.entity.render.element; + +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.registry.Registries; +import net.momirealms.craftengine.core.registry.WritableRegistry; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceKey; + +import java.util.Map; +import java.util.Optional; + +public class BlockEntityElementConfigs { + public static final Key ITEM_DISPLAY = Key.of("craftengine:item_display"); + public static final Key TEXT_DISPLAY = Key.of("craftengine:text_display"); + + public static void register(Key key, BlockEntityElementConfigFactory type) { + ((WritableRegistry) BuiltInRegistries.BLOCK_ENTITY_ELEMENT_TYPE) + .register(ResourceKey.create(Registries.BLOCK_ENTITY_ELEMENT_TYPE.location(), key), type); + } + + public static BlockEntityElementConfig fromMap(Map arguments) { + Key type = Optional.ofNullable(arguments.get("type")).map(String::valueOf).map(it -> Key.withDefaultNamespace(it, "craftengine")).orElse(ITEM_DISPLAY); + BlockEntityElementConfigFactory factory = BuiltInRegistries.BLOCK_ENTITY_ELEMENT_TYPE.getValue(type); + if (factory == null) { + throw new LocalizedResourceConfigException("warning.config.block.state.entity_renderer.invalid_type", type.toString()); + } + return factory.create(arguments); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/BlockEntityTicker.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/BlockEntityTicker.java new file mode 100644 index 000000000..6b5f57399 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/BlockEntityTicker.java @@ -0,0 +1,11 @@ +package net.momirealms.craftengine.core.block.entity.tick; + +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.CEWorld; + +public interface BlockEntityTicker { + + void tick(CEWorld world, BlockPos pos, ImmutableBlockState state, T blockEntity); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/DummyTickingBlockEntity.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/DummyTickingBlockEntity.java new file mode 100644 index 000000000..e85353685 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/DummyTickingBlockEntity.java @@ -0,0 +1,21 @@ +package net.momirealms.craftengine.core.block.entity.tick; + +import net.momirealms.craftengine.core.world.BlockPos; + +public class DummyTickingBlockEntity implements TickingBlockEntity { + public static final DummyTickingBlockEntity INSTANCE = new DummyTickingBlockEntity(); + + @Override + public boolean isValid() { + return false; + } + + @Override + public void tick() { + } + + @Override + public BlockPos pos() { + return BlockPos.ZERO; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/ReplaceableTickingBlockEntity.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/ReplaceableTickingBlockEntity.java new file mode 100644 index 000000000..74ba19c4d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/ReplaceableTickingBlockEntity.java @@ -0,0 +1,34 @@ +package net.momirealms.craftengine.core.block.entity.tick; + +import net.momirealms.craftengine.core.world.BlockPos; + +public class ReplaceableTickingBlockEntity implements TickingBlockEntity { + private TickingBlockEntity target; + + public ReplaceableTickingBlockEntity(TickingBlockEntity target) { + this.target = target; + } + + public TickingBlockEntity target() { + return target; + } + + public void setTicker(TickingBlockEntity target) { + this.target = target; + } + + @Override + public BlockPos pos() { + return this.target.pos(); + } + + @Override + public void tick() { + this.target.tick(); + } + + @Override + public boolean isValid() { + return this.target.isValid(); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/TickingBlockEntity.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/TickingBlockEntity.java new file mode 100644 index 000000000..99599744d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/TickingBlockEntity.java @@ -0,0 +1,12 @@ +package net.momirealms.craftengine.core.block.entity.tick; + +import net.momirealms.craftengine.core.world.BlockPos; + +public interface TickingBlockEntity { + + void tick(); + + boolean isValid(); + + BlockPos pos(); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/TickingBlockEntityImpl.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/TickingBlockEntityImpl.java new file mode 100644 index 000000000..12d09ee8c --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/TickingBlockEntityImpl.java @@ -0,0 +1,49 @@ +package net.momirealms.craftengine.core.block.entity.tick; + +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.logger.Debugger; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.chunk.CEChunk; + +public class TickingBlockEntityImpl implements TickingBlockEntity { + private final T blockEntity; + private final BlockEntityTicker ticker; + private final CEChunk chunk; + + public TickingBlockEntityImpl(CEChunk chunk, T blockEntity, BlockEntityTicker ticker) { + this.blockEntity = blockEntity; + this.ticker = ticker; + this.chunk = chunk; + } + + @Override + public BlockPos pos() { + return this.blockEntity.pos(); + } + + @Override + public void tick() { + // 还没加载完全 + if (this.blockEntity.world == null) return; + BlockPos pos = pos(); + ImmutableBlockState state = this.chunk.getBlockState(pos); + // 不是合法方块 + if (!this.blockEntity.isValidBlockState(state)) { + this.chunk.removeBlockEntity(pos); + Debugger.BLOCK_ENTITY.warn(() -> "Invalid block entity(" + this.blockEntity.getClass().getSimpleName() + ") with state " + state + " found at world " + this.chunk.world().name() + " " + pos, null); + return; + } + try { + this.ticker.tick(this.chunk.world(), pos, state, this.blockEntity); + } catch (Throwable t) { + CraftEngine.instance().logger().warn("Failed to tick block entity(" + this.blockEntity.getClass().getSimpleName() + ") at world " + this.chunk.world().name() + " " + pos, t); + } + } + + @Override + public boolean isValid() { + return this.blockEntity.isValid(); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/parser/BlockNbtParser.java b/core/src/main/java/net/momirealms/craftengine/core/block/parser/BlockNbtParser.java index 151709a49..176b0d177 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/parser/BlockNbtParser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/parser/BlockNbtParser.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.block.parser; +import net.momirealms.craftengine.core.block.BlockStateVariantProvider; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.util.StringReader; @@ -7,11 +8,13 @@ import net.momirealms.sparrow.nbt.CompoundTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.function.Function; + public final class BlockNbtParser { private BlockNbtParser() {} @Nullable - public static CompoundTag deserialize(@NotNull CustomBlock block, @NotNull String data) { + public static CompoundTag deserialize(@NotNull Function> propertyProvider, @NotNull String data) { StringReader reader = StringReader.simple(data); CompoundTag properties = new CompoundTag(); while (reader.canRead()) { @@ -24,7 +27,7 @@ public final class BlockNbtParser { if (propertyValue.isEmpty()) { return null; } - Property property = block.getProperty(propertyName); + Property property = propertyProvider.apply(propertyName); if (property != null) { property.createOptionalTag(propertyValue).ifPresent(tag -> { properties.put(propertyName, tag); @@ -38,4 +41,14 @@ public final class BlockNbtParser { } return properties; } + + @Nullable + public static CompoundTag deserialize(@NotNull CustomBlock block, @NotNull String data) { + return deserialize(block::getProperty, data); + } + + @Nullable + public static CompoundTag deserialize(@NotNull BlockStateVariantProvider variantProvider, @NotNull String data) { + return deserialize(variantProvider::getProperty, data); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java index a54c56afa..641c71a72 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java @@ -1,6 +1,6 @@ package net.momirealms.craftengine.core.block.properties; -import net.momirealms.craftengine.core.block.state.properties.*; +import net.momirealms.craftengine.core.block.properties.type.*; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Registries; @@ -21,6 +21,8 @@ public final class Properties { public static final Key HINGE = Key.of("craftengine:hinge"); public static final Key STAIRS_SHAPE = Key.of("craftengine:stairs_shape"); public static final Key SLAB_TYPE = Key.of("craftengine:slab_type"); + public static final Key SOFA_SHAPE = Key.of("craftengine:sofa_shape"); + public static final Key ANCHOR_TYPE = Key.of("craftengine:anchor_type"); static { register(BOOLEAN, BooleanProperty.FACTORY); @@ -36,6 +38,8 @@ public final class Properties { register(HINGE, new EnumProperty.Factory<>(DoorHinge.class)); register(STAIRS_SHAPE, new EnumProperty.Factory<>(StairsShape.class)); register(SLAB_TYPE, new EnumProperty.Factory<>(SlabType.class)); + register(SOFA_SHAPE, new EnumProperty.Factory<>(SofaShape.class)); + register(ANCHOR_TYPE, new EnumProperty.Factory<>(AnchorType.class)); } public static void register(Key key, PropertyFactory factory) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/properties/Property.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/Property.java index 84bbfda65..fc3370fac 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/properties/Property.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/Property.java @@ -167,4 +167,9 @@ public abstract class Property> { public static > String formatValue(Property property, Comparable value) { return property.valueName((T) value); } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "{clazz=" + this.clazz + ", name='" + this.name + "', values=" + this.possibleValues() + '}'; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/AnchorType.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/AnchorType.java new file mode 100644 index 000000000..26e95f0a5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/AnchorType.java @@ -0,0 +1,7 @@ +package net.momirealms.craftengine.core.block.properties.type; + +public enum AnchorType { + FLOOR, + WALL, + CEILING +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/DoorHinge.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/DoorHinge.java new file mode 100644 index 000000000..16ef73e49 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/DoorHinge.java @@ -0,0 +1,5 @@ +package net.momirealms.craftengine.core.block.properties.type; + +public enum DoorHinge { + LEFT, RIGHT +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/DoubleBlockHalf.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/DoubleBlockHalf.java new file mode 100644 index 000000000..1823e76dd --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/DoubleBlockHalf.java @@ -0,0 +1,5 @@ +package net.momirealms.craftengine.core.block.properties.type; + +public enum DoubleBlockHalf { + UPPER, LOWER +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/SingleBlockHalf.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/SingleBlockHalf.java new file mode 100644 index 000000000..04cee6405 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/SingleBlockHalf.java @@ -0,0 +1,5 @@ +package net.momirealms.craftengine.core.block.properties.type; + +public enum SingleBlockHalf { + TOP, BOTTOM +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/SlabType.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/SlabType.java new file mode 100644 index 000000000..2f1d2e1e5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/SlabType.java @@ -0,0 +1,7 @@ +package net.momirealms.craftengine.core.block.properties.type; + +public enum SlabType { + TOP, + BOTTOM, + DOUBLE +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/SofaShape.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/SofaShape.java new file mode 100644 index 000000000..82ef28632 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/SofaShape.java @@ -0,0 +1,7 @@ +package net.momirealms.craftengine.core.block.properties.type; + +public enum SofaShape { + STRAIGHT, + INNER_LEFT, + INNER_RIGHT, +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/state/properties/StairsShape.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/StairsShape.java similarity index 62% rename from core/src/main/java/net/momirealms/craftengine/core/block/state/properties/StairsShape.java rename to core/src/main/java/net/momirealms/craftengine/core/block/properties/type/StairsShape.java index b0cac6526..4d8616818 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/state/properties/StairsShape.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/type/StairsShape.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.block.state.properties; +package net.momirealms.craftengine.core.block.properties.type; public enum StairsShape { STRAIGHT, diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/state/properties/DoorHinge.java b/core/src/main/java/net/momirealms/craftengine/core/block/state/properties/DoorHinge.java deleted file mode 100644 index 811514a63..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/block/state/properties/DoorHinge.java +++ /dev/null @@ -1,5 +0,0 @@ -package net.momirealms.craftengine.core.block.state.properties; - -public enum DoorHinge { - LEFT, RIGHT -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/state/properties/DoubleBlockHalf.java b/core/src/main/java/net/momirealms/craftengine/core/block/state/properties/DoubleBlockHalf.java deleted file mode 100644 index 1891b3de8..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/block/state/properties/DoubleBlockHalf.java +++ /dev/null @@ -1,5 +0,0 @@ -package net.momirealms.craftengine.core.block.state.properties; - -public enum DoubleBlockHalf { - UPPER, LOWER -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/state/properties/SingleBlockHalf.java b/core/src/main/java/net/momirealms/craftengine/core/block/state/properties/SingleBlockHalf.java deleted file mode 100644 index c5b3b83e8..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/block/state/properties/SingleBlockHalf.java +++ /dev/null @@ -1,5 +0,0 @@ -package net.momirealms.craftengine.core.block.state.properties; - -public enum SingleBlockHalf { - TOP, BOTTOM -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/state/properties/SlabType.java b/core/src/main/java/net/momirealms/craftengine/core/block/state/properties/SlabType.java deleted file mode 100644 index 09f4fbbfb..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/block/state/properties/SlabType.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.momirealms.craftengine.core.block.state.properties; - -public enum SlabType { - TOP, - BOTTOM, - DOUBLE -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java index abc819e6a..8a2a4ad05 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java @@ -5,10 +5,12 @@ import net.momirealms.craftengine.core.entity.ItemDisplayContext; import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.pack.PendingConfigSection; import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.config.IdSectionConfigParser; import net.momirealms.craftengine.core.plugin.context.event.EventFunctions; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -31,7 +33,7 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { } @Override - public ConfigParser parser() { + public FurnitureParser parser() { return this.furnitureParser; } @@ -58,6 +60,11 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { return Optional.ofNullable(this.byId.get(id)); } + @Override + public Map loadedFurniture() { + return Collections.unmodifiableMap(this.byId); + } + @Override public void unload() { this.byId.clear(); @@ -69,8 +76,26 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { protected abstract CustomFurniture.Builder furnitureBuilder(); - public class FurnitureParser implements ConfigParser { + public class FurnitureParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] { "furniture" }; + private final List pendingConfigSections = new ArrayList<>(); + + public void addPendingConfigSection(PendingConfigSection section) { + this.pendingConfigSections.add(section); + } + + @Override + public void preProcess() { + for (PendingConfigSection section : this.pendingConfigSections) { + ResourceConfigUtils.runCatching( + section.path(), + section.node(), + () -> parseSection(section.pack(), section.path(), section.node(), section.id(), section.config()), + () -> GsonHelper.get().toJson(section.config()) + ); + } + this.pendingConfigSections.clear(); + } @Override public String[] sectionId() { @@ -84,8 +109,8 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { @SuppressWarnings("unchecked") @Override - public void parseSection(Pack pack, Path path, Key id, Map section) { - if (byId.containsKey(id)) { + public void parseSection(Pack pack, Path path, String node, Key id, Map section) { + if (AbstractFurnitureManager.this.byId.containsKey(id)) { throw new LocalizedResourceConfigException("warning.config.furniture.duplicate"); } EnumMap placements = new EnumMap<>(AnchorType.class); @@ -98,7 +123,7 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { // anchor type AnchorType anchorType = AnchorType.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH)); Map placementArguments = MiscUtils.castToMap(entry.getValue(), false); - Optional optionalLootSpawnOffset = Optional.ofNullable(placementArguments.get("loot-spawn-offset")).map(it -> MiscUtils.getAsVector3f(it, "loot-spawn-offset")); + Optional optionalLootSpawnOffset = Optional.ofNullable(placementArguments.get("loot-spawn-offset")).map(it -> ResourceConfigUtils.getAsVector3f(it, "loot-spawn-offset")); // furniture display elements List elements = new ArrayList<>(); List> elementConfigs = (List>) placementArguments.getOrDefault("elements", List.of()); @@ -108,10 +133,10 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { .applyDyedColor(ResourceConfigUtils.getAsBoolean(element.getOrDefault("apply-dyed-color", true), "apply-dyed-color")) .billboard(ResourceConfigUtils.getOrDefault(element.get("billboard"), o -> Billboard.valueOf(o.toString().toUpperCase(Locale.ENGLISH)), Billboard.FIXED)) .transform(ResourceConfigUtils.getOrDefault(element.get("transform"), o -> ItemDisplayContext.valueOf(o.toString().toUpperCase(Locale.ENGLISH)), ItemDisplayContext.NONE)) - .scale(MiscUtils.getAsVector3f(element.getOrDefault("scale", "1"), "scale")) - .position(MiscUtils.getAsVector3f(element.getOrDefault("position", "0"), "position")) - .translation(MiscUtils.getAsVector3f(element.getOrDefault("translation", "0"), "translation")) - .rotation(MiscUtils.getAsQuaternionf(element.getOrDefault("rotation", "0"), "rotation")) + .scale(ResourceConfigUtils.getAsVector3f(element.getOrDefault("scale", "1"), "scale")) + .position(ResourceConfigUtils.getAsVector3f(element.getOrDefault("position", "0"), "position")) + .translation(ResourceConfigUtils.getAsVector3f(element.getOrDefault("translation", "0"), "translation")) + .rotation(ResourceConfigUtils.getAsQuaternionf(element.getOrDefault("rotation", "0"), "rotation")) .build(); elements.add(furnitureElement); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java index 2a31d46de..68cf5fa05 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java @@ -9,6 +9,7 @@ import org.incendo.cloud.suggestion.Suggestion; import org.jetbrains.annotations.Nullable; import java.util.Collection; +import java.util.Map; import java.util.Optional; public interface FurnitureManager extends Manageable { @@ -30,6 +31,8 @@ public interface FurnitureManager extends Manageable { Optional furnitureById(Key id); + Map loadedFurniture(); + boolean isFurnitureRealEntity(int entityId); @Nullable diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxFactory.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxFactory.java index 3720eb9bd..6b85ae871 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxFactory.java @@ -1,6 +1,6 @@ package net.momirealms.craftengine.core.entity.furniture; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.List; import java.util.Map; @@ -15,8 +15,8 @@ public interface HitBoxFactory { return seats.stream() .map(arg -> { String[] split = arg.split(" "); - if (split.length == 1) return new Seat(MiscUtils.getAsVector3f(split[0], "seats"), 0, false); - return new Seat(MiscUtils.getAsVector3f(split[0], "seats"), Float.parseFloat(split[1]), true); + if (split.length == 1) return new Seat(ResourceConfigUtils.getAsVector3f(split[0], "seats"), 0, false); + return new Seat(ResourceConfigUtils.getAsVector3f(split[0], "seats"), Float.parseFloat(split[1]), true); }) .toArray(Seat[]::new); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java index 6675659ba..f0da08202 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java @@ -1,13 +1,18 @@ package net.momirealms.craftengine.core.entity.player; import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.core.advancement.AdvancementType; import net.momirealms.craftengine.core.entity.AbstractEntity; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.context.CooldownData; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.sound.SoundSource; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.Position; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; import org.jetbrains.annotations.NotNull; public abstract class Player extends AbstractEntity implements NetWorkUser { @@ -62,6 +67,8 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract boolean canPlace(BlockPos pos, Object state); + public abstract void sendToast(Component text, Item icon, AdvancementType type); + public abstract void sendActionBar(Component text); public abstract void sendMessage(Component text, boolean overlay); @@ -96,7 +103,19 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void playSound(Key sound, SoundSource source, float volume, float pitch); - public abstract void playSound(Key sound, BlockPos pos, SoundSource source, float volume, float pitch); + public abstract void playSound(Position pos, Key sound, SoundSource source, float volume, float pitch); + + public void playSound(BlockPos pos, Key sound, SoundSource source, float volume, float pitch) { + this.playSound(Vec3d.atCenterOf(pos), sound, source, volume, pitch); + } + + public void playSound(BlockPos pos, SoundData data, SoundSource source) { + this.playSound(pos, data.id(), source, data.volume().get(), data.pitch().get()); + } + + public void playSound(Position pos, SoundData data, SoundSource source) { + this.playSound(pos, data.id(), source, data.volume().get(), data.pitch().get()); + } public abstract void giveItem(Item item); @@ -148,4 +167,8 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void clearPotionEffects(); public abstract CooldownData cooldown(); + + public abstract void teleport(WorldPosition worldPosition); + + public abstract void damage(double amount, Key damageType); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileMeta.java b/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileMeta.java index 4dbefc9b4..7801b8f9d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileMeta.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileMeta.java @@ -3,7 +3,6 @@ package net.momirealms.craftengine.core.entity.projectile; import net.momirealms.craftengine.core.entity.Billboard; import net.momirealms.craftengine.core.entity.ItemDisplayContext; import net.momirealms.craftengine.core.util.Key; -import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; @@ -13,6 +12,5 @@ public record ProjectileMeta(Key item, Vector3f scale, Vector3f translation, Quaternionf rotation, - double range, - @Nullable ProjectileType type) { + double range) { } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileType.java b/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileType.java deleted file mode 100644 index 0c0e1d5ed..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileType.java +++ /dev/null @@ -1,5 +0,0 @@ -package net.momirealms.craftengine.core.entity.projectile; - -public enum ProjectileType { - TRIDENT -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java index adfbebdef..639076b6c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java @@ -5,8 +5,11 @@ import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.pack.ResourceLocation; +import net.momirealms.craftengine.core.pack.allocator.IdAllocator; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.config.IdSectionConfigParser; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; @@ -23,6 +26,9 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -40,7 +46,7 @@ public abstract class AbstractFontManager implements FontManager { private final EmojiParser emojiParser; private OffsetFont offsetFont; - protected Trie imageTagTrie; + protected Trie networkTagTrie; protected Trie emojiKeywordTrie; protected Map networkTagMapper; protected Map emojiMapper; @@ -53,6 +59,14 @@ public abstract class AbstractFontManager implements FontManager { this.emojiParser = new EmojiParser(); } + public ImageParser imageParser() { + return imageParser; + } + + public EmojiParser emojiParser() { + return emojiParser; + } + @Override public void load() { this.offsetFont = Optional.ofNullable(plugin.config().settings().getSection("image.offset-characters")) @@ -61,15 +75,35 @@ public abstract class AbstractFontManager implements FontManager { this.networkTagMapper = new HashMap<>(1024); } + @Override + public OffsetFont offsetFont() { + return offsetFont; + } + + @Override + public Map loadedImages() { + return Collections.unmodifiableMap(this.images); + } + + @Override + public Map emojis() { + return Collections.unmodifiableMap(this.emojis); + } + @Override public void unload() { this.fonts.clear(); this.images.clear(); this.illegalChars.clear(); this.emojis.clear(); + this.networkTagTrie = null; + this.emojiKeywordTrie = null; if (this.networkTagMapper != null) { this.networkTagMapper.clear(); } + if (this.emojiMapper != null) { + this.emojiMapper.clear(); + } } @Override @@ -88,7 +122,7 @@ public abstract class AbstractFontManager implements FontManager { this.registerImageTags(); this.registerShiftTags(); this.registerGlobalTags(); - this.buildImageTagTrie(); + this.buildNetworkTagTrie(); this.buildEmojiKeywordsTrie(); this.emojiList = new ArrayList<>(this.emojis.values()); this.allEmojiSuggestions = this.emojis.values().stream() @@ -131,11 +165,11 @@ public abstract class AbstractFontManager implements FontManager { @Override public Map matchTags(String json) { - if (this.imageTagTrie == null) { + if (this.networkTagTrie == null) { return Collections.emptyMap(); } Map tags = new HashMap<>(); - for (Token token : this.imageTagTrie.tokenize(json)) { + for (Token token : this.networkTagTrie.tokenize(json)) { if (token.isMatch()) { tags.put(token.getFragment(), this.networkTagMapper.get(token.getFragment())); } @@ -208,7 +242,7 @@ public abstract class AbstractFontManager implements FontManager { emoji.content(), PlayerOptionalContext.of(player, ContextHolder.builder() .withOptionalParameter(EmojiParameters.EMOJI, emoji.emojiImage()) - .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) + .withParameter(EmojiParameters.KEYWORD, emoji.keywords().getFirst()) ).tagResolvers()) ); if (emojis.size() >= maxTimes) break; @@ -305,8 +339,8 @@ public abstract class AbstractFontManager implements FontManager { .build(); } - private void buildImageTagTrie() { - this.imageTagTrie = Trie.builder() + private void buildNetworkTagTrie() { + this.networkTagTrie = Trie.builder() .ignoreOverlaps() .addKeywords(this.networkTagMapper.keySet()) .build(); @@ -366,7 +400,7 @@ public abstract class AbstractFontManager implements FontManager { return this.fonts.computeIfAbsent(key, Font::new); } - public class EmojiParser implements ConfigParser { + public class EmojiParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"emoji", "emojis"}; @Override @@ -380,18 +414,18 @@ public abstract class AbstractFontManager implements FontManager { } @Override - public void parseSection(Pack pack, Path path, Key id, Map section) { + public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (emojis.containsKey(id)) { - throw new LocalizedResourceConfigException("warning.config.emoji.duplicate", path, id); + throw new LocalizedResourceConfigException("warning.config.emoji.duplicate"); } String permission = (String) section.get("permission"); Object keywordsRaw = section.get("keywords"); if (keywordsRaw == null) { - throw new LocalizedResourceConfigException("warning.config.emoji.missing_keywords", path, id); + throw new LocalizedResourceConfigException("warning.config.emoji.missing_keywords"); } List keywords = MiscUtils.getAsStringList(keywordsRaw); if (keywords.isEmpty()) { - throw new LocalizedResourceConfigException("warning.config.emoji.missing_keywords", path, id); + throw new LocalizedResourceConfigException("warning.config.emoji.missing_keywords"); } Object rawContent = section.getOrDefault("content", ""); String content; @@ -410,7 +444,7 @@ public abstract class AbstractFontManager implements FontManager { if (bitmapImage.isPresent()) { image = bitmapImage.get().miniMessageAt(0, 0); } else { - throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", path, id, rawImage); + throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", rawImage); } } else if (split.length == 4) { Key imageId = new Key(split[0], split[1]); @@ -419,13 +453,13 @@ public abstract class AbstractFontManager implements FontManager { try { image = bitmapImage.get().miniMessageAt(Integer.parseInt(split[2]), Integer.parseInt(split[3])); } catch (ArrayIndexOutOfBoundsException e) { - throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", path, id, rawImage); + throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", rawImage); } } else { - throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", path, id, rawImage); + throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", rawImage); } } else { - throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", path, id, rawImage); + throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", rawImage); } } Emoji emoji = new Emoji(content, permission, image, keywords); @@ -433,8 +467,9 @@ public abstract class AbstractFontManager implements FontManager { } } - public class ImageParser implements ConfigParser { + public class ImageParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"images", "image"}; + private final Map idAllocators = new HashMap<>(); @Override public String[] sectionId() { @@ -447,129 +482,219 @@ public abstract class AbstractFontManager implements FontManager { } @Override - public void parseSection(Pack pack, Path path, Key id, Map section) { - if (images.containsKey(id)) { - throw new LocalizedResourceConfigException("warning.config.image.duplicate", path, id); + public void postProcess() { + for (Map.Entry entry : this.idAllocators.entrySet()) { + entry.getValue().processPendingAllocations(); + try { + entry.getValue().saveToCache(); + } catch (IOException e) { + AbstractFontManager.this.plugin.logger().warn("Error while saving codepoint allocation for font " + entry.getKey().asString(), e); + } + } + } + + @Override + public void preProcess() { + this.idAllocators.clear(); + } + + public IdAllocator getOrCreateIdAllocator(Key key) { + return this.idAllocators.computeIfAbsent(key, k -> { + IdAllocator newAllocator = new IdAllocator(plugin.dataFolderPath().resolve("cache").resolve("font").resolve(k.namespace()).resolve(k.value() + ".json")); + newAllocator.reset(Config.codepointStartingValue(k), 1114111); // utf16 + try { + newAllocator.loadFromCache(); + } catch (IOException e) { + AbstractFontManager.this.plugin.logger().warn("Error while loading chars data from cache for font " + k.asString(), e); + } + return newAllocator; + }); + } + + @Override + public void parseSection(Pack pack, Path path, String node, Key id, Map section) { + if (AbstractFontManager.this.images.containsKey(id)) { + throw new LocalizedResourceConfigException("warning.config.image.duplicate"); } Object file = section.get("file"); if (file == null) { - throw new LocalizedResourceConfigException("warning.config.image.missing_file", path, id); + throw new LocalizedResourceConfigException("warning.config.image.missing_file"); } - String resourceLocation = CharacterUtils.replaceBackslashWithSlash(file.toString()); + String resourceLocation = MiscUtils.make(CharacterUtils.replaceBackslashWithSlash(file.toString()), s -> s.endsWith(".png") ? s : s + ".png"); if (!ResourceLocation.isValid(resourceLocation)) { - throw new LocalizedResourceConfigException("warning.config.image.invalid_file_chars", path, id, resourceLocation); + throw new LocalizedResourceConfigException("warning.config.image.invalid_file_chars", resourceLocation); } - - String fontName = section.getOrDefault("font", "minecraft:default").toString(); + String fontName = section.getOrDefault("font", pack.namespace()+ ":default").toString(); if (!ResourceLocation.isValid(fontName)) { - throw new LocalizedResourceConfigException("warning.config.image.invalid_font_chars", path, id, fontName); + throw new LocalizedResourceConfigException("warning.config.image.invalid_font_chars", fontName); } - Key fontKey = Key.withDefaultNamespace(fontName, id.namespace()); - Font font = getOrCreateFont(fontKey); - List chars; + Key fontId = Key.withDefaultNamespace(fontName, id.namespace()); + Font font = getOrCreateFont(fontId); + + IdAllocator allocator = getOrCreateIdAllocator(fontId); + + int rows; + int columns; + List> futureCodepoints = new ArrayList<>(); Object charsObj = ResourceConfigUtils.get(section, "chars", "char"); + // 自动分配 if (charsObj == null) { - throw new LocalizedResourceConfigException("warning.config.image.missing_char", path, id); - } - if (charsObj instanceof List list) { - chars = MiscUtils.getAsStringList(list).stream().map(it -> { - if (it.startsWith("\\u")) { - return CharacterUtils.decodeUnicodeToChars(it); - } else { - return it.toCharArray(); + Object grid = section.get("grid-size"); + if (grid != null) { + String gridString = grid.toString(); + String[] split = gridString.split(","); + if (split.length != 2) { + throw new LocalizedResourceConfigException("warning.config.image.invalid_grid_size", gridString); } - }).toList(); - if (chars.isEmpty()) { - throw new LocalizedResourceConfigException("warning.config.image.missing_char", path, id); + rows = Integer.parseInt(split[0]); + columns = Integer.parseInt(split[1]); + int chars = rows * columns; + if (chars <= 0) { + throw new LocalizedResourceConfigException("warning.config.image.invalid_grid_size", gridString); + } + for (int i = 0; i < rows; i++) { + for (int j = 0; j < columns; j++) { + futureCodepoints.add(allocator.requestAutoId(id.asString() + ":" + i + ":" + j)); + } + } + } else { + rows = 1; + columns = 1; + futureCodepoints.add(allocator.requestAutoId(id.asString())); } - } else { - if (charsObj instanceof Integer integer) { - chars = List.of(new char[]{(char) integer.intValue()}); + } + // 使用了list + else if (charsObj instanceof List list) { + List charsList = MiscUtils.getAsStringList(list); + if (charsList.isEmpty() || charsList.getFirst().isEmpty()) { + throw new LocalizedResourceConfigException("warning.config.image.missing_char"); + } + int tempColumns = -1; + rows = charsList.size(); + for (int i = 0; i < charsList.size(); i++) { + String charString = charsList.get(i); + int[] codepoints; + if (charString.startsWith("\\u")) { + codepoints = CharacterUtils.charsToCodePoints(CharacterUtils.decodeUnicodeToChars(charString)); + } else { + codepoints = CharacterUtils.charsToCodePoints(charString.toCharArray()); + } + for (int j = 0; j < codepoints.length; j++) { + if (codepoints[j] == 0) { + futureCodepoints.add(CompletableFuture.completedFuture(0)); + } else { + futureCodepoints.add(allocator.assignFixedId(id.asString() + ":" + i + ":" + j, codepoints[j])); + } + } + if (tempColumns == -1) { + tempColumns = codepoints.length; + } else if (tempColumns != codepoints.length) { + throw new LocalizedResourceConfigException("warning.config.image.invalid_codepoint_grid"); + } + } + columns = tempColumns; + } + // 使用了具体的值 + else { + if (charsObj instanceof Integer codepoint) { + futureCodepoints.add(allocator.assignFixedId(id.asString(), codepoint)); + rows = 1; + columns = 1; } else { String character = charsObj.toString(); if (character.isEmpty()) { - throw new LocalizedResourceConfigException("warning.config.image.missing_char", path, id); + throw new LocalizedResourceConfigException("warning.config.image.missing_char"); } - if (character.length() == 1) { - chars = List.of(character.toCharArray()); + rows = 1; + int[] codepoints; + if (character.startsWith("\\u")) { + codepoints = CharacterUtils.charsToCodePoints(CharacterUtils.decodeUnicodeToChars(character)); } else { - if (character.startsWith("\\u")) { - chars = List.of(CharacterUtils.decodeUnicodeToChars(character)); + codepoints = CharacterUtils.charsToCodePoints(character.toCharArray()); + } + columns = codepoints.length; + for (int i = 0; i < codepoints.length; i++) { + if (codepoints[i] == 0) { + futureCodepoints.add(CompletableFuture.completedFuture(0)); } else { - // ??? TODO 需要测试特殊字符集 -// if (CharacterUtils.containsCombinedCharacter(character)) { -// TranslationManager.instance().log("warning.config.image.invalid_char", path.toString(), id.toString()); -// } - chars = List.of(character.toCharArray()); + futureCodepoints.add(allocator.assignFixedId(id.asString() + ":0:" + i, codepoints[i])); } } } } - int size = -1; - int[][] codepointGrid = new int[chars.size()][]; - for (int i = 0; i < chars.size(); ++i) { - int[] codepoints = CharacterUtils.charsToCodePoints(chars.get(i)); - for (int codepoint : codepoints) { - if (font.isCodepointInUse(codepoint)) { - BitmapImage image = font.bitmapImageByCodepoint(codepoint); - throw new LocalizedResourceConfigException("warning.config.image.codepoint_conflict", path, id, - fontKey.toString(), - CharacterUtils.encodeCharsToUnicode(Character.toChars(codepoint)), - new String(Character.toChars(codepoint)), - image.id().toString()); + CompletableFutures.allOf(futureCodepoints).whenComplete((v, t) -> ResourceConfigUtils.runCatching(path, node, () -> { + if (t != null) { + if (t instanceof CompletionException e) { + Throwable cause = e.getCause(); + if (cause instanceof IdAllocator.IdConflictException conflict) { + throw new LocalizedResourceConfigException("warning.config.image.codepoint.conflict", + fontId.toString(), + CharacterUtils.encodeCharsToUnicode(Character.toChars(conflict.id())), + new String(Character.toChars(conflict.id())), + conflict.previousOwner() + ); + } else if (cause instanceof IdAllocator.IdExhaustedException) { + throw new LocalizedResourceConfigException("warning.config.image.codepoint.exhausted", fontId.asString()); + } + } + throw new RuntimeException("Unknown error occurred", t); + } + + int[][] codepointGrid = new int[rows][columns]; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < columns; j++) { + try { + int codepoint = futureCodepoints.get(i * columns + j).get(); + codepointGrid[i][j] = codepoint; + } catch (InterruptedException | ExecutionException e) { + AbstractFontManager.this.plugin.logger().warn("Interrupted while allocating codepoint for image " + id.asString(), e); + return; + } } } - if (codepoints.length == 0) { - throw new LocalizedResourceConfigException("warning.config.image.missing_char", path, id); - } - codepointGrid[i] = codepoints; - if (size == -1) size = codepoints.length; - if (size != codepoints.length) { - throw new LocalizedResourceConfigException("warning.config.image.invalid_codepoint_grid", path, id); - } - } - Object heightObj = section.get("height"); - if (!resourceLocation.endsWith(".png")) resourceLocation += ".png"; - - if (heightObj == null) { - Key namespacedPath = Key.of(resourceLocation); - Path targetImagePath = pack.resourcePackFolder() - .resolve("assets") - .resolve(namespacedPath.namespace()) - .resolve("textures") - .resolve(namespacedPath.value()); - if (Files.exists(targetImagePath)) { - try (InputStream in = Files.newInputStream(targetImagePath)) { - BufferedImage image = ImageIO.read(in); - heightObj = image.getHeight() / codepointGrid.length; - } catch (IOException e) { - plugin.logger().warn("Failed to load image " + targetImagePath, e); - return; + Object heightObj = section.get("height"); + if (heightObj == null) { + Key namespacedPath = Key.of(resourceLocation); + Path targetImagePath = pack.resourcePackFolder() + .resolve("assets") + .resolve(namespacedPath.namespace()) + .resolve("textures") + .resolve(namespacedPath.value()); + if (Files.exists(targetImagePath)) { + try (InputStream in = Files.newInputStream(targetImagePath)) { + BufferedImage image = ImageIO.read(in); + heightObj = image.getHeight() / codepointGrid.length; + } catch (IOException e) { + plugin.logger().warn("Failed to load image " + targetImagePath, e); + return; + } + } else { + throw new LocalizedResourceConfigException("warning.config.image.missing_height"); } - } else { - throw new LocalizedResourceConfigException("warning.config.image.missing_height", path, id); } - } - int height = ResourceConfigUtils.getAsInt(heightObj, "height"); - int ascent = ResourceConfigUtils.getAsInt(section.getOrDefault("ascent", height - 1), "ascent"); - if (height < ascent) { - throw new LocalizedResourceConfigException("warning.config.image.height_ascent_conflict", path, id, String.valueOf(height), String.valueOf(ascent)); - } - - BitmapImage bitmapImage = new BitmapImage(id, fontKey, height, ascent, resourceLocation, codepointGrid); - for (int[] y : codepointGrid) { - for (int x : y) { - font.addBitmapImage(x, bitmapImage); + int height = ResourceConfigUtils.getAsInt(heightObj, "height"); + int ascent = ResourceConfigUtils.getAsInt(section.getOrDefault("ascent", height - 1), "ascent"); + if (height < ascent) { + throw new LocalizedResourceConfigException("warning.config.image.height_ascent_conflict", String.valueOf(height), String.valueOf(ascent)); } - } - images.put(id, bitmapImage); + BitmapImage bitmapImage = new BitmapImage(id, fontId, height, ascent, resourceLocation, codepointGrid); + for (int[] y : codepointGrid) { + for (int x : y) { + font.addBitmapImage(x, bitmapImage); + } + } + + AbstractFontManager.this.images.put(id, bitmapImage); + + }, () -> GsonHelper.get().toJson(section))); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java index aca5c9dd6..7cf65807b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java @@ -46,6 +46,12 @@ public interface FontManager extends Manageable { IllegalCharacterProcessResult processIllegalCharacters(String raw, char replacement); + OffsetFont offsetFont(); + + Map loadedImages(); + + Map emojis(); + ConfigParser[] parsers(); default EmojiTextProcessResult replaceMiniMessageEmoji(@NotNull String miniMessage, @Nullable Player player) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/OffsetFont.java b/core/src/main/java/net/momirealms/craftengine/core/font/OffsetFont.java index c1ae43aa3..26c7cea5a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/OffsetFont.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/OffsetFont.java @@ -12,7 +12,8 @@ import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; public class OffsetFont { - private final String font; + private static OffsetFont instance; + private final net.momirealms.craftengine.core.util.Key font; private final Key fontKey; private final String NEG_16; @@ -39,10 +40,14 @@ public class OffsetFont { .maximumSize(256) .build(); - public OffsetFont(Section section) { - font = section.getString("font", "minecraft:default"); - fontKey = Key.key(font); + public net.momirealms.craftengine.core.util.Key font() { + return font; + } + @SuppressWarnings("all") + public OffsetFont(Section section) { + font = net.momirealms.craftengine.core.util.Key.of(section.getString("font", "minecraft:default")); + fontKey = Key.key(font.namespace(), font.value()); NEG_16 = convertIfUnicode(section.getString("-16", "")); NEG_24 = convertIfUnicode(section.getString("-24", "")); NEG_32 = convertIfUnicode(section.getString("-32", "")); @@ -71,7 +76,7 @@ public class OffsetFont { public String createOffset(int offset, BiFunction tagDecorator) { if (offset == 0) return ""; - return tagDecorator.apply(this.fastLookup.get(offset, k -> k > 0 ? createPos(k) : createNeg(-k)), this.font); + return tagDecorator.apply(this.fastLookup.get(offset, k -> k > 0 ? createPos(k) : createNeg(-k)), this.font.asString()); } @SuppressWarnings("DuplicatedCode") @@ -148,7 +153,7 @@ public class OffsetFont { private String convertIfUnicode(String s) { if (s.startsWith("\\u")) { - return new String(CharacterUtils.decodeUnicodeToChars(font)); + return new String(CharacterUtils.decodeUnicodeToChars(font.asString())); } else { return s; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java index f2fb1bba1..7928753e7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java @@ -14,6 +14,7 @@ import net.momirealms.craftengine.core.pack.AbstractPackManager; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.pack.ResourceLocation; +import net.momirealms.craftengine.core.pack.allocator.IdAllocator; import net.momirealms.craftengine.core.pack.model.*; import net.momirealms.craftengine.core.pack.model.generation.AbstractModelGenerator; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; @@ -22,17 +23,21 @@ import net.momirealms.craftengine.core.pack.model.select.TrimMaterialSelectPrope import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.config.IdSectionConfigParser; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.event.EventFunctions; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.util.*; import org.incendo.cloud.suggestion.Suggestion; import org.incendo.cloud.type.Either; +import java.io.IOException; import java.nio.file.Path; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Stream; @@ -48,7 +53,6 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl protected final Map> customItemsById = new HashMap<>(); protected final Map> customItemsByPath = new HashMap<>(); protected final Map> customItemTags = new HashMap<>(); - protected final Map> cmdConflictChecker = new HashMap<>(); protected final Map modernItemModels1_21_4 = new HashMap<>(); protected final Map> modernItemModels1_21_2 = new HashMap<>(); protected final Map> legacyOverrides = new HashMap<>(); @@ -65,6 +69,14 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl ItemDataModifiers.init(); } + public ItemParser itemParser() { + return itemParser; + } + + public EquipmentParser equipmentParser() { + return equipmentParser; + } + protected static void registerVanillaItemExtraBehavior(ItemBehavior behavior, Key... items) { for (Key key : items) { VANILLA_ITEM_EXTRA_BEHAVIORS.computeIfAbsent(key, k -> new ArrayList<>()).add(behavior); @@ -124,7 +136,6 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl this.modernOverrides.clear(); this.customItemTags.clear(); this.equipments.clear(); - this.cmdConflictChecker.clear(); this.modernItemModels1_21_4.clear(); this.modernItemModels1_21_2.clear(); } @@ -229,8 +240,8 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl } @Override - public Collection items() { - return Collections.unmodifiableCollection(this.customItemsById.keySet()); + public Map> loadedItems() { + return Collections.unmodifiableMap(this.customItemsById); } @Override @@ -267,7 +278,7 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl protected abstract void registerArmorTrimPattern(Collection equipments); - public class EquipmentParser implements ConfigParser { + public class EquipmentParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"equipments", "equipment"}; @Override @@ -281,7 +292,7 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl } @Override - public void parseSection(Pack pack, Path path, Key id, Map section) { + public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (AbstractItemManager.this.equipments.containsKey(id)) { throw new LocalizedResourceConfigException("warning.config.equipment.duplicate"); } @@ -310,8 +321,9 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl } } - public class ItemParser implements ConfigParser { + public class ItemParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"items", "item"}; + private final Map idAllocators = new HashMap<>(); private boolean isModernFormatRequired() { return Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_4); @@ -321,6 +333,18 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl return Config.packMinVersion().isBelow(MinecraftVersions.V1_21_4); } + private boolean needsCustomModelDataCompatibility() { + return Config.packMinVersion().isBelow(MinecraftVersions.V1_21_2); + } + + private boolean needsItemModelCompatibility() { + return Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_2) && VersionHelper.isOrAbove1_21_2(); //todo 能否通过客户端包解决问题 + } + + public Map idAllocators() { + return this.idAllocators; + } + @Override public String[] sectionId() { return CONFIG_SECTION_NAME; @@ -332,218 +356,311 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl } @Override - public void parseSection(Pack pack, Path path, Key id, Map section) { + public void preProcess() { + this.idAllocators.clear(); + } + + @Override + public void postProcess() { + for (Map.Entry entry : this.idAllocators.entrySet()) { + entry.getValue().processPendingAllocations(); + try { + entry.getValue().saveToCache(); + } catch (IOException e) { + AbstractItemManager.this.plugin.logger().warn("Error while saving custom model data allocation for material " + entry.getKey().asString(), e); + } + } + } + + // 创建或获取已有的自动分配器 + private IdAllocator getOrCreateIdAllocator(Key key) { + return this.idAllocators.computeIfAbsent(key, k -> { + IdAllocator newAllocator = new IdAllocator(plugin.dataFolderPath().resolve("cache").resolve("custom-model-data").resolve(k.value() + ".json")); + newAllocator.reset(Config.customModelDataStartingValue(k), 16_777_216); + try { + newAllocator.loadFromCache(); + } catch (IOException e) { + AbstractItemManager.this.plugin.logger().warn("Error while loading custom model data from cache for material " + k.asString(), e); + } + return newAllocator; + }); + } + + @Override + public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (AbstractItemManager.this.customItemsById.containsKey(id)) { throw new LocalizedResourceConfigException("warning.config.item.duplicate"); } - + // 创建UniqueKey,仅缓存用 UniqueKey uniqueId = UniqueKey.create(id); // 判断是不是原版物品 boolean isVanillaItem = isVanillaItem(id); - Key material = Key.from(isVanillaItem ? id.value() : ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("material"), "warning.config.item.missing_material").toLowerCase(Locale.ENGLISH)); - Key clientBoundMaterial = section.containsKey("client-bound-material") ? Key.from(section.get("client-bound-material").toString().toLowerCase(Locale.ENGLISH)) : material; - // 如果是原版物品,那么custom-model-data只能是0,即使用户设置了其他值 - int customModelData = isVanillaItem ? 0 : ResourceConfigUtils.getAsInt(section.getOrDefault("custom-model-data", 0), "custom-model-data"); - boolean clientBoundModel = section.containsKey("client-bound-model") ? ResourceConfigUtils.getAsBoolean(section.get("client-bound-model"), "client-bound-model") : Config.globalClientboundModel(); - if (customModelData < 0) { - throw new LocalizedResourceConfigException("warning.config.item.invalid_custom_model_data", String.valueOf(customModelData)); - } - if (customModelData > 16_777_216) { - throw new LocalizedResourceConfigException("warning.config.item.bad_custom_model_data", String.valueOf(customModelData)); - } - // item-model值 - Key itemModelKey = null; + // 读取服务端侧材质 + Key material = isVanillaItem ? id : Key.from(ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("material"), "warning.config.item.missing_material").toLowerCase(Locale.ROOT)); + // 读取客户端侧材质 + Key clientBoundMaterial = VersionHelper.PREMIUM && section.containsKey("client-bound-material") ? Key.from(section.get("client-bound-material").toString().toLowerCase(Locale.ROOT)) : material; - CustomItem.Builder itemBuilder = createPlatformItemBuilder(uniqueId, material, clientBoundMaterial); - boolean hasItemModelSection = section.containsKey("item-model"); + // custom model data + CompletableFuture customModelDataFuture; + boolean forceCustomModelData; - // 如果custom-model-data不为0 - if (customModelData > 0) { - if (clientBoundModel) itemBuilder.clientBoundDataModifier(new CustomModelDataModifier<>(customModelData)); - else itemBuilder.dataModifier(new CustomModelDataModifier<>(customModelData)); - } - // 如果没有item-model选项被配置,同时这个物品又含有 model 区域 - else if (!hasItemModelSection && section.containsKey("model") && VersionHelper.isOrAbove1_21_2()) { - // 那么使用物品id当成item-model的值 - itemModelKey = Key.from(section.getOrDefault("item-model", id.toString()).toString()); - // 但是有个前提,id必须是有效的resource location - if (ResourceLocation.isValid(itemModelKey.toString())) { - if (clientBoundModel) itemBuilder.clientBoundDataModifier(new ItemModelModifier<>(itemModelKey)); - else itemBuilder.dataModifier(new ItemModelModifier<>(itemModelKey)); - } else { - itemModelKey = null; + if (!isVanillaItem) { + // 如果用户指定了,说明要手动分配,不管他是什么版本,都强制设置模型值 + if (section.containsKey("custom-model-data")) { + int customModelData = ResourceConfigUtils.getAsInt(section.getOrDefault("custom-model-data", 0), "custom-model-data"); + if (customModelData < 0) { + throw new LocalizedResourceConfigException("warning.config.item.invalid_custom_model_data", String.valueOf(customModelData)); + } + if (customModelData > 16_777_216) { + throw new LocalizedResourceConfigException("warning.config.item.bad_custom_model_data", String.valueOf(customModelData)); + } + customModelDataFuture = getOrCreateIdAllocator(clientBoundMaterial).assignFixedId(id.asString(), customModelData); + forceCustomModelData = true; } - } - - // 如果有item-model - if (hasItemModelSection && VersionHelper.isOrAbove1_21_2()) { - itemModelKey = Key.from(section.get("item-model").toString()); - if (clientBoundModel) itemBuilder.clientBoundDataModifier(new ItemModelModifier<>(itemModelKey)); - else itemBuilder.dataModifier(new ItemModelModifier<>(itemModelKey)); - } - - // 对于不重要的配置,可以仅警告,不返回 - ExceptionCollector collector = new ExceptionCollector<>(); - - // 应用物品数据 - try { - applyDataModifiers(MiscUtils.castToMap(section.get("data"), true), itemBuilder::dataModifier); - } catch (LocalizedResourceConfigException e) { - collector.add(e); - } - // 应用客户端侧数据 - try { - if (VersionHelper.PREMIUM) { - applyDataModifiers(MiscUtils.castToMap(section.get("client-bound-data"), true), itemBuilder::clientBoundDataModifier); - } - } catch (LocalizedResourceConfigException e) { - collector.add(e); - } - - // 如果不是原版物品,那么加入ce的标识符 - if (!isVanillaItem) - itemBuilder.dataModifier(new IdModifier<>(id)); - - // 事件 - Map>> eventTriggerListMap; - try { - eventTriggerListMap = EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event")); - } catch (LocalizedResourceConfigException e) { - collector.add(e); - eventTriggerListMap = Map.of(); - } - - // 设置 - ItemSettings settings; - try { - settings = Optional.ofNullable(ResourceConfigUtils.get(section, "settings")) - .map(map -> ItemSettings.fromMap(MiscUtils.castToMap(map, true))) - .map(it -> isVanillaItem ? it.canPlaceRelatedVanillaBlock(true) : it) - .orElse(ItemSettings.of().canPlaceRelatedVanillaBlock(isVanillaItem)); - } catch (LocalizedResourceConfigException e) { - collector.add(e); - settings = ItemSettings.of().canPlaceRelatedVanillaBlock(isVanillaItem); - } - - // 行为 - List behaviors; - try { - behaviors = ItemBehaviors.fromObj(pack, path, id, ResourceConfigUtils.get(section, "behavior", "behaviors")); - } catch (LocalizedResourceConfigException e) { - collector.add(e); - behaviors = Collections.emptyList(); - } - - // 如果有物品更新器 - if (section.containsKey("updater")) { - Map updater = ResourceConfigUtils.getAsMap(section.get("updater"), "updater"); - List versions = new ArrayList<>(2); - for (Map.Entry entry : updater.entrySet()) { - try { - int version = Integer.parseInt(entry.getKey()); - versions.add(new ItemUpdateConfig.Version( - version, - ResourceConfigUtils.parseConfigAsList(entry.getValue(), map -> ItemUpdaters.fromMap(id, map)).toArray(new ItemUpdater[0]) - )); - } catch (NumberFormatException ignored) { + // 用户没指定custom-model-data,则看当前资源包版本兼容需求 + else { + forceCustomModelData = false; + // 如果最低版本要1.21.1以下支持 + if (needsCustomModelDataCompatibility()) { + customModelDataFuture = getOrCreateIdAllocator(clientBoundMaterial).requestAutoId(id.asString()); + } + // 否则不主动分配模型值 + else { + customModelDataFuture = CompletableFuture.completedFuture(0); } } - ItemUpdateConfig config = new ItemUpdateConfig(versions); - itemBuilder.updater(config); - itemBuilder.dataModifier(new ItemVersionModifier<>(config.maxVersion())); + } else { + forceCustomModelData = false; + // 原版物品不应该有这个 + customModelDataFuture = CompletableFuture.completedFuture(0); } - // 构建自定义物品 - CustomItem customItem = itemBuilder - .isVanillaItem(isVanillaItem) - .behaviors(behaviors) - .settings(settings) - .events(eventTriggerListMap) - .build(); - - // 添加到缓存 - addCustomItem(customItem); - - // 如果有类别,则添加 - if (section.containsKey("category")) { - AbstractItemManager.this.plugin.itemBrowserManager().addExternalCategoryMember(id, MiscUtils.getAsStringList(section.get("category")).stream().map(Key::of).toList()); - } - - // 模型配置区域,如果这里被配置了,那么用户必须要配置custom-model-data或item-model - Map modelSection = MiscUtils.castToMap(section.get("model"), true); - Map legacyModelSection = MiscUtils.castToMap(section.get("legacy-model"), true); - if (modelSection == null && legacyModelSection == null) { - collector.throwIfPresent(); - return; - } - - boolean needsModelSection = isModernFormatRequired() || (needsLegacyCompatibility() && legacyModelSection == null); - // 只对自定义物品有这个限制 - if (!isVanillaItem) { - // 既没有模型值也没有item-model - if (customModelData == 0 && itemModelKey == null) { - collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.missing_model_id")); + // 当模型值完成分配的时候 + customModelDataFuture.whenComplete((cmd, throwable) -> ResourceConfigUtils.runCatching(path, node, () -> { + int customModelData; + if (throwable != null) { + // 检测custom model data 冲突 + if (throwable instanceof IdAllocator.IdConflictException exception) { + if (section.containsKey("model") || section.containsKey("legacy-model")) { + throw new LocalizedResourceConfigException("warning.config.item.custom_model_data.conflict", String.valueOf(exception.id()), exception.previousOwner()); + } + customModelData = exception.id(); + } + // custom model data 已被用尽,不太可能 + else if (throwable instanceof IdAllocator.IdExhaustedException) { + throw new LocalizedResourceConfigException("warning.config.item.custom_model_data.exhausted", clientBoundMaterial.asString()); + } + // 未知错误 + else { + Debugger.ITEM.warn(() -> "Unknown error while allocating custom model data.", throwable); + return; + } + } else { + customModelData = cmd; } - } - // 新版格式 - ItemModel modernModel = null; - // 旧版格式 - TreeSet legacyOverridesModels = null; - // 如果需要支持新版item model 或者用户需要旧版本兼容,但是没配置legacy-model - if (needsModelSection) { - // 1.21.4+必须要配置model区域,如果不需要高版本兼容,则可以只写legacy-model - if (modelSection == null) { - collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.missing_model")); - return; + // item model + Key itemModel = null; + boolean forceItemModel = false; + + // 如果这个版本可以使用 item model + if (!isVanillaItem && needsItemModelCompatibility()) { + // 如果用户主动设定了item model,那么肯定要设置 + if (section.containsKey("item-model")) { + itemModel = Key.from(section.get("item-model").toString()); + forceItemModel = true; + } + // 用户没设置item model也没设置custom model data,那么为他生成一个基于物品id的item model + else if (customModelData == 0 || Config.alwaysUseItemModel()) { + itemModel = id; + } + // 用户没设置item model但是有custom model data,那么就使用custom model data } + + // 是否使用客户端侧模型 + boolean clientBoundModel = VersionHelper.PREMIUM && (section.containsKey("client-bound-model") ? ResourceConfigUtils.getAsBoolean(section.get("client-bound-model"), "client-bound-model") : Config.globalClientboundModel()); + + CustomItem.Builder itemBuilder = createPlatformItemBuilder(uniqueId, material, clientBoundMaterial); + + // 模型配置区域,如果这里被配置了,那么用户必须要配置custom-model-data或item-model + Map modelSection = MiscUtils.castToMap(section.get("model"), true); + Map legacyModelSection = MiscUtils.castToMap(section.get("legacy-model"), true); + boolean hasModelSection = modelSection != null || legacyModelSection != null; + + if (customModelData > 0 && (hasModelSection || forceCustomModelData)) { + if (clientBoundModel) itemBuilder.clientBoundDataModifier(new CustomModelDataModifier<>(customModelData)); + else itemBuilder.dataModifier(new CustomModelDataModifier<>(customModelData)); + } + if (itemModel != null && (hasModelSection || forceItemModel)) { + if (clientBoundModel) itemBuilder.clientBoundDataModifier(new ItemModelModifier<>(itemModel)); + else itemBuilder.dataModifier(new ItemModelModifier<>(itemModel)); + } + + // 对于不重要的配置,可以仅警告,不返回 + ExceptionCollector collector = new ExceptionCollector<>(); + + // 应用物品数据 try { - modernModel = ItemModels.fromMap(modelSection); - for (ModelGeneration generation : modernModel.modelsToGenerate()) { - prepareModelGeneration(generation); + applyDataModifiers(MiscUtils.castToMap(section.get("data"), true), itemBuilder::dataModifier); + } catch (LocalizedResourceConfigException e) { + collector.add(e); + } + + // 应用客户端侧数据 + try { + if (VersionHelper.PREMIUM) { + applyDataModifiers(MiscUtils.castToMap(section.get("client-bound-data"), true), itemBuilder::clientBoundDataModifier); } } catch (LocalizedResourceConfigException e) { - collector.addAndThrow(e); + collector.add(e); } - } - // 如果需要旧版本兼容 - if (needsLegacyCompatibility()) { - if (legacyModelSection != null) { + + // 如果不是原版物品,那么加入ce的标识符 + if (!isVanillaItem) + itemBuilder.dataModifier(new IdModifier<>(id)); + + // 事件 + Map>> eventTriggerListMap; + try { + eventTriggerListMap = EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event")); + } catch (LocalizedResourceConfigException e) { + collector.add(e); + eventTriggerListMap = Map.of(); + } + + // 设置 + ItemSettings settings; + try { + settings = Optional.ofNullable(ResourceConfigUtils.get(section, "settings")) + .map(map -> ItemSettings.fromMap(MiscUtils.castToMap(map, true))) + .map(it -> isVanillaItem ? it.canPlaceRelatedVanillaBlock(true) : it) + .orElse(ItemSettings.of().canPlaceRelatedVanillaBlock(isVanillaItem)); + } catch (LocalizedResourceConfigException e) { + collector.add(e); + settings = ItemSettings.of().canPlaceRelatedVanillaBlock(isVanillaItem); + } + + // 行为 + List behaviors; + try { + behaviors = ResourceConfigUtils.parseConfigAsList(ResourceConfigUtils.get(section, "behavior", "behaviors"), map -> ItemBehaviors.fromMap(pack, path, node, id, map)); + } catch (LocalizedResourceConfigException e) { + collector.add(e); + behaviors = Collections.emptyList(); + } + + // 如果有物品更新器 + if (section.containsKey("updater")) { + Map updater = ResourceConfigUtils.getAsMap(section.get("updater"), "updater"); + List versions = new ArrayList<>(2); + for (Map.Entry entry : updater.entrySet()) { + try { + int version = Integer.parseInt(entry.getKey()); + versions.add(new ItemUpdateConfig.Version( + version, + ResourceConfigUtils.parseConfigAsList(entry.getValue(), map -> ItemUpdaters.fromMap(id, map)).toArray(new ItemUpdater[0]) + )); + } catch (NumberFormatException ignored) { + } + } + ItemUpdateConfig config = new ItemUpdateConfig(versions); + itemBuilder.updater(config); + itemBuilder.dataModifier(new ItemVersionModifier<>(config.maxVersion())); + } + + // 构建自定义物品 + CustomItem customItem = itemBuilder + .isVanillaItem(isVanillaItem) + .behaviors(behaviors) + .settings(settings) + .events(eventTriggerListMap) + .build(); + + // 添加到缓存 + addCustomItem(customItem); + + // 如果有类别,则添加 + if (section.containsKey("category")) { + AbstractItemManager.this.plugin.itemBrowserManager().addExternalCategoryMember(id, MiscUtils.getAsStringList(section.get("category")).stream().map(Key::of).toList()); + } + + /* + * ======================== + * + * 模型配置分界线 + * + * ======================== + */ + + // 原版物品还改模型?自己替换json去 + if (isVanillaItem) { + return; + } + + if (!hasModelSection) { + collector.throwIfPresent(); + return; + } + + // 只对自定义物品有这个限制,既没有模型值也没有item-model + if (customModelData == 0 && itemModel == null) { + collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.missing_model_id")); + } + + // 新版格式 + ItemModel modernModel = null; + // 旧版格式 + TreeSet legacyOverridesModels = null; + // 如果需要支持新版item model 或者用户需要旧版本兼容,但是没配置legacy-model + if (isModernFormatRequired() || (needsLegacyCompatibility() && legacyModelSection == null)) { + // 1.21.4+必须要配置model区域,如果不需要高版本兼容,则可以只写legacy-model + if (modelSection == null) { + collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.missing_model")); + return; + } try { - LegacyItemModel legacyItemModel = LegacyItemModel.fromMap(legacyModelSection, customModelData); - for (ModelGeneration generation : legacyItemModel.modelsToGenerate()) { + modernModel = ItemModels.fromMap(modelSection); + for (ModelGeneration generation : modernModel.modelsToGenerate()) { prepareModelGeneration(generation); } - legacyOverridesModels = new TreeSet<>(legacyItemModel.overrides()); } catch (LocalizedResourceConfigException e) { collector.addAndThrow(e); } - } else { - legacyOverridesModels = new TreeSet<>(); - processModelRecursively(modernModel, new LinkedHashMap<>(), legacyOverridesModels, clientBoundMaterial, customModelData); - if (legacyOverridesModels.isEmpty()) { - collector.add(new LocalizedResourceConfigException("warning.config.item.legacy_model.cannot_convert", path.toString(), id.asString())); + } + // 如果需要旧版本兼容 + if (needsLegacyCompatibility()) { + if (legacyModelSection != null) { + try { + LegacyItemModel legacyItemModel = LegacyItemModel.fromMap(legacyModelSection, customModelData); + for (ModelGeneration generation : legacyItemModel.modelsToGenerate()) { + prepareModelGeneration(generation); + } + legacyOverridesModels = new TreeSet<>(legacyItemModel.overrides()); + } catch (LocalizedResourceConfigException e) { + collector.addAndThrow(e); + } + } else { + legacyOverridesModels = new TreeSet<>(); + processModelRecursively(modernModel, new LinkedHashMap<>(), legacyOverridesModels, clientBoundMaterial, customModelData); + if (legacyOverridesModels.isEmpty()) { + collector.add(new LocalizedResourceConfigException("warning.config.item.legacy_model.cannot_convert")); + } } } - } - // 自定义物品的model处理 - if (!isVanillaItem) { + boolean hasLegacyModel = legacyOverridesModels != null && !legacyOverridesModels.isEmpty(); + boolean hasModernModel = modernModel != null; + + // 自定义物品的model处理 // 这个item-model是否存在,且是原版item-model - boolean isVanillaItemModel = itemModelKey != null && AbstractPackManager.PRESET_ITEMS.containsKey(itemModelKey); + boolean isVanillaItemModel = itemModel != null && AbstractPackManager.PRESET_ITEMS.containsKey(itemModel); // 使用了自定义模型值 if (customModelData != 0) { // 如果用户主动设置了item-model且为原版物品,则使用item-model为基础模型,否则使用其视觉材质对应的item-model - Key finalBaseModel = isVanillaItemModel ? itemModelKey : clientBoundMaterial; - // 检查cmd冲突 - Map conflict = AbstractItemManager.this.cmdConflictChecker.computeIfAbsent(finalBaseModel, k -> new HashMap<>()); - if (conflict.containsKey(customModelData)) { - collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.custom_model_data_conflict", String.valueOf(customModelData), conflict.get(customModelData).toString())); - } - conflict.put(customModelData, id); + Key finalBaseModel = isVanillaItemModel ? itemModel : clientBoundMaterial; // 添加新版item model - if (isModernFormatRequired() && modernModel != null) { + if (isModernFormatRequired() && hasModernModel) { TreeMap map = AbstractItemManager.this.modernOverrides.computeIfAbsent(finalBaseModel, k -> new TreeMap<>()); map.put(customModelData, new ModernItemModel( modernModel, @@ -552,41 +669,33 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl )); } // 添加旧版 overrides - if (needsLegacyCompatibility() && legacyOverridesModels != null && !legacyOverridesModels.isEmpty()) { + if (needsLegacyCompatibility() && hasLegacyModel) { TreeSet lom = AbstractItemManager.this.legacyOverrides.computeIfAbsent(finalBaseModel, k -> new TreeSet<>()); lom.addAll(legacyOverridesModels); } } else if (isVanillaItemModel) { - collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.item_model.conflict", itemModelKey.asString())); + collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.item_model.conflict", itemModel.asString())); } // 使用了item-model组件,且不是原版物品的 - if (itemModelKey != null && !isVanillaItemModel) { - if (isModernFormatRequired() && modernModel != null) { - AbstractItemManager.this.modernItemModels1_21_4.put(itemModelKey, new ModernItemModel( + if (itemModel != null && !isVanillaItemModel) { + if (isModernFormatRequired() && hasModernModel) { + AbstractItemManager.this.modernItemModels1_21_4.put(itemModel, new ModernItemModel( modernModel, ResourceConfigUtils.getAsBoolean(section.getOrDefault("oversized-in-gui", true), "oversized-in-gui"), ResourceConfigUtils.getAsBoolean(section.getOrDefault("hand-animation-on-swap", true), "hand-animation-on-swap") )); } - if (Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_2) && needsLegacyCompatibility() && legacyOverridesModels != null && !legacyOverridesModels.isEmpty()) { - TreeSet lom = AbstractItemManager.this.modernItemModels1_21_2.computeIfAbsent(itemModelKey, k -> new TreeSet<>()); + if (needsItemModelCompatibility() && needsLegacyCompatibility() && hasLegacyModel) { + TreeSet lom = AbstractItemManager.this.modernItemModels1_21_2.computeIfAbsent(itemModel, k -> new TreeSet<>()); lom.addAll(legacyOverridesModels); } } - } else { - // 原版物品的item model覆写 - if (isModernFormatRequired()) { - AbstractItemManager.this.modernItemModels1_21_4.put(id, new ModernItemModel( - modernModel, - ResourceConfigUtils.getAsBoolean(section.getOrDefault("oversized-in-gui", true), "oversized-in-gui"), - ResourceConfigUtils.getAsBoolean(section.getOrDefault("hand-animation-on-swap", true), "hand-animation-on-swap") - )); - } - } - // 抛出异常 - collector.throwIfPresent(); + // 抛出异常 + collector.throwIfPresent(); + + }, () -> GsonHelper.get().toJson(section))); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/BuildableItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/BuildableItem.java index 4e996c007..3b2798539 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/BuildableItem.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/BuildableItem.java @@ -1,7 +1,6 @@ package net.momirealms.craftengine.core.item; import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.util.Key; public interface BuildableItem { @@ -27,18 +26,18 @@ public interface BuildableItem { } default I buildItemStack() { - return buildItemStack(ItemBuildContext.EMPTY, 1); + return buildItemStack(ItemBuildContext.empty(), 1); } default I buildItemStack(int count) { - return buildItemStack(ItemBuildContext.EMPTY, count); + return buildItemStack(ItemBuildContext.empty(), count); } default I buildItemStack(Player player) { - return this.buildItemStack(new ItemBuildContext(player, ContextHolder.EMPTY), 1); + return this.buildItemStack(ItemBuildContext.of(player), 1); } default I buildItemStack(Player player, int count) { - return this.buildItemStack(new ItemBuildContext(player, ContextHolder.EMPTY), count); + return this.buildItemStack(ItemBuildContext.of(player), count); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java index b02cbea0b..bb7a71ed9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java @@ -16,6 +16,10 @@ import java.util.Optional; public interface CustomItem extends BuildableItem { + /** + * Since CraftEngine allows users to add certain functionalities to vanilla items, this custom item might actually be a vanilla item. + * This will be refactored before the 1.0 release, but no changes will be made for now to ensure compatibility. + */ boolean isVanillaItem(); Key id(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemBuildContext.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemBuildContext.java index a8b7dd219..1a9009dd6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemBuildContext.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemBuildContext.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.item; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; @@ -10,12 +11,23 @@ import org.jetbrains.annotations.Nullable; import java.util.Map; public class ItemBuildContext extends PlayerOptionalContext { - public static final ItemBuildContext EMPTY = new ItemBuildContext(null, ContextHolder.EMPTY); + + /** + * Use {@link #empty()} instead + */ + @Deprecated(since = "0.0.63", forRemoval = true) + public static final ItemBuildContext EMPTY = new ItemBuildContext(null, ContextHolder.empty()); + public static final TagResolver[] EMPTY_RESOLVERS = empty().tagResolvers(); public ItemBuildContext(@Nullable Player player, @NotNull ContextHolder contexts) { super(player, contexts); } + @NotNull + public static ItemBuildContext empty() { + return new ItemBuildContext(null, ContextHolder.empty()); + } + @NotNull public static ItemBuildContext of(@Nullable Player player, @NotNull ContextHolder contexts) { return new ItemBuildContext(player, contexts); @@ -29,7 +41,7 @@ public class ItemBuildContext extends PlayerOptionalContext { @NotNull public static ItemBuildContext of(@Nullable Player player) { - if (player == null) return new ItemBuildContext(null, ContextHolder.EMPTY); + if (player == null) return new ItemBuildContext(null, ContextHolder.empty()); return new ItemBuildContext(player, new ContextHolder(Map.of(DirectContextParameters.PLAYER, () -> player))); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java index 77bf9f1a5..2e1c33f3b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java @@ -66,13 +66,9 @@ public final class ItemKeys { public static final Key MAGENTA_DYE = Key.of("minecraft:magenta_dye"); public static final Key PINK_DYE = Key.of("minecraft:pink_dye"); - public static final Key[] AXES = new Key[] { - WOODEN_AXE, STONE_AXE, IRON_AXE, GOLDEN_AXE, DIAMOND_AXE, NETHERITE_AXE - }; + public static final Key[] AXES = new Key[] {WOODEN_AXE, STONE_AXE, IRON_AXE, GOLDEN_AXE, DIAMOND_AXE, NETHERITE_AXE}; - public static final Key[] WATER_BUCKETS = new Key[] { - WATER_BUCKET, COD_BUCKET, SALMON_BUCKET, TROPICAL_FISH_BUCKET, TADPOLE_BUCKET, PUFFERFISH_BUCKET, AXOLOTL_BUCKET - }; + public static final Key[] WATER_BUCKETS = new Key[] {WATER_BUCKET, COD_BUCKET, SALMON_BUCKET, TROPICAL_FISH_BUCKET, TADPOLE_BUCKET, PUFFERFISH_BUCKET, AXOLOTL_BUCKET}; public static final Key[] DYES = new Key[] { WHITE_DYE, LIGHT_GRAY_DYE, GRAY_DYE, BLACK_DYE, BROWN_DYE, RED_DYE, ORANGE_DYE, YELLOW_DYE, LIME_DYE, diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java index 4f34eff44..a255c23de 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java @@ -54,7 +54,12 @@ public interface ItemManager extends Manageable, ModelGenerator { Item fromByteArray(byte[] bytes); - Collection items(); + Map> loadedItems(); + + @Deprecated(forRemoval = true) + default Collection items() { + return loadedItems().keySet(); + } ExternalItemSource getExternalItemSource(String name); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java index f8d5ad125..076316376 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java @@ -3,7 +3,6 @@ package net.momirealms.craftengine.core.item; import net.momirealms.craftengine.core.entity.Billboard; import net.momirealms.craftengine.core.entity.ItemDisplayContext; import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta; -import net.momirealms.craftengine.core.entity.projectile.ProjectileType; import net.momirealms.craftengine.core.item.equipment.ComponentBasedEquipment; import net.momirealms.craftengine.core.item.equipment.Equipment; import net.momirealms.craftengine.core.item.modifier.EquippableModifier; @@ -25,7 +24,7 @@ import java.util.stream.Collectors; public class ItemSettings { int fuelTime; Set tags = Set.of(); - Tristate canRepair = Tristate.UNDEFINED; + Repairable repairable = Repairable.UNDEFINED; List anvilRepairItems = List.of(); boolean renameable = true; boolean canPlaceRelatedVanillaBlock = false; @@ -89,7 +88,7 @@ public class ItemSettings { newSettings.fuelTime = settings.fuelTime; newSettings.tags = settings.tags; newSettings.equipment = settings.equipment; - newSettings.canRepair = settings.canRepair; + newSettings.repairable = settings.repairable; newSettings.anvilRepairItems = settings.anvilRepairItems; newSettings.renameable = settings.renameable; newSettings.canPlaceRelatedVanillaBlock = settings.canPlaceRelatedVanillaBlock; @@ -128,8 +127,8 @@ public class ItemSettings { return canPlaceRelatedVanillaBlock; } - public Tristate canRepair() { - return canRepair; + public Repairable repairable() { + return repairable; } public int fuelTime() { @@ -233,8 +232,8 @@ public class ItemSettings { return this; } - public ItemSettings canRepair(Tristate canRepair) { - this.canRepair = canRepair; + public ItemSettings repairable(Repairable repairable) { + this.repairable = repairable; return this; } @@ -315,8 +314,14 @@ public class ItemSettings { static { registerFactory("repairable", (value -> { - boolean bool = ResourceConfigUtils.getAsBoolean(value, "repairable"); - return settings -> settings.canRepair(bool ? Tristate.TRUE : Tristate.FALSE); + if (value instanceof Map mapValue) { + Map repairableData = ResourceConfigUtils.getAsMap(mapValue, "repairable"); + Repairable repairable = Repairable.fromMap(repairableData); + return settings -> settings.repairable(repairable); + } else { + boolean bool = ResourceConfigUtils.getAsBoolean(value, "repairable"); + return settings -> settings.repairable(bool ? Repairable.TRUE : Repairable.FALSE); + } })); registerFactory("enchantable", (value -> { boolean bool = ResourceConfigUtils.getAsBoolean(value, "enchantable"); @@ -399,12 +404,11 @@ public class ItemSettings { Key customTridentItemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(args.get("item"), "warning.config.item.settings.projectile.missing_item")); ItemDisplayContext displayType = ItemDisplayContext.valueOf(args.getOrDefault("display-transform", "NONE").toString().toUpperCase(Locale.ENGLISH)); Billboard billboard = Billboard.valueOf(args.getOrDefault("billboard", "FIXED").toString().toUpperCase(Locale.ENGLISH)); - Vector3f translation = MiscUtils.getAsVector3f(args.getOrDefault("translation", "0"), "translation"); - Vector3f scale = MiscUtils.getAsVector3f(args.getOrDefault("scale", "1"), "scale"); - Quaternionf rotation = MiscUtils.getAsQuaternionf(ResourceConfigUtils.get(args, "rotation-left", "rotation"), "rotation-left"); - ProjectileType type = Optional.ofNullable(args.get("type")).map(String::valueOf).map(it -> ProjectileType.valueOf(it.toUpperCase(Locale.ENGLISH))).orElse(null); + Vector3f translation = ResourceConfigUtils.getAsVector3f(args.getOrDefault("translation", "0"), "translation"); + Vector3f scale = ResourceConfigUtils.getAsVector3f(args.getOrDefault("scale", "1"), "scale"); + Quaternionf rotation = ResourceConfigUtils.getAsQuaternionf(ResourceConfigUtils.get(args, "rotation"), "rotation"); double range = ResourceConfigUtils.getAsDouble(args.getOrDefault("range", 1), "range"); - return settings -> settings.projectileMeta(new ProjectileMeta(customTridentItemId, displayType, billboard, scale, translation, rotation, range, type)); + return settings -> settings.projectileMeta(new ProjectileMeta(customTridentItemId, displayType, billboard, scale, translation, rotation, range)); })); registerFactory("helmet", (value -> { Map args = MiscUtils.castToMap(value, false); @@ -426,14 +430,14 @@ public class ItemSettings { if (value instanceof Integer i) { return settings -> settings.dyeColor(Color.fromDecimal(i)); } else { - return settings -> settings.dyeColor(Color.fromVector3f(MiscUtils.getAsVector3f(value, "dye-color"))); + return settings -> settings.dyeColor(Color.fromVector3f(ResourceConfigUtils.getAsVector3f(value, "dye-color"))); } })); registerFactory("firework-color", (value -> { if (value instanceof Integer i) { return settings -> settings.fireworkColor(Color.fromDecimal(i)); } else { - return settings -> settings.fireworkColor(Color.fromVector3f(MiscUtils.getAsVector3f(value, "firework-color"))); + return settings -> settings.fireworkColor(Color.fromVector3f(ResourceConfigUtils.getAsVector3f(value, "firework-color"))); } })); registerFactory("food", (value -> { diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/behavior/EmptyItemBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/item/behavior/EmptyItemBehavior.java index 68c0c1e49..6546c56c3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/behavior/EmptyItemBehavior.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/behavior/EmptyItemBehavior.java @@ -13,7 +13,7 @@ public class EmptyItemBehavior extends ItemBehavior { public static class Factory implements ItemBehaviorFactory { @Override - public ItemBehavior create(Pack pack, Path path, Key id, Map arguments) { + public ItemBehavior create(Pack pack, Path path, String node, Key id, Map arguments) { return INSTANCE; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviorFactory.java b/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviorFactory.java index 4b5ebe888..0f90c6223 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviorFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviorFactory.java @@ -8,5 +8,5 @@ import java.util.Map; public interface ItemBehaviorFactory { - ItemBehavior create(Pack pack, Path path, Key id, Map arguments); + ItemBehavior create(Pack pack, Path path, String node, Key id, Map arguments); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviors.java b/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviors.java index 6ab3f1d38..f27f4effd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviors.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/behavior/ItemBehaviors.java @@ -6,13 +6,10 @@ import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceKey; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; import java.util.Map; public class ItemBehaviors { @@ -23,7 +20,7 @@ public class ItemBehaviors { .register(ResourceKey.create(Registries.ITEM_BEHAVIOR_FACTORY.location(), key), factory); } - public static ItemBehavior fromMap(Pack pack, Path path, Key id, Map map) { + public static ItemBehavior fromMap(Pack pack, Path path, String node, Key id, Map map) { if (map == null || map.isEmpty()) return EmptyItemBehavior.INSTANCE; String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.item.behavior.missing_type"); Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE); @@ -31,25 +28,6 @@ public class ItemBehaviors { if (factory == null) { throw new LocalizedResourceConfigException("warning.config.item.behavior.invalid_type", type); } - return factory.create(pack, path, id, map); - } - - public static List fromList(Pack pack, Path path, Key id, List> list) { - List behaviors = new ArrayList<>(list.size()); - for (Map map : list) { - behaviors.add(fromMap(pack, path, id, map)); - } - return behaviors; - } - - @SuppressWarnings("unchecked") - public static List fromObj(Pack pack, Path path, Key id, Object behaviorObj) { - if (behaviorObj instanceof Map) { - return List.of(fromMap(pack, path, id, MiscUtils.castToMap(behaviorObj, false))); - } else if (behaviorObj instanceof List) { - return fromList(pack, path, id, (List>) behaviorObj); - } else { - return List.of(); - } + return factory.create(pack, path, node, id, map); } } \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/context/BlockPlaceContext.java b/core/src/main/java/net/momirealms/craftengine/core/item/context/BlockPlaceContext.java index cdc2ddd82..2cb96abb7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/context/BlockPlaceContext.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/context/BlockPlaceContext.java @@ -48,4 +48,23 @@ public class BlockPlaceContext extends UseOnContext { public Direction getNearestLookingDirection() { return Direction.orderedByNearest(this.getPlayer())[0]; } + + public Direction[] getNearestLookingDirections() { + Direction[] directions = Direction.orderedByNearest(this.getPlayer()); + if (!this.replaceClicked) { + Direction clickedFace = this.getClickedFace(); + int i = 0; + + while (i < directions.length && directions[i] != clickedFace.opposite()) { + i++; + } + + if (i > 0) { + System.arraycopy(directions, 0, directions, 1, i); + directions[0] = clickedFace.opposite(); + } + + } + return directions; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/equipment/ComponentBasedEquipment.java b/core/src/main/java/net/momirealms/craftengine/core/item/equipment/ComponentBasedEquipment.java index d8d52d807..f60d497de 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/equipment/ComponentBasedEquipment.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/equipment/ComponentBasedEquipment.java @@ -131,7 +131,7 @@ public class ComponentBasedEquipment extends AbstractEquipment implements Suppli return new DyeableData(ResourceConfigUtils.getAsInt(data.get("color-when-undyed"), "color-when-undyed")); } } - return new DyeableData(null); + return null; } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DyedColorModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DyedColorModifier.java index 2ce36e797..81e964094 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DyedColorModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DyedColorModifier.java @@ -6,7 +6,7 @@ import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.util.Color; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; @@ -54,7 +54,7 @@ public class DyedColorModifier implements SimpleNetworkItemDataModifier { if (arg instanceof Integer integer) { return new DyedColorModifier<>(Color.fromDecimal(integer)); } else { - Vector3f vector3f = MiscUtils.getAsVector3f(arg, "dyed-color"); + Vector3f vector3f = ResourceConfigUtils.getAsVector3f(arg, "dyed-color"); return new DyedColorModifier<>(Color.fromVector3f(vector3f)); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ExternalModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ExternalModifier.java index a424feb2a..4844f542e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ExternalModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ExternalModifier.java @@ -8,12 +8,13 @@ import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.jetbrains.annotations.NotNull; -import java.util.Locale; -import java.util.Map; +import java.util.*; public class ExternalModifier implements ItemDataModifier { public static final Factory FACTORY = new Factory<>(); + private static final ThreadLocal> BUILD_STACK = ThreadLocal.withInitial(LinkedHashSet::new); private final String id; private final ExternalItemSource provider; @@ -38,14 +39,36 @@ public class ExternalModifier implements ItemDataModifier { @SuppressWarnings("unchecked") @Override public Item apply(Item item, ItemBuildContext context) { - I another = this.provider.build(id, context); - if (another == null) { - CraftEngine.instance().logger().warn("'" + id + "' could not be found in " + provider.plugin()); + Dependency dependency = new Dependency(provider.plugin(), id); + Set buildStack = BUILD_STACK.get(); + + if (buildStack.contains(dependency)) { + StringJoiner dependencyChain = new StringJoiner(" -> "); + buildStack.forEach(element -> dependencyChain.add(element.asString())); + dependencyChain.add(dependency.asString()); + CraftEngine.instance().logger().warn( + "Failed to build '" + this.id + "' from plugin '" + provider.plugin() + "' due to dependency loop: " + dependencyChain + ); return item; } - Item anotherWrapped = (Item) CraftEngine.instance().itemManager().wrap(another); - item.merge(anotherWrapped); - return item; + + buildStack.add(dependency); + try { + I another = this.provider.build(this.id, context); + if (another == null) { + CraftEngine.instance().logger().warn("'" + this.id + "' could not be found in " + provider.plugin()); + return item; + } + Item anotherWrapped = (Item) CraftEngine.instance().itemManager().wrap(another); + item.merge(anotherWrapped); + return item; + } catch (Throwable e) { + CraftEngine.instance().logger().warn("Failed to build item '" + this.id + "' from plugin '" + provider.plugin() + "'", e); + return item; + } finally { + buildStack.remove(dependency); + BUILD_STACK.remove(); + } } public static class Factory implements ItemDataModifierFactory { @@ -60,4 +83,11 @@ public class ExternalModifier implements ItemDataModifier { return new ExternalModifier<>(id, ResourceConfigUtils.requireNonNullOrThrow(provider, () -> new LocalizedResourceConfigException("warning.config.item.data.external.invalid_source", plugin))); } } + + private record Dependency(String source, String id) { + + public @NotNull String asString() { + return source + "[id=" + id + "]"; + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java index 2e2b739df..22c095387 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java @@ -6,6 +6,7 @@ import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.config.IdSectionConfigParser; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.UniqueKey; @@ -106,19 +107,21 @@ public abstract class AbstractRecipeManager implements RecipeManager { if (recipe instanceof AbstractedFixedResultRecipe fixedResult) { this.byResult.computeIfAbsent(fixedResult.result().item().id(), k -> new ArrayList<>()).add(recipe); } - HashSet usedKeys = new HashSet<>(); - for (Ingredient ingredient : recipe.ingredientsInUse()) { - for (UniqueKey holder : ingredient.items()) { - Key key = holder.key(); - if (usedKeys.add(key)) { - this.byIngredient.computeIfAbsent(key, k -> new ArrayList<>()).add(recipe); + if (recipe.canBeSearchedByIngredients()) { + HashSet usedKeys = new HashSet<>(); + for (Ingredient ingredient : recipe.ingredientsInUse()) { + for (UniqueKey holder : ingredient.items()) { + Key key = holder.key(); + if (usedKeys.add(key)) { + this.byIngredient.computeIfAbsent(key, k -> new ArrayList<>()).add(recipe); + } } } } return true; } - public class RecipeParser implements ConfigParser { + public class RecipeParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"recipes", "recipe"}; @Override @@ -132,10 +135,10 @@ public abstract class AbstractRecipeManager implements RecipeManager { } @Override - public void parseSection(Pack pack, Path path, Key id, Map section) { + public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (!Config.enableRecipeSystem()) return; if (AbstractRecipeManager.this.byId.containsKey(id)) { - throw new LocalizedResourceConfigException("warning.config.recipe.duplicate", path, id); + throw new LocalizedResourceConfigException("warning.config.recipe.duplicate"); } Recipe recipe = RecipeSerializers.fromMap(id, section); try { diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeSerializer.java index d6a3460f5..43dd6dfc5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeSerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeSerializer.java @@ -9,6 +9,12 @@ import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; import net.momirealms.craftengine.core.item.recipe.result.PostProcessor; import net.momirealms.craftengine.core.item.recipe.result.PostProcessors; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.condition.AllOfCondition; +import net.momirealms.craftengine.core.plugin.context.event.EventConditions; +import net.momirealms.craftengine.core.plugin.context.event.EventFunctions; +import net.momirealms.craftengine.core.plugin.context.function.Function; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.*; import org.jetbrains.annotations.NotNull; @@ -24,6 +30,23 @@ public abstract class AbstractRecipeSerializer> implement new VanillaRecipeReader1_20_5() : new VanillaRecipeReader1_20(); + @SuppressWarnings("unchecked") + protected Function[] functions(Map arguments) { + Object functions = ResourceConfigUtils.get(arguments, "functions", "function"); + if (functions == null) return null; + List> functionList = ResourceConfigUtils.parseConfigAsList(functions, EventFunctions::fromMap); + return functionList.toArray(new Function[0]); + } + + protected Condition conditions(Map arguments) { + Object conditions = ResourceConfigUtils.get(arguments, "conditions", "condition"); + if (conditions == null) return null; + List> conditionList = ResourceConfigUtils.parseConfigAsList(conditions, EventConditions::fromMap); + if (conditionList.isEmpty()) return null; + if (conditionList.size() == 1) return conditionList.getFirst(); + return new AllOfCondition<>(conditionList); + } + protected boolean showNotification(Map arguments) { return ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("show-notification", true), "show-notification"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/ConditionalRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/ConditionalRecipe.java new file mode 100644 index 000000000..fd0a8b649 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/ConditionalRecipe.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.item.recipe; + +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; + +public interface ConditionalRecipe { + + boolean canUse(final PlayerOptionalContext context); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java index 655f40ea4..13783dabc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java @@ -4,17 +4,37 @@ import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.function.Function; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.Nullable; -public abstract class CustomCraftingTableRecipe extends AbstractGroupedRecipe { +public abstract class CustomCraftingTableRecipe extends AbstractGroupedRecipe implements ConditionalRecipe { protected final CraftingRecipeCategory category; private final CustomRecipeResult visualResult; + private final Function[] craftingFunctions; + private final Condition craftingCondition; - protected CustomCraftingTableRecipe(Key id, boolean showNotification, CustomRecipeResult result, @Nullable CustomRecipeResult visualResult, String group, CraftingRecipeCategory category) { + protected CustomCraftingTableRecipe(Key id, + boolean showNotification, + CustomRecipeResult result, + @Nullable CustomRecipeResult visualResult, + String group, + CraftingRecipeCategory category, + Function[] craftingFunctions, + Condition craftingCondition) { super(id, showNotification, result, group); this.category = category == null ? CraftingRecipeCategory.MISC : category; this.visualResult = visualResult; + this.craftingFunctions = craftingFunctions; + this.craftingCondition = craftingCondition; + } + + @Override + public boolean canUse(PlayerOptionalContext context) { + if (this.craftingCondition == null) return true; + return this.craftingCondition.test(context); } public CraftingRecipeCategory category() { @@ -49,4 +69,8 @@ public abstract class CustomCraftingTableRecipe extends AbstractGroupedRecipe return super.result.buildItem(context); } } + + public Function[] craftingFunctions() { + return craftingFunctions; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java index 40af56a0f..75613d5d8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java @@ -5,6 +5,9 @@ import com.google.gson.JsonObject; import net.momirealms.craftengine.core.item.recipe.input.CraftingInput; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.function.Function; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; @@ -24,8 +27,10 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { CustomRecipeResult visualResult, String group, CraftingRecipeCategory category, - Pattern pattern) { - super(id, showNotification, result, visualResult, group, category); + Pattern pattern, + Function[] craftingFunctions, + Condition craftingCondition) { + super(id, showNotification, result, visualResult, group, category, craftingFunctions, craftingCondition); this.pattern = pattern; this.parsedPattern = pattern.parse(); } @@ -169,7 +174,9 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { parseResult(arguments), parseVisualResult(arguments), arguments.containsKey("group") ? arguments.get("group").toString() : null, craftingRecipeCategory(arguments), - new Pattern<>(pattern.toArray(new String[0]), ingredients) + new Pattern<>(pattern.toArray(new String[0]), ingredients), + functions(arguments), + conditions(arguments) ); } @@ -182,7 +189,9 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { null, VANILLA_RECIPE_HELPER.readGroup(json), VANILLA_RECIPE_HELPER.craftingCategory(json), - new Pattern<>(VANILLA_RECIPE_HELPER.craftingShapedPattern(json), ingredients) + new Pattern<>(VANILLA_RECIPE_HELPER.craftingShapedPattern(json), ingredients), + null, + null ); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java index 678c75543..a60c241a4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java @@ -4,6 +4,9 @@ import com.google.gson.JsonObject; import net.momirealms.craftengine.core.item.recipe.input.CraftingInput; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.function.Function; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import org.jetbrains.annotations.NotNull; @@ -23,8 +26,10 @@ public class CustomShapelessRecipe extends CustomCraftingTableRecipe { CustomRecipeResult visualResult, String group, CraftingRecipeCategory category, - List> ingredients) { - super(id, showNotification, result, visualResult, group, category); + List> ingredients, + Function[] craftingFunctions, + Condition craftingCondition) { + super(id, showNotification, result, visualResult, group, category, craftingFunctions, craftingCondition); this.ingredients = ingredients; this.placementInfo = PlacementInfo.create(ingredients); } @@ -88,7 +93,9 @@ public class CustomShapelessRecipe extends CustomCraftingTableRecipe { parseResult(arguments), parseVisualResult(arguments), arguments.containsKey("group") ? arguments.get("group").toString() : null, craftingRecipeCategory(arguments), - ingredients + ingredients, + functions(arguments), + conditions(arguments) ); } @@ -99,7 +106,9 @@ public class CustomShapelessRecipe extends CustomCraftingTableRecipe { parseResult(VANILLA_RECIPE_HELPER.craftingResult(json.getAsJsonObject("result"))), null, VANILLA_RECIPE_HELPER.readGroup(json), VANILLA_RECIPE_HELPER.craftingCategory(json), - VANILLA_RECIPE_HELPER.shapelessIngredients(json.getAsJsonArray("ingredients")).stream().map(this::toIngredient).toList() + VANILLA_RECIPE_HELPER.shapelessIngredients(json.getAsJsonArray("ingredients")).stream().map(this::toIngredient).toList(), + null, + null ); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java index 9ebe583a7..852b8ff8e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java @@ -7,6 +7,8 @@ import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.input.SmithingInput; import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Registries; @@ -20,13 +22,14 @@ import java.util.List; import java.util.Map; import java.util.Objects; -public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecipe { +public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecipe implements ConditionalRecipe { public static final Serializer SERIALIZER = new Serializer<>(); private final Ingredient base; private final Ingredient template; private final Ingredient addition; private final boolean mergeComponents; private final List processors; + private final Condition condition; public CustomSmithingTransformRecipe(Key id, boolean showNotification, @@ -35,7 +38,8 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip @Nullable Ingredient addition, CustomRecipeResult result, List processors, - boolean mergeComponents + boolean mergeComponents, + Condition condition ) { super(id, showNotification, result); this.base = base; @@ -43,6 +47,13 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip this.addition = addition; this.processors = processors; this.mergeComponents = mergeComponents; + this.condition = condition; + } + + @Override + public boolean canUse(PlayerOptionalContext context) { + if (this.condition != null) return this.condition.test(context); + return true; } @SuppressWarnings("unchecked") @@ -140,14 +151,23 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip toIngredient(addition), parseResult(arguments), ItemDataProcessors.fromMapList(processors), - mergeComponents + mergeComponents, + conditions(arguments) ); } @Override public CustomSmithingTransformRecipe readJson(Key id, JsonObject json) { - return new CustomSmithingTransformRecipe<>(id, - true, toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("template"))), Objects.requireNonNull(toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("base")))), toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("addition"))), parseResult(VANILLA_RECIPE_HELPER.smithingResult(json.getAsJsonObject("result"))), null, true + return new CustomSmithingTransformRecipe<>( + id, + true, + toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("template"))), + Objects.requireNonNull(toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("base")))), + toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("addition"))), + parseResult(VANILLA_RECIPE_HELPER.smithingResult(json.getAsJsonObject("result"))), + null, + true, + null ); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java index a85d30573..020a5d460 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java @@ -6,6 +6,8 @@ import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.input.SmithingInput; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -18,31 +20,41 @@ import java.util.List; import java.util.Map; import java.util.Objects; -public class CustomSmithingTrimRecipe extends AbstractRecipe { +public class CustomSmithingTrimRecipe extends AbstractRecipe implements ConditionalRecipe { public static final Serializer SERIALIZER = new Serializer<>(); private final Ingredient base; private final Ingredient template; private final Ingredient addition; @Nullable // 1.21.5 private final Key pattern; + @Nullable + private final Condition condition; public CustomSmithingTrimRecipe(@NotNull Key id, boolean showNotification, @NotNull Ingredient template, @NotNull Ingredient base, @NotNull Ingredient addition, - @Nullable Key pattern + @Nullable Key pattern, + @Nullable Condition condition ) { super(id, showNotification); this.base = base; this.template = template; this.addition = addition; this.pattern = pattern; + this.condition = condition; if (pattern == null && VersionHelper.isOrAbove1_21_5()) { throw new IllegalStateException("SmithingTrimRecipe cannot have a null pattern on 1.21.5 and above."); } } + @Override + public boolean canUse(PlayerOptionalContext context) { + if (this.condition != null) return this.condition.test(context); + return true; + } + @SuppressWarnings("unchecked") @Override public T assemble(RecipeInput input, ItemBuildContext context) { @@ -103,6 +115,11 @@ public class CustomSmithingTrimRecipe extends AbstractRecipe { return pattern; } + @Override + public boolean canBeSearchedByIngredients() { + return false; + } + @SuppressWarnings({"DuplicatedCode"}) public static class Serializer extends AbstractRecipeSerializer> { @@ -117,7 +134,8 @@ public class CustomSmithingTrimRecipe extends AbstractRecipe { ResourceConfigUtils.requireNonNullOrThrow(toIngredient(template), "warning.config.recipe.smithing_trim.missing_template_type"), ResourceConfigUtils.requireNonNullOrThrow(toIngredient(base), "warning.config.recipe.smithing_trim.missing_base"), ResourceConfigUtils.requireNonNullOrThrow(toIngredient(addition), "warning.config.recipe.smithing_trim.missing_addition"), - pattern + pattern, + conditions(arguments) ); } @@ -128,7 +146,8 @@ public class CustomSmithingTrimRecipe extends AbstractRecipe { Objects.requireNonNull(toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("template")))), Objects.requireNonNull(toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("base")))), Objects.requireNonNull(toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("addition")))), - VersionHelper.isOrAbove1_21_5() ? Key.of(json.get("pattern").getAsString()) : null + VersionHelper.isOrAbove1_21_5() ? Key.of(json.get("pattern").getAsString()) : null, + null ); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Recipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Recipe.java index db3d2f675..ff5864916 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Recipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Recipe.java @@ -25,4 +25,8 @@ public interface Recipe { default boolean showNotification() { return true; } + + default boolean canBeSearchedByIngredients() { + return true; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/network/legacy/LegacyRecipeTypes.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/network/legacy/LegacyRecipeTypes.java index f162b1be0..89ef272af 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/network/legacy/LegacyRecipeTypes.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/network/legacy/LegacyRecipeTypes.java @@ -35,7 +35,10 @@ public final class LegacyRecipeTypes { public static final Key SMITHING_TRIM = Key.of("smithing_trim"); public static final Key DECORATED_POT_RECIPE = Key.of("crafting_decorated_pot"); - public static void register() { + public static void init() { + } + + static { register(SHAPED_RECIPE, new LegacyRecipe.Type(LegacyShapedRecipe::read)); register(SHAPELESS_RECIPE, new LegacyRecipe.Type(LegacyShapelessRecipe::read)); register(ARMOR_DYE, new LegacyRecipe.Type(LegacyCustomRecipe::read)); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/network/modern/display/RecipeDisplayTypes.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/network/modern/display/RecipeDisplayTypes.java index da82d2a0d..81d781991 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/network/modern/display/RecipeDisplayTypes.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/network/modern/display/RecipeDisplayTypes.java @@ -15,7 +15,10 @@ public final class RecipeDisplayTypes { public static final Key STONECUTTER = Key.of("stonecutter"); public static final Key SMITHING = Key.of("smithing"); - public static void register() { + public static void init() { + } + + static { register(CRAFTING_SHAPELESS, new RecipeDisplay.Type(ShapelessCraftingRecipeDisplay::read)); register(CRAFTING_SHAPED, new RecipeDisplay.Type(ShapedCraftingRecipeDisplay::read)); register(FURNACE, new RecipeDisplay.Type(FurnaceRecipeDisplay::read)); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/network/modern/display/slot/SlotDisplayTypes.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/network/modern/display/slot/SlotDisplayTypes.java index 36e63baf4..123f8dc0c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/network/modern/display/slot/SlotDisplayTypes.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/network/modern/display/slot/SlotDisplayTypes.java @@ -18,7 +18,10 @@ public final class SlotDisplayTypes { public static final Key WITH_REMAINDER = Key.of("with_remainder"); public static final Key COMPOSITE = Key.of("composite"); - public static void register() { + public static void init() { + } + + static { register(EMPTY, new SlotDisplay.Type(EmptySlotDisplay::read)); register(ANY_FUEL, new SlotDisplay.Type(AnyFuelDisplay::read)); register(ITEM, new SlotDisplay.Type(ItemSlotDisplay::read)); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/setting/Repairable.java b/core/src/main/java/net/momirealms/craftengine/core/item/setting/Repairable.java new file mode 100644 index 000000000..36ff26fe3 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/setting/Repairable.java @@ -0,0 +1,20 @@ +package net.momirealms.craftengine.core.item.setting; + +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.Tristate; + +import java.util.Map; + +public record Repairable(Tristate craftingTable, Tristate anvilRepair, Tristate anvilCombine) { + + public static final Repairable UNDEFINED = new Repairable(Tristate.UNDEFINED, Tristate.UNDEFINED, Tristate.UNDEFINED); + public static final Repairable TRUE = new Repairable(Tristate.TRUE, Tristate.TRUE, Tristate.TRUE); + public static final Repairable FALSE = new Repairable(Tristate.FALSE, Tristate.FALSE, Tristate.FALSE); + + public static Repairable fromMap(Map map) { + Tristate craftingTable = map.containsKey("crafting-table") ? Tristate.of(ResourceConfigUtils.getAsBoolean(map.get("crafting-table"), "crafting-table")) : Tristate.UNDEFINED; + Tristate anvilRepair = map.containsKey("anvil-repair") ? Tristate.of(ResourceConfigUtils.getAsBoolean(map.get("anvil-repair"), "anvil-repair")) : Tristate.UNDEFINED; + Tristate anvilCombine = map.containsKey("anvil-combine") ? Tristate.of(ResourceConfigUtils.getAsBoolean(map.get("anvil-combine"), "anvil-combine")) : Tristate.UNDEFINED; + return new Repairable(craftingTable, anvilRepair, anvilCombine); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/AbstractVanillaLootManager.java b/core/src/main/java/net/momirealms/craftengine/core/loot/AbstractVanillaLootManager.java index fbacf55cb..80b937630 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/AbstractVanillaLootManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/AbstractVanillaLootManager.java @@ -8,7 +8,7 @@ import java.util.Optional; public abstract class AbstractVanillaLootManager implements VanillaLootManager { protected final Map blockLoots = new HashMap<>(); - // TODO More entity NBT + // TODO 实现一个基于entity data的生物战利品系统 protected final Map entityLoots = new HashMap<>(); public AbstractVanillaLootManager() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/LootPool.java b/core/src/main/java/net/momirealms/craftengine/core/loot/LootPool.java index 41694006f..f07bd5d3c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/LootPool.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/LootPool.java @@ -8,7 +8,7 @@ import net.momirealms.craftengine.core.loot.function.LootFunction; import net.momirealms.craftengine.core.loot.function.LootFunctions; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.MutableInt; import net.momirealms.craftengine.core.util.RandomUtils; @@ -44,7 +44,7 @@ public class LootPool { } if (this.compositeCondition.test(context)) { Consumer> consumer = LootFunction.decorate(this.compositeFunction, lootConsumer, context); - int i = this.rolls.getInt(context) + MCUtils.fastFloor(this.bonusRolls.getFloat(context) * context.luck()); + int i = this.rolls.getInt(context) + MiscUtils.fastFloor(this.bonusRolls.getFloat(context) * context.luck()); for (int j = 0; j < i; ++j) { this.addRandomItem(createFunctionApplier(consumer, context), context); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/entry/AbstractLootEntryContainer.java b/core/src/main/java/net/momirealms/craftengine/core/loot/entry/AbstractLootEntryContainer.java index 7538d3f7f..0f860d061 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/entry/AbstractLootEntryContainer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/entry/AbstractLootEntryContainer.java @@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.loot.entry; import net.momirealms.craftengine.core.loot.LootContext; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; import java.util.List; import java.util.function.Predicate; @@ -14,7 +14,7 @@ public abstract class AbstractLootEntryContainer implements LootEntryContaine protected AbstractLootEntryContainer(List> conditions) { this.conditions = conditions; - this.compositeCondition = MCUtils.allOf(conditions); + this.compositeCondition = MiscUtils.allOf(conditions); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/function/AbstractLootConditionalFunction.java b/core/src/main/java/net/momirealms/craftengine/core/loot/function/AbstractLootConditionalFunction.java index 89a918222..7169d2cad 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/function/AbstractLootConditionalFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/function/AbstractLootConditionalFunction.java @@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.loot.function; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.loot.LootContext; import net.momirealms.craftengine.core.plugin.context.Condition; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; import java.util.List; import java.util.function.Predicate; @@ -14,7 +14,7 @@ public abstract class AbstractLootConditionalFunction implements LootFunction public AbstractLootConditionalFunction(List> predicates) { this.predicates = predicates; - this.compositePredicates = MCUtils.allOf(predicates); + this.compositePredicates = MiscUtils.allOf(predicates); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/function/LimitCountFunction.java b/core/src/main/java/net/momirealms/craftengine/core/loot/function/LimitCountFunction.java new file mode 100644 index 000000000..8ecc4fc2b --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/function/LimitCountFunction.java @@ -0,0 +1,65 @@ +package net.momirealms.craftengine.core.loot.function; + +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.loot.LootConditions; +import net.momirealms.craftengine.core.loot.LootContext; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; +import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; +import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class LimitCountFunction extends AbstractLootConditionalFunction { + public static final Factory FACTORY = new Factory<>(); + @Nullable + private final NumberProvider min; + @Nullable + private final NumberProvider max; + + public LimitCountFunction(List> predicates, @Nullable NumberProvider min, @Nullable NumberProvider max) { + super(predicates); + this.min = min; + this.max = max; + } + + @Override + public Key type() { + return LootFunctions.LIMIT_COUNT; + } + + @Override + protected Item applyInternal(Item item, LootContext context) { + int amount = item.count(); + if (min != null) { + int minAmount = min.getInt(context); + if (amount < minAmount) { + item.count(minAmount); + } + } + if (max != null) { + int maxAmount = max.getInt(context); + if (amount > maxAmount) { + item.count(maxAmount); + } + } + return item; + } + + public static class Factory implements LootFunctionFactory { + @SuppressWarnings("unchecked") + @Override + public LootFunction create(Map arguments) { + Object min = arguments.get("min"); + Object max = arguments.get("max"); + List> conditions = Optional.ofNullable(arguments.get("conditions")) + .map(it -> LootConditions.fromMapList((List>) it)) + .orElse(Collections.emptyList()); + return new LimitCountFunction<>(conditions, min == null ? null : NumberProviders.fromObject(min), max == null ? null : NumberProviders.fromObject(max)); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/function/LootFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/loot/function/LootFunctions.java index c2233c20f..c9d221c95 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/function/LootFunctions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/function/LootFunctions.java @@ -20,12 +20,14 @@ public class LootFunctions { public static final Key SET_COUNT = Key.from("craftengine:set_count"); public static final Key EXPLOSION_DECAY = Key.from("craftengine:explosion_decay"); public static final Key DROP_EXP = Key.from("craftengine:drop_exp"); + public static final Key LIMIT_COUNT = Key.from("craftengine:limit_count"); static { register(SET_COUNT, SetCountFunction.FACTORY); register(EXPLOSION_DECAY, ExplosionDecayFunction.FACTORY); register(APPLY_BONUS, ApplyBonusCountFunction.FACTORY); register(DROP_EXP, DropExpFunction.FACTORY); + register(LIMIT_COUNT, LimitCountFunction.FACTORY); } public static void register(Key key, LootFunctionFactory factory) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index 99313220e..6ee669d9a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java @@ -5,7 +5,6 @@ import com.google.common.collect.Multimap; import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; import com.google.gson.*; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.momirealms.craftengine.core.font.BitmapImage; import net.momirealms.craftengine.core.font.Font; import net.momirealms.craftengine.core.item.equipment.ComponentBasedEquipment; @@ -24,7 +23,6 @@ import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.pack.model.generation.ModelGenerator; import net.momirealms.craftengine.core.pack.model.rangedisptach.CustomModelDataRangeDispatchProperty; import net.momirealms.craftengine.core.pack.obfuscation.ObfA; -import net.momirealms.craftengine.core.pack.obfuscation.ResourcePackGenerationException; import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.pack.revision.Revisions; import net.momirealms.craftengine.core.plugin.CraftEngine; @@ -42,6 +40,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.scanner.ScannerException; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; @@ -84,18 +83,21 @@ public abstract class AbstractPackManager implements PackManager { } private final CraftEngine plugin; - private final BiConsumer eventDispatcher; + private final Consumer cacheEventDispatcher; + private final BiConsumer generationEventDispatcher; private final Map loadedPacks = new HashMap<>(); private final Map sectionParsers = new HashMap<>(); + private final TreeSet sortedParsers = new TreeSet<>(); private final JsonObject vanillaAtlas; private Map cachedConfigFiles = Collections.emptyMap(); private Map cachedAssetFiles = Collections.emptyMap(); protected BiConsumer zipGenerator; protected ResourcePackHost resourcePackHost; - public AbstractPackManager(CraftEngine plugin, BiConsumer eventDispatcher) { + public AbstractPackManager(CraftEngine plugin, Consumer cacheEventDispatcher, BiConsumer generationEventDispatcher) { this.plugin = plugin; - this.eventDispatcher = eventDispatcher; + this.cacheEventDispatcher = cacheEventDispatcher; + this.generationEventDispatcher = generationEventDispatcher; this.zipGenerator = (p1, p2) -> {}; Path resourcesFolder = this.plugin.dataFolderPath().resolve("resources"); try { @@ -122,7 +124,7 @@ public abstract class AbstractPackManager implements PackManager { loadInternalList("models", "block/", VANILLA_MODELS::add); loadInternalList("models", "item/", VANILLA_MODELS::add); - + loadInternalList("models", "item/legacy/", key -> VANILLA_MODELS.add(Key.of(key.namespace(), "item/" + key.value().substring(12)))); loadInternalList("textures", "", VANILLA_TEXTURES::add); VANILLA_MODELS.add(Key.of("minecraft", "builtin/entity")); VANILLA_MODELS.add(Key.of("minecraft", "item/player_head")); @@ -168,7 +170,7 @@ public abstract class AbstractPackManager implements PackManager { JsonArray fileList = listJson.getAsJsonArray("files"); for (JsonElement element : fileList) { if (element instanceof JsonPrimitive primitive) { - callback.accept(Key.of(prefix + FileUtils.pathWithoutExtension(primitive.getAsString()))); + callback.accept(Key.of("minecraft", prefix + FileUtils.pathWithoutExtension(primitive.getAsString()))); } } JsonArray directoryList = listJson.getAsJsonArray("directories"); @@ -245,16 +247,21 @@ public abstract class AbstractPackManager implements PackManager { // magicConstructor.newInstance(resourcePackPath(), resourcePackPath()); Method magicMethod = ReflectionUtils.getMethod(magicClazz, void.class); assert magicMethod != null; - this.zipGenerator = (p1, p2) -> { + final String magicStr1 = StringUtils.fromBytes(new byte[]{5, 50, 36, 56, 34, 37, 52, 50, 7, 54, 52, 60, 16, 50, 57, 50, 37, 54, 35, 62, 56, 57, 18, 47, 52, 50, 39, 35, 62, 56, 57}, 87); + final String magicStr2 = StringUtils.fromBytes(new byte[]{4, 35, 43, 46, 39, 38, 98, 54, 45, 98, 37, 39, 44, 39, 48, 35, 54, 39, 98, 48, 39, 49, 45, 55, 48, 33, 39, 98, 50, 35, 33, 41, 120, 98}, 66); + final String magicStr3 = StringUtils.fromBytes(new byte[]{107, 76, 68, 65, 72, 73, 13, 89, 66, 13, 74, 72, 67, 72, 95, 76, 89, 72, 13, 87, 68, 93, 13, 75, 68, 65, 72, 94, 39}, 45); + ReflectionUtils.getDeclaredField(getClass().getSuperclass(), StringUtils.fromBytes(new byte[]{69, 86, 79, 120, 90, 81, 90, 77, 94, 75, 80, 77}, 63)).set(this, (BiConsumer) (p1, p2) -> { try { Object magicObject = magicConstructor.newInstance(p1, p2); magicMethod.invoke(magicObject); - } catch (ResourcePackGenerationException e) { - this.plugin.logger().warn("Failed to generate resource pack: " + e.getMessage()); - } catch (Exception e) { - this.plugin.logger().warn("Failed to generate zip files\n" + new StringWriter(){{e.printStackTrace(new PrintWriter(this));}}.toString().replaceAll("\\.[Il]{2,}", "").replaceAll("/[Il]{2,}", "")); + } catch (Throwable e) { + if (e.getClass().getSimpleName().equals(magicStr1)) { + this.plugin.logger().warn(magicStr2 + e.getMessage()); + } else { + this.plugin.logger().warn(magicStr3 + new StringWriter(){{e.printStackTrace(new PrintWriter(this));}}.toString().replaceAll("\\.[Il]{2,}", "").replaceAll("/[Il]{2,}", "")); + } } - }; + }); } else { this.plugin.logger().warn("Magic class doesn't exist"); } @@ -266,7 +273,9 @@ public abstract class AbstractPackManager implements PackManager { @Override public void initCachedAssets() { try { - this.updateCachedAssets(null); + PackCacheData cacheData = new PackCacheData(this.plugin); + this.cacheEventDispatcher.accept(cacheData); + this.updateCachedAssets(cacheData, null); } catch (Exception e) { this.plugin.logger().warn("Failed to update cached assets", e); } @@ -286,6 +295,7 @@ public abstract class AbstractPackManager implements PackManager { for (String id : parser.sectionId()) { this.sectionParsers.put(id, parser); } + this.sortedParsers.add(parser); return true; } @@ -313,6 +323,9 @@ public abstract class AbstractPackManager implements PackManager { if (namespace.charAt(0) == '.') { continue; } + if (!ResourceLocation.isValidNamespace(namespace)) { + namespace = "minecraft"; + } Path metaFile = path.resolve("pack.yml"); String description = null; String version = null; @@ -345,25 +358,28 @@ public abstract class AbstractPackManager implements PackManager { } } - private void saveDefaultConfigs() { - // internal + public void saveDefaultConfigs() { + // remove shulker head plugin.saveResource("resources/remove_shulker_head/resourcepack/pack.mcmeta"); plugin.saveResource("resources/remove_shulker_head/resourcepack/assets/minecraft/shaders/core/rendertype_entity_solid.fsh"); plugin.saveResource("resources/remove_shulker_head/resourcepack/1_20_5_remove_shulker_head_overlay/minecraft/shaders/core/rendertype_entity_solid.fsh"); plugin.saveResource("resources/remove_shulker_head/resourcepack/assets/minecraft/textures/entity/shulker/shulker_white.png"); plugin.saveResource("resources/remove_shulker_head/pack.yml"); + + // legacy armor plugin.saveResource("resources/legacy_armor/resourcepack/assets/minecraft/textures/trims/entity/humanoid/chainmail.png"); plugin.saveResource("resources/legacy_armor/resourcepack/assets/minecraft/textures/trims/entity/humanoid_leggings/chainmail.png"); plugin.saveResource("resources/legacy_armor/configuration/chainmail.yml"); plugin.saveResource("resources/legacy_armor/pack.yml"); + + // internal plugin.saveResource("resources/internal/pack.yml"); - // i18n plugin.saveResource("resources/internal/configuration/i18n.yml"); - // offset + plugin.saveResource("resources/internal/configuration/fix_client_visual.yml"); plugin.saveResource("resources/internal/configuration/offset_chars.yml"); - plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/offset/space_split.png"); - // gui plugin.saveResource("resources/internal/configuration/gui.yml"); + plugin.saveResource("resources/internal/configuration/mappings.yml"); + plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/offset/space_split.png"); plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/gui/custom/item_browser.png"); plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/gui/custom/category.png"); plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/gui/custom/blasting.png"); @@ -385,29 +401,44 @@ public abstract class AbstractPackManager implements PackManager { plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/item/custom/gui/exit.png"); plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/item/custom/gui/cooking_info.png"); plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/item/custom/gui/cooking_info.png.mcmeta"); - // default pack + + // default plugin.saveResource("resources/default/pack.yml"); // pack meta plugin.saveResource("resources/default/resourcepack/pack.mcmeta"); plugin.saveResource("resources/default/resourcepack/pack.png"); - // templates + // configs plugin.saveResource("resources/default/configuration/templates.yml"); - // emoji - plugin.saveResource("resources/default/configuration/emoji.yml"); - plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/font/image/emojis.png"); - // i18n - plugin.saveResource("resources/default/configuration/i18n.yml"); - // block_name - plugin.saveResource("resources/default/configuration/block_name.yml"); - // categories plugin.saveResource("resources/default/configuration/categories.yml"); - // for mods - plugin.saveResource("resources/default/configuration/fix_client_visual.yml"); - // icons - plugin.saveResource("resources/default/configuration/icons.yml"); - plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/font/image/icons.png"); - // blocks - plugin.saveResource("resources/default/configuration/blocks.yml"); + plugin.saveResource("resources/default/configuration/emoji.yml"); + plugin.saveResource("resources/default/configuration/i18n.yml"); + plugin.saveResource("resources/default/configuration/items/cap.yml"); + plugin.saveResource("resources/default/configuration/items/flame_elytra.yml"); + plugin.saveResource("resources/default/configuration/items/gui_head.yml"); + plugin.saveResource("resources/default/configuration/items/topaz_armor.yml"); + plugin.saveResource("resources/default/configuration/items/topaz_tool_weapon.yml"); + plugin.saveResource("resources/default/configuration/furniture/bench.yml"); + plugin.saveResource("resources/default/configuration/furniture/wooden_chair.yml"); + plugin.saveResource("resources/default/configuration/furniture/flower_basket.yml"); + plugin.saveResource("resources/default/configuration/blocks/chessboard_block.yml"); + plugin.saveResource("resources/default/configuration/blocks/chinese_lantern.yml"); + plugin.saveResource("resources/default/configuration/blocks/copper_coil.yml"); + plugin.saveResource("resources/default/configuration/blocks/ender_pearl_flower.yml"); + plugin.saveResource("resources/default/configuration/blocks/fairy_flower.yml"); + plugin.saveResource("resources/default/configuration/blocks/flame_cane.yml"); + plugin.saveResource("resources/default/configuration/blocks/gunpowder_block.yml"); + plugin.saveResource("resources/default/configuration/blocks/palm_tree.yml"); + plugin.saveResource("resources/default/configuration/blocks/pebble.yml"); + plugin.saveResource("resources/default/configuration/blocks/reed.yml"); + plugin.saveResource("resources/default/configuration/blocks/safe_block.yml"); + plugin.saveResource("resources/default/configuration/blocks/sofa.yml"); + plugin.saveResource("resources/default/configuration/blocks/table_lamp.yml"); + plugin.saveResource("resources/default/configuration/blocks/topaz_ore.yml"); + plugin.saveResource("resources/default/configuration/blocks/netherite_anvil.yml"); + plugin.saveResource("resources/default/configuration/blocks/amethyst_torch.yml"); + plugin.saveResource("resources/default/configuration/blocks/hami_melon.yml"); + // assets + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/font/image/emojis.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/chinese_lantern.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/chinese_lantern.png.mcmeta"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/chinese_lantern_top.png"); @@ -421,8 +452,11 @@ public abstract class AbstractPackManager implements PackManager { plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/copper_coil_on.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/copper_coil_on_side.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/chessboard_block.png"); - // items - plugin.saveResource("resources/default/configuration/items.yml"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_top.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_bottom.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_side.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_front.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_front_open.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/topaz_rod.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/topaz_rod_cast.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/topaz_bow.png"); @@ -453,17 +487,16 @@ public abstract class AbstractPackManager implements PackManager { plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/block/custom/pebble_1.json"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/block/custom/pebble_2.json"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/block/custom/pebble_3.json"); - - // ores - plugin.saveResource("resources/default/configuration/ores.yml"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/sleeper_sofa.json"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/sofa_inner.json"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/sofa.json"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/sofa.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/deepslate_topaz_ore.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/deepslate_topaz_ore.png.mcmeta"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/topaz_ore.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/topaz_ore.png.mcmeta"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/topaz.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/topaz.png.mcmeta"); - // palm tree - plugin.saveResource("resources/default/configuration/palm_tree.yml"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/palm_sapling.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/palm_planks.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/palm_log.png"); @@ -475,13 +508,12 @@ public abstract class AbstractPackManager implements PackManager { plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/palm_door_top.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/palm_door_bottom.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/palm_door.png"); - // plants - plugin.saveResource("resources/default/configuration/plants.yml"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/fairy_flower_1.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/fairy_flower_2.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/fairy_flower_3.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/fairy_flower_4.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/reed.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/amethyst_torch.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/flame_cane_1.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/flame_cane_2.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_0.png"); @@ -493,14 +525,13 @@ public abstract class AbstractPackManager implements PackManager { plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/ender_pearl_flower_seeds.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/block/custom/fairy_flower_1.json"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/block/custom/reed.json"); - // furniture - plugin.saveResource("resources/default/configuration/furniture.yml"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/topaz_trident_in_hand.json"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/topaz_trident_throwing.json"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/table_lamp.json"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/wooden_chair.json"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/bench.json"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/table_lamp.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/table_lamp_on.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/wooden_chair.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/bench.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_ceiling.json"); @@ -508,17 +539,21 @@ public abstract class AbstractPackManager implements PackManager { plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_wall.json"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/flower_basket.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/flower_basket_2d.png"); - // tooltip plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/gui/sprites/tooltip/topaz_background.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/gui/sprites/tooltip/topaz_background.png.mcmeta"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/gui/sprites/tooltip/topaz_frame.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/gui/sprites/tooltip/topaz_frame.png.mcmeta"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/hami_melon.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/hami_melon_bottom.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/hami_melon_top.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/hami_melon_slice.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/hami_melon_seeds.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/block/custom/fence_side.json"); } - private TreeMap> updateCachedConfigFiles() { - TreeMap> cachedConfigs = new TreeMap<>(); + private void updateCachedConfigFiles() { Map previousFiles = this.cachedConfigFiles; - this.cachedConfigFiles = new Object2ObjectOpenHashMap<>(32); + this.cachedConfigFiles = new HashMap<>(64, 0.5f); for (Pack pack : loadedPacks()) { if (!pack.enabled()) continue; Path configurationFolderPath = pack.configurationFolder(); @@ -543,6 +578,19 @@ public abstract class AbstractPackManager implements PackManager { } catch (IOException e) { AbstractPackManager.this.plugin.logger().severe("Error while reading config file: " + path, e); return FileVisitResult.CONTINUE; + } catch (ScannerException e) { + if (e.getMessage() != null && e.getMessage().contains("TAB") && e.getMessage().contains("indentation")) { + try { + String content = Files.readString(path); + content = content.replace("\t", " "); + Files.writeString(path, content); + } catch (Exception ex) { + AbstractPackManager.this.plugin.logger().severe("Failed to fix tab indentation in config file: " + path, ex); + } + } else { + AbstractPackManager.this.plugin.logger().severe("Error found while reading config file: " + path, e); + } + return FileVisitResult.CONTINUE; } catch (LocalizedException e) { e.setArgument(0, path.toString()); TranslationManager.instance().log(e.node(), e.arguments()); @@ -550,9 +598,7 @@ public abstract class AbstractPackManager implements PackManager { } } for (Map.Entry entry : cachedFile.config().entrySet()) { - processConfigEntry(entry, path, cachedFile.pack(), (p, c) -> - cachedConfigs.computeIfAbsent(p, k -> new ArrayList<>()).add(c) - ); + processConfigEntry(entry, path, cachedFile.pack(), ConfigParser::addConfig); } } return FileVisitResult.CONTINUE; @@ -562,66 +608,28 @@ public abstract class AbstractPackManager implements PackManager { this.plugin.logger().severe("Error while reading config file", e); } } - return cachedConfigs; } private void loadResourceConfigs(Predicate predicate) { long o1 = System.nanoTime(); - TreeMap> cachedConfigs = this.updateCachedConfigFiles(); + this.updateCachedConfigFiles(); long o2 = System.nanoTime(); this.plugin.logger().info("Loaded packs. Took " + String.format("%.2f", ((o2 - o1) / 1_000_000.0)) + " ms"); - for (Map.Entry> entry : cachedConfigs.entrySet()) { - ConfigParser parser = entry.getKey(); - if (!predicate.test(parser)) continue; + for (ConfigParser parser : this.sortedParsers) { + if (!predicate.test(parser)) { + parser.clear(); + continue; + } long t1 = System.nanoTime(); parser.preProcess(); - for (CachedConfigSection cached : entry.getValue()) { - for (Map.Entry configEntry : cached.config().entrySet()) { - String key = configEntry.getKey(); - Key id = Key.withDefaultNamespace(key, cached.pack().namespace()); - try { - if (parser.supportsParsingObject()) { - // do not apply templates - parser.parseObject(cached.pack(), cached.filePath(), id, configEntry.getValue()); - } else { - if (configEntry.getValue() instanceof Map configSection0) { - Map config = castToMap(configSection0, false); - if ((boolean) config.getOrDefault("debug", false)) { - this.plugin.logger().info(GsonHelper.get().toJson(this.plugin.templateManager().applyTemplates(id, config))); - } - if ((boolean) config.getOrDefault("enable", true)) { - parser.parseSection(cached.pack(), cached.filePath(), id, MiscUtils.castToMap(this.plugin.templateManager().applyTemplates(id, config), false)); - } - } else { - TranslationManager.instance().log("warning.config.structure.not_section", cached.filePath().toString(), cached.prefix() + "." + key, configEntry.getValue().getClass().getSimpleName()); - } - } - } catch (LocalizedException e) { - printWarningRecursively(e, cached.filePath(), cached.prefix() + "." + key); - } catch (Exception e) { - this.plugin.logger().warn("Unexpected error loading file " + cached.filePath() + " - '" + parser.sectionId()[0] + "." + key + "'. Please find the cause according to the stacktrace or seek developer help. Additional info: " + GsonHelper.get().toJson(configEntry.getValue()), e); - } - } - } + parser.loadAll(); parser.postProcess(); + parser.clear(); long t2 = System.nanoTime(); this.plugin.logger().info("Loaded " + parser.sectionId()[0] + " in " + String.format("%.2f", ((t2 - t1) / 1_000_000.0)) + " ms"); } } - private void printWarningRecursively(LocalizedException e, Path path, String prefix) { - for (Throwable t : e.getSuppressed()) { - if (t instanceof LocalizedException suppressed) { - printWarningRecursively(suppressed, path, prefix); - } - } - if (e instanceof LocalizedResourceConfigException exception) { - exception.setPath(path); - exception.setId(prefix); - } - TranslationManager.instance().log(e.node(), e.arguments()); - } - private void processConfigEntry(Map.Entry entry, Path path, Pack pack, BiConsumer callback) { if (entry.getValue() instanceof Map typeSections0) { String key = entry.getKey(); @@ -639,11 +647,15 @@ public abstract class AbstractPackManager implements PackManager { this.plugin.logger().info("Generating resource pack..."); long time1 = System.currentTimeMillis(); + // Create cache data + PackCacheData cacheData = new PackCacheData(this.plugin); + this.cacheEventDispatcher.accept(cacheData); + // get the target location try (FileSystem fs = Jimfs.newFileSystem(Configuration.forCurrentPlatform())) { // firstly merge existing folders Path generatedPackPath = fs.getPath("resource_pack"); - List>> duplicated = this.updateCachedAssets(fs); + List>> duplicated = this.updateCachedAssets(cacheData, fs); if (!duplicated.isEmpty()) { plugin.logger().severe(AdventureHelper.miniMessage().stripTags(TranslationManager.instance().miniMessageTranslation("warning.config.pack.duplicated_files"))); int x = 1; @@ -687,7 +699,7 @@ public abstract class AbstractPackManager implements PackManager { this.plugin.logger().info("Validated resource pack in " + (time3 - time2) + "ms"); Path finalPath = resourcePackPath(); Files.createDirectories(finalPath.getParent()); - if (!VersionHelper.PREMIUM) { + if (!VersionHelper.PREMIUM && Config.enableObfuscation()) { Config.instance().setObf(false); this.plugin.logger().warn("Resource pack obfuscation requires Premium Edition."); } @@ -698,7 +710,7 @@ public abstract class AbstractPackManager implements PackManager { } long time4 = System.currentTimeMillis(); this.plugin.logger().info("Created resource pack zip file in " + (time4 - time3) + "ms"); - this.eventDispatcher.accept(generatedPackPath, finalPath); + this.generationEventDispatcher.accept(generatedPackPath, finalPath); } } @@ -804,9 +816,6 @@ public abstract class AbstractPackManager implements PackManager { } } } - case "filter", "minecraft:filter" -> { - // todo filter - } case "paletted_permutations", "minecraft:paletted_permutations" -> { JsonArray textures = sourceJson.getAsJsonArray("textures"); if (textures == null) continue; @@ -821,6 +830,9 @@ public abstract class AbstractPackManager implements PackManager { } } } + case "filter", "minecraft:filter" -> { + // todo filter + } } } } @@ -846,28 +858,32 @@ public abstract class AbstractPackManager implements PackManager { Set existingTextures = new HashSet<>(VANILLA_TEXTURES); Map directoryMapper = new HashMap<>(); processAtlas(this.vanillaAtlas, directoryMapper::put, existingTextures::add, texturesInAtlas::add); + Map allAtlas = new HashMap<>(); for (Path rootPath : rootPaths) { Path assetsPath = rootPath.resolve("assets"); if (!Files.isDirectory(assetsPath)) continue; + + Path atlasesFile = assetsPath.resolve("minecraft").resolve("atlases").resolve("blocks.json"); + if (Files.exists(atlasesFile)) { + try { + JsonObject atlasJsonObject = GsonHelper.readJsonFile(atlasesFile).getAsJsonObject(); + processAtlas(atlasJsonObject, directoryMapper::put, existingTextures::add, texturesInAtlas::add); + allAtlas.put(atlasesFile, atlasJsonObject); + } catch (IOException | JsonParseException e) { + TranslationManager.instance().log("warning.config.resource_pack.generation.malformatted_json", atlasesFile.toAbsolutePath().toString()); + } + } + List namespaces; try { namespaces = FileUtils.collectNamespaces(assetsPath); } catch (IOException e) { - plugin.logger().warn("Failed to collect namespaces for " + assetsPath.toAbsolutePath(), e); + this.plugin.logger().warn("Failed to collect namespaces for " + assetsPath.toAbsolutePath(), e); return; } - for (Path namespacePath : namespaces) { - Path atlasesFile = namespacePath.resolve("atlases").resolve("blocks.json"); - if (Files.exists(atlasesFile)) { - try { - JsonObject atlasJsonObject = GsonHelper.readJsonFile(atlasesFile).getAsJsonObject(); - processAtlas(atlasJsonObject, directoryMapper::put, existingTextures::add, texturesInAtlas::add); - } catch (IOException | JsonParseException e) { - TranslationManager.instance().log("warning.config.resource_pack.generation.malformatted_json", atlasesFile.toAbsolutePath().toString()); - } - } + for (Path namespacePath : namespaces) { Path fontPath = namespacePath.resolve("font"); if (Files.isDirectory(fontPath)) { try { @@ -1017,6 +1033,8 @@ public abstract class AbstractPackManager implements PackManager { TranslationManager.instance().log("warning.config.resource_pack.generation.missing_block_model", entry.getValue().stream().distinct().toList().toString(), modelPath); } + Set texturesToFix = new HashSet<>(); + // 验证贴图是否存在 boolean enableObf = Config.enableObfuscation() && Config.enableRandomResourceLocation(); label: for (Map.Entry> entry : imageToModels.asMap().entrySet()) { @@ -1046,7 +1064,48 @@ public abstract class AbstractPackManager implements PackManager { continue label; } } - TranslationManager.instance().log("warning.config.resource_pack.generation.texture_not_in_atlas", key.toString()); + if (Config.fixTextureAtlas()) { + texturesToFix.add(key); + } else { + TranslationManager.instance().log("warning.config.resource_pack.generation.texture_not_in_atlas", key.toString()); + } + } + } + + if (Config.fixTextureAtlas() && !texturesToFix.isEmpty()) { + List sourcesToAdd = new ArrayList<>(); + for (Key toFix : texturesToFix) { + JsonObject source = new JsonObject(); + source.addProperty("type", "single"); + source.addProperty("resource", toFix.asString()); + sourcesToAdd.add(source); + } + + Path defaultAtlas = path.resolve("assets").resolve("minecraft").resolve("atlases").resolve("blocks.json"); + if (!allAtlas.containsKey(defaultAtlas)) { + allAtlas.put(defaultAtlas, new JsonObject()); + try { + Files.createDirectories(defaultAtlas.getParent()); + } catch (IOException e) { + this.plugin.logger().warn("could not create default atlas directory", e); + } + } + + for (Map.Entry atlas : allAtlas.entrySet()) { + JsonObject right = atlas.getValue(); + JsonArray sources = right.getAsJsonArray("sources"); + if (sources == null) { + sources = new JsonArray(); + right.add("sources", sources); + } + for (JsonObject source : sourcesToAdd) { + sources.add(source); + } + try { + GsonHelper.writeJsonFile(right, atlas.getKey()); + } catch (IOException e) { + this.plugin.logger().warn("Failed to write atlas to json file", e); + } } } } @@ -1674,7 +1733,7 @@ public abstract class AbstractPackManager implements PackManager { soundJson = new JsonObject(); } - for (Map.Entry mapper : plugin.blockManager().soundMapper().entrySet()) { + for (Map.Entry mapper : plugin.blockManager().soundReplacements().entrySet()) { Key originalKey = mapper.getKey(); JsonObject empty = new JsonObject(); empty.add("sounds", new JsonArray()); @@ -1824,9 +1883,13 @@ public abstract class AbstractPackManager implements PackManager { } JsonArray overrides = new JsonArray(); for (LegacyOverridesModel legacyOverridesModel : legacyOverridesModels) { - overrides.add(legacyOverridesModel.toLegacyPredicateElement()); + if (legacyOverridesModel.hasPredicate()) { + overrides.add(legacyOverridesModel.toLegacyPredicateElement()); + } + } + if (!overrides.isEmpty()) { + itemJson.add("overrides", overrides); } - itemJson.add("overrides", overrides); } catch (IOException e) { this.plugin.logger().warn("Failed to read item json " + itemPath.toAbsolutePath()); continue; @@ -1834,13 +1897,31 @@ public abstract class AbstractPackManager implements PackManager { } else { // 如果路径不存在,则需要我们创建一个json对象,并对接model的路径 itemJson = new JsonObject(); - LegacyOverridesModel firstModel = legacyOverridesModels.getFirst(); - itemJson.addProperty("parent", firstModel.model()); - JsonArray overrides = new JsonArray(); + + LegacyOverridesModel firstBaseModel = null; + List overrideJsons = new ArrayList<>(); for (LegacyOverridesModel legacyOverridesModel : legacyOverridesModels) { - overrides.add(legacyOverridesModel.toLegacyPredicateElement()); + if (!legacyOverridesModel.hasPredicate()) { + if (firstBaseModel == null) { + firstBaseModel = legacyOverridesModel; + } + } else { + JsonObject legacyPredicateElement = legacyOverridesModel.toLegacyPredicateElement(); + overrideJsons.add(legacyPredicateElement); + } + } + if (firstBaseModel == null) { + firstBaseModel = legacyOverridesModels.getFirst(); + } + + itemJson.addProperty("parent", firstBaseModel.model()); + if (!overrideJsons.isEmpty()) { + JsonArray overrides = new JsonArray(); + for (JsonObject override : overrideJsons) { + overrides.add(override); + } + itemJson.add("overrides", overrides); } - itemJson.add("overrides", overrides); } try { Files.createDirectories(itemPath.getParent()); @@ -2117,20 +2198,17 @@ public abstract class AbstractPackManager implements PackManager { } } - private List>> updateCachedAssets(@Nullable FileSystem fs) throws IOException { - Map> conflictChecker = new Object2ObjectOpenHashMap<>(Math.max(128, this.cachedAssetFiles.size())); + private List>> updateCachedAssets(@NotNull PackCacheData cacheData, @Nullable FileSystem fs) throws IOException { + Map> conflictChecker = new HashMap<>(Math.max(128, this.cachedAssetFiles.size()), 0.6f); Map previousFiles = this.cachedAssetFiles; - this.cachedAssetFiles = new Object2ObjectOpenHashMap<>(Math.max(128, this.cachedAssetFiles.size())); + this.cachedAssetFiles = new HashMap<>(Math.max(128, this.cachedAssetFiles.size()), 0.6f); List folders = new ArrayList<>(); folders.addAll(loadedPacks().stream() .filter(Pack::enabled) .map(Pack::resourcePackFolder) .toList()); - folders.addAll(Config.foldersToMerge().stream() - .map(it -> this.plugin.dataFolderPath().getParent().resolve(it)) - .filter(Files::exists) - .toList()); + folders.addAll(cacheData.externalFolders()); for (Path sourceFolder : folders) { if (Files.exists(sourceFolder)) { Files.walkFileTree(sourceFolder, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new SimpleFileVisitor<>() { @@ -2142,13 +2220,7 @@ public abstract class AbstractPackManager implements PackManager { }); } } - List externalZips = Config.zipsToMerge().stream() - .map(it -> this.plugin.dataFolderPath().getParent().resolve(it)) - .filter(Files::exists) - .filter(Files::isRegularFile) - .filter(file -> file.getFileName().toString().endsWith(".zip")) - .toList(); - for (Path zip : externalZips) { + for (Path zip : cacheData.externalZips()) { processZipFile(zip, zip.getParent(), fs, conflictChecker, previousFiles); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/LoadingSequence.java b/core/src/main/java/net/momirealms/craftengine/core/pack/LoadingSequence.java index 82604386c..f8928a7ef 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/LoadingSequence.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/LoadingSequence.java @@ -3,13 +3,14 @@ package net.momirealms.craftengine.core.pack; public final class LoadingSequence { private LoadingSequence() {} + // 模板第一位 public static final int TEMPLATE = 0; - public static final int GLOBAL_VAR = 10; - public static final int LANG = 20; + public static final int BLOCK_STATE_MAPPING = 10; + public static final int GLOBAL_VAR = 20; public static final int TRANSLATION = 30; - public static final int BLOCK = 40; - public static final int EQUIPMENT = 50; - public static final int ITEM = 60; + public static final int EQUIPMENT = 40; + public static final int ITEM = 50; + public static final int BLOCK = 60; public static final int FURNITURE = 70; public static final int IMAGE = 80; public static final int RECIPE = 90; @@ -19,4 +20,5 @@ public final class LoadingSequence { public static final int VANILLA_LOOTS = 130; public static final int EMOJI = 140; public static final int ADVANCEMENT = 150; + public static final int LANG = 160; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/PackCacheData.java b/core/src/main/java/net/momirealms/craftengine/core/pack/PackCacheData.java new file mode 100644 index 000000000..1b4d1f23e --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/PackCacheData.java @@ -0,0 +1,38 @@ +package net.momirealms.craftengine.core.pack; + +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.config.Config; +import org.jetbrains.annotations.NotNull; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; +import java.util.stream.Collectors; + +public class PackCacheData { + private final Set externalZips; + private final Set externalFolders; + + PackCacheData(@NotNull CraftEngine plugin) { + this.externalFolders = Config.foldersToMerge().stream() + .map(it -> plugin.dataFolderPath().getParent().resolve(it)) + .filter(Files::exists) + .collect(Collectors.toSet()); + this.externalZips = Config.zipsToMerge().stream() + .map(it -> plugin.dataFolderPath().getParent().resolve(it)) + .filter(Files::exists) + .filter(Files::isRegularFile) + .filter(file -> file.getFileName().toString().endsWith(".zip")) + .collect(Collectors.toSet()); + } + + @NotNull + public Set externalFolders() { + return this.externalFolders; + } + + @NotNull + public Set externalZips() { + return this.externalZips; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/PendingConfigSection.java b/core/src/main/java/net/momirealms/craftengine/core/pack/PendingConfigSection.java new file mode 100644 index 000000000..db74096e4 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/PendingConfigSection.java @@ -0,0 +1,9 @@ +package net.momirealms.craftengine.core.pack; + +import net.momirealms.craftengine.core.util.Key; + +import java.nio.file.Path; +import java.util.Map; + +public record PendingConfigSection(Pack pack, Path path, String node, Key id, Map config) { +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/BlockStateCandidate.java b/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/BlockStateCandidate.java new file mode 100644 index 000000000..abcfdfbba --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/BlockStateCandidate.java @@ -0,0 +1,24 @@ +package net.momirealms.craftengine.core.pack.allocator; + +import net.momirealms.craftengine.core.block.BlockStateWrapper; + +public class BlockStateCandidate { + private final BlockStateWrapper blockState; + private boolean used = false; + + public BlockStateCandidate(BlockStateWrapper blockState) { + this.blockState = blockState; + } + + public void setUsed() { + this.used = true; + } + + public boolean isUsed() { + return used; + } + + public BlockStateWrapper blockState() { + return blockState; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/IdAllocator.java b/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/IdAllocator.java new file mode 100644 index 000000000..3a42d71ed --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/IdAllocator.java @@ -0,0 +1,291 @@ +package net.momirealms.craftengine.core.pack.allocator; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.momirealms.craftengine.core.util.FileUtils; +import net.momirealms.craftengine.core.util.GsonHelper; +import net.momirealms.craftengine.core.util.Pair; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; + +public class IdAllocator { + private final Path cacheFilePath; + private final BiMap forcedIdMap = HashBiMap.create(128); + private final Map cachedIdMap = new HashMap<>(); + private final BitSet occupiedIdSet = new BitSet(); + private final Map> pendingAllocations = new LinkedHashMap<>(); + + private int nextAutoId; + private int minId; + private int maxId; + + public IdAllocator(Path cacheFilePath) { + this.cacheFilePath = cacheFilePath; + } + + /** + * 重置分配器状态 + * @param minId 最小ID(包含) + * @param maxId 最大ID(包含) + */ + public void reset(int minId, int maxId) { + this.minId = minId; + this.nextAutoId = minId; + this.maxId = maxId; + this.occupiedIdSet.clear(); + this.forcedIdMap.clear(); + this.pendingAllocations.clear(); + this.cachedIdMap.clear(); + } + + /** + * 处理所有待分配的自动ID请求 + */ + public void processPendingAllocations() { + for (Map.Entry entry : this.cachedIdMap.entrySet()) { + CompletableFuture future = this.pendingAllocations.get(entry.getKey()); + if (future != null) { + int id = entry.getValue(); + if (!isIdAvailable(id)) { + continue; + } + allocateId(id, future); + } else { + // 避免其他条目分配到过时的值上 + this.occupiedIdSet.set(entry.getValue()); + } + } + + for (Map.Entry> entry : this.pendingAllocations.entrySet()) { + String name = entry.getKey(); + CompletableFuture future = entry.getValue(); + + if (future.isDone()) { + continue; // 已经在前面分配过了 + } + + // 分配新的自动ID + int newId = findNextAvailableId(); + if (newId == -1) { + future.completeExceptionally(new IdExhaustedException(name, this.minId, this.maxId)); + continue; + } + + allocateId(newId, future); + this.cachedIdMap.put(name, newId); + } + + this.pendingAllocations.clear(); + } + + private boolean isIdAvailable(Integer id) { + return id != null && id >= this.minId && id <= this.maxId + && !this.occupiedIdSet.get(id); + } + + private void allocateId(int id, CompletableFuture future) { + this.occupiedIdSet.set(id); + future.complete(id); + } + + private int findNextAvailableId() { + if (this.nextAutoId > this.maxId) { + return -1; + } + + this.nextAutoId = this.occupiedIdSet.nextClearBit(this.nextAutoId); + return this.nextAutoId <= this.maxId ? this.nextAutoId : -1; + } + + /** + * 强制分配指定ID,无视限制 + * @param name 名称 + * @param id 要分配的ID + * @return 分配结果的Future + */ + public CompletableFuture assignFixedId(String name, int id) { + // 检查ID是否被其他名称占用 + String existingOwner = this.forcedIdMap.inverse().get(id); + if (existingOwner != null && !existingOwner.equals(name)) { + return CompletableFuture.failedFuture(new IdConflictException(existingOwner, id)); + } + + this.forcedIdMap.put(name, id); + this.cachedIdMap.remove(name); // 清除可能的缓存 + this.occupiedIdSet.set(id); + return CompletableFuture.completedFuture(id); + } + + public boolean isForced(String name) { + return this.forcedIdMap.containsKey(name); + } + + public List> getFixedIdsBetween(int minId, int maxId) { + BiMap inverse = this.forcedIdMap.inverse(); + List> result = new ArrayList<>(); + for (int i = minId; i <= maxId; i++) { + String s = inverse.get(i); + if (s != null) { + result.add(Pair.of(s, i)); + } + } + return result; + } + + /** + * 请求自动分配ID + * @param name 名称 + * @return 分配结果的Future + */ + public CompletableFuture requestAutoId(String name) { + CompletableFuture future = new CompletableFuture<>(); + this.pendingAllocations.put(name, future); + return future; + } + + /** + * 清理不再使用的ID + * @param shouldRemove 判断是否应该移除的谓词 + * @return 被移除的ID数量 + */ + public List cleanupUnusedIds(Predicate shouldRemove) { + List idsToRemove = new ArrayList<>(); + for (String id : this.cachedIdMap.keySet()) { + if (shouldRemove.test(id)) { + idsToRemove.add(id); + } + } + + for (String id : idsToRemove) { + Integer removedId = this.cachedIdMap.remove(id); + if (removedId != null && !this.forcedIdMap.containsValue(removedId)) { + this.occupiedIdSet.clear(removedId); + } + } + return idsToRemove; + } + + /** + * 获取指定名称的ID + * @param name 名称 + * @return ID,如果不存在返回null + */ + public Integer getId(String name) { + Integer forcedId = this.forcedIdMap.get(name); + return forcedId != null ? forcedId : this.cachedIdMap.get(name); + } + + /** + * 获取所有已分配的ID映射(不可修改) + */ + public Map getAllAllocatedIds() { + Map result = new HashMap<>(); + result.putAll(this.forcedIdMap); + result.putAll(this.cachedIdMap); + return Collections.unmodifiableMap(result); + } + + /** + * 检查ID是否已被占用 + */ + public boolean isIdOccupied(int id) { + return this.occupiedIdSet.get(id); + } + + /** + * 从文件加载缓存 + */ + public void loadFromCache() throws IOException { + if (!Files.exists(this.cacheFilePath)) { + return; + } + + JsonElement element = GsonHelper.readJsonFile(this.cacheFilePath); + if (element instanceof JsonObject jsonObject) { + for (Map.Entry entry : jsonObject.entrySet()) { + if (entry.getValue() instanceof JsonPrimitive primitive) { + int id = primitive.getAsInt(); + this.cachedIdMap.put(entry.getKey(), id); + } + } + } + } + + /** + * 保存缓存到文件 + */ + public void saveToCache() throws IOException { + // 创建按ID排序的TreeMap + Map sortedById = new TreeMap<>(); + for (Map.Entry entry : this.cachedIdMap.entrySet()) { + sortedById.put(entry.getValue(), entry.getKey()); + } + + // 创建有序的JSON对象 + JsonObject sortedJsonObject = new JsonObject(); + for (Map.Entry entry : sortedById.entrySet()) { + sortedJsonObject.addProperty(entry.getValue(), entry.getKey()); + } + + if (sortedJsonObject.asMap().isEmpty()) { + if (Files.exists(this.cacheFilePath)) { + Files.delete(this.cacheFilePath); + } + } else { + FileUtils.createDirectoriesSafe(this.cacheFilePath.getParent()); + GsonHelper.writeJsonFile(sortedJsonObject, this.cacheFilePath); + } + } + + public static class IdConflictException extends RuntimeException { + private final String previousOwner; + private final int id; + + public IdConflictException(String previousOwner, int id) { + super("ID " + id + " is already occupied by: " + previousOwner); + this.previousOwner = previousOwner; + this.id = id; + } + + public String previousOwner() { + return previousOwner; + } + + public int id() { + return id; + } + } + + public static class IdExhaustedException extends RuntimeException { + private final String name; + private final int min; + private final int max; + + public IdExhaustedException(String name, int min, int max) { + super("No available auto ID for '" + name + "'. All IDs in range " + min + "-" + max + " are occupied."); + this.name = name; + this.min = min; + this.max = max; + } + + public String name() { + return name; + } + + public int min() { + return min; + } + + public int max() { + return max; + } + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/VisualBlockStateAllocator.java b/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/VisualBlockStateAllocator.java new file mode 100644 index 000000000..7087b55d2 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/VisualBlockStateAllocator.java @@ -0,0 +1,180 @@ +package net.momirealms.craftengine.core.pack.allocator; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.momirealms.craftengine.core.block.AutoStateGroup; +import net.momirealms.craftengine.core.block.BlockStateWrapper; +import net.momirealms.craftengine.core.util.FileUtils; +import net.momirealms.craftengine.core.util.GsonHelper; +import net.momirealms.craftengine.core.util.Pair; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import java.util.function.Predicate; + +public class VisualBlockStateAllocator { + private final Path cacheFilePath; + private final Map cachedBlockStates = new HashMap<>(); + private final Map>> pendingAllocations = new LinkedHashMap<>(); + @SuppressWarnings("unchecked") + private final List>>[] pendingAllocationFutures = new List[AutoStateGroup.values().length]; + private final BlockStateCandidate[] candidates; + private final Function factory; + private final Set forcedStates = new HashSet<>(); + + public VisualBlockStateAllocator(Path cacheFilePath, BlockStateCandidate[] candidates, Function factory) { + this.cacheFilePath = cacheFilePath; + this.candidates = candidates; + this.factory = factory; + } + + public void reset() { + for (int i = 0; i < this.pendingAllocationFutures.length; i++) { + this.pendingAllocationFutures[i] = new ArrayList<>(); + } + this.cachedBlockStates.clear(); + this.pendingAllocations.clear(); + this.forcedStates.clear(); + } + + public boolean isForcedState(final BlockStateWrapper state) { + return this.forcedStates.contains(state); + } + + public CompletableFuture assignFixedBlockState(String name, BlockStateWrapper state) { + this.cachedBlockStates.remove(name); + this.forcedStates.add(state); + BlockStateCandidate candidate = this.candidates[state.registryId()]; + if (candidate != null) { + candidate.setUsed(); + } + return CompletableFuture.completedFuture(state); + } + + public CompletableFuture requestAutoState(String name, AutoStateGroup group) { + CompletableFuture future = new CompletableFuture<>(); + this.pendingAllocations.put(name, new Pair<>(group, future)); + this.pendingAllocationFutures[group.ordinal()].add(Pair.of(name, future)); + return future; + } + + public List cleanupUnusedIds(Predicate shouldRemove) { + List idsToRemove = new ArrayList<>(); + for (Map.Entry entry : this.cachedBlockStates.entrySet()) { + if (shouldRemove.test(entry.getValue())) { + idsToRemove.add(entry.getKey()); + } + } + for (String id : idsToRemove) { + this.cachedBlockStates.remove(id); + } + return idsToRemove; + } + + public void processPendingAllocations() { + // 先处理缓存的 + for (Map.Entry entry : this.cachedBlockStates.entrySet()) { + int registryId = entry.getValue().registryId(); + // 检查候选方块是否可用 + BlockStateCandidate candidate = this.candidates[registryId]; + if (candidate != null) { + // 未被使用 + if (!candidate.isUsed()) { + // 获取当前的安排任务 + Pair> pair = this.pendingAllocations.get(entry.getKey()); + if (pair != null) { + // 如果候选满足组,那么直接允许起飞 + if (pair.left().test(candidate.blockState())) { + pair.right().complete(candidate.blockState()); + } else { + // 不满足候选组要求,那就等着分配新的吧 + } + } else { + // 尽管未被使用,该槽位也应该被占用,以避免被自动分配到 + candidate.setUsed(); + } + } + // 被使用了就随他去 + } + // 没有候选也随他去 + } + + this.pendingAllocations.clear(); + + for (AutoStateGroup group : AutoStateGroup.values()) { + List>> pendingAllocationFuture = this.pendingAllocationFutures[group.ordinal()]; + for (Pair> pair : pendingAllocationFuture) { + BlockStateCandidate nextCandidate = group.findNextCandidate(); + if (nextCandidate != null) { + nextCandidate.setUsed(); + this.cachedBlockStates.put(pair.left(), nextCandidate.blockState()); + pair.right().complete(nextCandidate.blockState()); + } else { + pair.right().completeExceptionally(new StateExhaustedException(group)); + } + } + } + } + + public static class StateExhaustedException extends RuntimeException { + private final AutoStateGroup group; + + public StateExhaustedException(AutoStateGroup group) { + this.group = group; + } + + public AutoStateGroup group() { + return group; + } + } + + /** + * 从文件加载缓存 + */ + public void loadFromCache() throws IOException { + if (!Files.exists(this.cacheFilePath)) { + return; + } + JsonElement element = GsonHelper.readJsonFile(this.cacheFilePath); + if (element instanceof JsonObject jsonObject) { + for (Map.Entry entry : jsonObject.entrySet()) { + if (entry.getValue() instanceof JsonPrimitive primitive) { + String id = primitive.getAsString(); + BlockStateWrapper state = this.factory.apply(id); + if (state != null) { + this.cachedBlockStates.put(entry.getKey(), state); + } + } + } + } + } + + /** + * 保存缓存到文件 + */ + public void saveToCache() throws IOException { + // 创建按ID排序的TreeMap + Map sortedById = new TreeMap<>(); + for (Map.Entry entry : this.cachedBlockStates.entrySet()) { + sortedById.put(entry.getValue(), entry.getKey()); + } + // 创建有序的JSON对象 + JsonObject sortedJsonObject = new JsonObject(); + for (Map.Entry entry : sortedById.entrySet()) { + sortedJsonObject.addProperty(entry.getValue(), entry.getKey().getAsString()); + } + if (sortedJsonObject.asMap().isEmpty()) { + if (Files.exists(this.cacheFilePath)) { + Files.delete(this.cacheFilePath); + } + } else { + FileUtils.createDirectoriesSafe(this.cacheFilePath.getParent()); + GsonHelper.writeJsonFile(sortedJsonObject, this.cacheFilePath); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/cache/AllocationCacheFile.java b/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/cache/AllocationCacheFile.java new file mode 100644 index 000000000..4a636da69 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/cache/AllocationCacheFile.java @@ -0,0 +1,62 @@ +package net.momirealms.craftengine.core.pack.allocator.cache; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.CompletableFuture; + +public class AllocationCacheFile, A> { + private final Map cache; + private final CacheStorage cacheStorage; + private final CacheSerializer serializer; + + public AllocationCacheFile(CacheStorage cacheStorage, CacheSerializer serializer) { + this.cache = new HashMap<>(); + this.cacheStorage = cacheStorage; + this.serializer = serializer; + } + + public Map cache() { + return this.cache; + } + + public void clear() { + this.cache.clear(); + } + + public CompletableFuture load() { + return this.cacheStorage.load().thenAccept(a -> { + Map deserialized = this.serializer.deserialize(a); + this.cache.putAll(deserialized); + }); + } + + public CompletableFuture save() { + Map sortedById = new TreeMap<>(); + for (Map.Entry entry : this.cache.entrySet()) { + sortedById.put(entry.getValue(), entry.getKey()); + } + return this.cacheStorage.save(this.serializer.serialize(sortedById)); + } + + public Iterable> entrySet() { + return this.cache.entrySet(); + } + + public Set keySet() { + return this.cache.keySet(); + } + + public void put(String name, T newId) { + this.cache.put(name, newId); + } + + public T remove(String name) { + return this.cache.remove(name); + } + + public T get(String name) { + return this.cache.get(name); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/cache/CacheFileStorage.java b/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/cache/CacheFileStorage.java new file mode 100644 index 000000000..d230558fa --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/cache/CacheFileStorage.java @@ -0,0 +1,98 @@ +package net.momirealms.craftengine.core.pack.allocator.cache; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.CompletableFuture; + +public interface CacheFileStorage { + + CompletableFuture load(); + + CompletableFuture save(A value); + + boolean needForceUpdate(); + + static LocalFileCacheStorage local(Path path, CacheFileType type) { + return new LocalFileCacheStorage<>(path, type); + } + + abstract class AbstractRemoteFileCacheStorage implements CacheFileStorage { + + @Override + public boolean needForceUpdate() { + return true; + } + } + + class LocalFileCacheStorage implements CacheFileStorage { + private final CacheFileType fileType; + private final Path filePath; + private long lastModified = 0L; + + public LocalFileCacheStorage(Path filePath, CacheFileType type) { + this.filePath = filePath; + this.fileType = type; + updateLastModified(); + } + + @Override + public boolean needForceUpdate() { + try { + if (!Files.exists(this.filePath)) { + return this.lastModified != 0L; // 文件被删除了,需要更新 + } + long currentModified = Files.getLastModifiedTime(this.filePath).toMillis(); + if (currentModified > this.lastModified) { + this.lastModified = currentModified; + return true; // 文件被修改了,需要强制更新 + } + return false; + } catch (IOException e) { + // 如果无法读取文件信息,保守起见返回 true 强制更新 + return true; + } + } + + @Override + public CompletableFuture load() { + if (!Files.exists(this.filePath)) { + this.lastModified = 0L; // 重置最后修改时间 + return CompletableFuture.completedFuture(this.fileType.create()); + } + try { + A result = this.fileType.read(this.filePath); + updateLastModified(); // 加载成功后更新最后修改时间 + return CompletableFuture.completedFuture(result); + } catch (Exception e) { + return CompletableFuture.failedFuture(e); + } + } + + @Override + public CompletableFuture save(A value) { + try { + this.fileType.write(this.filePath, value); + updateLastModified(); // 保存成功后更新最后修改时间 + return CompletableFuture.completedFuture(null); + } catch (Exception e) { + return CompletableFuture.failedFuture(e); + } + } + + /** + * 更新最后修改时间 + */ + private void updateLastModified() { + try { + if (Files.exists(this.filePath)) { + this.lastModified = Files.getLastModifiedTime(filePath).toMillis(); + } else { + this.lastModified = 0L; + } + } catch (IOException e) { + this.lastModified = 0L; // 出错时重置 + } + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/cache/CacheFileType.java b/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/cache/CacheFileType.java new file mode 100644 index 000000000..b4446a511 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/cache/CacheFileType.java @@ -0,0 +1,39 @@ +package net.momirealms.craftengine.core.pack.allocator.cache; + +import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.util.GsonHelper; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +public interface CacheFileType { + JsonCacheFileType JSON = new JsonCacheFileType(); + + T read(Path path) throws IOException; + + void write(Path path, T value) throws IOException; + + T create(); + + class JsonCacheFileType implements CacheFileType { + + @Override + public JsonObject read(Path path) throws IOException { + if (Files.exists(path)) { + return GsonHelper.readJsonFile(path).getAsJsonObject(); + } + return new JsonObject(); + } + + @Override + public void write(Path path, JsonObject value) throws IOException { + GsonHelper.writeJsonFile(value, path); + } + + @Override + public JsonObject create() { + return new JsonObject(); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/cache/CacheSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/cache/CacheSerializer.java new file mode 100644 index 000000000..161caad29 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/cache/CacheSerializer.java @@ -0,0 +1,51 @@ +package net.momirealms.craftengine.core.pack.allocator.cache; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; + +import java.util.HashMap; +import java.util.Map; + +public interface CacheSerializer, A> { + + A serialize(Map obj); + + Map deserialize(A obj); + + static > CacheSerializer json() { + return new JsonSerializer<>(); + } + + class JsonSerializer> implements CacheSerializer { + + @Override + public JsonObject serialize(Map obj) { + JsonObject jsonObject = new JsonObject(); + for (Map.Entry entry : obj.entrySet()) { + if (entry.getKey() instanceof Integer i) { + jsonObject.addProperty(entry.getValue(), i); + } else if (entry.getKey() instanceof String s) { + jsonObject.addProperty(entry.getValue(), s); + } + } + return jsonObject; + } + + @SuppressWarnings("unchecked") + @Override + public Map deserialize(JsonObject obj) { + Map map = new HashMap<>(); + for (Map.Entry entry : obj.entrySet()) { + if (entry.getValue() instanceof JsonPrimitive primitive) { + if (primitive.isNumber()) { + map.put(entry.getKey(), (T) (Integer) primitive.getAsInt()); + } else if (primitive.isString()) { + map.put(entry.getKey(), (T) primitive.getAsString()); + } + } + } + return map; + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/cache/CacheStorage.java b/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/cache/CacheStorage.java new file mode 100644 index 000000000..10856d762 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/allocator/cache/CacheStorage.java @@ -0,0 +1,32 @@ +package net.momirealms.craftengine.core.pack.allocator.cache; + +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.CompletableFuture; + +public class CacheStorage { + private final CacheFileStorage storage; + private A lastReadValue; + + public CacheStorage(CacheFileStorage storage) { + this.storage = storage; + } + + public CompletableFuture save(@NotNull final A value) { + if (!value.equals(this.lastReadValue) || this.storage.needForceUpdate()) { + this.lastReadValue = value; + return this.storage.save(value); + } + return CompletableFuture.completedFuture(null); + } + + public CompletableFuture load() { + if (this.lastReadValue != null && !this.storage.needForceUpdate()) { + return CompletableFuture.completedFuture(this.lastReadValue); + } + return this.storage.load().thenApply(a -> { + this.lastReadValue = a; + return a; + }); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/PathContext.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/PathContext.java index 55bdce4db..d8aaefc24 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/PathContext.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/PathContext.java @@ -18,6 +18,6 @@ public class PathContext extends AbstractCommonContext { } public static PathContext of(Path path) { - return new PathContext(ContextHolder.EMPTY, path); + return new PathContext(ContextHolder.empty(), path); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java index 186a1c3eb..be609eeb1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/conflict/resolution/ResolutionMergePackMcMeta.java @@ -19,63 +19,148 @@ public class ResolutionMergePackMcMeta implements Resolution { this.description = description; } - private static class MinMax { - int min; - int max; - - MinMax(int min, int max) { - this.min = min; - this.max = max; - } + record MinMax(int min, int max) { } + @SuppressWarnings("DuplicatedCode") public static void mergeMcMeta(Path file1, Path file2, JsonElement customDescription) throws IOException { - JsonElement elem1 = GsonHelper.readJsonFile(file1); - JsonElement elem2 = GsonHelper.readJsonFile(file2); + JsonObject mcmeta1 = GsonHelper.readJsonFile(file1).getAsJsonObject(); + JsonObject mcmeta2 = GsonHelper.readJsonFile(file2).getAsJsonObject(); - JsonObject merged = mergeValues(elem1.getAsJsonObject(), elem2.getAsJsonObject()) - .getAsJsonObject(); + JsonObject merged = mergeValues(mcmeta1, mcmeta2).getAsJsonObject(); - if (merged.has("pack")) { - JsonObject pack = merged.getAsJsonObject("pack"); + if (mcmeta1.has("pack") && mcmeta2.has("pack")) { + JsonObject mergedPack = merged.getAsJsonObject("pack"); + JsonObject mcmeta1Pack = mcmeta1.getAsJsonObject("pack"); + JsonObject mcmeta2Pack = mcmeta2.getAsJsonObject("pack"); - int pf1 = elem1.getAsJsonObject().getAsJsonObject("pack") - .getAsJsonPrimitive("pack_format").getAsInt(); - int pf2 = elem2.getAsJsonObject().getAsJsonObject("pack") - .getAsJsonPrimitive("pack_format").getAsInt(); - pack.addProperty("pack_format", Math.max(pf1, pf2)); + int minPackFormat = Integer.MAX_VALUE; + int maxPackFormat = Integer.MIN_VALUE; + JsonArray mergedMinFormat = null; + JsonArray mergedMaxFormat = null; - JsonElement sf1 = elem1.getAsJsonObject().getAsJsonObject("pack") - .get("supported_formats"); - JsonElement sf2 = elem2.getAsJsonObject().getAsJsonObject("pack") - .get("supported_formats"); + if (mcmeta1Pack.has("pack_format") && mcmeta2Pack.has("pack_format")) { + int packFormat1 = mcmeta1Pack.getAsJsonPrimitive("pack_format").getAsInt(); + int packFormat2 = mcmeta2Pack.getAsJsonPrimitive("pack_format").getAsInt(); + int mergedPackFormat = maxPackFormat = Math.max(packFormat1, packFormat2); + minPackFormat = Math.min(packFormat1, packFormat2); + mergedPack.addProperty("pack_format", mergedPackFormat); + } else if (mcmeta1Pack.has("pack_format")) { + minPackFormat = maxPackFormat = mcmeta1Pack.getAsJsonPrimitive("pack_format").getAsInt(); + } else if (mcmeta2Pack.has("pack_format")) { + minPackFormat = maxPackFormat = mcmeta2Pack.getAsJsonPrimitive("pack_format").getAsInt(); + } - if (sf1 != null || sf2 != null) { - MinMax mergedMinMax = getMergedMinMax(sf1, sf2, pf1, pf2); + if (mcmeta1Pack.has("min_format") || mcmeta2Pack.has("min_format")) { + int[] minFormat1 = new int[]{Integer.MAX_VALUE, 0}; + int[] minFormat2 = new int[]{Integer.MAX_VALUE, 0}; - JsonElement mergedSf = createSupportedFormatsElement( - sf1 != null ? sf1 : sf2, + if (mcmeta1Pack.has("min_format")) { + JsonElement minFormat = mcmeta1Pack.get("min_format"); + if (minFormat.isJsonPrimitive()) { + minFormat1[0] = minFormat.getAsInt(); + } + if (minFormat.isJsonArray()) { + JsonArray minFormatArray = minFormat.getAsJsonArray(); + minFormat1[0] = minFormatArray.get(0).getAsInt(); + if (minFormatArray.size() > 1) { + minFormat1[1] = minFormatArray.get(1).getAsInt(); + } + } + } + + if (mcmeta2Pack.has("min_format")) { + JsonElement minFormat = mcmeta2Pack.get("min_format"); + if (minFormat.isJsonPrimitive()) { + minFormat2[0] = minFormat.getAsInt(); + } + if (mcmeta2Pack.isJsonArray()) { + JsonArray minFormatArray = minFormat.getAsJsonArray(); + minFormat2[0] = minFormatArray.get(0).getAsInt(); + if (minFormatArray.size() > 1) { + minFormat2[1] = minFormatArray.get(1).getAsInt(); + } + } + } + minPackFormat = Math.min(minPackFormat, Math.min(minFormat1[0], minFormat2[0])); + mergedMinFormat = new JsonArray(2); + mergedMinFormat.add(minPackFormat); + mergedMinFormat.add(Math.min(minFormat1[1], minFormat2[1])); + mergedPack.add("min_format", mergedMinFormat); + } + + if (mcmeta1Pack.has("max_format") || mcmeta2Pack.has("max_format")) { + int[] maxFormat1 = new int[]{Integer.MIN_VALUE, 0}; + int[] maxFormat2 = new int[]{Integer.MIN_VALUE, 0}; + + if (mcmeta1Pack.has("max_format")) { + JsonElement maxFormat = mcmeta1Pack.get("max_format"); + if (maxFormat.isJsonPrimitive()) { + maxFormat1[0] = maxFormat.getAsInt(); + } + if (maxFormat.isJsonArray()) { + JsonArray maxFormatArray = maxFormat.getAsJsonArray(); + maxFormat1[0] = maxFormatArray.get(0).getAsInt(); + if (maxFormatArray.size() > 1) { + maxFormat1[1] = maxFormatArray.get(1).getAsInt(); + } + } + } + + if (mcmeta2Pack.has("max_format")) { + JsonElement maxFormat = mcmeta2Pack.get("max_format"); + if (maxFormat.isJsonPrimitive()) { + maxFormat2[0] = maxFormat.getAsInt(); + } + if (maxFormat.isJsonArray()) { + JsonArray maxFormatArray = maxFormat.getAsJsonArray(); + maxFormat2[0] = maxFormatArray.get(0).getAsInt(); + if (maxFormatArray.size() > 1) { + maxFormat2[1] = maxFormatArray.get(1).getAsInt(); + } + } + } + + maxPackFormat = Math.max(maxPackFormat, Math.max(maxFormat1[0], maxFormat2[0])); + mergedMaxFormat = new JsonArray(2); + mergedMaxFormat.add(maxPackFormat); + mergedMaxFormat.add(Math.max(maxFormat1[1], maxFormat2[1])); + mergedPack.add("max_format", mergedMaxFormat); + } + + JsonElement supportedFormats1 = mcmeta1Pack.get("supported_formats"); + JsonElement supportedFormats2 = mcmeta2Pack.get("supported_formats"); + + if (supportedFormats1 != null || supportedFormats2 != null) { + MinMax mergedMinMax = getMergedMinMax(supportedFormats1, supportedFormats2, minPackFormat, maxPackFormat); + JsonElement mergedSupportedFormats = createSupportedFormatsElement( + supportedFormats1 != null ? supportedFormats1 : supportedFormats2, mergedMinMax.min, mergedMinMax.max ); - - pack.add("supported_formats", mergedSf); + if (mergedMinFormat != null && !mergedMinFormat.isEmpty()) { + mergedMinFormat.set(0, new JsonPrimitive(Math.min(mergedMinMax.min, mergedMinFormat.get(0).getAsInt()))); + } + if (mergedMaxFormat != null && !mergedMaxFormat.isEmpty()) { + mergedMaxFormat.set(0, new JsonPrimitive(Math.max(mergedMinMax.max, mergedMaxFormat.get(0).getAsInt()))); + } + mergedPack.add("supported_formats", mergedSupportedFormats); } if (customDescription != null) { - pack.add("description", customDescription); + mergedPack.add("description", customDescription); } else { - JsonPrimitive desc1 = elem1.getAsJsonObject().getAsJsonObject("pack") + JsonPrimitive description1 = mcmeta1.getAsJsonObject().getAsJsonObject("pack") .getAsJsonPrimitive("description"); - JsonPrimitive desc2 = elem2.getAsJsonObject().getAsJsonObject("pack") + JsonPrimitive description2 = mcmeta2.getAsJsonObject().getAsJsonObject("pack") .getAsJsonPrimitive("description"); - String mergedDesc = (desc1 != null ? desc1.getAsString() : "") - + (desc1 != null && desc2 != null ? "\n" : "") - + (desc2 != null ? desc2.getAsString() : ""); + String mergedDesc = (description1 != null ? description1.getAsString() : "") + + (description1 != null && description2 != null ? "\n" : "") + + (description2 != null ? description2.getAsString() : ""); if (!mergedDesc.isEmpty()) { - pack.addProperty("description", mergedDesc); + mergedPack.addProperty("description", mergedDesc); } } } @@ -83,16 +168,14 @@ public class ResolutionMergePackMcMeta implements Resolution { GsonHelper.writeJsonFile(merged, file1); } - private static MinMax getMergedMinMax(JsonElement sf1, JsonElement sf2, int pf1, int pf2) { + private static MinMax getMergedMinMax(JsonElement sf1, JsonElement sf2, int minPackFormat, int maxPackFormat) { MinMax mm1 = parseSupportedFormats(sf1); MinMax mm2 = parseSupportedFormats(sf2); int finalMin = Math.min(mm1.min, mm2.min); int finalMax = Math.max(mm1.max, mm2.max); - int pfMin = Math.min(pf1, pf2); - int pfMax = Math.max(pf1, pf2); - finalMin = Math.min(pfMin, finalMin); - finalMax = Math.max(pfMax, finalMax); + finalMin = Math.min(minPackFormat, finalMin); + finalMax = Math.max(maxPackFormat, finalMax); return new MinMax(finalMin, finalMax); } @@ -110,7 +193,7 @@ public class ResolutionMergePackMcMeta implements Resolution { if (supported.isJsonArray()) { JsonArray arr = supported.getAsJsonArray(); int min = arr.get(0).getAsInt(); - int max = arr.get(arr.size()-1).getAsInt(); + int max = arr.get(arr.size() - 1).getAsInt(); return new MinMax(min, max); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/LobFileHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/LobFileHost.java index 275521957..22a0dfc37 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/LobFileHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/LobFileHost.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.pack.host.impl; +import com.google.gson.annotations.SerializedName; import com.google.gson.reflect.TypeToken; import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData; import net.momirealms.craftengine.core.pack.host.ResourcePackHost; @@ -99,9 +100,9 @@ public class LobFileHost implements ResourcePackHost { public String getSpaceUsageText() { if (this.accountInfo == null) return "Usage data not available"; return String.format("Storage: %d/%d MB (%.1f%% used)", - this.accountInfo.getSpaceUsed() / 1_000_000, - this.accountInfo.getSpaceQuota() / 1_000_000, - (this.accountInfo.getSpaceUsed() * 100.0) / this.accountInfo.getSpaceQuota() + this.accountInfo.account.usage.spaceUsed / 1_000_000, + this.accountInfo.account.limits.spaceQuota / 1_000_000, + (this.accountInfo.account.usage.spaceUsed * 100.0) / this.accountInfo.account.limits.spaceQuota ); } @@ -172,7 +173,7 @@ public class LobFileHost implements ResourcePackHost { .thenApply(response -> { if (response.statusCode() == 200) { AccountInfo info = GsonHelper.get().fromJson(response.body(), AccountInfo.class); - if (info.isSuccess()) { + if (info.success) { this.accountInfo = info; return info; } @@ -278,31 +279,33 @@ public class LobFileHost implements ResourcePackHost { } } - @SuppressWarnings({"all"}) - public static class AccountInfo { - private boolean success; - private Map account_info; - private Map account_limits; - private Map account_usage; + public record AccountInfo( + boolean success, + Account account + ) {} - public String getEmail() { - return (String) this.account_info.get("email"); - } + public record Account( + Info info, + Limits limits, + Usage usage + ) {} - public int getSpaceQuota() { - return this.account_limits.getOrDefault("space_quota", 0); - } + public record Info( + String email, + String level, + @SerializedName("api_key") String apiKey, + @SerializedName("time_created") String timeCreated + ) {} - public int getSpaceUsed() { - return this.account_usage.getOrDefault("space_used", 0); - } + public record Limits( + @SerializedName("space_quota") long spaceQuota, + @SerializedName("slots_quota") long slotsQuota, + @SerializedName("max_file_size") long maxFileSize, + @SerializedName("max_file_download_speed") long maxFileDownloadSpeed + ) {} - public int getSlotsUsed() { - return this.account_usage.getOrDefault("slots_used", 0); - } - - public boolean isSuccess() { - return this.success; - } - } + public record Usage( + @SerializedName("space_used") long spaceUsed, + @SerializedName("slots_used") long slotsUsed + ) {} } \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/LegacyOverridesModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/LegacyOverridesModel.java index cbb4f5179..6f93c7777 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/LegacyOverridesModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/LegacyOverridesModel.java @@ -27,7 +27,7 @@ public class LegacyOverridesModel implements Comparable { } public boolean hasPredicate() { - return !predicate.isEmpty(); + return predicate != null && !predicate.isEmpty(); } public String model() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ComponentConditionProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ComponentConditionProperty.java index 379ffab71..b0907744e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ComponentConditionProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ComponentConditionProperty.java @@ -1,6 +1,8 @@ package net.momirealms.craftengine.core.pack.model.condition; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -10,9 +12,9 @@ public class ComponentConditionProperty implements ConditionProperty { public static final Factory FACTORY = new Factory(); public static final Reader READER = new Reader(); private final String predicate; - private final String value; + private final JsonElement value; - public ComponentConditionProperty(String predicate, String value) { + public ComponentConditionProperty(String predicate, JsonElement value) { this.predicate = predicate; this.value = value; } @@ -26,15 +28,15 @@ public class ComponentConditionProperty implements ConditionProperty { public void accept(JsonObject jsonObject) { jsonObject.addProperty("property", type().toString()); jsonObject.addProperty("predicate", this.predicate); - jsonObject.addProperty("value", this.value); + jsonObject.add("value", this.value); } public static class Factory implements ConditionPropertyFactory { @Override public ConditionProperty create(Map arguments) { String predicate = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("predicate"), "warning.config.item.model.condition.component.missing_predicate"); - String value = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("value"), "warning.config.item.model.condition.component.missing_value"); - return new ComponentConditionProperty(predicate, value); + JsonElement jsonElement = GsonHelper.get().toJsonTree(ResourceConfigUtils.requireNonNullOrThrow(arguments.get("value"), "warning.config.item.model.condition.component.missing_value")); + return new ComponentConditionProperty(predicate, jsonElement); } } @@ -42,7 +44,7 @@ public class ComponentConditionProperty implements ConditionProperty { @Override public ConditionProperty read(JsonObject json) { String predicate = json.get("predicate").getAsString(); - String value = json.get("value").getAsString(); + JsonElement value = json.get("value"); return new ComponentConditionProperty(predicate, value); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/display/DisplayMeta.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/display/DisplayMeta.java index 31549e547..fb416275d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/display/DisplayMeta.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/generation/display/DisplayMeta.java @@ -1,6 +1,6 @@ package net.momirealms.craftengine.core.pack.model.generation.display; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.joml.Vector3f; import java.util.Map; @@ -10,15 +10,15 @@ public record DisplayMeta(Vector3f rotation, Vector3f translation, Vector3f scal public static DisplayMeta fromMap(Map map) { Vector3f rotation = null; if (map.containsKey("rotation")) { - rotation = MiscUtils.getAsVector3f(map.get("rotation"), "rotation"); + rotation = ResourceConfigUtils.getAsVector3f(map.get("rotation"), "rotation"); } Vector3f translation = null; if (map.containsKey("translation")) { - translation = MiscUtils.getAsVector3f(map.get("translation"), "translation"); + translation = ResourceConfigUtils.getAsVector3f(map.get("translation"), "translation"); } Vector3f scale = null; if (map.containsKey("scale")) { - scale = MiscUtils.getAsVector3f(map.get("scale"), "scale"); + scale = ResourceConfigUtils.getAsVector3f(map.get("scale"), "scale"); } return new DisplayMeta(rotation, translation, scale); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java index a78856e6f..31327c6c2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java @@ -51,7 +51,8 @@ public abstract class CraftEngine implements Plugin { protected PluginLogger logger; protected Config config; protected Platform platform; - protected ClassPathAppender classPathAppender; + protected ClassPathAppender sharedClassPathAppender; + protected ClassPathAppender privateClassPathAppender; protected DependencyManager dependencyManager; protected SchedulerAdapter scheduler; protected NetworkManager networkManager; @@ -91,6 +92,8 @@ public abstract class CraftEngine implements Plugin { protected CraftEngine(Consumer reloadEventDispatcher) { instance = this; this.reloadEventDispatcher = reloadEventDispatcher; + ((Logger) LogManager.getRootLogger()).addFilter(new LogFilter()); + ((Logger) LogManager.getRootLogger()).addFilter(new DisconnectLogFilter()); } public static CraftEngine instance() { @@ -101,11 +104,9 @@ public abstract class CraftEngine implements Plugin { } protected void onPluginLoad() { - RecipeDisplayTypes.register(); - SlotDisplayTypes.register(); - LegacyRecipeTypes.register(); - ((Logger) LogManager.getRootLogger()).addFilter(new LogFilter()); - ((Logger) LogManager.getRootLogger()).addFilter(new DisconnectLogFilter()); + RecipeDisplayTypes.init(); + SlotDisplayTypes.init(); + LegacyRecipeTypes.init(); } public record ReloadResult(boolean success, long asyncTime, long syncTime) { @@ -279,7 +280,7 @@ public abstract class CraftEngine implements Plugin { // register furniture parser this.packManager.registerConfigSectionParser(this.furnitureManager.parser()); // register block parser - this.packManager.registerConfigSectionParser(this.blockManager.parser()); + this.packManager.registerConfigSectionParsers(this.blockManager.parsers()); // register recipe parser this.packManager.registerConfigSectionParser(this.recipeManager.parser()); // register category parser @@ -338,8 +339,13 @@ public abstract class CraftEngine implements Plugin { } @Override - public ClassPathAppender classPathAppender() { - return classPathAppender; + public ClassPathAppender sharedClassPathAppender() { + return sharedClassPathAppender; + } + + @Override + public ClassPathAppender privateClassPathAppender() { + return privateClassPathAppender; } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/Platform.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/Platform.java index 03e0c52b2..6a8ec531b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/Platform.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/Platform.java @@ -1,6 +1,9 @@ package net.momirealms.craftengine.core.plugin; import com.google.gson.JsonElement; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.particle.ParticleType; import net.momirealms.sparrow.nbt.Tag; public interface Platform { @@ -14,4 +17,8 @@ public interface Platform { Tag snbtToSparrowNBT(String nbt); Tag javaToSparrowNBT(Object object); + + World getWorld(String name); + + ParticleType getParticleType(Key name); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/Plugin.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/Plugin.java index 3521c3914..1813c2bd8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/Plugin.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/Plugin.java @@ -35,7 +35,9 @@ public interface Plugin { PluginLogger logger(); - ClassPathAppender classPathAppender(); + ClassPathAppender sharedClassPathAppender(); + + ClassPathAppender privateClassPathAppender(); File dataFolderFile(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/AbstractConfigParser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/AbstractConfigParser.java new file mode 100644 index 000000000..cbbc609fb --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/AbstractConfigParser.java @@ -0,0 +1,32 @@ +package net.momirealms.craftengine.core.plugin.config; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.momirealms.craftengine.core.pack.CachedConfigSection; + +public abstract class AbstractConfigParser implements ConfigParser { + protected final ObjectArrayList configStorage; + + public AbstractConfigParser() { + this.configStorage = new ObjectArrayList<>(); + } + + @Override + public void addConfig(CachedConfigSection section) { + this.configStorage.add(section); + } + + @Override + public void loadAll() { + Object[] elements = this.configStorage.elements(); + for (int i = 0, size = this.configStorage.size(); i < size; i++) { + parseSection((CachedConfigSection) elements[i]); + } + } + + @Override + public void clear() { + this.configStorage.clear(); + } + + protected abstract void parseSection(CachedConfigSection section); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java index 8956ca100..788862341 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java @@ -44,6 +44,7 @@ public class Config { protected boolean checkUpdate; protected boolean metrics; protected boolean filterConfigurationPhaseDisconnect; + protected Locale forcedLocale; protected boolean debug$common; protected boolean debug$packet; @@ -63,7 +64,8 @@ public class Config { protected boolean resource_pack$protection$crash_tools$method_2; protected boolean resource_pack$protection$crash_tools$method_3; - protected boolean resource_pack$validate$enable; + protected boolean resource_pack$validation$enable; + protected boolean resource_pack$validation$fix_atlas; protected boolean resource_pack$exclude_core_shaders; protected boolean resource_pack$protection$obfuscation$enable; @@ -93,17 +95,19 @@ public class Config { protected boolean resource_pack$delivery$send_on_join; protected boolean resource_pack$delivery$resend_on_upload; protected boolean resource_pack$delivery$auto_upload; + protected boolean resource_pack$delivery$strict_player_uuid_validation; protected Path resource_pack$delivery$file_to_upload; protected Component resource_pack$send$prompt; protected boolean light_system$force_update_light; + protected boolean light_system$async_update; protected boolean light_system$enable; protected int chunk_system$compression_method; protected boolean chunk_system$restore_vanilla_blocks_on_chunk_unload; protected boolean chunk_system$restore_custom_blocks_on_chunk_load; protected boolean chunk_system$sync_custom_blocks_on_chunk_load; - protected boolean chunk_system$cache_system; + protected boolean chunk_system$cache_system = true; protected boolean chunk_system$injection$use_fast_method; protected boolean chunk_system$injection$target; protected boolean chunk_system$process_invalid_furniture$enable; @@ -120,6 +124,8 @@ public class Config { protected boolean block$predict_breaking; protected int block$predict_breaking_interval; protected double block$extended_interaction_range; + protected boolean block$chunk_relighter; + protected int block$serverside_blocks = -1; protected boolean recipe$enable; protected boolean recipe$disable_vanilla_recipes$all; @@ -131,21 +137,24 @@ public class Config { protected boolean image$illegal_characters_filter$anvil; protected boolean image$illegal_characters_filter$sign; protected boolean image$illegal_characters_filter$book; - protected boolean image$intercept_packets$system_chat; - protected boolean image$intercept_packets$tab_list; - protected boolean image$intercept_packets$actionbar; - protected boolean image$intercept_packets$title; - protected boolean image$intercept_packets$bossbar; - protected boolean image$intercept_packets$container; - protected boolean image$intercept_packets$team; - protected boolean image$intercept_packets$scoreboard; - protected boolean image$intercept_packets$entity_name; - protected boolean image$intercept_packets$text_display; - protected boolean image$intercept_packets$armor_stand; - protected boolean image$intercept_packets$player_info; - protected boolean image$intercept_packets$set_score; - protected boolean image$intercept_packets$item; - protected boolean image$intercept_packets$advancement; + protected int image$codepoint_starting_value$default; + protected Map image$codepoint_starting_value$overrides; + + protected boolean network$intercept_packets$system_chat; + protected boolean network$intercept_packets$tab_list; + protected boolean network$intercept_packets$actionbar; + protected boolean network$intercept_packets$title; + protected boolean network$intercept_packets$bossbar; + protected boolean network$intercept_packets$container; + protected boolean network$intercept_packets$team; + protected boolean network$intercept_packets$scoreboard; + protected boolean network$intercept_packets$entity_name; + protected boolean network$intercept_packets$text_display; + protected boolean network$intercept_packets$armor_stand; + protected boolean network$intercept_packets$player_info; + protected boolean network$intercept_packets$set_score; + protected boolean network$intercept_packets$item; + protected boolean network$intercept_packets$advancement; protected boolean item$client_bound_model; protected boolean item$non_italic_tag; @@ -153,6 +162,9 @@ public class Config { protected boolean item$update_triggers$click_in_inventory; protected boolean item$update_triggers$drop; protected boolean item$update_triggers$pick_up; + protected int item$custom_model_data_starting_value$default; + protected Map item$custom_model_data_starting_value$overrides; + protected boolean item$always_use_item_model; protected String equipment$sacrificed_vanilla_armor$type; protected Key equipment$sacrificed_vanilla_armor$asset_id; @@ -172,7 +184,7 @@ public class Config { instance = this; } - public void load() { + public boolean updateConfigCache() { // 文件不存在,则保存 if (!Files.exists(this.configFilePath)) { this.plugin.saveResource("config.yml"); @@ -190,13 +202,20 @@ public class Config { this.updateConfigVersion(configFileBytes); } } - // 加载配置文件 - this.loadSettings(); this.lastModified = lastModified; this.size = size; + return true; } } catch (IOException e) { - this.plugin.logger().severe("Failed to load config.yml", e); + this.plugin.logger().severe("Failed to update config.yml", e); + } + return false; + } + + public void load() { + boolean isUpdated = updateConfigCache(); + if (isUpdated) { + loadFullSettings(); } } @@ -224,6 +243,9 @@ public class Config { .builder() .setVersioning(new BasicVersioning("config-version")) .addIgnoredRoute(PluginProperties.getValue("config"), "resource-pack.delivery.hosting", '.') + .addIgnoredRoute(PluginProperties.getValue("config"), "chunk-system.process-invalid-blocks.convert", '.') + .addIgnoredRoute(PluginProperties.getValue("config"), "chunk-system.process-invalid-furniture.convert", '.') + .addIgnoredRoute(PluginProperties.getValue("config"), "item.custom-model-data-starting-value.overrides", '.') .build()); } try { @@ -233,9 +255,15 @@ public class Config { } } - private void loadSettings() { + public void loadForcedLocale() { YamlDocument config = settings(); - plugin.translationManager().forcedLocale(TranslationManager.parseLocale(config.getString("forced-locale", ""))); + forcedLocale = TranslationManager.parseLocale(config.getString("forced-locale", "")); + } + + @SuppressWarnings("DuplicatedCode") + public void loadFullSettings() { + YamlDocument config = settings(); + forcedLocale = TranslationManager.parseLocale(config.getString("forced-locale", "")); // basics metrics = config.getBoolean("metrics", false); @@ -267,12 +295,13 @@ public class Config { resource_pack$delivery$kick_if_declined = config.getBoolean("resource-pack.delivery.kick-if-declined", true); resource_pack$delivery$kick_if_failed_to_apply = config.getBoolean("resource-pack.delivery.kick-if-failed-to-apply", true); resource_pack$delivery$auto_upload = config.getBoolean("resource-pack.delivery.auto-upload", true); + resource_pack$delivery$strict_player_uuid_validation = config.getBoolean("resource-pack.delivery.strict-player-uuid-validation", true); resource_pack$delivery$file_to_upload = resolvePath(config.getString("resource-pack.delivery.file-to-upload", "./generated/resource_pack.zip")); resource_pack$send$prompt = AdventureHelper.miniMessage().deserialize(config.getString("resource-pack.delivery.prompt", "To fully experience our server, please accept our custom resource pack.")); resource_pack$protection$crash_tools$method_1 = config.getBoolean("resource-pack.protection.crash-tools.method-1", false); resource_pack$protection$crash_tools$method_2 = config.getBoolean("resource-pack.protection.crash-tools.method-2", false); resource_pack$protection$crash_tools$method_3 = config.getBoolean("resource-pack.protection.crash-tools.method-3", false); - resource_pack$protection$obfuscation$enable = config.getBoolean("resource-pack.protection.obfuscation.enable", false); + resource_pack$protection$obfuscation$enable = VersionHelper.PREMIUM && config.getBoolean("resource-pack.protection.obfuscation.enable", false); resource_pack$protection$obfuscation$seed = config.getLong("resource-pack.protection.obfuscation.seed", 0L); resource_pack$protection$obfuscation$fake_directory = config.getBoolean("resource-pack.protection.obfuscation.fake-directory", false); resource_pack$protection$obfuscation$escape_unicode = config.getBoolean("resource-pack.protection.obfuscation.escape-unicode", false); @@ -289,7 +318,8 @@ public class Config { resource_pack$protection$obfuscation$resource_location$bypass_models = config.getStringList("resource-pack.protection.obfuscation.resource-location.bypass-models"); resource_pack$protection$obfuscation$resource_location$bypass_sounds = config.getStringList("resource-pack.protection.obfuscation.resource-location.bypass-sounds"); resource_pack$protection$obfuscation$resource_location$bypass_equipments = config.getStringList("resource-pack.protection.obfuscation.resource-location.bypass-equipments"); - resource_pack$validate$enable = config.getBoolean("resource-pack.validate.enable", true); + resource_pack$validation$enable = config.getBoolean("resource-pack.validation.enable", true); + resource_pack$validation$fix_atlas = VersionHelper.PREMIUM && config.getBoolean("resource-pack.validation.fix-atlas", true); resource_pack$exclude_core_shaders = config.getBoolean("resource-pack.exclude-core-shaders", false); resource_pack$overlay_format = config.getString("resource-pack.overlay-format", "overlay_{version}"); if (!resource_pack$overlay_format.contains("{version}")) { @@ -311,6 +341,7 @@ public class Config { // light light_system$force_update_light = config.getBoolean("light-system.force-update-light", false); + light_system$async_update = config.getBoolean("light-system.async-update", true); light_system$enable = config.getBoolean("light-system.enable", true); // chunk @@ -370,12 +401,29 @@ public class Config { equipment$sacrificed_vanilla_armor$humanoid_leggings = Key.of(config.getString("equipment.sacrificed-vanilla-armor.humanoid-leggings", "minecraft:trims/entity/humanoid_leggings/chainmail")); // item - item$client_bound_model = config.getBoolean("item.client-bound-model", false); + item$client_bound_model = config.getBoolean("item.client-bound-model", true) && VersionHelper.PREMIUM; item$non_italic_tag = config.getBoolean("item.non-italic-tag", false); item$update_triggers$attack = config.getBoolean("item.update-triggers.attack", false); item$update_triggers$click_in_inventory = config.getBoolean("item.update-triggers.click-in-inventory", false); item$update_triggers$drop = config.getBoolean("item.update-triggers.drop", false); item$update_triggers$pick_up = config.getBoolean("item.update-triggers.pick-up", false); + item$custom_model_data_starting_value$default = config.getInt("item.custom-model-data-starting-value.default", 10000); + item$always_use_item_model = config.getBoolean("item.always-use-item-model", true) && VersionHelper.isOrAbove1_21_2(); + + Section customModelDataOverridesSection = config.getSection("item.custom-model-data-starting-value.overrides"); + if (customModelDataOverridesSection != null) { + Map customModelDataOverrides = new HashMap<>(); + for (Map.Entry entry : customModelDataOverridesSection.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof String s) { + customModelDataOverrides.put(Key.of(entry.getKey()), Integer.parseInt(s)); + } else if (entry.getValue() instanceof Integer i) { + customModelDataOverrides.put(Key.of(entry.getKey()), i); + } + } + item$custom_model_data_starting_value$overrides = customModelDataOverrides; + } else { + item$custom_model_data_starting_value$overrides = Map.of(); + } // block block$sound_system$enable = config.getBoolean("block.sound-system.enable", true); @@ -384,6 +432,11 @@ public class Config { block$predict_breaking = config.getBoolean("block.predict-breaking.enable", true); block$predict_breaking_interval = Math.max(config.getInt("block.predict-breaking.interval", 10), 1); block$extended_interaction_range = Math.max(config.getDouble("block.predict-breaking.extended-interaction-range", 0.5), 0.0); + block$chunk_relighter = config.getBoolean("block.chunk-relighter", true); + if (firstTime) { + block$serverside_blocks = Math.min(config.getInt("block.serverside-blocks", 2000), 10_0000); + if (block$serverside_blocks < 0) block$serverside_blocks = 0; + } // recipe recipe$enable = config.getBoolean("recipe.enable", true); @@ -397,21 +450,38 @@ public class Config { image$illegal_characters_filter$chat = config.getBoolean("image.illegal-characters-filter.chat", true); image$illegal_characters_filter$command = config.getBoolean("image.illegal-characters-filter.command", true); image$illegal_characters_filter$sign = config.getBoolean("image.illegal-characters-filter.sign", true); - image$intercept_packets$system_chat = config.getBoolean("image.intercept-packets.system-chat", true); - image$intercept_packets$tab_list = config.getBoolean("image.intercept-packets.tab-list", true); - image$intercept_packets$actionbar = config.getBoolean("image.intercept-packets.actionbar", true); - image$intercept_packets$title = config.getBoolean("image.intercept-packets.title", true); - image$intercept_packets$bossbar = config.getBoolean("image.intercept-packets.bossbar", true); - image$intercept_packets$container = config.getBoolean("image.intercept-packets.container", true); - image$intercept_packets$team = config.getBoolean("image.intercept-packets.team", true); - image$intercept_packets$scoreboard = config.getBoolean("image.intercept-packets.scoreboard", true); - image$intercept_packets$entity_name = config.getBoolean("image.intercept-packets.entity-name", false); - image$intercept_packets$text_display = config.getBoolean("image.intercept-packets.text-display", true); - image$intercept_packets$armor_stand = config.getBoolean("image.intercept-packets.armor-stand", true); - image$intercept_packets$player_info = config.getBoolean("image.intercept-packets.player-info", true); - image$intercept_packets$set_score = config.getBoolean("image.intercept-packets.set-score", true); - image$intercept_packets$item = config.getBoolean("image.intercept-packets.item", true); - image$intercept_packets$advancement = config.getBoolean("image.intercept-packets.advancement", true); + + image$codepoint_starting_value$default = config.getInt("image.codepoint-starting-value.default", 0); + Section codepointOverridesSection = config.getSection("image.codepoint-starting-value.overrides"); + if (codepointOverridesSection != null) { + Map codepointOverrides = new HashMap<>(); + for (Map.Entry entry : codepointOverridesSection.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof String s) { + codepointOverrides.put(Key.of(entry.getKey()), Integer.parseInt(s)); + } else if (entry.getValue() instanceof Integer i) { + codepointOverrides.put(Key.of(entry.getKey()), i); + } + } + image$codepoint_starting_value$overrides = codepointOverrides; + } else { + image$codepoint_starting_value$overrides = Map.of(); + } + + network$intercept_packets$system_chat = config.getBoolean("network.intercept-packets.system-chat", true); + network$intercept_packets$tab_list = config.getBoolean("network.intercept-packets.tab-list", true); + network$intercept_packets$actionbar = config.getBoolean("network.intercept-packets.actionbar", true); + network$intercept_packets$title = config.getBoolean("network.intercept-packets.title", true); + network$intercept_packets$bossbar = config.getBoolean("network.intercept-packets.bossbar", true); + network$intercept_packets$container = config.getBoolean("network.intercept-packets.container", true); + network$intercept_packets$team = config.getBoolean("network.intercept-packets.team", true); + network$intercept_packets$scoreboard = config.getBoolean("network.intercept-packets.scoreboard", true); + network$intercept_packets$entity_name = config.getBoolean("network.intercept-packets.entity-name", false); + network$intercept_packets$text_display = config.getBoolean("network.intercept-packets.text-display", true); + network$intercept_packets$armor_stand = config.getBoolean("network.intercept-packets.armor-stand", true); + network$intercept_packets$player_info = config.getBoolean("network.intercept-packets.player-info", true); + network$intercept_packets$set_score = config.getBoolean("network.intercept-packets.set-score", true); + network$intercept_packets$item = config.getBoolean("network.intercept-packets.item", true); + network$intercept_packets$advancement = config.getBoolean("network.intercept-packets.advancement", true); // emoji emoji$contexts$chat = config.getBoolean("emoji.contexts.chat", true); @@ -433,6 +503,10 @@ public class Config { return MinecraftVersion.parse(version); } + public static Locale forcedLocale() { + return instance.forcedLocale; + } + public static String configVersion() { return instance.configVersion; } @@ -449,6 +523,14 @@ public class Config { return instance.debug$item; } + public static boolean debugBlockEntity() { + return false; + } + + public static boolean debugBlock() { + return false; + } + public static boolean debugFurniture() { return instance.debug$furniture; } @@ -465,6 +547,14 @@ public class Config { return instance.metrics; } + public static int serverSideBlocks() { + return instance.block$serverside_blocks; + } + + public static boolean alwaysUseItemModel() { + return instance.item$always_use_item_model; + } + public static boolean filterConfigurationPhaseDisconnect() { return instance.filterConfigurationPhaseDisconnect; } @@ -580,6 +670,9 @@ public class Config { public static boolean autoUpload() { return instance.resource_pack$delivery$auto_upload; } + public static boolean strictPlayerUuidValidation() { + return instance.resource_pack$delivery$strict_player_uuid_validation; + } public static Path fileToUpload() { return instance.resource_pack$delivery$file_to_upload; @@ -705,6 +798,20 @@ public class Config { return instance.furniture$hide_base_entity; } + public static int customModelDataStartingValue(Key material) { + if (instance.item$custom_model_data_starting_value$overrides.containsKey(material)) { + return instance.item$custom_model_data_starting_value$overrides.get(material); + } + return instance.item$custom_model_data_starting_value$default; + } + + public static int codepointStartingValue(Key font) { + if (instance.image$codepoint_starting_value$overrides.containsKey(font)) { + return instance.image$codepoint_starting_value$overrides.get(font); + } + return instance.image$codepoint_starting_value$default; + } + public static int compressionMethod() { int id = instance.chunk_system$compression_method; if (id <= 0 || id > CompressionMethod.METHOD_COUNT) { @@ -714,63 +821,63 @@ public class Config { } public static boolean interceptSystemChat() { - return instance.image$intercept_packets$system_chat; + return instance.network$intercept_packets$system_chat; } public static boolean interceptTabList() { - return instance.image$intercept_packets$tab_list; + return instance.network$intercept_packets$tab_list; } public static boolean interceptActionBar() { - return instance.image$intercept_packets$actionbar; + return instance.network$intercept_packets$actionbar; } public static boolean interceptTitle() { - return instance.image$intercept_packets$title; + return instance.network$intercept_packets$title; } public static boolean interceptBossBar() { - return instance.image$intercept_packets$bossbar; + return instance.network$intercept_packets$bossbar; } public static boolean interceptContainer() { - return instance.image$intercept_packets$container; + return instance.network$intercept_packets$container; } public static boolean interceptTeam() { - return instance.image$intercept_packets$team; + return instance.network$intercept_packets$team; } public static boolean interceptEntityName() { - return instance.image$intercept_packets$entity_name; + return instance.network$intercept_packets$entity_name; } public static boolean interceptScoreboard() { - return instance.image$intercept_packets$scoreboard; + return instance.network$intercept_packets$scoreboard; } public static boolean interceptTextDisplay() { - return instance.image$intercept_packets$text_display; + return instance.network$intercept_packets$text_display; } public static boolean interceptArmorStand() { - return instance.image$intercept_packets$armor_stand; + return instance.network$intercept_packets$armor_stand; } public static boolean interceptPlayerInfo() { - return instance.image$intercept_packets$player_info; + return instance.network$intercept_packets$player_info; } public static boolean interceptSetScore() { - return instance.image$intercept_packets$set_score; + return instance.network$intercept_packets$set_score; } public static boolean interceptItem() { - return instance.image$intercept_packets$item; + return instance.network$intercept_packets$item; } public static boolean interceptAdvancement() { - return instance.image$intercept_packets$advancement; + return instance.network$intercept_packets$advancement; } public static boolean predictBreaking() { @@ -822,7 +929,11 @@ public class Config { } public static boolean validateResourcePack() { - return instance.resource_pack$validate$enable; + return instance.resource_pack$validation$enable; + } + + public static boolean fixTextureAtlas() { + return instance.resource_pack$validation$fix_atlas; } public static boolean excludeShaders() { @@ -873,6 +984,14 @@ public class Config { return instance.item$update_triggers$drop; } + public static boolean enableChunkRelighter() { + return instance.block$chunk_relighter; + } + + public static boolean asyncLightUpdate() { + return instance.light_system$async_update; + } + public void setObf(boolean enable) { this.resource_pack$protection$obfuscation$enable = enable; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigParser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigParser.java index 901fbe8a9..5fc06c91e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigParser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigParser.java @@ -1,30 +1,14 @@ package net.momirealms.craftengine.core.plugin.config; -import net.momirealms.craftengine.core.pack.Pack; -import net.momirealms.craftengine.core.plugin.locale.LocalizedException; -import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.pack.CachedConfigSection; import org.jetbrains.annotations.NotNull; -import java.nio.file.Path; -import java.util.Map; - public interface ConfigParser extends Comparable { String[] sectionId(); - default void parseSection(Pack pack, Path path, Key id, Map section) throws LocalizedException { - this.parseObject(pack, path, id, section); - } - - default void parseObject(Pack pack, Path path, Key id, Object object) throws LocalizedException { - } - int loadingSequence(); - default boolean supportsParsingObject() { - return false; - } - @Override default int compareTo(@NotNull ConfigParser another) { return Integer.compare(loadingSequence(), another.loadingSequence()); @@ -35,4 +19,10 @@ public interface ConfigParser extends Comparable { default void preProcess() { } + + void addConfig(CachedConfigSection section); + + void loadAll(); + + void clear(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/IdObjectConfigParser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/IdObjectConfigParser.java new file mode 100644 index 000000000..2a7f49f8d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/IdObjectConfigParser.java @@ -0,0 +1,31 @@ +package net.momirealms.craftengine.core.plugin.config; + +import net.momirealms.craftengine.core.pack.CachedConfigSection; +import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.util.GsonHelper; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.nio.file.Path; +import java.util.Map; + +public abstract class IdObjectConfigParser extends AbstractConfigParser { + + @Override + protected void parseSection(CachedConfigSection cached) { + for (Map.Entry configEntry : cached.config().entrySet()) { + String key = configEntry.getKey(); + Key id = Key.withDefaultNamespace(key, cached.pack().namespace()); + String node = cached.prefix() + "." + key; + ResourceConfigUtils.runCatching( + cached.filePath(), + node, + () -> parseObject(cached.pack(), cached.filePath(), node, id, configEntry.getValue()), + () -> GsonHelper.get().toJson(configEntry.getValue()) + ); + } + } + + protected abstract void parseObject(Pack pack, Path path, String node, Key id, Object object) throws LocalizedException; +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/IdSectionConfigParser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/IdSectionConfigParser.java new file mode 100644 index 000000000..ff5828380 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/IdSectionConfigParser.java @@ -0,0 +1,48 @@ +package net.momirealms.craftengine.core.plugin.config; + +import net.momirealms.craftengine.core.pack.CachedConfigSection; +import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; +import net.momirealms.craftengine.core.util.GsonHelper; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.nio.file.Path; +import java.util.Map; + +import static net.momirealms.craftengine.core.util.MiscUtils.castToMap; + +public abstract class IdSectionConfigParser extends AbstractConfigParser { + + @Override + protected void parseSection(CachedConfigSection cached) { + for (Map.Entry configEntry : cached.config().entrySet()) { + String key = configEntry.getKey(); + Key id = Key.withDefaultNamespace(key, cached.pack().namespace()); + if (!(configEntry.getValue() instanceof Map section)) { + TranslationManager.instance().log("warning.config.structure.not_section", + cached.filePath().toString(), cached.prefix() + "." + key, configEntry.getValue().getClass().getSimpleName()); + continue; + } + Map config = castToMap(section, false); + if ((boolean) config.getOrDefault("debug", false)) { + CraftEngine.instance().logger().info(GsonHelper.get().toJson(CraftEngine.instance().templateManager().applyTemplates(id, config))); + } + if (!(boolean) config.getOrDefault("enable", true)) { + continue; + } + String node = cached.prefix() + "." + key; + ResourceConfigUtils.runCatching( + cached.filePath(), + node, + () -> parseSection(cached.pack(), cached.filePath(), node, id, MiscUtils.castToMap(CraftEngine.instance().templateManager().applyTemplates(id, config), false)), + () -> GsonHelper.get().toJson(section) + ); + } + } + + protected abstract void parseSection(Pack pack, Path path, String node, Key id, Map section) throws LocalizedException; +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/SectionConfigParser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/SectionConfigParser.java new file mode 100644 index 000000000..626a9ffc8 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/SectionConfigParser.java @@ -0,0 +1,25 @@ +package net.momirealms.craftengine.core.plugin.config; + +import net.momirealms.craftengine.core.pack.CachedConfigSection; +import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.util.GsonHelper; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.nio.file.Path; +import java.util.Map; + +public abstract class SectionConfigParser extends AbstractConfigParser { + + @Override + protected void parseSection(CachedConfigSection cached) { + ResourceConfigUtils.runCatching( + cached.filePath(), + cached.prefix(), + () -> parseSection(cached.pack(), cached.filePath(), cached.config()), + () -> GsonHelper.get().toJson(cached.config()) + ); + } + + protected abstract void parseSection(Pack pack, Path path, Map section) throws LocalizedException; +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java index 398af24f7..3356a760f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.plugin.config.template; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.config.IdObjectConfigParser; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; @@ -36,7 +37,7 @@ public class TemplateManagerImpl implements TemplateManager { return this.templateParser; } - public class TemplateParser implements ConfigParser { + public class TemplateParser extends IdObjectConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"templates", "template"}; @Override @@ -50,14 +51,9 @@ public class TemplateManagerImpl implements TemplateManager { } @Override - public boolean supportsParsingObject() { - return true; - } - - @Override - public void parseObject(Pack pack, Path path, Key id, Object obj) { + public void parseObject(Pack pack, Path path, String node, Key id, Object obj) { if (templates.containsKey(id)) { - throw new LocalizedResourceConfigException("warning.config.template.duplicate", path.toString(), id.toString()); + throw new LocalizedResourceConfigException("warning.config.template.duplicate"); } // 预处理会将 string类型的键或值解析为ArgumentString,以加速模板应用。所以处理后不可能存在String类型。 templates.put(id, preprocessUnknownValue(obj)); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ContextHolder.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ContextHolder.java index 5e54f2ea8..59d678797 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ContextHolder.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ContextHolder.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.plugin.context; import com.google.common.collect.ImmutableMap; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.HashMap; @@ -10,11 +11,32 @@ import java.util.Optional; import java.util.function.Supplier; public class ContextHolder { + /** + * Use {@link #empty()} instead + */ + @Deprecated(forRemoval = true, since = "0.0.63") public static final ContextHolder EMPTY = ContextHolder.builder().immutable(true).build(); + protected final Map, Supplier> params; + private final boolean immutable; + + public ContextHolder(Map, Supplier> params, boolean immutable) { + this.params = immutable ? ImmutableMap.copyOf(params) : new HashMap<>(params); + this.immutable = immutable; + } public ContextHolder(Map, Supplier> params) { - this.params = params; + this.params = new HashMap<>(params); + this.immutable = true; + } + + @NotNull + public static ContextHolder empty() { + return ContextHolder.builder().build(); + } + + public boolean immutable() { + return this.immutable; } public boolean has(ContextKey key) { @@ -67,7 +89,7 @@ public class ContextHolder { } public static class Builder { - private final Map, Supplier> params = new HashMap<>(); + private final Map, Supplier> params = new HashMap<>(8); private boolean immutable = false; public Builder() {} @@ -113,7 +135,7 @@ public class ContextHolder { } public ContextHolder build() { - return new ContextHolder(this.immutable ? ImmutableMap.copyOf(this.params) : this.params); + return new ContextHolder(this.params, this.immutable); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/GlobalVariableManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/GlobalVariableManager.java index eb0b3ae25..078a8c600 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/GlobalVariableManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/GlobalVariableManager.java @@ -4,7 +4,9 @@ import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.Manageable; import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.config.IdObjectConfigParser; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.Nullable; import java.nio.file.Path; @@ -34,7 +36,7 @@ public class GlobalVariableManager implements Manageable { return this.parser; } - public class GlobalVariableParser implements ConfigParser { + public class GlobalVariableParser extends IdObjectConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"global-variables", "global-variable"}; @Override @@ -48,14 +50,9 @@ public class GlobalVariableManager implements Manageable { } @Override - public boolean supportsParsingObject() { - return true; - } - - @Override - public void parseObject(Pack pack, Path path, net.momirealms.craftengine.core.util.Key id, Object object) throws LocalizedException { + public void parseObject(Pack pack, Path path, String node, Key id, Object object) throws LocalizedException { if (object != null) { - globalVariables.put(id.value(), object.toString()); + GlobalVariableManager.this.globalVariables.put(id.value(), object.toString()); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/NetworkTextReplaceContext.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/NetworkTextReplaceContext.java index 42f7c5984..1b80021c5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/NetworkTextReplaceContext.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/NetworkTextReplaceContext.java @@ -7,7 +7,7 @@ import net.momirealms.craftengine.core.plugin.text.minimessage.*; import java.util.Map; -public final class NetworkTextReplaceContext extends AbstractChainParameterContext { +public final class NetworkTextReplaceContext extends AbstractChainParameterContext implements PlayerContext { private final Player player; public NetworkTextReplaceContext(Player player) { @@ -19,6 +19,7 @@ public final class NetworkTextReplaceContext extends AbstractChainParameterConte return new NetworkTextReplaceContext(player); } + @Override public Player player() { return player; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/PlayerContext.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/PlayerContext.java new file mode 100644 index 000000000..89bc0f1fc --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/PlayerContext.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.plugin.context; + +import net.momirealms.craftengine.core.entity.player.Player; + +public interface PlayerContext { + + Player player(); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/PlayerOptionalContext.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/PlayerOptionalContext.java index 72667d538..e38a1668d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/PlayerOptionalContext.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/PlayerOptionalContext.java @@ -10,8 +10,13 @@ import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Map; -public class PlayerOptionalContext extends AbstractChainParameterContext implements Context { - public static final PlayerOptionalContext EMPTY = new PlayerOptionalContext(null, ContextHolder.EMPTY); +public class PlayerOptionalContext extends AbstractChainParameterContext implements PlayerContext { + /** + * Use {@link #empty()} instead + */ + @Deprecated(forRemoval = true) + public static final PlayerOptionalContext EMPTY = new PlayerOptionalContext(null, ContextHolder.empty()); + protected final Player player; public PlayerOptionalContext(@Nullable Player player, @@ -40,10 +45,16 @@ public class PlayerOptionalContext extends AbstractChainParameterContext impleme @NotNull public static PlayerOptionalContext of(@Nullable Player player) { - if (player == null) return EMPTY; + if (player == null) return empty(); return new PlayerOptionalContext(player, new ContextHolder(Map.of(DirectContextParameters.PLAYER, () -> player))); } + @NotNull + public static PlayerOptionalContext empty() { + return new PlayerOptionalContext(null, ContextHolder.empty()); + } + + @Override @Nullable public Player player() { return this.player; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AllOfCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AllOfCondition.java index a1776b43d..761f07dc7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AllOfCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AllOfCondition.java @@ -4,7 +4,6 @@ import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MCUtils; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -18,7 +17,7 @@ public class AllOfCondition implements Condition { protected final Predicate condition; public AllOfCondition(List> conditions) { - this.condition = MCUtils.allOf(conditions); + this.condition = MiscUtils.allOf(conditions); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AlwaysFalseCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AlwaysFalseCondition.java new file mode 100644 index 000000000..127070193 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AlwaysFalseCondition.java @@ -0,0 +1,23 @@ +package net.momirealms.craftengine.core.plugin.context.condition; + +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.util.Key; + +import java.util.Map; + +public class AlwaysFalseCondition implements Condition { + + @Override + public Key type() { + return CommonConditions.ALWAYS_FALSE; + } + + public static class FactoryImpl implements ConditionFactory { + + @Override + public Condition create(Map arguments) { + return new AlwaysFalseCondition<>(); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/EmptyCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AlwaysTrueCondition.java similarity index 77% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/EmptyCondition.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AlwaysTrueCondition.java index dda586d78..c36d53cb9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/EmptyCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AlwaysTrueCondition.java @@ -6,11 +6,11 @@ import net.momirealms.craftengine.core.util.Key; import java.util.Map; -public class EmptyCondition implements Condition { +public class AlwaysTrueCondition implements Condition { @Override public Key type() { - return CommonConditions.EMPTY; + return CommonConditions.ALWAYS_TRUE; } @Override @@ -22,7 +22,7 @@ public class EmptyCondition implements Condition { @Override public Condition create(Map arguments) { - return new EmptyCondition<>(); + return new AlwaysTrueCondition<>(); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AnyOfCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AnyOfCondition.java index e01568f5b..278bb9421 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AnyOfCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/AnyOfCondition.java @@ -4,7 +4,6 @@ import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MCUtils; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -18,7 +17,7 @@ public class AnyOfCondition implements Condition { protected final Predicate condition; public AnyOfCondition(List> conditions) { - this.condition = MCUtils.anyOf(conditions); + this.condition = MiscUtils.anyOf(conditions); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/CommonConditions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/CommonConditions.java index d9a1eadae..156c17856 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/CommonConditions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/CommonConditions.java @@ -5,7 +5,8 @@ import net.momirealms.craftengine.core.util.Key; public final class CommonConditions { private CommonConditions() {} - public static final Key EMPTY = Key.of("craftengine:empty"); + public static final Key ALWAYS_TRUE = Key.of("craftengine:always_true"); + public static final Key ALWAYS_FALSE = Key.of("craftengine:always_false"); public static final Key ALL_OF = Key.of("craftengine:all_of"); public static final Key ANY_OF = Key.of("craftengine:any_of"); public static final Key INVERTED = Key.of("craftengine:inverted"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/ExpressionCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/ExpressionCondition.java index 7ae158e1f..3dc4a67e2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/ExpressionCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/ExpressionCondition.java @@ -27,7 +27,7 @@ public class ExpressionCondition implements Condition @Override public boolean test(CTX ctx) { - String exp = expression.get(ctx); + String exp = this.expression.get(ctx); Expression expr = new Expression(exp); try { return expr.evaluate().getBooleanValue(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchBlockPropertyCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchBlockPropertyCondition.java index e5ce7b198..41db9e150 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchBlockPropertyCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchBlockPropertyCondition.java @@ -2,8 +2,8 @@ package net.momirealms.craftengine.core.plugin.context.condition; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.StatePropertyAccessor; import net.momirealms.craftengine.core.block.properties.Property; -import net.momirealms.craftengine.core.block.state.StatePropertyAccessor; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java index fbb129296..e23bee3d9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java @@ -42,6 +42,10 @@ public class EventFunctions { register(CommonFunctions.REMOVE_FURNITURE, new RemoveFurnitureFunction.FactoryImpl<>(EventConditions::fromMap)); register(CommonFunctions.REPLACE_FURNITURE, new ReplaceFurnitureFunction.FactoryImpl<>(EventConditions::fromMap)); register(CommonFunctions.MYTHIC_MOBS_SKILL, new MythicMobsSkillFunction.FactoryImpl<>(EventConditions::fromMap)); + register(CommonFunctions.TELEPORT, new TeleportFunction.FactoryImpl<>(EventConditions::fromMap)); + register(CommonFunctions.SET_VARIABLE, new SetVariableFunction.FactoryImpl<>(EventConditions::fromMap)); + register(CommonFunctions.TOAST, new ToastFunction.FactoryImpl<>(EventConditions::fromMap)); + register(CommonFunctions.DAMAGE, new DamageFunction.FactoryImpl<>(EventConditions::fromMap)); } public static void register(Key key, FunctionFactory factory) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/AbstractConditionalFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/AbstractConditionalFunction.java index 44d22ba1a..4c20d65a7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/AbstractConditionalFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/AbstractConditionalFunction.java @@ -2,7 +2,6 @@ package net.momirealms.craftengine.core.plugin.context.function; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; -import net.momirealms.craftengine.core.util.MCUtils; import net.momirealms.craftengine.core.util.MiscUtils; import java.util.ArrayList; @@ -16,7 +15,7 @@ public abstract class AbstractConditionalFunction implement public AbstractConditionalFunction(List> predicates) { this.predicates = predicates; - this.compositePredicates = MCUtils.allOf(predicates); + this.compositePredicates = MiscUtils.allOf(predicates); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ActionBarFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ActionBarFunction.java index 9f3781528..bcd5041cb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ActionBarFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ActionBarFunction.java @@ -5,8 +5,6 @@ import net.momirealms.craftengine.core.plugin.context.*; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelector; import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors; -import net.momirealms.craftengine.core.plugin.context.text.TextProvider; -import net.momirealms.craftengine.core.plugin.context.text.TextProviders; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -16,12 +14,12 @@ import java.util.List; import java.util.Map; public class ActionBarFunction extends AbstractConditionalFunction { - private final TextProvider message; + private final String message; private final PlayerSelector selector; - public ActionBarFunction(List> predicates, @Nullable PlayerSelector selector, TextProvider messages) { + public ActionBarFunction(List> predicates, @Nullable PlayerSelector selector, String message) { super(predicates); - this.message = messages; + this.message = message; this.selector = selector; } @@ -29,12 +27,12 @@ public class ActionBarFunction extends AbstractConditionalF public void runInternal(CTX ctx) { if (this.selector == null) { ctx.getOptionalParameter(DirectContextParameters.PLAYER).ifPresent(it -> { - it.sendActionBar(AdventureHelper.miniMessage().deserialize(this.message.get(ctx), ctx.tagResolvers())); + it.sendActionBar(AdventureHelper.miniMessage().deserialize(this.message, ctx.tagResolvers())); }); } else { for (Player viewer : this.selector.get(ctx)) { - RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer, ContextHolder.EMPTY)); - viewer.sendActionBar(AdventureHelper.miniMessage().deserialize(this.message.get(relationalContext), relationalContext.tagResolvers())); + RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer)); + viewer.sendActionBar(AdventureHelper.miniMessage().deserialize(this.message, relationalContext.tagResolvers())); } } } @@ -53,7 +51,7 @@ public class ActionBarFunction extends AbstractConditionalF @Override public Function create(Map arguments) { String message = ResourceConfigUtils.requireNonEmptyStringOrThrow(ResourceConfigUtils.get(arguments, "actionbar", "message"), "warning.config.function.actionbar.missing_actionbar"); - return new ActionBarFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), TextProviders.fromString(message)); + return new ActionBarFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), message); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/BreakBlockFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/BreakBlockFunction.java index 025cda880..268f23984 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/BreakBlockFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/BreakBlockFunction.java @@ -7,7 +7,7 @@ import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; import java.util.List; import java.util.Map; @@ -28,7 +28,7 @@ public class BreakBlockFunction extends AbstractConditional @Override public void runInternal(CTX ctx) { Optional optionalPlayer = ctx.getOptionalParameter(DirectContextParameters.PLAYER); - optionalPlayer.ifPresent(player -> player.breakBlock(MCUtils.fastFloor(x.getDouble(ctx)), MCUtils.fastFloor(y.getDouble(ctx)), MCUtils.fastFloor(z.getDouble(ctx)))); + optionalPlayer.ifPresent(player -> player.breakBlock(MiscUtils.fastFloor(x.getDouble(ctx)), MiscUtils.fastFloor(y.getDouble(ctx)), MiscUtils.fastFloor(z.getDouble(ctx)))); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommandFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommandFunction.java index 2447d64ff..fc6c360fa 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommandFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommandFunction.java @@ -45,7 +45,7 @@ public class CommandFunction extends AbstractConditionalFun )); } else { for (Player viewer : this.selector.get(ctx)) { - RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer, ContextHolder.EMPTY)); + RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer)); executeCommands(relationalContext, this.asEvent ? viewer::performCommandAsEvent : command1 -> viewer.performCommand(command1, this.asOp)); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java index bede6e946..56c9ab931 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java @@ -31,4 +31,8 @@ public final class CommonFunctions { public static final Key REMOVE_FURNITURE = Key.of("craftengine:remove_furniture"); public static final Key REPLACE_FURNITURE = Key.of("craftengine:replace_furniture"); public static final Key MYTHIC_MOBS_SKILL = Key.of("craftengine:mythic_mobs_skill"); + public static final Key TELEPORT = Key.of("craftengine:teleport"); + public static final Key TOAST = Key.of("craftengine:toast"); + public static final Key SET_VARIABLE = Key.of("craftengine:set_variable"); + public static final Key DAMAGE = Key.of("craftengine:damage"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java new file mode 100644 index 000000000..6ebb22d35 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java @@ -0,0 +1,51 @@ +package net.momirealms.craftengine.core.plugin.context.function; + +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; +import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelector; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.List; +import java.util.Map; + +public class DamageFunction extends AbstractConditionalFunction { + private final PlayerSelector selector; + private final Key damageType; + private final NumberProvider amount; + + public DamageFunction(PlayerSelector selector, Key damageType, NumberProvider amount, List> predicates) { + super(predicates); + this.selector = selector; + this.damageType = damageType; + this.amount = amount; + } + + @Override + protected void runInternal(CTX ctx) { + selector.get(ctx).forEach(p -> p.damage(amount.getDouble(ctx), damageType)); + } + + @Override + public Key type() { + return CommonFunctions.DAMAGE; + } + + public static class FactoryImpl extends AbstractFactory { + + public FactoryImpl(java.util.function.Function, Condition> factory) { + super(factory); + } + + @Override + public Function create(Map arguments) { + PlayerSelector selector = PlayerSelectors.fromObject(arguments.getOrDefault("target", "self"), conditionFactory()); + Key damageType = Key.of(ResourceConfigUtils.getAsString(arguments.getOrDefault("damage-type", "generic"))); + NumberProvider amount = NumberProviders.fromObject(arguments.getOrDefault("amount", 1f)); + return new DamageFunction<>(selector, damageType, amount, getPredicates(arguments)); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/LevelerExpFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/LevelerExpFunction.java index dd369f792..51316f239 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/LevelerExpFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/LevelerExpFunction.java @@ -36,7 +36,7 @@ public class LevelerExpFunction extends AbstractConditional }); } else { for (Player target : this.selector.get(ctx)) { - RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(target, ContextHolder.EMPTY)); + RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(target)); CraftEngine.instance().compatibilityManager().addLevelerExp(target, this.plugin, this.leveler, this.count.getDouble(relationalContext)); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MessageFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MessageFunction.java index 640d26b41..c69d3663e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MessageFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MessageFunction.java @@ -5,8 +5,6 @@ import net.momirealms.craftengine.core.plugin.context.*; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelector; import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors; -import net.momirealms.craftengine.core.plugin.context.text.TextProvider; -import net.momirealms.craftengine.core.plugin.context.text.TextProviders; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; @@ -17,11 +15,11 @@ import java.util.List; import java.util.Map; public class MessageFunction extends AbstractConditionalFunction { - private final List messages; + private final List messages; private final PlayerSelector selector; private final boolean overlay; - public MessageFunction(List> predicates, @Nullable PlayerSelector selector, List messages, boolean overlay) { + public MessageFunction(List> predicates, @Nullable PlayerSelector selector, List messages, boolean overlay) { super(predicates); this.messages = messages; this.selector = selector; @@ -32,15 +30,15 @@ public class MessageFunction extends AbstractConditionalFun public void runInternal(CTX ctx) { if (this.selector == null) { ctx.getOptionalParameter(DirectContextParameters.PLAYER).ifPresent(it -> { - for (TextProvider c : this.messages) { - it.sendMessage(AdventureHelper.miniMessage().deserialize(c.get(ctx), ctx.tagResolvers()), this.overlay); + for (String c : this.messages) { + it.sendMessage(AdventureHelper.miniMessage().deserialize(c, ctx.tagResolvers()), this.overlay); } }); } else { for (Player viewer : this.selector.get(ctx)) { - RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer, ContextHolder.EMPTY)); - for (TextProvider c : this.messages) { - viewer.sendMessage(AdventureHelper.miniMessage().deserialize(c.get(relationalContext), relationalContext.tagResolvers()), this.overlay); + RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer)); + for (String c : this.messages) { + viewer.sendMessage(AdventureHelper.miniMessage().deserialize(c, relationalContext.tagResolvers()), this.overlay); } } } @@ -60,7 +58,7 @@ public class MessageFunction extends AbstractConditionalFun @Override public Function create(Map arguments) { Object message = ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(arguments, "messages", "message"), "warning.config.function.command.missing_message"); - List messages = MiscUtils.getAsStringList(message).stream().map(TextProviders::fromString).toList(); + List messages = MiscUtils.getAsStringList(message); boolean overlay = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("overlay", false), "overlay"); return new MessageFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), messages, overlay); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/OpenWindowFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/OpenWindowFunction.java index 9799f63e2..bbe2fe3d0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/OpenWindowFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/OpenWindowFunction.java @@ -6,8 +6,6 @@ import net.momirealms.craftengine.core.plugin.context.*; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelector; import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors; -import net.momirealms.craftengine.core.plugin.context.text.TextProvider; -import net.momirealms.craftengine.core.plugin.context.text.TextProviders; import net.momirealms.craftengine.core.plugin.gui.GuiType; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.AdventureHelper; @@ -24,9 +22,9 @@ import java.util.Optional; public class OpenWindowFunction extends AbstractConditionalFunction { private final PlayerSelector selector; private final GuiType guiType; - private final TextProvider optionalTitle; + private final String optionalTitle; - public OpenWindowFunction(List> predicates, @Nullable PlayerSelector selector, GuiType guiType, TextProvider optionalTitle) { + public OpenWindowFunction(List> predicates, @Nullable PlayerSelector selector, GuiType guiType, String optionalTitle) { super(predicates); this.selector = selector; this.guiType = guiType; @@ -39,15 +37,15 @@ public class OpenWindowFunction extends AbstractConditional ctx.getOptionalParameter(DirectContextParameters.PLAYER).ifPresent(it -> { CraftEngine.instance().guiManager().openInventory(it, this.guiType); if (this.optionalTitle != null) { - CraftEngine.instance().guiManager().updateInventoryTitle(it, AdventureHelper.miniMessage().deserialize(this.optionalTitle.get(ctx), ctx.tagResolvers())); + CraftEngine.instance().guiManager().updateInventoryTitle(it, AdventureHelper.miniMessage().deserialize(this.optionalTitle, ctx.tagResolvers())); } }); } else { for (Player viewer : this.selector.get(ctx)) { CraftEngine.instance().guiManager().openInventory(viewer, this.guiType); if (this.optionalTitle != null) { - RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer, ContextHolder.EMPTY)); - CraftEngine.instance().guiManager().updateInventoryTitle(viewer, AdventureHelper.miniMessage().deserialize(this.optionalTitle.get(relationalContext), relationalContext.tagResolvers())); + RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer)); + CraftEngine.instance().guiManager().updateInventoryTitle(viewer, AdventureHelper.miniMessage().deserialize(this.optionalTitle, relationalContext.tagResolvers())); } } } @@ -66,7 +64,7 @@ public class OpenWindowFunction extends AbstractConditional @Override public Function create(Map arguments) { - TextProvider title = Optional.ofNullable(arguments.get("title")).map(String::valueOf).map(TextProviders::fromString).orElse(null); + String title = Optional.ofNullable(arguments.get("title")).map(String::valueOf).orElse(null); String rawType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("gui-type"), "warning.config.function.open_window.missing_gui_type"); try { GuiType type = GuiType.valueOf(rawType.toUpperCase(Locale.ENGLISH)); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ParticleFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ParticleFunction.java index c97d47bd3..4893d19f8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ParticleFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ParticleFunction.java @@ -1,115 +1,25 @@ package net.momirealms.craftengine.core.plugin.context.function; -import net.momirealms.craftengine.core.block.BlockStateWrapper; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; -import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; -import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; -import net.momirealms.craftengine.core.util.Color; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.LazyReference; -import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.world.Position; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.craftengine.core.world.particle.*; +import net.momirealms.craftengine.core.world.particle.ParticleConfig; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.function.Supplier; public class ParticleFunction extends AbstractConditionalFunction { - public static final Map, ParticleData>> DATA_TYPES = new HashMap<>(); + private final ParticleConfig config; - static { - registerParticleData(map -> new BlockStateData( - LazyReference.lazyReference(new Supplier<>() { - final String blockState = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("block-state"), "warning.config.function.particle.missing_block_state"); - @Override - public BlockStateWrapper get() { - return CraftEngine.instance().blockManager().createBlockState(this.blockState); - } - })), - ParticleTypes.BLOCK, ParticleTypes.FALLING_DUST, ParticleTypes.DUST_PILLAR, ParticleTypes.BLOCK_CRUMBLE, ParticleTypes.BLOCK_MARKER); - registerParticleData(map -> new ColorData( - Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("color"), "warning.config.function.particle.missing_color").split(","))), - ParticleTypes.ENTITY_EFFECT, ParticleTypes.TINTED_LEAVES); - registerParticleData(map -> new JavaTypeData( - ResourceConfigUtils.getAsFloat(map.get("charge"), "charge")), - ParticleTypes.SCULK_CHARGE); - registerParticleData(map -> new JavaTypeData( - ResourceConfigUtils.getAsInt(map.get("shriek"), "shriek")), - ParticleTypes.SHRIEK); - registerParticleData(map -> new DustData( - Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("color"), "warning.config.function.particle.missing_color").split(",")), - ResourceConfigUtils.getAsFloat(map.getOrDefault("scale", 1), "scale")), - ParticleTypes.DUST); - registerParticleData(map -> new DustTransitionData( - Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("from"), "warning.config.function.particle.missing_from").split(",")), - Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("to"), "warning.config.function.particle.missing_to").split(",")), - ResourceConfigUtils.getAsFloat(map.getOrDefault("scale", 1), "scale")), - ParticleTypes.DUST_COLOR_TRANSITION); - registerParticleData(map -> new ItemStackData( - LazyReference.lazyReference(new Supplier<>() { - final Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("item"), "warning.config.function.particle.missing_item")); - @Override - public Item get() { - return CraftEngine.instance().itemManager().createWrappedItem(this.itemId, null); - } - }) - ), - ParticleTypes.ITEM); - registerParticleData(map -> new VibrationData( - NumberProviders.fromObject(map.getOrDefault("target-x", 0)), - NumberProviders.fromObject(map.getOrDefault("target-y", 0)), - NumberProviders.fromObject(map.getOrDefault("target-z", 0)), - NumberProviders.fromObject(map.getOrDefault("arrival-time", 10))), - ParticleTypes.VIBRATION); - registerParticleData(map -> new TrailData( - NumberProviders.fromObject(map.getOrDefault("target-x", 0)), - NumberProviders.fromObject(map.getOrDefault("target-y", 0)), - NumberProviders.fromObject(map.getOrDefault("target-z", 0)), - Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("color"), "warning.config.function.particle.missing_color").split(",")), - NumberProviders.fromObject(map.getOrDefault("duration", 10))), - ParticleTypes.TRAIL); - } - - public static void registerParticleData(java.util.function.Function, ParticleData> function, Key... types) { - for (Key type : types) { - DATA_TYPES.put(type, function); - } - } - - private final Key particleType; - private final NumberProvider x; - private final NumberProvider y; - private final NumberProvider z; - private final NumberProvider count; - private final NumberProvider xOffset; - private final NumberProvider yOffset; - private final NumberProvider zOffset; - private final NumberProvider speed; - private final ParticleData particleData; - - public ParticleFunction(Key particleType, NumberProvider x, NumberProvider y, NumberProvider z, NumberProvider count, - NumberProvider xOffset, NumberProvider yOffset, NumberProvider zOffset, NumberProvider speed, ParticleData particleData, List> predicates) { + public ParticleFunction(ParticleConfig config, List> predicates) { super(predicates); - this.particleType = particleType; - this.count = count; - this.xOffset = xOffset; - this.yOffset = yOffset; - this.zOffset = zOffset; - this.speed = speed; - this.x = x; - this.y = y; - this.z = z; - this.particleData = particleData; + this.config = config; } @Override @@ -117,8 +27,8 @@ public class ParticleFunction extends AbstractConditionalFu Optional optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION); if (optionalWorldPosition.isPresent()) { World world = optionalWorldPosition.get().world(); - Position position = new Vec3d(this.x.getDouble(ctx), this.y.getDouble(ctx), this.z.getDouble(ctx)); - world.spawnParticle(position, this.particleType, this.count.getInt(ctx), this.xOffset.getDouble(ctx), this.yOffset.getDouble(ctx), this.zOffset.getDouble(ctx), this.speed.getDouble(ctx), this.particleData, ctx); + Position position = new Vec3d(config.x.getDouble(ctx), config.y.getDouble(ctx), config.z.getDouble(ctx)); + world.spawnParticle(position, config.particleType, config.count.getInt(ctx), config.xOffset.getDouble(ctx), config.yOffset.getDouble(ctx), config.zOffset.getDouble(ctx), config.speed.getDouble(ctx), config.particleData, ctx); } } @@ -135,17 +45,7 @@ public class ParticleFunction extends AbstractConditionalFu @Override public Function create(Map arguments) { - Key particleType = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("particle"), "warning.config.function.particle.missing_particle")); - NumberProvider x = NumberProviders.fromObject(arguments.getOrDefault("x", "")); - NumberProvider y = NumberProviders.fromObject(arguments.getOrDefault("y", "")); - NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "")); - NumberProvider count = NumberProviders.fromObject(arguments.getOrDefault("count", 1)); - NumberProvider xOffset = NumberProviders.fromObject(arguments.getOrDefault("offset-x", 0)); - NumberProvider yOffset = NumberProviders.fromObject(arguments.getOrDefault("offset-y", 0)); - NumberProvider zOffset = NumberProviders.fromObject(arguments.getOrDefault("offset-z", 0)); - NumberProvider speed = NumberProviders.fromObject(arguments.getOrDefault("speed", 0)); - return new ParticleFunction<>(particleType, x, y, z, count, xOffset, yOffset, zOffset, speed, - Optional.ofNullable(ParticleFunction.DATA_TYPES.get(particleType)).map(it -> it.apply(arguments)).orElse(null), getPredicates(arguments)); + return new ParticleFunction<>(ParticleConfig.fromMap$function(arguments), getPredicates(arguments)); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlaceBlockFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlaceBlockFunction.java index 15167ef06..3068766eb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlaceBlockFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlaceBlockFunction.java @@ -10,7 +10,7 @@ import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.LazyReference; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldPosition; @@ -40,7 +40,7 @@ public class PlaceBlockFunction extends AbstractConditional Optional optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION); if (optionalWorldPosition.isPresent()) { World world = optionalWorldPosition.get().world(); - world.setBlockAt(MCUtils.fastFloor(this.x.getDouble(ctx)), MCUtils.fastFloor(this.y.getDouble(ctx)), MCUtils.fastFloor(this.z.getDouble(ctx)), this.lazyBlockState.get(), this.updateFlags.getInt(ctx)); + world.setBlockAt(MiscUtils.fastFloor(this.x.getDouble(ctx)), MiscUtils.fastFloor(this.y.getDouble(ctx)), MiscUtils.fastFloor(this.z.getDouble(ctx)), this.lazyBlockState.get(), this.updateFlags.getInt(ctx)); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PotionEffectFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PotionEffectFunction.java index 0b91844ee..fa25af75f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PotionEffectFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PotionEffectFunction.java @@ -39,7 +39,7 @@ public class PotionEffectFunction extends AbstractCondition }); } else { for (Player target : this.selector.get(ctx)) { - RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(target, ContextHolder.EMPTY)); + RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(target)); target.addPotionEffect(this.potionEffectType, this.duration.getInt(relationalContext), this.amplifier.getInt(relationalContext), this.ambient, this.particles); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RunFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RunFunction.java index 801e13667..020c44fe7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RunFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RunFunction.java @@ -7,7 +7,7 @@ import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.WorldPosition; @@ -48,7 +48,7 @@ public class RunFunction extends AbstractConditionalFunctio for (Function function : functions) { function.run(ctx); } - }, delay, pos.world().platformWorld(), MCUtils.fastFloor(pos.x()) >> 4, MCUtils.fastFloor(pos.z()) >> 4); + }, delay, pos.world().platformWorld(), MiscUtils.fastFloor(pos.x()) >> 4, MiscUtils.fastFloor(pos.z()) >> 4); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetCooldownFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetCooldownFunction.java index 8019f8717..5634d5f19 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetCooldownFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetCooldownFunction.java @@ -41,7 +41,7 @@ public class SetCooldownFunction extends AbstractConditiona }); } else { for (Player target : this.selector.get(ctx)) { - RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(target, ContextHolder.EMPTY)); + RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(target)); long millis = TimeUtils.parseToMillis(this.time.get(relationalContext)); CooldownData data = target.cooldown(); if (this.add) data.addCooldown(this.id, millis); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetFoodFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetFoodFunction.java index 70a1e3f6e..544a2a147 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetFoodFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetFoodFunction.java @@ -33,7 +33,7 @@ public class SetFoodFunction extends AbstractConditionalFun optionalPlayer.ifPresent(player -> player.setFoodLevel(this.add ? player.foodLevel() + this.count.getInt(ctx) : this.count.getInt(ctx))); } else { for (Player target : this.selector.get(ctx)) { - RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(target, ContextHolder.EMPTY)); + RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(target)); target.setFoodLevel(this.add ? target.foodLevel() + this.count.getInt(relationalContext) : this.count.getInt(relationalContext)); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetSaturationFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetSaturationFunction.java index 6b002aedd..9c6d24e98 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetSaturationFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetSaturationFunction.java @@ -33,7 +33,7 @@ public class SetSaturationFunction extends AbstractConditio optionalPlayer.ifPresent(player -> player.setSaturation(this.add ? player.saturation() + this.count.getFloat(ctx) : this.count.getFloat(ctx))); } else { for (Player target : this.selector.get(ctx)) { - RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(target, ContextHolder.EMPTY)); + RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(target)); target.setSaturation(this.add ? target.saturation() + this.count.getFloat(relationalContext) : this.count.getFloat(relationalContext)); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetVariableFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetVariableFunction.java new file mode 100644 index 000000000..4d8c47419 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetVariableFunction.java @@ -0,0 +1,72 @@ +package net.momirealms.craftengine.core.plugin.context.function; + +import com.mojang.datafixers.util.Either; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.ContextHolder; +import net.momirealms.craftengine.core.plugin.context.ContextKey; +import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; +import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; +import net.momirealms.craftengine.core.plugin.context.text.TextProvider; +import net.momirealms.craftengine.core.plugin.context.text.TextProviders; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.List; +import java.util.Map; + +public class SetVariableFunction extends AbstractConditionalFunction { + private final Either either; + private final String variableName; + private final boolean asInt; + + public SetVariableFunction(List> predicates, String variableName, Either either, boolean asInt) { + super(predicates); + this.either = either; + this.variableName = variableName; + this.asInt = asInt; + } + + @Override + public void runInternal(CTX ctx) { + ContextHolder contexts = ctx.contexts(); + if (contexts.immutable()) return; + this.either.ifLeft(text -> contexts.withParameter(ContextKey.direct("var_" + this.variableName), text.get(ctx))) + .ifRight(number -> contexts.withParameter(ContextKey.direct("var_" + this.variableName), asInt ? number.getInt(ctx) : number.getDouble(ctx))); + } + + @Override + public Key type() { + return CommonFunctions.SET_VARIABLE; + } + + public static class FactoryImpl extends AbstractFactory { + + public FactoryImpl(java.util.function.Function, Condition> factory) { + super(factory); + } + + @Override + public Function create(Map arguments) { + String variableName = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("name"), "warning.config.function.set_variable.missing_name"); + if (arguments.containsKey("number")) { + return new SetVariableFunction<>( + getPredicates(arguments), + variableName, + Either.right(NumberProviders.fromObject(arguments.get("number"))), + ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("as-int", false), "as-int") + ); + } else if (arguments.containsKey("text")) { + return new SetVariableFunction<>( + getPredicates(arguments), + variableName, + Either.left(TextProviders.fromString(arguments.get("text").toString())), + false + ); + } else { + throw new LocalizedResourceConfigException("warning.config.function.set_variable.missing_value"); + } + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SpawnFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SpawnFurnitureFunction.java index 66b527272..39a879c3e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SpawnFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SpawnFurnitureFunction.java @@ -88,8 +88,8 @@ public class SpawnFurnitureFunction extends AbstractConditi NumberProvider x = NumberProviders.fromObject(arguments.getOrDefault("x", "")); NumberProvider y = NumberProviders.fromObject(arguments.getOrDefault("y", "")); NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "")); - NumberProvider pitch = NumberProviders.fromObject(arguments.getOrDefault("pitch", "")); - NumberProvider yaw = NumberProviders.fromObject(arguments.getOrDefault("yaw", "")); + NumberProvider pitch = NumberProviders.fromObject(arguments.getOrDefault("pitch", "")); + NumberProvider yaw = NumberProviders.fromObject(arguments.getOrDefault("yaw", "")); AnchorType anchorType = ResourceConfigUtils.getAsEnum(arguments.get("anchor-type"), AnchorType.class, null); boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound"); return new SpawnFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, anchorType, playSound, getPredicates(arguments)); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/TeleportFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/TeleportFunction.java new file mode 100644 index 000000000..f207afed2 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/TeleportFunction.java @@ -0,0 +1,97 @@ +package net.momirealms.craftengine.core.plugin.context.function; + +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.context.*; +import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; +import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelector; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors; +import net.momirealms.craftengine.core.plugin.context.text.TextProvider; +import net.momirealms.craftengine.core.plugin.context.text.TextProviders; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.WorldPosition; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class TeleportFunction extends AbstractConditionalFunction { + private final PlayerSelector selector; + @Nullable + private final TextProvider world; + private final NumberProvider x; + private final NumberProvider y; + private final NumberProvider z; + private final NumberProvider pitch; + private final NumberProvider yaw; + + public TeleportFunction(List> predicates, @Nullable PlayerSelector selector, @Nullable TextProvider world, + NumberProvider x, NumberProvider y, NumberProvider z, NumberProvider pitch, NumberProvider yaw) { + super(predicates); + this.selector = selector; + this.world = world; + this.x = x; + this.y = y; + this.z = z; + this.pitch = pitch; + this.yaw = yaw; + } + + @SuppressWarnings("DuplicatedCode") + @Override + public void runInternal(CTX ctx) { + if (this.selector == null) { + ctx.getOptionalParameter(DirectContextParameters.PLAYER).ifPresent(it -> it.teleport(new WorldPosition( + Optional.ofNullable(this.world).map(w -> w.get(ctx)).map(w -> CraftEngine.instance().platform().getWorld(w)).orElse(it.world()), + this.x.getDouble(ctx), + this.y.getDouble(ctx), + this.z.getDouble(ctx), + this.pitch.getFloat(ctx), + this.yaw.getFloat(ctx)) + )); + } else { + for (Player viewer : this.selector.get(ctx)) { + RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer)); + viewer.teleport(new WorldPosition( + Optional.ofNullable(this.world).map(w -> w.get(relationalContext)).map(w -> CraftEngine.instance().platform().getWorld(w)).orElse(viewer.world()), + this.x.getDouble(relationalContext), + this.y.getDouble(relationalContext), + this.z.getDouble(relationalContext), + this.pitch.getFloat(relationalContext), + this.yaw.getFloat(relationalContext)) + ); + } + } + } + + @Override + public Key type() { + return CommonFunctions.TELEPORT; + } + + public static class FactoryImpl extends AbstractFactory { + + public FactoryImpl(java.util.function.Function, Condition> factory) { + super(factory); + } + + @Override + public Function create(Map arguments) { + TextProvider world = Optional.ofNullable(arguments.get("world")).map(String::valueOf).map(TextProviders::fromString).orElse(null); + NumberProvider x = NumberProviders.fromObject(ResourceConfigUtils.requireNonNullOrThrow(arguments.get("x"), "warning.config.function.teleport.missing_x")); + NumberProvider y = NumberProviders.fromObject(ResourceConfigUtils.requireNonNullOrThrow(arguments.get("y"), "warning.config.function.teleport.missing_y")); + NumberProvider z = NumberProviders.fromObject(ResourceConfigUtils.requireNonNullOrThrow(arguments.get("z"), "warning.config.function.teleport.missing_z")); + NumberProvider yaw = NumberProviders.fromObject(arguments.getOrDefault("yaw", 0)); + NumberProvider pitch = NumberProviders.fromObject(arguments.getOrDefault("pitch", 0)); + return new TeleportFunction<>( + getPredicates(arguments), + PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), + world, x, y, z, pitch, yaw + ); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/TitleFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/TitleFunction.java index 43eb8d195..97a56b443 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/TitleFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/TitleFunction.java @@ -7,8 +7,6 @@ import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelector; import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors; -import net.momirealms.craftengine.core.plugin.context.text.TextProvider; -import net.momirealms.craftengine.core.plugin.context.text.TextProviders; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.Nullable; @@ -18,14 +16,14 @@ import java.util.Map; public class TitleFunction extends AbstractConditionalFunction { private final PlayerSelector selector; - private final TextProvider main; - private final TextProvider sub; + private final String main; + private final String sub; private final NumberProvider fadeIn; private final NumberProvider stay; private final NumberProvider fadeOut; public TitleFunction(List> predicates, @Nullable PlayerSelector selector, - TextProvider main, TextProvider sub, NumberProvider fadeIn, NumberProvider stay, NumberProvider fadeOut) { + String main, String sub, NumberProvider fadeIn, NumberProvider stay, NumberProvider fadeOut) { super(predicates); this.selector = selector; this.main = main; @@ -39,16 +37,16 @@ public class TitleFunction extends AbstractConditionalFunct public void runInternal(CTX ctx) { if (this.selector == null) { ctx.getOptionalParameter(DirectContextParameters.PLAYER).ifPresent(it -> it.sendTitle( - AdventureHelper.miniMessage().deserialize(this.main.get(ctx), ctx.tagResolvers()), - AdventureHelper.miniMessage().deserialize(this.sub.get(ctx), ctx.tagResolvers()), + AdventureHelper.miniMessage().deserialize(this.main, ctx.tagResolvers()), + AdventureHelper.miniMessage().deserialize(this.sub, ctx.tagResolvers()), this.fadeIn.getInt(ctx), this.stay.getInt(ctx), this.fadeOut.getInt(ctx) )); } else { for (Player viewer : this.selector.get(ctx)) { - RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer, ContextHolder.EMPTY)); + RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer)); viewer.sendTitle( - AdventureHelper.miniMessage().deserialize(this.main.get(relationalContext), relationalContext.tagResolvers()), - AdventureHelper.miniMessage().deserialize(this.sub.get(relationalContext), relationalContext.tagResolvers()), + AdventureHelper.miniMessage().deserialize(this.main, relationalContext.tagResolvers()), + AdventureHelper.miniMessage().deserialize(this.sub, relationalContext.tagResolvers()), this.fadeIn.getInt(relationalContext), this.stay.getInt(relationalContext), this.fadeOut.getInt(relationalContext) ); } @@ -73,8 +71,7 @@ public class TitleFunction extends AbstractConditionalFunct NumberProvider fadeIn = NumberProviders.fromObject(arguments.getOrDefault("fade-in", 10)); NumberProvider stay = NumberProviders.fromObject(arguments.getOrDefault("stay", 20)); NumberProvider fadeOut = NumberProviders.fromObject(arguments.getOrDefault("fade-out", 5)); - return new TitleFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), - TextProviders.fromString(title), TextProviders.fromString(subtitle), fadeIn, stay, fadeOut); + return new TitleFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), title, subtitle, fadeIn, stay, fadeOut); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ToastFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ToastFunction.java new file mode 100644 index 000000000..853cc7c28 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ToastFunction.java @@ -0,0 +1,83 @@ +package net.momirealms.craftengine.core.plugin.context.function; + +import net.momirealms.craftengine.core.advancement.AdvancementType; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.context.*; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelector; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.util.EnumUtils; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public class ToastFunction extends AbstractConditionalFunction { + private final PlayerSelector selector; + private final String toast; + private final java.util.function.Function> icon; + private final AdvancementType advancementType; + + public ToastFunction(List> predicates, + @Nullable PlayerSelector selector, + String toast, + java.util.function.Function> icon, + AdvancementType advancementType) { + super(predicates); + this.selector = selector; + this.toast = toast; + this.icon = icon; + this.advancementType = advancementType; + } + + @Override + public void runInternal(CTX ctx) { + if (this.selector == null) { + ctx.getOptionalParameter(DirectContextParameters.PLAYER).ifPresent(it -> it.sendToast(AdventureHelper.miniMessage().deserialize(this.toast, ctx.tagResolvers()), this.icon.apply(it), this.advancementType)); + } else { + for (Player viewer : this.selector.get(ctx)) { + RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer)); + viewer.sendToast(AdventureHelper.miniMessage().deserialize(this.toast, relationalContext.tagResolvers()), this.icon.apply(viewer), this.advancementType); + } + } + } + + @Override + public Key type() { + return CommonFunctions.TOAST; + } + + public static class FactoryImpl extends AbstractFactory { + + public FactoryImpl(java.util.function.Function, Condition> factory) { + super(factory); + } + + @Override + public Function create(Map arguments) { + AdvancementType advancementType; + String advancementName = arguments.getOrDefault("advancement-type", "goal").toString(); + try { + advancementType = AdvancementType.valueOf(advancementName.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException e) { + throw new LocalizedResourceConfigException("warning.config.function.toast.invalid_advancement_type", advancementName, EnumUtils.toString(AdvancementType.values())); + } + String toast = ResourceConfigUtils.requireNonEmptyStringOrThrow(ResourceConfigUtils.get(arguments, "toast", "message"), "warning.config.function.toast.missing_toast"); + Key item = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(ResourceConfigUtils.get(arguments, "item", "icon"), "warning.config.function.toast.missing_icon")); + return new ToastFunction<>( + getPredicates(arguments), + PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), + toast, + player -> CraftEngine.instance().itemManager().createWrappedItem(item, player), + advancementType + ); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/BinomialNumberProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/BinomialNumberProvider.java new file mode 100644 index 000000000..3d2306df8 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/BinomialNumberProvider.java @@ -0,0 +1,51 @@ +package net.momirealms.craftengine.core.plugin.context.number; + +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.RandomUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.Map; + +public record BinomialNumberProvider(NumberProvider trials, NumberProvider successProbability) implements NumberProvider { + public static final Factory FACTORY = new Factory(); + + @Override + public float getFloat(Context context) { + return getInt(context); + } + + @Override + public double getDouble(Context context) { + return getInt(context); + } + + @Override + public int getInt(Context context) { + int trialCount = this.trials.getInt(context); + float probability = this.successProbability.getFloat(context); + int successCount = 0; + + for (int i = 0; i < trialCount; i++) { + if (RandomUtils.generateRandomFloat(0, 1) < probability) { + successCount++; + } + } + return successCount; + } + + @Override + public Key type() { + return NumberProviders.BINOMIAL; + } + + public static class Factory implements NumberProviderFactory { + + @Override + public NumberProvider create(Map arguments) { + Object trials = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("extra"), "warning.config.number.binomial.missing_extra"); + Object successProbability = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("probability"), "warning.config.number.binomial.missing_probability"); + return new BinomialNumberProvider(NumberProviders.fromObject(trials), NumberProviders.fromObject(successProbability)); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/ExpressionNumberProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/ExpressionNumberProvider.java index 43bf33fcf..e00e459d6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/ExpressionNumberProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/ExpressionNumberProvider.java @@ -12,7 +12,7 @@ import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; public class ExpressionNumberProvider implements NumberProvider { - public static final FactoryImpl FACTORY = new FactoryImpl(); + public static final Factory FACTORY = new Factory(); private final String expr; public ExpressionNumberProvider(String expr) { @@ -52,7 +52,7 @@ public class ExpressionNumberProvider implements NumberProvider { return this.expr; } - public static class FactoryImpl implements NumberProviderFactory { + public static class Factory implements NumberProviderFactory { @Override public NumberProvider create(Map arguments) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/FixedNumberProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/FixedNumberProvider.java index d636f5268..5deeeb5a0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/FixedNumberProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/FixedNumberProvider.java @@ -9,7 +9,7 @@ import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; public class FixedNumberProvider implements NumberProvider { - public static final FactoryImpl FACTORY = new FactoryImpl(); + public static final Factory FACTORY = new Factory(); private final double value; public FixedNumberProvider(double value) { @@ -35,7 +35,7 @@ public class FixedNumberProvider implements NumberProvider { return new FixedNumberProvider(value); } - public static class FactoryImpl implements NumberProviderFactory { + public static class Factory implements NumberProviderFactory { @Override public NumberProvider create(Map arguments) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/GaussianNumberProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/GaussianNumberProvider.java index 6ca708f2b..4554a46ec 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/GaussianNumberProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/GaussianNumberProvider.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.plugin.context.number; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; @@ -10,8 +10,7 @@ import java.util.Random; import java.util.concurrent.ThreadLocalRandom; public class GaussianNumberProvider implements NumberProvider { - public static final FactoryImpl FACTORY = new FactoryImpl(); - + public static final Factory FACTORY = new Factory(); private final double min; private final double max; private final double mean; @@ -55,7 +54,7 @@ public class GaussianNumberProvider implements NumberProvider { } attempts++; } - return MCUtils.clamp(this.mean, this.min, this.max); + return MiscUtils.clamp(this.mean, this.min, this.max); } @Override @@ -83,7 +82,7 @@ public class GaussianNumberProvider implements NumberProvider { return stdDev; } - public static class FactoryImpl implements NumberProviderFactory { + public static class Factory implements NumberProviderFactory { @Override public NumberProvider create(Map arguments) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/NumberProviders.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/NumberProviders.java index 49ce77b1a..0b46ffb92 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/NumberProviders.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/NumberProviders.java @@ -18,6 +18,7 @@ public class NumberProviders { public static final Key UNIFORM = Key.of("craftengine:uniform"); public static final Key EXPRESSION = Key.of("craftengine:expression"); public static final Key GAUSSIAN = Key.of("craftengine:gaussian"); + public static final Key BINOMIAL = Key.of("craftengine:binomial"); static { register(FIXED, FixedNumberProvider.FACTORY); @@ -25,6 +26,7 @@ public class NumberProviders { register(UNIFORM, UniformNumberProvider.FACTORY); register(GAUSSIAN, GaussianNumberProvider.FACTORY); register(EXPRESSION, ExpressionNumberProvider.FACTORY); + register(BINOMIAL, BinomialNumberProvider.FACTORY); } public static void register(Key key, NumberProviderFactory factory) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/UniformNumberProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/UniformNumberProvider.java index dca387d0e..eb56593d0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/UniformNumberProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/UniformNumberProvider.java @@ -8,7 +8,7 @@ import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Map; public class UniformNumberProvider implements NumberProvider { - public static final FactoryImpl FACTORY = new FactoryImpl(); + public static final Factory FACTORY = new Factory(); private final NumberProvider min; private final NumberProvider max; @@ -45,7 +45,7 @@ public class UniformNumberProvider implements NumberProvider { return NumberProviders.UNIFORM; } - public static class FactoryImpl implements NumberProviderFactory { + public static class Factory implements NumberProviderFactory { @Override public NumberProvider create(Map arguments) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java index 465b49809..27529a7e5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java @@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.plugin.context.parameter; import net.momirealms.craftengine.core.entity.Entity; import net.momirealms.craftengine.core.plugin.context.ChainParameterProvider; import net.momirealms.craftengine.core.plugin.context.ContextKey; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; import java.util.HashMap; import java.util.Map; @@ -19,9 +19,9 @@ public class EntityParameterProvider implements ChainParameterProvider { CONTEXT_FUNCTIONS.put(DirectContextParameters.YAW, Entity::xRot); CONTEXT_FUNCTIONS.put(DirectContextParameters.PITCH, Entity::yRot); CONTEXT_FUNCTIONS.put(DirectContextParameters.POSITION, Entity::position); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MCUtils.fastFloor(p.x())); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MCUtils.fastFloor(p.y())); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MCUtils.fastFloor(p.z())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MiscUtils.fastFloor(p.x())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MiscUtils.fastFloor(p.y())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MiscUtils.fastFloor(p.z())); CONTEXT_FUNCTIONS.put(DirectContextParameters.NAME, Entity::name); CONTEXT_FUNCTIONS.put(DirectContextParameters.UUID, Entity::uuid); CONTEXT_FUNCTIONS.put(DirectContextParameters.WORLD, Entity::world); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PlayerParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PlayerParameterProvider.java index 25c3df127..ee6033ccd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PlayerParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PlayerParameterProvider.java @@ -5,7 +5,7 @@ import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.context.ChainParameterProvider; import net.momirealms.craftengine.core.plugin.context.ContextKey; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; import java.util.HashMap; import java.util.Map; @@ -21,9 +21,9 @@ public class PlayerParameterProvider implements ChainParameterProvider { CONTEXT_FUNCTIONS.put(DirectContextParameters.PITCH, Entity::xRot); CONTEXT_FUNCTIONS.put(DirectContextParameters.YAW, Entity::yRot); CONTEXT_FUNCTIONS.put(DirectContextParameters.POSITION, Entity::position); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MCUtils.fastFloor(p.x())); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MCUtils.fastFloor(p.y())); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MCUtils.fastFloor(p.z())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MiscUtils.fastFloor(p.x())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MiscUtils.fastFloor(p.y())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MiscUtils.fastFloor(p.z())); CONTEXT_FUNCTIONS.put(DirectContextParameters.FOOD, Player::foodLevel); CONTEXT_FUNCTIONS.put(DirectContextParameters.SATURATION, Player::saturation); CONTEXT_FUNCTIONS.put(DirectContextParameters.NAME, Player::name); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PositionParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PositionParameterProvider.java index b7e2b26b6..c646658fb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PositionParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PositionParameterProvider.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.plugin.context.parameter; import net.momirealms.craftengine.core.plugin.context.ChainParameterProvider; import net.momirealms.craftengine.core.plugin.context.ContextKey; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.world.WorldPosition; import java.util.HashMap; @@ -20,9 +20,9 @@ public class PositionParameterProvider implements ChainParameterProvider MCUtils.fastFloor(p.x())); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MCUtils.fastFloor(p.y())); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MCUtils.fastFloor(p.z())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MiscUtils.fastFloor(p.x())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MiscUtils.fastFloor(p.y())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MiscUtils.fastFloor(p.z())); } @SuppressWarnings("unchecked") diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/AllPlayerSelector.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/AllPlayerSelector.java index edd33b388..b8686e3db 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/AllPlayerSelector.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/selector/AllPlayerSelector.java @@ -8,7 +8,7 @@ import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; import java.util.ArrayList; import java.util.Arrays; @@ -21,7 +21,7 @@ public class AllPlayerSelector implements PlayerSelector predicate; public AllPlayerSelector(List> predicates) { - this.predicate = MCUtils.allOf(predicates); + this.predicate = MiscUtils.allOf(predicates); } public AllPlayerSelector() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependencies.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependencies.java index 37eae795e..5adccc541 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependencies.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependencies.java @@ -1,13 +1,9 @@ package net.momirealms.craftengine.core.plugin.dependency; -import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.dependency.relocation.Relocation; -import java.io.IOException; import java.util.Collections; import java.util.List; -import java.util.jar.JarFile; -import java.util.zip.ZipEntry; public class Dependencies { @@ -143,25 +139,6 @@ public class Dependencies { Collections.emptyList() ); - public static final Dependency SLF4J_API = new Dependency( - "slf4j-api", - "org.slf4j", - "slf4j-api", - Collections.emptyList() - ); - - public static final Dependency SLF4J_SIMPLE = new Dependency( - "slf4j-simple", - "org.slf4j", - "slf4j-simple", - Collections.emptyList() - ) { - @Override - public String getVersion() { - return Dependencies.SLF4J_API.getVersion(); - } - }; - public static final Dependency COMMONS_LANG3 = new Dependency( "commons-lang3", "org{}apache{}commons", @@ -180,16 +157,7 @@ public class Dependencies { "commons-imaging", "org{}apache{}commons", "commons-imaging", - List.of(Relocation.of("commons", "org{}apache{}commons")), - (p) -> { - try (JarFile jarFile = new JarFile(p.toFile())) { - ZipEntry entry = jarFile.getEntry("net/momirealms/craftengine/libraries/commons/imaging/Imaging.class"); - return entry != null; - } catch (IOException e) { - CraftEngine.instance().logger().warn("Error reading jar file", e); - return false; - } - } + List.of(Relocation.of("commons", "org{}apache{}commons")) ); public static final Dependency BYTE_BUDDY = new Dependency( @@ -224,7 +192,8 @@ public class Dependencies { "option", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ); public static final Dependency ADVENTURE_API = new Dependency( @@ -233,7 +202,8 @@ public class Dependencies { "adventure-api", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ); public static final Dependency ADVENTURE_NBT = new Dependency( @@ -242,7 +212,8 @@ public class Dependencies { "adventure-nbt", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ) { @Override public String getVersion() { @@ -256,7 +227,8 @@ public class Dependencies { "adventure-key", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ) { @Override public String getVersion() { @@ -270,7 +242,8 @@ public class Dependencies { "examination-api", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ); public static final Dependency EXAMINATION_STRING = new Dependency( @@ -279,7 +252,8 @@ public class Dependencies { "examination-string", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ) { @Override public String getVersion() { @@ -293,7 +267,8 @@ public class Dependencies { "adventure-text-minimessage", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ) { @Override public String getVersion() { @@ -307,7 +282,8 @@ public class Dependencies { "adventure-text-serializer-commons", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ) { @Override public String getVersion() { @@ -321,7 +297,8 @@ public class Dependencies { "adventure-text-serializer-gson", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ) { @Override public String getVersion() { @@ -335,7 +312,8 @@ public class Dependencies { "adventure-text-serializer-json-legacy-impl", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ) { @Override public String getVersion() { @@ -349,7 +327,8 @@ public class Dependencies { "adventure-text-serializer-legacy", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ) { @Override public String getVersion() { @@ -363,7 +342,8 @@ public class Dependencies { "adventure-text-serializer-json", List.of(Relocation.of("option", "net{}kyori{}option"), Relocation.of("examination", "net{}kyori{}examination"), - Relocation.of("adventure", "net{}kyori{}adventure")) + Relocation.of("adventure", "net{}kyori{}adventure")), + true ) { @Override public String getVersion() { @@ -392,32 +372,42 @@ public class Dependencies { List.of(Relocation.of("evalex", "com{}ezylang{}evalex")) ); + public static final Dependency JIMFS = new Dependency( + "jimfs", + "com{}google{}jimfs", + "jimfs", + List.of(Relocation.of("jimfs", "com{}google{}common{}jimfs")) + ); + public static final Dependency NETTY_HTTP = new Dependency( "netty-codec-http", "io{}netty", "netty-codec-http", - Collections.emptyList() + List.of( + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") + ) ); public static final Dependency NETTY_HTTP2 = new Dependency( "netty-codec-http2", "io{}netty", "netty-codec-http2", - Collections.emptyList() + List.of(Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2")) ); public static final Dependency REACTIVE_STREAMS = new Dependency( "reactive-streams", "org{}reactivestreams", "reactive-streams", - List.of(Relocation.of("reactivestreams", "org{}reactivestreams")) - ); - - public static final Dependency JIMFS = new Dependency( - "jimfs", - "com{}google{}jimfs", - "jimfs", - List.of(Relocation.of("jimfs", "com{}google{}common{}jimfs")) + List.of( + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") + ) ); public static final Dependency AMAZON_AWSSDK_S3 = new Dependency( @@ -426,7 +416,11 @@ public class Dependencies { "s3", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ); @@ -436,7 +430,11 @@ public class Dependencies { "netty-nio-client", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -451,7 +449,11 @@ public class Dependencies { "sdk-core", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -466,7 +468,11 @@ public class Dependencies { "auth", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -481,7 +487,11 @@ public class Dependencies { "regions", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -496,7 +506,11 @@ public class Dependencies { "identity-spi", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -511,7 +525,11 @@ public class Dependencies { "http-client-spi", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -526,7 +544,11 @@ public class Dependencies { "protocol-core", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -541,7 +563,11 @@ public class Dependencies { "aws-xml-protocol", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -556,7 +582,11 @@ public class Dependencies { "json-utils", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -571,7 +601,11 @@ public class Dependencies { "aws-core", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -586,7 +620,11 @@ public class Dependencies { "utils", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -601,7 +639,11 @@ public class Dependencies { "annotations", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -616,7 +658,11 @@ public class Dependencies { "crt-core", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -631,7 +677,11 @@ public class Dependencies { "checksums", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -646,7 +696,11 @@ public class Dependencies { "eventstream", List.of( Relocation.of("eventstream", "software{}amazon{}eventstream"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ); @@ -656,7 +710,11 @@ public class Dependencies { "profiles", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -671,7 +729,11 @@ public class Dependencies { "retries", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -686,7 +748,11 @@ public class Dependencies { "endpoints-spi", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -701,7 +767,11 @@ public class Dependencies { "arns", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -716,7 +786,11 @@ public class Dependencies { "aws-query-protocol", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -731,7 +805,11 @@ public class Dependencies { "http-auth-aws", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -746,7 +824,11 @@ public class Dependencies { "http-auth-spi", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -761,7 +843,11 @@ public class Dependencies { "http-auth", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -776,7 +862,11 @@ public class Dependencies { "http-auth-aws-eventstream", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -791,7 +881,11 @@ public class Dependencies { "checksums-spi", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -806,7 +900,11 @@ public class Dependencies { "retries-spi", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -821,7 +919,11 @@ public class Dependencies { "metrics-spi", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override @@ -836,7 +938,11 @@ public class Dependencies { "third-party-jackson-core", List.of( Relocation.of("awssdk", "software{}amazon{}awssdk"), - Relocation.of("reactivestreams", "org{}reactivestreams") + Relocation.of("reactivestreams", "org{}reactivestreams"), + Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"), + Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"), + Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"), + Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy") ) ) { @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependency.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependency.java index 893716bac..ef1713a5c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependency.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/Dependency.java @@ -15,21 +15,36 @@ public class Dependency { private final String rawArtifactId; private final List relocations; private final Predicate verifier; + private final boolean shared; public Dependency(String id, String groupId, String artifactId, List relocations) { + this(id, groupId, artifactId, relocations, false); + } + + public Dependency(String id, String groupId, String artifactId, List relocations, boolean shared) { this.id = id; this.groupId = groupId; this.rawArtifactId = artifactId; this.relocations = relocations; this.verifier = (p) -> true; + this.shared = shared; } public Dependency(String id, String groupId, String artifactId, List relocations, Predicate verifier) { + this(id, groupId, artifactId, relocations, verifier, false); + } + + public Dependency(String id, String groupId, String artifactId, List relocations, Predicate verifier, boolean shared) { this.id = id; this.groupId = groupId; this.rawArtifactId = artifactId; this.relocations = relocations; this.verifier = verifier; + this.shared = shared; + } + + public boolean shared() { + return shared; } public boolean verify(Path remapped) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/DependencyManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/DependencyManagerImpl.java index 886f830d2..5e8eeec28 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/DependencyManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/DependencyManagerImpl.java @@ -22,7 +22,8 @@ import java.util.stream.Stream; public class DependencyManagerImpl implements DependencyManager { private final DependencyRegistry registry; private final Path cacheDirectory; - private final ClassPathAppender classPathAppender; + private final ClassPathAppender sharedClassPathAppender; + private final ClassPathAppender privateClassPathAppender; private final Map loaded = Collections.synchronizedMap(new HashMap<>()); private final Map, IsolatedClassLoader> loaders = new HashMap<>(); private final RelocationHandler relocationHandler; @@ -33,7 +34,8 @@ public class DependencyManagerImpl implements DependencyManager { this.plugin = plugin; this.registry = new DependencyRegistry(); this.cacheDirectory = setupCacheDirectory(plugin); - this.classPathAppender = plugin.classPathAppender(); + this.sharedClassPathAppender = plugin.sharedClassPathAppender(); + this.privateClassPathAppender = plugin.privateClassPathAppender(); this.loadingExecutor = plugin.scheduler().async(); this.relocationHandler = new RelocationHandler(this); } @@ -108,8 +110,14 @@ public class DependencyManagerImpl implements DependencyManager { this.loaded.put(dependency, file); - if (this.classPathAppender != null && this.registry.shouldAutoLoad(dependency)) { - this.classPathAppender.addJarToClasspath(file); + if (dependency.shared()) { + if (this.sharedClassPathAppender != null && this.registry.shouldAutoLoad(dependency)) { + this.sharedClassPathAppender.addJarToClasspath(file); + } + } else { + if (this.privateClassPathAppender != null && this.registry.shouldAutoLoad(dependency)) { + this.privateClassPathAppender.addJarToClasspath(file); + } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/relocation/Relocation.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/relocation/Relocation.java index 2833d3f3d..10a29eda1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/relocation/Relocation.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/dependency/relocation/Relocation.java @@ -6,7 +6,7 @@ public final class Relocation { private static final String RELOCATION_PREFIX = "net.momirealms.craftengine.libraries."; public static Relocation of(String id, String pattern) { - return new Relocation(pattern.replace("{}", "."), RELOCATION_PREFIX + id); + return new Relocation(pattern.replace("{}", "."), RELOCATION_PREFIX + id.replace("{}", ".")); } private final String pattern; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/Category.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/Category.java index b5b139770..c4c18bb80 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/Category.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/Category.java @@ -26,9 +26,7 @@ public class Category implements Comparable { } public void addMember(String member) { - if (!this.members.contains(member)) { - this.members.add(member); - } + this.members.add(member); } public Key id() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java index fd5b95839..b4d6d30f4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java @@ -12,6 +12,7 @@ import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.config.IdSectionConfigParser; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.gui.*; @@ -95,7 +96,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { return Optional.ofNullable(this.byId.get(key)); } - public class CategoryParser implements ConfigParser { + public class CategoryParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"categories", "category"}; @Override @@ -109,12 +110,12 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { } @Override - public void parseSection(Pack pack, Path path, Key id, Map section) { + public void parseSection(Pack pack, Path path, String node, Key id, Map section) { String name = section.getOrDefault("name", id).toString(); List members = MiscUtils.getAsStringList(section.getOrDefault("list", List.of())); Key icon = Key.of(section.getOrDefault("icon", ItemKeys.STONE).toString()); int priority = ResourceConfigUtils.getAsInt(section.getOrDefault("priority", 0), "priority"); - Category category = new Category(id, name, MiscUtils.getAsStringList(section.getOrDefault("lore", List.of())), icon, members.stream().distinct().toList(), priority, + Category category = new Category(id, name, MiscUtils.getAsStringList(section.getOrDefault("lore", List.of())), icon, new ArrayList<>(members), priority, ResourceConfigUtils.getAsBoolean(section.getOrDefault("hidden", false), "hidden")); if (ItemBrowserManagerImpl.this.byId.containsKey(id)) { ItemBrowserManagerImpl.this.byId.get(id).merge(category); @@ -161,8 +162,8 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { this.plugin.logger().warn("Can't not find item " + it.icon() + " for category icon"); return null; } - item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.miniMessage().deserialize(it.displayName(), ItemBuildContext.EMPTY.tagResolvers()))); - item.loreJson(it.displayLore().stream().map(lore -> AdventureHelper.componentToJson(AdventureHelper.miniMessage().deserialize(lore, ItemBuildContext.EMPTY.tagResolvers()))).toList()); + item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.miniMessage().deserialize(it.displayName(), ItemBuildContext.EMPTY_RESOLVERS))); + item.loreJson(it.displayLore().stream().map(lore -> AdventureHelper.componentToJson(AdventureHelper.miniMessage().deserialize(lore, ItemBuildContext.EMPTY_RESOLVERS))).toList()); return new ItemWithAction(item, (element, click) -> { click.cancel(); player.playSound(Constants.SOUND_CLICK_BUTTON); @@ -240,21 +241,27 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { if (it.charAt(0) == '#') { String subCategoryId = it.substring(1); Category subCategory = this.byId.get(Key.of(subCategoryId)); - if (subCategory == null) return null; - Item item = this.plugin.itemManager().createWrappedItem(subCategory.icon(), player); - if (ItemUtils.isEmpty(item)) { - if (!subCategory.icon().equals(ItemKeys.AIR)) { - item = this.plugin.itemManager().createWrappedItem(ItemKeys.BARRIER, player); - item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.miniMessage().deserialize(subCategory.displayName(), ItemBuildContext.EMPTY.tagResolvers()))); - item.loreJson(subCategory.displayLore().stream().map(lore -> AdventureHelper.componentToJson(AdventureHelper.miniMessage().deserialize(lore, ItemBuildContext.EMPTY.tagResolvers()))).toList()); - } + Item item; + if (subCategory == null) { + item = Objects.requireNonNull(this.plugin.itemManager().createWrappedItem(ItemKeys.BARRIER, player)); + item.customNameJson(AdventureHelper.componentToJson(Component.text(subCategoryId).color(NamedTextColor.RED).decoration(TextDecoration.ITALIC, false))); } else { - item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.miniMessage().deserialize(subCategory.displayName(), ItemBuildContext.EMPTY.tagResolvers()))); - item.loreJson(subCategory.displayLore().stream().map(lore -> AdventureHelper.componentToJson(AdventureHelper.miniMessage().deserialize(lore, ItemBuildContext.EMPTY.tagResolvers()))).toList()); + item = this.plugin.itemManager().createWrappedItem(subCategory.icon(), player); + if (ItemUtils.isEmpty(item)) { + if (!subCategory.icon().equals(ItemKeys.AIR)) { + item = Objects.requireNonNull(this.plugin.itemManager().createWrappedItem(ItemKeys.BARRIER, player)); + item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.miniMessage().deserialize(subCategory.displayName(), ItemBuildContext.EMPTY_RESOLVERS))); + item.loreJson(subCategory.displayLore().stream().map(lore -> AdventureHelper.componentToJson(AdventureHelper.miniMessage().deserialize(lore, ItemBuildContext.EMPTY_RESOLVERS))).toList()); + } + } else { + item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.miniMessage().deserialize(subCategory.displayName(), ItemBuildContext.EMPTY_RESOLVERS))); + item.loreJson(subCategory.displayLore().stream().map(lore -> AdventureHelper.componentToJson(AdventureHelper.miniMessage().deserialize(lore, ItemBuildContext.EMPTY_RESOLVERS))).toList()); + } } return new ItemWithAction(item, (element, click) -> { click.cancel(); player.playSound(Constants.SOUND_CLICK_BUTTON); + if (subCategory == null) return; openCategoryPage(click.clicker(), subCategory.id(), element.gui(), canOpenNoRecipePage); }); } else { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/I18NData.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/I18NData.java index a87b62c10..4d8689096 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/I18NData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/I18NData.java @@ -26,7 +26,7 @@ public class I18NData { if (blockOptional.isPresent()) { List states = blockOptional.get().variantProvider().states(); if (states.size() == 1) { - return List.of("block." + stateToRealBlockId(states.get(0))); + return List.of("block." + stateToRealBlockId(states.getFirst())); } else { ArrayList processed = new ArrayList<>(); for (ImmutableBlockState state : states) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LocalizedResourceConfigException.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LocalizedResourceConfigException.java index 994e4c785..5017b6012 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LocalizedResourceConfigException.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LocalizedResourceConfigException.java @@ -37,7 +37,7 @@ public class LocalizedResourceConfigException extends LocalizedException { super.setArgument(0, path.toString()); } - public void setId(String id) { + public void setNode(String id) { super.setArgument(1, id); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java index d4ec4c773..7c95068a7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java @@ -50,8 +50,6 @@ public interface TranslationManager extends Manageable { return miniMessageTranslation(key, null); } - void forcedLocale(Locale locale); - String miniMessageTranslation(String key, @Nullable Locale locale); default Component render(Component component) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java index 041436ec6..98db56742 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java @@ -7,10 +7,10 @@ import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.Plugin; import net.momirealms.craftengine.core.plugin.PluginProperties; -import net.momirealms.craftengine.core.plugin.config.ConfigParser; -import net.momirealms.craftengine.core.plugin.config.StringKeyConstructor; -import net.momirealms.craftengine.core.plugin.config.TranslationConfigConstructor; +import net.momirealms.craftengine.core.plugin.config.*; +import net.momirealms.craftengine.core.plugin.text.minimessage.ImageTag; import net.momirealms.craftengine.core.plugin.text.minimessage.IndexedArgumentTag; +import net.momirealms.craftengine.core.plugin.text.minimessage.ShiftTag; import net.momirealms.craftengine.core.util.AdventureHelper; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -27,6 +27,7 @@ import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import java.util.stream.Collectors; public class TranslationManagerImpl implements TranslationManager { @@ -38,7 +39,6 @@ public class TranslationManagerImpl implements TranslationManager { private final String langVersion; private final String[] supportedLanguages; private final Map translationFallback = new LinkedHashMap<>(); - private Locale forcedLocale = null; private Locale selectedLocale = DEFAULT_LOCALE; private MiniMessageTranslationRegistry registry; private final Map clientLangData = new HashMap<>(); @@ -67,11 +67,6 @@ public class TranslationManagerImpl implements TranslationManager { return new ConfigParser[] {this.langParser, this.i18nParser}; } - @Override - public void forcedLocale(Locale locale) { - this.forcedLocale = locale; - } - @Override public void delayedLoad() { this.clientLangData.values().forEach(I18NData::processTranslations); @@ -98,8 +93,8 @@ public class TranslationManagerImpl implements TranslationManager { } private void setSelectedLocale() { - if (this.forcedLocale != null) { - this.selectedLocale = forcedLocale; + if (Config.forcedLocale() != null) { + this.selectedLocale = Config.forcedLocale(); return; } @@ -251,7 +246,7 @@ public class TranslationManagerImpl implements TranslationManager { } } - public class I18NParser implements ConfigParser { + public class I18NParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"i18n", "internationalization", "translation", "translations"}; @Override @@ -265,10 +260,10 @@ public class TranslationManagerImpl implements TranslationManager { } @Override - public void parseSection(Pack pack, Path path, net.momirealms.craftengine.core.util.Key id, Map section) { + public void parseSection(Pack pack, Path path, String node, net.momirealms.craftengine.core.util.Key id, Map section) { Locale locale = TranslationManager.parseLocale(id.value()); if (locale == null) { - throw new LocalizedResourceConfigException("warning.config.i18n.unknown_locale", path, id); + throw new LocalizedResourceConfigException("warning.config.i18n.unknown_locale"); } Map bundle = new HashMap<>(); @@ -282,8 +277,12 @@ public class TranslationManagerImpl implements TranslationManager { } } - public class LangParser implements ConfigParser { + public class LangParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"lang", "language", "languages"}; + private final Function langProcessor = s -> { + Component deserialize = AdventureHelper.miniMessage().deserialize(AdventureHelper.legacyToMiniMessage(s), ShiftTag.INSTANCE, ImageTag.INSTANCE); + return AdventureHelper.getLegacy().serialize(deserialize); + }; @Override public int loadingSequence() { @@ -296,12 +295,12 @@ public class TranslationManagerImpl implements TranslationManager { } @Override - public void parseSection(Pack pack, Path path, net.momirealms.craftengine.core.util.Key id, Map section) { + public void parseSection(Pack pack, Path path, String node, net.momirealms.craftengine.core.util.Key id, Map section) { String langId = id.value().toLowerCase(Locale.ENGLISH); Map sectionData = section.entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, - entry -> String.valueOf(entry.getValue()) + entry -> this.langProcessor.apply(String.valueOf(entry.getValue())) )); TranslationManagerImpl.this.addClientTranslation(langId, sectionData); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/Debugger.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/Debugger.java index fba7f7fa9..49dcb3ef7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/Debugger.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/Debugger.java @@ -10,7 +10,9 @@ public enum Debugger { PACKET(Config::debugPacket), FURNITURE(Config::debugFurniture), RESOURCE_PACK(Config::debugResourcePack), - ITEM(Config::debugItem); + ITEM(Config::debugItem), + BLOCK(Config::debugBlock), + BLOCK_ENTITY(Config::debugBlockEntity); private final Supplier condition; @@ -26,7 +28,11 @@ public enum Debugger { public void warn(Supplier message, Throwable e) { if (this.condition.get()) { - CraftEngine.instance().logger().warn("[DEBUG] " + message.get(), e); + if (e != null) { + CraftEngine.instance().logger().warn("[DEBUG] " + message.get(), e); + } else { + CraftEngine.instance().logger().warn("[DEBUG] " + message.get()); + } } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/ModPacket.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/ModPacket.java new file mode 100644 index 000000000..9776a4fee --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/ModPacket.java @@ -0,0 +1,21 @@ +package net.momirealms.craftengine.core.plugin.network; + +import io.netty.buffer.ByteBuf; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkDecoder; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkMemberEncoder; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; +import net.momirealms.craftengine.core.util.ResourceKey; + +public interface ModPacket { + + ResourceKey> type(); + + default void handle(NetWorkUser user) { + } + + static NetworkCodec codec(NetworkMemberEncoder networkMemberEncoder, NetworkDecoder networkDecoder) { + return NetworkCodec.ofMember(networkMemberEncoder, networkDecoder); + } + +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java index 83661fd2f..501200a8a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java @@ -4,8 +4,9 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.plugin.Plugin; +import net.momirealms.craftengine.core.util.IntIdentityList; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.ChunkPos; +import net.momirealms.craftengine.core.world.chunk.ChunkStatus; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -30,11 +31,19 @@ public interface NetWorkUser { String name(); - void setName(String name); + boolean isNameVerified(); + + void setUnverifiedName(String name); + + void setVerifiedName(String name); UUID uuid(); - void setUUID(UUID uuid); + boolean isUUIDVerified(); + + void setUnverifiedUUID(UUID uuid); + + void setVerifiedUUID(UUID uuid); void sendPacket(Object packet, boolean immediately); @@ -78,9 +87,18 @@ public interface NetWorkUser { boolean shouldProcessFinishConfiguration(); - boolean isChunkTracked(ChunkPos chunkPos); + boolean isChunkTracked(long chunkPos); - void setChunkTrackStatus(ChunkPos chunkPos, boolean tracked); + ChunkStatus getTrackedChunk(long chunkPos); + + void addTrackedChunk(long chunkPos, ChunkStatus chunkStatus); void clearTrackedChunks(); + + void removeTrackedChunk(long chunkPos); + + @Nullable + IntIdentityList clientBlockList(); + + void setClientBlockList(IntIdentityList integers); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java index 808dd56eb..2db50e789 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java @@ -22,6 +22,8 @@ public interface NetworkManager extends Manageable { Channel getChannel(Player player); + int remapBlockState(int stateId, boolean enableMod); + Player[] onlineUsers(); default void sendPacket(@NotNull NetWorkUser player, Object packet) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/PacketFlow.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/PacketFlow.java new file mode 100644 index 000000000..2311d2fad --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/PacketFlow.java @@ -0,0 +1,6 @@ +package net.momirealms.craftengine.core.plugin.network; + +public enum PacketFlow { + SERVERBOUND, + CLIENTBOUND; +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkCodec.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkCodec.java new file mode 100644 index 000000000..356ee14f7 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkCodec.java @@ -0,0 +1,32 @@ +package net.momirealms.craftengine.core.plugin.network.codec; + +import java.util.function.Function; + +public interface NetworkCodec extends NetworkEncoder, NetworkDecoder { + + default NetworkCodec map(Function factory, Function getter) { + return new NetworkCodec<>() { + @Override + public V decode(B in) { + return factory.apply(NetworkCodec.this.decode(in)); + } + + @Override + public void encode(B out, V value) { + NetworkCodec.this.encode(out, getter.apply(value)); + } + }; + } + + static NetworkCodec ofMember(final NetworkMemberEncoder networkMemberEncoder, final NetworkDecoder networkDecoder) { + return new NetworkCodec<>() { + public V decode(B in) { + return networkDecoder.decode(in); + } + + public void encode(B out, V value) { + networkMemberEncoder.encode(value, out); + } + }; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodecs.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkCodecs.java similarity index 87% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodecs.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkCodecs.java index 4642578bc..a75ebda9d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodecs.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkCodecs.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.bukkit.plugin.network.payload; +package net.momirealms.craftengine.core.plugin.network.codec; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; @@ -6,7 +6,7 @@ import io.netty.buffer.ByteBufOutputStream; import io.netty.buffer.ByteBufUtil; import io.netty.handler.codec.DecoderException; import io.netty.handler.codec.EncoderException; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.NBT; import net.momirealms.sparrow.nbt.Tag; @@ -21,9 +21,9 @@ import java.util.OptionalInt; /** * 随便写了点方便后面重构和客户端通讯 */ -public interface NetWorkCodecs { +public interface NetworkCodecs { - NetWorkCodec BOOLEAN = new NetWorkCodec<>() { + NetworkCodec BOOLEAN = new NetworkCodec<>() { @Override public Boolean decode(ByteBuf in) { return in.readBoolean(); @@ -35,7 +35,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec BYTE = new NetWorkCodec<>() { + NetworkCodec BYTE = new NetworkCodec<>() { @Override public Byte decode(ByteBuf in) { return in.readByte(); @@ -47,9 +47,9 @@ public interface NetWorkCodecs { } }; - NetWorkCodec ROTATION_BYTE = BYTE.map(MCUtils::unpackDegrees, MCUtils::packDegrees); + NetworkCodec ROTATION_BYTE = BYTE.map(MiscUtils::unpackDegrees, MiscUtils::packDegrees); - NetWorkCodec SHORT = new NetWorkCodec<>() { + NetworkCodec SHORT = new NetworkCodec<>() { @Override public Short decode(ByteBuf in) { return in.readShort(); @@ -61,7 +61,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec UNSIGNED_SHORT = new NetWorkCodec<>() { + NetworkCodec UNSIGNED_SHORT = new NetworkCodec<>() { @Override public Integer decode(ByteBuf in) { return in.readUnsignedShort(); @@ -73,7 +73,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec INTEGER = new NetWorkCodec<>() { + NetworkCodec INTEGER = new NetworkCodec<>() { @Override public Integer decode(ByteBuf in) { return in.readInt(); @@ -85,7 +85,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec VAR_INTEGER = new NetWorkCodec<>() { + NetworkCodec VAR_INTEGER = new NetworkCodec<>() { @Override public Integer decode(ByteBuf in) { int result = 0; @@ -111,12 +111,12 @@ public interface NetWorkCodecs { } }; - NetWorkCodec OPTIONAL_VAR_INTEGER = VAR_INTEGER.map( + NetworkCodec OPTIONAL_VAR_INTEGER = VAR_INTEGER.map( integer -> integer == 0 ? OptionalInt.empty() : OptionalInt.of(integer - 1), optionalInt -> optionalInt.isPresent() ? optionalInt.getAsInt() + 1 : 0 ); - NetWorkCodec LONG = new NetWorkCodec<>() { + NetworkCodec LONG = new NetworkCodec<>() { @Override public Long decode(ByteBuf in) { return in.readLong(); @@ -128,7 +128,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec VAR_LONG = new NetWorkCodec<>() { + NetworkCodec VAR_LONG = new NetworkCodec<>() { @Override public Long decode(ByteBuf in) { long result = 0L; @@ -154,7 +154,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec FLOAT = new NetWorkCodec<>() { + NetworkCodec FLOAT = new NetworkCodec<>() { @Override public Float decode(ByteBuf in) { return in.readFloat(); @@ -166,7 +166,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec DOUBLE = new NetWorkCodec<>() { + NetworkCodec DOUBLE = new NetworkCodec<>() { @Override public Double decode(ByteBuf in) { return in.readDouble(); @@ -178,7 +178,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec BYTE_ARRAY = new NetWorkCodec<>() { + NetworkCodec BYTE_ARRAY = new NetworkCodec<>() { @Override public byte[] decode(ByteBuf in) { int maxSize = in.readableBytes(); @@ -199,7 +199,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec LONG_ARRAY = new NetWorkCodec<>() { + NetworkCodec LONG_ARRAY = new NetworkCodec<>() { @Override public long[] decode(ByteBuf in) { int arrayLength = VAR_INTEGER.decode(in); @@ -224,7 +224,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec STRING_UTF8 = new NetWorkCodec<>() { + NetworkCodec STRING_UTF8 = new NetworkCodec<>() { private static final int MAX_STRING_LENGTH = 32767; @Override @@ -273,7 +273,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec TAG = new NetWorkCodec<>() { + NetworkCodec TAG = new NetworkCodec<>() { @Override public Tag decode(ByteBuf in) { int initialIndex = in.readerIndex(); @@ -304,7 +304,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec COMPOUND_TAG = TAG.map(tag -> { + NetworkCodec COMPOUND_TAG = TAG.map(tag -> { if (tag instanceof CompoundTag compoundTag) { return compoundTag; } else { @@ -312,7 +312,7 @@ public interface NetWorkCodecs { } }, tag -> tag); - NetWorkCodec> OPTIONAL_COMPOUND_TAG = new NetWorkCodec<>() { + NetworkCodec> OPTIONAL_COMPOUND_TAG = new NetworkCodec<>() { @Override public Optional decode(ByteBuf in) { int initialIndex = in.readerIndex(); @@ -347,7 +347,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec VECTOR3F = new NetWorkCodec<>() { + NetworkCodec VECTOR3F = new NetworkCodec<>() { @Override public Vector3f decode(ByteBuf in) { return new Vector3f(in.readFloat(), in.readFloat(), in.readFloat()); @@ -361,7 +361,7 @@ public interface NetWorkCodecs { } }; - NetWorkCodec QUATERNIONF = new NetWorkCodec<>() { + NetworkCodec QUATERNIONF = new NetworkCodec<>() { @Override public Quaternionf decode(ByteBuf in) { return new Quaternionf(in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat()); @@ -376,9 +376,9 @@ public interface NetWorkCodecs { } }; - NetWorkCodec CONTAINER_ID = VAR_INTEGER; + NetworkCodec CONTAINER_ID = VAR_INTEGER; - NetWorkCodec RGB_COLOR = new NetWorkCodec<>() { + NetworkCodec RGB_COLOR = new NetworkCodec<>() { @Override public Integer decode(ByteBuf in) { return 255 << 24 | in.readByte() & 0xFF << 16 | in.readByte() & 0xFF << 8 | in.readByte() & 0xFF; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkDecoder.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkDecoder.java new file mode 100644 index 000000000..b6e1bd5a8 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkDecoder.java @@ -0,0 +1,5 @@ +package net.momirealms.craftengine.core.plugin.network.codec; + +public interface NetworkDecoder { + T decode(I in); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkEncoder.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkEncoder.java new file mode 100644 index 000000000..0c79a9164 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkEncoder.java @@ -0,0 +1,5 @@ +package net.momirealms.craftengine.core.plugin.network.codec; + +public interface NetworkEncoder { + void encode(O out, T value); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkMemberEncoder.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkMemberEncoder.java new file mode 100644 index 000000000..0b96c1b7a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/codec/NetworkMemberEncoder.java @@ -0,0 +1,6 @@ +package net.momirealms.craftengine.core.plugin.network.codec; + +@FunctionalInterface +public interface NetworkMemberEncoder { + void encode(T object, O object2); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/ImageTag.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/ImageTag.java index 6ba90c439..e04d70f62 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/ImageTag.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/ImageTag.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.core.plugin.text.minimessage; -import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.Context; import net.kyori.adventure.text.minimessage.ParsingException; import net.kyori.adventure.text.minimessage.tag.Tag; @@ -12,7 +11,6 @@ import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.List; import java.util.Optional; public class ImageTag implements TagResolver { @@ -34,9 +32,9 @@ public class ImageTag implements TagResolver { if (arguments.hasNext()) { int row = arguments.popOr("No argument row provided").asInt().orElseThrow(() -> ctx.newException("Invalid argument number", arguments)); int column = arguments.popOr("No argument column provided").asInt().orElseThrow(() -> ctx.newException("Invalid argument number", arguments)); - return Tag.selfClosingInserting(Component.empty().children(List.of(optional.get().componentAt(row,column)))); + return Tag.selfClosingInserting(optional.get().componentAt(row,column)); } else { - return Tag.selfClosingInserting(Component.empty().children(List.of(optional.get().componentAt(0,0)))); + return Tag.selfClosingInserting(optional.get().componentAt(0,0)); } } else { throw ctx.newException("Invalid image id", arguments); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/PlaceholderTag.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/PlaceholderTag.java index 019d915e6..51a74c794 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/PlaceholderTag.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/PlaceholderTag.java @@ -6,7 +6,7 @@ import net.kyori.adventure.text.minimessage.tag.Tag; import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.PlayerContext; import net.momirealms.craftengine.core.util.AdventureHelper; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,7 +26,7 @@ public class PlaceholderTag implements TagResolver { String rawArgument = arguments.popOr("No argument relational placeholder provided").toString(); if (rawArgument.contains("<")) rawArgument = AdventureHelper.resolvePlainStringTags(rawArgument, this.context.tagResolvers()); String placeholder = "%" + rawArgument + "%"; - String parsed = this.context instanceof PlayerOptionalContext playerOptionalContext ? CraftEngine.instance().compatibilityManager().parse(playerOptionalContext.player(), placeholder) : CraftEngine.instance().compatibilityManager().parse(null, placeholder); + String parsed = this.context instanceof PlayerContext playerContext ? CraftEngine.instance().compatibilityManager().parse(playerContext.player(), placeholder) : CraftEngine.instance().compatibilityManager().parse(null, placeholder); if (parsed.equals(placeholder)) { parsed = arguments.popOr("No default papi value provided").toString(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/AbstractMappedRegistry.java b/core/src/main/java/net/momirealms/craftengine/core/registry/AbstractMappedRegistry.java index efefaa5ba..c095c0f32 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/AbstractMappedRegistry.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/AbstractMappedRegistry.java @@ -9,12 +9,15 @@ import java.util.*; public abstract class AbstractMappedRegistry implements WritableRegistry { protected final ResourceKey> key; - protected final Map> byResourceLocation = new HashMap<>(512); - protected final Map, Holder.Reference> byResourceKey = new HashMap<>(512); - protected final List> byId = new ArrayList<>(512); + protected final Map> byResourceLocation; + protected final Map, Holder.Reference> byResourceKey; + protected final List> byId; - protected AbstractMappedRegistry(ResourceKey> key) { + protected AbstractMappedRegistry(ResourceKey> key, int expectedSize) { this.key = key; + this.byResourceLocation = new HashMap<>(expectedSize); + this.byResourceKey = new HashMap<>(expectedSize); + this.byId = new ArrayList<>(expectedSize); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java index 806b20c22..d2dba3025 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java @@ -2,6 +2,8 @@ package net.momirealms.craftengine.core.registry; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.entity.BlockEntityType; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; import net.momirealms.craftengine.core.block.properties.PropertyFactory; import net.momirealms.craftengine.core.entity.furniture.HitBoxFactory; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; @@ -40,53 +42,59 @@ import net.momirealms.craftengine.core.plugin.context.condition.ConditionFactory import net.momirealms.craftengine.core.plugin.context.function.FunctionFactory; import net.momirealms.craftengine.core.plugin.context.number.NumberProviderFactory; import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectorFactory; +import net.momirealms.craftengine.core.plugin.network.ModPacket; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; import net.momirealms.craftengine.core.util.ResourceKey; public class BuiltInRegistries { - public static final Registry BLOCK = createDynamicBoundRegistry(Registries.BLOCK); - public static final Registry BLOCK_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.BLOCK_BEHAVIOR_FACTORY); - public static final Registry> ITEM_DATA_MODIFIER_FACTORY = createConstantBoundRegistry(Registries.ITEM_DATA_MODIFIER_FACTORY); - public static final Registry ITEM_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.ITEM_BEHAVIOR_FACTORY); - public static final Registry PROPERTY_FACTORY = createConstantBoundRegistry(Registries.PROPERTY_FACTORY); - public static final Registry> LOOT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.LOOT_FUNCTION_FACTORY); - public static final Registry> LOOT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.LOOT_CONDITION_FACTORY); - public static final Registry> LOOT_ENTRY_CONTAINER_FACTORY = createConstantBoundRegistry(Registries.LOOT_ENTRY_CONTAINER_FACTORY); - public static final Registry NUMBER_PROVIDER_FACTORY = createConstantBoundRegistry(Registries.NUMBER_PROVIDER_FACTORY); - public static final Registry TEMPLATE_ARGUMENT_FACTORY = createConstantBoundRegistry(Registries.TEMPLATE_ARGUMENT_FACTORY); - public static final Registry ITEM_MODEL_FACTORY = createConstantBoundRegistry(Registries.ITEM_MODEL_FACTORY); - public static final Registry ITEM_MODEL_READER = createConstantBoundRegistry(Registries.ITEM_MODEL_READER); - public static final Registry TINT_FACTORY = createConstantBoundRegistry(Registries.TINT_FACTORY); - public static final Registry TINT_READER = createConstantBoundRegistry(Registries.TINT_READER); - public static final Registry SPECIAL_MODEL_FACTORY = createConstantBoundRegistry(Registries.SPECIAL_MODEL_FACTORY); - public static final Registry SPECIAL_MODEL_READER = createConstantBoundRegistry(Registries.SPECIAL_MODEL_READER); - public static final Registry RANGE_DISPATCH_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_FACTORY); - public static final Registry RANGE_DISPATCH_PROPERTY_READER = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_READER); - public static final Registry CONDITION_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_FACTORY); - public static final Registry CONDITION_PROPERTY_READER = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_READER); - public static final Registry SELECT_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.SELECT_PROPERTY_FACTORY); - public static final Registry SELECT_PROPERTY_READER = createConstantBoundRegistry(Registries.SELECT_PROPERTY_READER); - public static final Registry>> RECIPE_SERIALIZER = createConstantBoundRegistry(Registries.RECIPE_FACTORY); - public static final Registry FORMULA_FACTORY = createConstantBoundRegistry(Registries.FORMULA_FACTORY); - public static final Registry> PATH_MATCHER_FACTORY = createConstantBoundRegistry(Registries.PATH_MATCHER_FACTORY); - public static final Registry RESOLUTION_FACTORY = createConstantBoundRegistry(Registries.RESOLUTION_FACTORY); - public static final Registry SMITHING_RESULT_PROCESSOR_FACTORY = createConstantBoundRegistry(Registries.SMITHING_RESULT_PROCESSOR_FACTORY); - public static final Registry HITBOX_FACTORY = createConstantBoundRegistry(Registries.HITBOX_FACTORY); - public static final Registry RESOURCE_PACK_HOST_FACTORY = createConstantBoundRegistry(Registries.RESOURCE_PACK_HOST_FACTORY); - public static final Registry> EVENT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.EVENT_FUNCTION_FACTORY); - public static final Registry> EVENT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.EVENT_CONDITION_FACTORY); - public static final Registry> PLAYER_SELECTOR_FACTORY = createConstantBoundRegistry(Registries.PLAYER_SELECTOR_FACTORY); - public static final Registry EQUIPMENT_FACTORY = createConstantBoundRegistry(Registries.EQUIPMENT_FACTORY); - public static final Registry SLOT_DISPLAY_TYPE = createConstantBoundRegistry(Registries.SLOT_DISPLAY_TYPE); - public static final Registry RECIPE_DISPLAY_TYPE = createConstantBoundRegistry(Registries.RECIPE_DISPLAY_TYPE); - public static final Registry LEGACY_RECIPE_TYPE = createConstantBoundRegistry(Registries.LEGACY_RECIPE_TYPE); - public static final Registry> RECIPE_POST_PROCESSOR_TYPE = createConstantBoundRegistry(Registries.RECIPE_POST_PROCESSOR_TYPE); - public static final Registry> ITEM_UPDATER_TYPE = createConstantBoundRegistry(Registries.ITEM_UPDATER_TYPE); + public static final Registry BLOCK = createDynamicBoundRegistry(Registries.BLOCK, 512); + public static final Registry BLOCK_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.BLOCK_BEHAVIOR_FACTORY, 64); + public static final Registry> ITEM_DATA_MODIFIER_FACTORY = createConstantBoundRegistry(Registries.ITEM_DATA_MODIFIER_FACTORY, 64); + public static final Registry ITEM_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.ITEM_BEHAVIOR_FACTORY, 64); + public static final Registry PROPERTY_FACTORY = createConstantBoundRegistry(Registries.PROPERTY_FACTORY, 16); + public static final Registry> LOOT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.LOOT_FUNCTION_FACTORY, 32); + public static final Registry> LOOT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.LOOT_CONDITION_FACTORY, 32); + public static final Registry> LOOT_ENTRY_CONTAINER_FACTORY = createConstantBoundRegistry(Registries.LOOT_ENTRY_CONTAINER_FACTORY, 16); + public static final Registry NUMBER_PROVIDER_FACTORY = createConstantBoundRegistry(Registries.NUMBER_PROVIDER_FACTORY, 16); + public static final Registry TEMPLATE_ARGUMENT_FACTORY = createConstantBoundRegistry(Registries.TEMPLATE_ARGUMENT_FACTORY, 16); + public static final Registry ITEM_MODEL_FACTORY = createConstantBoundRegistry(Registries.ITEM_MODEL_FACTORY, 16); + public static final Registry ITEM_MODEL_READER = createConstantBoundRegistry(Registries.ITEM_MODEL_READER, 16); + public static final Registry TINT_FACTORY = createConstantBoundRegistry(Registries.TINT_FACTORY, 16); + public static final Registry TINT_READER = createConstantBoundRegistry(Registries.TINT_READER, 16); + public static final Registry SPECIAL_MODEL_FACTORY = createConstantBoundRegistry(Registries.SPECIAL_MODEL_FACTORY, 16); + public static final Registry SPECIAL_MODEL_READER = createConstantBoundRegistry(Registries.SPECIAL_MODEL_READER, 16); + public static final Registry RANGE_DISPATCH_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_FACTORY, 16); + public static final Registry RANGE_DISPATCH_PROPERTY_READER = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_READER, 16); + public static final Registry CONDITION_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_FACTORY, 16); + public static final Registry CONDITION_PROPERTY_READER = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_READER, 16); + public static final Registry SELECT_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.SELECT_PROPERTY_FACTORY, 16); + public static final Registry SELECT_PROPERTY_READER = createConstantBoundRegistry(Registries.SELECT_PROPERTY_READER, 16); + public static final Registry>> RECIPE_SERIALIZER = createConstantBoundRegistry(Registries.RECIPE_FACTORY, 16); + public static final Registry FORMULA_FACTORY = createConstantBoundRegistry(Registries.FORMULA_FACTORY, 16); + public static final Registry> PATH_MATCHER_FACTORY = createConstantBoundRegistry(Registries.PATH_MATCHER_FACTORY, 16); + public static final Registry RESOLUTION_FACTORY = createConstantBoundRegistry(Registries.RESOLUTION_FACTORY, 16); + public static final Registry SMITHING_RESULT_PROCESSOR_FACTORY = createConstantBoundRegistry(Registries.SMITHING_RESULT_PROCESSOR_FACTORY, 16); + public static final Registry HITBOX_FACTORY = createConstantBoundRegistry(Registries.HITBOX_FACTORY, 16); + public static final Registry RESOURCE_PACK_HOST_FACTORY = createConstantBoundRegistry(Registries.RESOURCE_PACK_HOST_FACTORY, 16); + public static final Registry> EVENT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.EVENT_FUNCTION_FACTORY, 128); + public static final Registry> EVENT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.EVENT_CONDITION_FACTORY, 128); + public static final Registry> PLAYER_SELECTOR_FACTORY = createConstantBoundRegistry(Registries.PLAYER_SELECTOR_FACTORY, 16); + public static final Registry EQUIPMENT_FACTORY = createConstantBoundRegistry(Registries.EQUIPMENT_FACTORY, 8); + public static final Registry SLOT_DISPLAY_TYPE = createConstantBoundRegistry(Registries.SLOT_DISPLAY_TYPE, 16); + public static final Registry RECIPE_DISPLAY_TYPE = createConstantBoundRegistry(Registries.RECIPE_DISPLAY_TYPE, 16); + public static final Registry LEGACY_RECIPE_TYPE = createConstantBoundRegistry(Registries.LEGACY_RECIPE_TYPE, 16); + public static final Registry> RECIPE_POST_PROCESSOR_TYPE = createConstantBoundRegistry(Registries.RECIPE_POST_PROCESSOR_TYPE, 16); + public static final Registry> ITEM_UPDATER_TYPE = createConstantBoundRegistry(Registries.ITEM_UPDATER_TYPE, 16); + public static final Registry> MOD_PACKET = createConstantBoundRegistry(Registries.MOD_PACKET, 16); + public static final Registry> BLOCK_ENTITY_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_TYPE, 128); + public static final Registry BLOCK_ENTITY_ELEMENT_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_ELEMENT_TYPE, 16); - private static Registry createConstantBoundRegistry(ResourceKey> key) { - return new ConstantBoundRegistry<>(key); + private static Registry createConstantBoundRegistry(ResourceKey> key, int expectedSize) { + return new ConstantBoundRegistry<>(key, expectedSize); } - private static Registry createDynamicBoundRegistry(ResourceKey> key) { - return new DynamicBoundRegistry<>(key); + private static Registry createDynamicBoundRegistry(ResourceKey> key, int expectedSize) { + return new DynamicBoundRegistry<>(key, expectedSize); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/ConstantBoundRegistry.java b/core/src/main/java/net/momirealms/craftengine/core/registry/ConstantBoundRegistry.java index cf3da4675..974d1a01d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/ConstantBoundRegistry.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/ConstantBoundRegistry.java @@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.registry; import it.unimi.dsi.fastutil.objects.Reference2IntMap; import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceKey; import java.util.IdentityHashMap; @@ -11,11 +11,12 @@ import java.util.Map; import java.util.Objects; public class ConstantBoundRegistry extends AbstractMappedRegistry { - protected final Reference2IntMap toId = MCUtils.make(new Reference2IntOpenHashMap<>(), map -> map.defaultReturnValue(-1)); - protected final Map> byValue = new IdentityHashMap<>(512); + protected final Reference2IntMap toId = MiscUtils.init(new Reference2IntOpenHashMap<>(), map -> map.defaultReturnValue(-1)); + protected final Map> byValue; - public ConstantBoundRegistry(ResourceKey> key) { - super(key); + public ConstantBoundRegistry(ResourceKey> key, int expectedSize) { + super(key, expectedSize); + this.byValue = new IdentityHashMap<>(expectedSize); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/DynamicBoundRegistry.java b/core/src/main/java/net/momirealms/craftengine/core/registry/DynamicBoundRegistry.java index 47808d13f..fef205d22 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/DynamicBoundRegistry.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/DynamicBoundRegistry.java @@ -8,8 +8,8 @@ import java.util.Objects; public class DynamicBoundRegistry extends AbstractMappedRegistry { - public DynamicBoundRegistry(ResourceKey> key) { - super(key); + public DynamicBoundRegistry(ResourceKey> key, int expectedSize) { + super(key, expectedSize); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/Holder.java b/core/src/main/java/net/momirealms/craftengine/core/registry/Holder.java index b1e319931..c746856fa 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/Holder.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/Holder.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.registry; import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceKey; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; @@ -89,7 +90,7 @@ public interface Holder { } @Override - public String toString() { + public @NotNull String toString() { return "Direct{" + this.value + "}"; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java index d06a53394..dcf5fe8bc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java @@ -2,6 +2,8 @@ package net.momirealms.craftengine.core.registry; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.entity.BlockEntityType; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; import net.momirealms.craftengine.core.block.properties.PropertyFactory; import net.momirealms.craftengine.core.entity.furniture.HitBoxFactory; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; @@ -40,6 +42,9 @@ import net.momirealms.craftengine.core.plugin.context.condition.ConditionFactory import net.momirealms.craftengine.core.plugin.context.function.FunctionFactory; import net.momirealms.craftengine.core.plugin.context.number.NumberProviderFactory; import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectorFactory; +import net.momirealms.craftengine.core.plugin.network.ModPacket; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceKey; @@ -83,4 +88,7 @@ public class Registries { public static final ResourceKey> LEGACY_RECIPE_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("legacy_recipe_type")); public static final ResourceKey>> RECIPE_POST_PROCESSOR_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("recipe_post_processor_type")); public static final ResourceKey>> ITEM_UPDATER_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("item_updater_type")); + public static final ResourceKey>> MOD_PACKET = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("mod_packet_type")); + public static final ResourceKey>> BLOCK_ENTITY_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("block_entity_type")); + public static final ResourceKey> BLOCK_ENTITY_ELEMENT_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("block_entity_element_type")); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java b/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java index a0892da26..3c5886417 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java @@ -5,6 +5,7 @@ import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.config.IdSectionConfigParser; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.*; @@ -65,8 +66,8 @@ public abstract class AbstractSoundManager implements SoundManager { protected abstract void registerSounds(Collection sounds); - public class SongParser implements ConfigParser { - public static final String[] CONFIG_SECTION_NAME = new String[] {"jukebox_songs", "jukebox_song", "jukebox-songs", "jukebox-song"}; + public class SongParser extends IdSectionConfigParser { + public static final String[] CONFIG_SECTION_NAME = new String[] {"jukebox-songs", "jukebox-song", "jukebox_songs", "jukebox_song"}; @Override public int loadingSequence() { @@ -79,7 +80,7 @@ public abstract class AbstractSoundManager implements SoundManager { } @Override - public void parseSection(Pack pack, Path path, Key id, Map section) { + public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (AbstractSoundManager.this.songs.containsKey(id)) { throw new LocalizedResourceConfigException("warning.config.jukebox_song.duplicate"); } @@ -92,7 +93,7 @@ public abstract class AbstractSoundManager implements SoundManager { } } - public class SoundParser implements ConfigParser { + public class SoundParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"sounds", "sound"}; @Override @@ -106,7 +107,7 @@ public abstract class AbstractSoundManager implements SoundManager { } @Override - public void parseSection(Pack pack, Path path, Key id, Map section) { + public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (AbstractSoundManager.this.byId.containsKey(id)) { throw new LocalizedResourceConfigException("warning.config.sound.duplicate"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java index 949b5cf74..339c860ae 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java @@ -10,6 +10,7 @@ import java.util.Optional; import java.util.function.Supplier; public record SoundData(Key id, SoundValue volume, SoundValue pitch) { + public static final SoundData EMPTY = new SoundData(Key.of("minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_1); public static SoundData create(Object obj, SoundValue volume, SoundValue pitch) { if (obj instanceof String key) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundSet.java b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundSet.java new file mode 100644 index 000000000..409f9cd7b --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundSet.java @@ -0,0 +1,92 @@ +package net.momirealms.craftengine.core.sound; + +import net.momirealms.craftengine.core.block.BlockKeys; +import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public record SoundSet(List blocks, Key openSound, Key closeSound) { + // Trapdoor sound sets + public static final SoundSet WOODEN_TRAPDOOR = new SoundSet(BlockKeys.WOODEN_TRAPDOORS, Sounds.WOODEN_TRAPDOOR_OPEN, Sounds.WOODEN_TRAPDOOR_CLOSE); + public static final SoundSet NETHER_WOOD_TRAPDOOR = new SoundSet(BlockKeys.NETHER_TRAPDOORS, Sounds.NETHER_WOOD_TRAPDOOR_OPEN, Sounds.NETHER_WOOD_TRAPDOOR_CLOSE); + public static final SoundSet BAMBOO_WOOD_TRAPDOOR = new SoundSet(BlockKeys.BAMBOO_TRAPDOORS, Sounds.BAMBOO_WOOD_TRAPDOOR_OPEN, Sounds.BAMBOO_WOOD_TRAPDOOR_CLOSE); + public static final SoundSet CHERRY_WOOD_TRAPDOOR = new SoundSet(BlockKeys.CHERRY_TRAPDOORS, Sounds.CHERRY_WOOD_TRAPDOOR_OPEN, Sounds.CHERRY_WOOD_TRAPDOOR_CLOSE); + public static final SoundSet COPPER_TRAPDOOR = new SoundSet(BlockKeys.COPPER_TRAPDOORS, Sounds.COPPER_TRAPDOOR_OPEN, Sounds.COPPER_TRAPDOOR_CLOSE); + + // Door sound sets + public static final SoundSet WOODEN_DOOR = new SoundSet(BlockKeys.WOODEN_DOORS, Sounds.WOODEN_DOOR_OPEN, Sounds.WOODEN_DOOR_CLOSE); + public static final SoundSet NETHER_WOOD_DOOR = new SoundSet(BlockKeys.NETHER_DOORS, Sounds.NETHER_WOOD_DOOR_OPEN, Sounds.NETHER_WOOD_DOOR_CLOSE); + public static final SoundSet BAMBOO_WOOD_DOOR = new SoundSet(BlockKeys.BAMBOO_DOORS, Sounds.BAMBOO_WOOD_DOOR_OPEN, Sounds.BAMBOO_WOOD_DOOR_CLOSE); + public static final SoundSet CHERRY_WOOD_DOOR = new SoundSet(BlockKeys.CHERRY_DOORS, Sounds.CHERRY_WOOD_DOOR_OPEN, Sounds.CHERRY_WOOD_DOOR_CLOSE); + public static final SoundSet COPPER_DOOR = new SoundSet(BlockKeys.COPPER_DOORS, Sounds.COPPER_DOOR_OPEN, Sounds.COPPER_DOOR_CLOSE); + + // Button sound sets + public static final SoundSet WOODEN_BUTTON = new SoundSet(BlockKeys.WOODEN_BUTTONS, Sounds.WOODEN_BUTTON_CLICK_ON, Sounds.WOODEN_BUTTON_CLICK_OFF); + public static final SoundSet NETHER_WOOD_BUTTON = new SoundSet(BlockKeys.NETHER_BUTTONS, Sounds.NETHER_WOOD_BUTTON_CLICK_ON, Sounds.NETHER_WOOD_BUTTON_CLICK_OFF); + public static final SoundSet BAMBOO_WOOD_BUTTON = new SoundSet(BlockKeys.BAMBOO_BUTTONS, Sounds.BAMBOO_WOOD_BUTTON_CLICK_ON, Sounds.BAMBOO_WOOD_BUTTON_CLICK_OFF); + public static final SoundSet CHERRY_WOOD_BUTTON = new SoundSet(BlockKeys.CHERRY_BUTTONS, Sounds.CHERRY_WOOD_BUTTON_CLICK_ON, Sounds.CHERRY_WOOD_BUTTON_CLICK_OFF); + public static final SoundSet STONE_BUTTON = new SoundSet(BlockKeys.STONE_BUTTONS, Sounds.STONE_BUTTON_CLICK_ON, Sounds.STONE_BUTTON_CLICK_OFF); + + // Fence gate sound sets + public static final SoundSet WOODEN_FENCE_GATE = new SoundSet(BlockKeys.WOODEN_FENCE_GATES, Sounds.WOODEN_FENCE_GATE_OPEN, Sounds.WOODEN_FENCE_GATE_CLOSE); + public static final SoundSet NETHER_WOOD_FENCE_GATE = new SoundSet(BlockKeys.NETHER_FENCE_GATES, Sounds.NETHER_WOOD_FENCE_GATE_OPEN, Sounds.NETHER_WOOD_FENCE_GATE_CLOSE); + public static final SoundSet BAMBOO_WOOD_FENCE_GATE = new SoundSet(BlockKeys.BAMBOO_FENCE_GATES, Sounds.BAMBOO_WOOD_FENCE_GATE_OPEN, Sounds.BAMBOO_WOOD_FENCE_GATE_CLOSE); + public static final SoundSet CHERRY_WOOD_FENCE_GATE = new SoundSet(BlockKeys.CHERRY_FENCE_GATES, Sounds.CHERRY_WOOD_FENCE_GATE_OPEN, Sounds.CHERRY_WOOD_FENCE_GATE_CLOSE); + + // 获取所有声音集合的便捷方法 + public static List getAllSoundSets() { + return List.of( + // Trapdoors + WOODEN_TRAPDOOR, NETHER_WOOD_TRAPDOOR, BAMBOO_WOOD_TRAPDOOR, + CHERRY_WOOD_TRAPDOOR, COPPER_TRAPDOOR, + // Doors + WOODEN_DOOR, NETHER_WOOD_DOOR, BAMBOO_WOOD_DOOR, + CHERRY_WOOD_DOOR, COPPER_DOOR, + // Fence gates + WOODEN_FENCE_GATE, NETHER_WOOD_FENCE_GATE, + BAMBOO_WOOD_FENCE_GATE, CHERRY_WOOD_FENCE_GATE, + // Buttons + WOODEN_BUTTON, NETHER_WOOD_BUTTON, BAMBOO_WOOD_BUTTON, + CHERRY_WOOD_BUTTON, STONE_BUTTON + ); + } + + // 按类型获取声音集合的方法 + public static List getTrapdoorSoundSets() { + return List.of(WOODEN_TRAPDOOR, NETHER_WOOD_TRAPDOOR, BAMBOO_WOOD_TRAPDOOR, + CHERRY_WOOD_TRAPDOOR, COPPER_TRAPDOOR); + } + + public static List getDoorSoundSets() { + return List.of(WOODEN_DOOR, NETHER_WOOD_DOOR, BAMBOO_WOOD_DOOR, + CHERRY_WOOD_DOOR, COPPER_DOOR); + } + + public static List getFenceGateSoundSets() { + return List.of(WOODEN_FENCE_GATE, NETHER_WOOD_FENCE_GATE, + BAMBOO_WOOD_FENCE_GATE, CHERRY_WOOD_FENCE_GATE); + } + + public static List getButtonSoundSets() { + return List.of(WOODEN_BUTTON, NETHER_WOOD_BUTTON, BAMBOO_WOOD_BUTTON, + CHERRY_WOOD_BUTTON, STONE_BUTTON); + } + + private static final Map SOUND_SETS = new HashMap<>(); + + static { + for (SoundSet set : getAllSoundSets()) { + set.blocks.forEach(block -> { + SOUND_SETS.put(block, set); + }); + } + } + + @Nullable + public static SoundSet getByBlock(Key blockType) { + return SOUND_SETS.get(blockType); + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/sound/Sounds.java b/core/src/main/java/net/momirealms/craftengine/core/sound/Sounds.java index a5e6000df..252754c00 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/sound/Sounds.java +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/Sounds.java @@ -1,35 +1,10 @@ package net.momirealms.craftengine.core.sound; -import net.momirealms.craftengine.core.block.BlockKeys; import net.momirealms.craftengine.core.util.Key; -import java.util.List; - public final class Sounds { private Sounds() {} - public static final List WOODEN_TRAPDOORS = List.of(BlockKeys.OAK_TRAPDOOR, BlockKeys.SPRUCE_TRAPDOOR, BlockKeys.BIRCH_TRAPDOOR, - BlockKeys.ACACIA_TRAPDOOR, BlockKeys.PALE_OAK_TRAPDOOR, BlockKeys.DARK_OAK_TRAPDOOR, BlockKeys.MANGROVE_TRAPDOOR, BlockKeys.JUNGLE_TRAPDOOR); - public static final List CHERRY_TRAPDOORS = List.of(BlockKeys.CHERRY_TRAPDOOR); - public static final List BAMBOO_TRAPDOORS = List.of(BlockKeys.BAMBOO_TRAPDOOR); - public static final List NETHER_TRAPDOORS = List.of(BlockKeys.WARPED_TRAPDOOR, BlockKeys.CRIMSON_TRAPDOOR); - public static final List COPPER_TRAPDOORS = List.of(BlockKeys.COPPER_TRAPDOOR, BlockKeys.EXPOSED_COPPER_TRAPDOOR, BlockKeys.WEATHERED_COPPER_TRAPDOOR, BlockKeys.OXIDIZED_COPPER_DOOR, - BlockKeys.WAXED_COPPER_TRAPDOOR, BlockKeys.WAXED_EXPOSED_COPPER_TRAPDOOR, BlockKeys.WAXED_WEATHERED_COPPER_TRAPDOOR, BlockKeys.WAXED_OXIDIZED_COPPER_TRAPDOOR); - - public static final List WOODEN_DOORS = List.of(BlockKeys.OAK_DOOR, BlockKeys.SPRUCE_DOOR, BlockKeys.BIRCH_DOOR, - BlockKeys.ACACIA_DOOR, BlockKeys.PALE_OAK_DOOR, BlockKeys.DARK_OAK_DOOR, BlockKeys.MANGROVE_DOOR, BlockKeys.JUNGLE_DOOR); - public static final List CHERRY_DOORS = List.of(BlockKeys.CHERRY_DOOR); - public static final List BAMBOO_DOORS = List.of(BlockKeys.BAMBOO_DOOR); - public static final List NETHER_DOORS = List.of(BlockKeys.WARPED_DOOR, BlockKeys.CRIMSON_DOOR); - public static final List COPPER_DOORS = List.of(BlockKeys.COPPER_DOOR, BlockKeys.EXPOSED_COPPER_DOOR, BlockKeys.WEATHERED_COPPER_DOOR, BlockKeys.OXIDIZED_COPPER_DOOR, - BlockKeys.WAXED_COPPER_DOOR, BlockKeys.WAXED_EXPOSED_COPPER_DOOR, BlockKeys.WAXED_WEATHERED_COPPER_DOOR, BlockKeys.WAXED_OXIDIZED_COPPER_DOOR); - - public static final List WOODEN_FENCE_GATES = List.of(BlockKeys.OAK_FENCE_GATE, BlockKeys.SPRUCE_FENCE_GATE, BlockKeys.BIRCH_FENCE_GATE, - BlockKeys.ACACIA_FENCE_GATE, BlockKeys.PALE_OAK_FENCE_GATE, BlockKeys.DARK_OAK_FENCE_GATE, BlockKeys.MANGROVE_FENCE_GATE, BlockKeys.JUNGLE_FENCE_GATE); - public static final List CHERRY_FENCE_GATES = List.of(BlockKeys.CHERRY_FENCE_GATE); - public static final List BAMBOO_FENCE_GATES = List.of(BlockKeys.BAMBOO_FENCE_GATE); - public static final List NETHER_FENCE_GATES = List.of(BlockKeys.WARPED_FENCE_GATE, BlockKeys.CRIMSON_FENCE_GATE); - public static final Key WOODEN_TRAPDOOR_OPEN = Key.of("block.wooden_trapdoor.open"); public static final Key WOODEN_TRAPDOOR_CLOSE = Key.of("block.wooden_trapdoor.close"); public static final Key WOODEN_DOOR_OPEN = Key.of("block.wooden_door.open"); @@ -58,4 +33,14 @@ public final class Sounds { public static final Key COPPER_TRAPDOOR_CLOSE = Key.of("block.copper_trapdoor.close"); public static final Key COPPER_DOOR_OPEN = Key.of("block.copper_door.open"); public static final Key COPPER_DOOR_CLOSE = Key.of("block.copper_door.close"); + public static final Key STONE_BUTTON_CLICK_OFF = Key.of("block.stone_button.click_off"); + public static final Key STONE_BUTTON_CLICK_ON = Key.of("block.stone_button.click_on"); + public static final Key CHERRY_WOOD_BUTTON_CLICK_OFF = Key.of("block.cherry_wood_button.click_off"); + public static final Key CHERRY_WOOD_BUTTON_CLICK_ON = Key.of("block.cherry_wood_button.click_on"); + public static final Key NETHER_WOOD_BUTTON_CLICK_OFF = Key.of("block.nether_wood_button.click_off"); + public static final Key NETHER_WOOD_BUTTON_CLICK_ON = Key.of("block.nether_wood_button.click_on"); + public static final Key BAMBOO_WOOD_BUTTON_CLICK_OFF = Key.of("block.bamboo_wood_button.click_off"); + public static final Key BAMBOO_WOOD_BUTTON_CLICK_ON = Key.of("block.bamboo_wood_button.click_on"); + public static final Key WOODEN_BUTTON_CLICK_OFF = Key.of("block.wooden_button.click_off"); + public static final Key WOODEN_BUTTON_CLICK_ON = Key.of("block.wooden_button.click_on"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java b/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java index d24d8d4ca..c17579e65 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java @@ -1,9 +1,6 @@ package net.momirealms.craftengine.core.util; import com.google.gson.JsonElement; -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.key.Key; -import net.kyori.adventure.sound.Sound; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ComponentIteratorType; import net.kyori.adventure.text.TextComponent; @@ -13,6 +10,7 @@ import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.json.JSONOptions; import net.kyori.adventure.text.serializer.json.legacyimpl.NBTLegacyHoverEventSerializer; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.sparrow.nbt.Tag; @@ -33,6 +31,7 @@ public class AdventureHelper { private final MiniMessage miniMessageCustom; private final GsonComponentSerializer gsonComponentSerializer; private final NBTComponentSerializer nbtComponentSerializer; + private final LegacyComponentSerializer legacyComponentSerializer; private static final TextReplacementConfig REPLACE_LF = TextReplacementConfig.builder().matchLiteral("\n").replacement(Component.newline()).build(); /** * This iterator slices a component into individual parts that @@ -65,6 +64,7 @@ public class AdventureHelper { b.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_KEY_AS_TYPE_AND_UUID_AS_ID, true); }); } + this.legacyComponentSerializer = LegacyComponentSerializer.builder().build(); this.gsonComponentSerializer = builder.build(); this.nbtComponentSerializer = NBTComponentSerializer.builder() .editItem(item -> { @@ -84,20 +84,10 @@ public class AdventureHelper { private static final AdventureHelper INSTANCE = new AdventureHelper(); } - /** - * Retrieves the singleton instance of AdventureHelper. - * - * @return the singleton instance - */ public static AdventureHelper getInstance() { return SingletonHolder.INSTANCE; } - /** - * Retrieves the MiniMessage instance. - * - * @return the MiniMessage instance - */ public static MiniMessage miniMessage() { return getInstance().miniMessage; } @@ -106,70 +96,22 @@ public class AdventureHelper { return getInstance().miniMessageCustom; } + public static LegacyComponentSerializer getLegacy() { + return getInstance().legacyComponentSerializer; + } + public static MiniMessage strictMiniMessage() { return getInstance().miniMessageStrict; } - /** - * Retrieves the GsonComponentSerializer instance. - * - * @return the GsonComponentSerializer instance - */ public static GsonComponentSerializer getGson() { return getInstance().gsonComponentSerializer; } - /** - * Retrieves the NBTComponentSerializer instance. - * - * @return the NBTComponentSerializer instance - */ public static NBTComponentSerializer getNBT() { return getInstance().nbtComponentSerializer; } - /** - * Sends a message to an audience. - * - * @param audience the audience to send the message to - * @param message the message component - */ - public static void sendMessage(Audience audience, Component message) { - audience.sendMessage(message); - } - - /** - * Plays a sound for an audience. - * - * @param audience the audience to play the sound for - * @param sound the sound to play - */ - public static void playSound(Audience audience, Sound sound) { - audience.playSound(sound); - } - - /** - * Surrounds text with a MiniMessage font tag. - * - * @param text the text to surround - * @param font the font as a {@link Key} - * @return the text surrounded by the MiniMessage font tag - */ - public static String surroundWithMiniMessageFont(String text, Key font) { - return "" + text + ""; - } - - /** - * Surrounds text with a MiniMessage font tag. - * - * @param text the text to surround - * @param font the font as a {@link String} - * @return the text surrounded by the MiniMessage font tag - */ - public static String surroundWithMiniMessageFont(String text, String font) { - return "" + text + ""; - } - /** * Converts a JSON string to a MiniMessage string. * @@ -365,6 +307,8 @@ public class AdventureHelper { return text.replaceText(builder -> builder.match(Pattern.compile(patternString)) .replacement((result, b) -> - replacements.get(result.group()).apply(context))); + Optional.ofNullable(replacements.get(result.group())).orElseThrow(() -> new IllegalStateException("Could not find tag '" + result.group() + "'")).apply(context) + ) + ); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/BlockEntityTickersList.java b/core/src/main/java/net/momirealms/craftengine/core/util/BlockEntityTickersList.java new file mode 100644 index 000000000..124269086 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/BlockEntityTickersList.java @@ -0,0 +1,111 @@ +/** + * This implementation references the BlockEntityTickersList implementation by Winds Studio, + * available at: https://github.com/Winds-Studio/Leaf/blob/b9ebff/leaf-server/src/main/java/org/dreeam/leaf/util/list/BlockEntityTickersList.java + *

+ * This work is licensed under the GNU General Public License v3.0 (GPLv3) + */ +package net.momirealms.craftengine.core.util; + +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.momirealms.craftengine.core.block.entity.tick.TickingBlockEntity; + +import java.util.Arrays; +import java.util.Collection; + +/** + * A list for ServerLevel's blockEntityTickers + *

+ * This list behaves identically to ObjectArrayList, but it has an additional method, `removeAllByIndex`, that allows a list of integers to be passed indicating what + * indexes should be deleted from the list + *

+ * This is faster than using removeAll, since we don't need to compare the identity of each block entity, and faster than looping thru each index manually and deleting with remove, + * since we don't need to resize the array every single remove. + */ +public final class BlockEntityTickersList extends ObjectArrayList { + + private final IntOpenHashSet toRemove = new IntOpenHashSet(); + private int startSearchFromIndex = -1; + + /** + * Creates a new array list with {@link #DEFAULT_INITIAL_CAPACITY} capacity. + */ + public BlockEntityTickersList() { + super(); + } + + /** + * Creates a new array list and fills it with a given collection. + * + * @param c a collection that will be used to fill the array list. + */ + public BlockEntityTickersList(final Collection c) { + super(c); + } + + /** + * Marks an entry as removed + * + * @param index the index of the item on the list to be marked as removed + */ + public void markAsRemoved(final int index) { + // The block entities list always loop starting from 0, so we only need to check if the startSearchFromIndex is -1 and that's it + if (this.startSearchFromIndex == -1) + this.startSearchFromIndex = index; + + this.toRemove.add(index); + } + + /** + * Removes elements that have been marked as removed. + */ + public void removeMarkedEntries() { + if (this.startSearchFromIndex == -1) // No entries in the list, skip + return; + + removeAllByIndex(startSearchFromIndex, toRemove); + toRemove.clear(); + this.startSearchFromIndex = -1; // Reset the start search index + } + + /** + * Removes elements by their index. + */ + private void removeAllByIndex(final int startSearchFromIndex, final IntOpenHashSet c) { // can't use Set because we want to avoid autoboxing when using contains + final int requiredMatches = c.size(); + if (requiredMatches == 0) + return; // exit early, we don't need to do anything + + final Object[] a = this.a; + int writeIndex = startSearchFromIndex; + int lastCopyIndex = startSearchFromIndex; + int matches = 0; + + for (int readIndex = startSearchFromIndex; readIndex < size; readIndex++) { + if (c.contains(readIndex)) { + matches++; + final int blockLength = readIndex - lastCopyIndex; + if (blockLength > 0) { + System.arraycopy(a, lastCopyIndex, a, writeIndex, blockLength); + writeIndex += blockLength; + } + lastCopyIndex = readIndex + 1; + + if (matches == requiredMatches) { + break; + } + } + } + + final int finalBlockLength = size - lastCopyIndex; + if (finalBlockLength > 0) { + System.arraycopy(a, lastCopyIndex, a, writeIndex, finalBlockLength); + writeIndex += finalBlockLength; + } + + if (writeIndex < size) { + Arrays.fill(a, writeIndex, size, null); + } + size = writeIndex; + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/CharacterUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/CharacterUtils.java index 90abef1e1..08a9155e2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/CharacterUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/CharacterUtils.java @@ -2,25 +2,21 @@ package net.momirealms.craftengine.core.util; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import java.util.regex.Pattern; import java.util.stream.IntStream; public class CharacterUtils { - private static final Pattern PATTERN = Pattern.compile("[\\p{Mn}\\p{Me}\\p{Mc}\\p{Cf}]"); - private CharacterUtils() {} public static char[] decodeUnicodeToChars(String unicodeString) { int count = unicodeString.length() / 6; if (unicodeString.length() % 6 != 0) { - throw new LocalizedResourceConfigException("warning.config.image.invalid_unicode_string_length"); + throw new LocalizedResourceConfigException("warning.config.image.invalid_unicode_string", unicodeString); } char[] chars = new char[count]; for (int i = 0, j = 0; j < count; i += 6, j++) { String hex = unicodeString.substring(i + 2, i + 6); try { - int codePoint = Integer.parseInt(hex, 16); - chars[j] = (char) codePoint; + chars[j] = (char) Integer.parseInt(hex, 16); } catch (NumberFormatException e) { throw new LocalizedResourceConfigException("warning.config.image.invalid_hex_value", e, hex); } @@ -72,29 +68,6 @@ public class CharacterUtils { return builder.toString(); } - public static boolean containsCombinedCharacter(String input) { - if (input == null || input.isEmpty() || input.length() == 1) return false; - for (int i = 0; i < input.length();) { - int codePoint = input.codePointAt(i); - i += Character.charCount(codePoint); - int type = Character.getType(codePoint); - if (type == Character.NON_SPACING_MARK || - type == Character.ENCLOSING_MARK || - type == Character.COMBINING_SPACING_MARK || - type == Character.FORMAT || - type == Character.CONTROL || - type == Character.SURROGATE || - type == Character.PRIVATE_USE || - PATTERN.matcher(new String(Character.toChars(codePoint))).find() - ) return true; - if (i < input.length()) { - int nextCodePoint = input.codePointAt(i); - if (Character.isSurrogatePair(Character.toChars(codePoint)[0], Character.toChars(nextCodePoint)[0])) return true; - } - } - return false; - } - public static String replaceBackslashWithSlash(String input) { if (input == null || input.isEmpty()) { return input; diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Color.java b/core/src/main/java/net/momirealms/craftengine/core/util/Color.java index 6884afedd..5f37d42b0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Color.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Color.java @@ -38,7 +38,7 @@ public class Color { } public static Color fromVector3f(Vector3f vec) { - return new Color(0 << 24 /*不可省略*/ | MCUtils.fastFloor(vec.x) << 16 | MCUtils.fastFloor(vec.y) << 8 | MCUtils.fastFloor(vec.z)); + return new Color(0 << 24 /*不可省略*/ | MiscUtils.fastFloor(vec.x) << 16 | MiscUtils.fastFloor(vec.y) << 8 | MiscUtils.fastFloor(vec.z)); } public static int opaque(int color) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ConcurrentUUID2ReferenceChainedHashTable.java b/core/src/main/java/net/momirealms/craftengine/core/util/ConcurrentUUID2ReferenceChainedHashTable.java new file mode 100644 index 000000000..f654ab07b --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ConcurrentUUID2ReferenceChainedHashTable.java @@ -0,0 +1,1155 @@ +/** + * This implementation references the ConcurrentUtil implementation by Tuinity, + * available at: https://github.com/Tuinity/ConcurrentUtil + *

+ * This work is licensed under the GNU General Public License v3.0 (GPLv3) + */ +package net.momirealms.craftengine.core.util; + +import ca.spottedleaf.concurrentutil.util.*; + +import java.lang.invoke.VarHandle; +import java.util.*; +import java.util.concurrent.atomic.LongAdder; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * Concurrent hashtable implementation supporting mapping UUID values onto non-null Object + * values with support for multiple writer and multiple reader threads. + */ +@SuppressWarnings("unchecked") +public class ConcurrentUUID2ReferenceChainedHashTable implements Iterable> { + protected static final int DEFAULT_CAPACITY = 16; + protected static final float DEFAULT_LOAD_FACTOR = 0.75f; + protected static final int MAXIMUM_CAPACITY = Integer.MIN_VALUE >>> 1; + + protected final LongAdder size = new LongAdder(); + protected final float loadFactor; + + protected volatile TableEntry[] table; + + protected static final int THRESHOLD_NO_RESIZE = -1; + protected static final int THRESHOLD_RESIZING = -2; + protected volatile int threshold; + protected static final VarHandle THRESHOLD_HANDLE = ConcurrentUtil.getVarHandle(ConcurrentUUID2ReferenceChainedHashTable.class, "threshold", int.class); + + protected final int getThresholdAcquire() { + return (int)THRESHOLD_HANDLE.getAcquire(this); + } + + protected final int getThresholdVolatile() { + return (int)THRESHOLD_HANDLE.getVolatile(this); + } + + protected final void setThresholdPlain(final int threshold) { + THRESHOLD_HANDLE.set(this, threshold); + } + + protected final void setThresholdRelease(final int threshold) { + THRESHOLD_HANDLE.setRelease(this, threshold); + } + + protected final void setThresholdVolatile(final int threshold) { + THRESHOLD_HANDLE.setVolatile(this, threshold); + } + + protected final int compareExchangeThresholdVolatile(final int expect, final int update) { + return (int)THRESHOLD_HANDLE.compareAndExchange(this, expect, update); + } + + protected Values values; + protected EntrySet entrySet; + + public ConcurrentUUID2ReferenceChainedHashTable() { + this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR); + } + + protected static int getTargetThreshold(final int capacity, final float loadFactor) { + final double ret = (double)capacity * (double)loadFactor; + if (Double.isInfinite(ret) || ret >= ((double)Integer.MAX_VALUE)) { + return THRESHOLD_NO_RESIZE; + } + + return (int)Math.ceil(ret); + } + + protected static int getCapacityFor(final int capacity) { + if (capacity <= 0) { + throw new IllegalArgumentException("Invalid capacity: " + capacity); + } + if (capacity >= MAXIMUM_CAPACITY) { + return MAXIMUM_CAPACITY; + } + return IntegerUtil.roundCeilLog2(capacity); + } + + protected ConcurrentUUID2ReferenceChainedHashTable(final int capacity, final float loadFactor) { + final int tableSize = getCapacityFor(capacity); + + if (loadFactor <= 0.0 || !Float.isFinite(loadFactor)) { + throw new IllegalArgumentException("Invalid load factor: " + loadFactor); + } + + if (tableSize == MAXIMUM_CAPACITY) { + this.setThresholdPlain(THRESHOLD_NO_RESIZE); + } else { + this.setThresholdPlain(getTargetThreshold(tableSize, loadFactor)); + } + + this.loadFactor = loadFactor; + // noinspection unchecked + this.table = (TableEntry[])new TableEntry[tableSize]; + } + + public static ConcurrentUUID2ReferenceChainedHashTable createWithCapacity(final int capacity) { + return createWithCapacity(capacity, DEFAULT_LOAD_FACTOR); + } + + public static ConcurrentUUID2ReferenceChainedHashTable createWithCapacity(final int capacity, final float loadFactor) { + return new ConcurrentUUID2ReferenceChainedHashTable<>(capacity, loadFactor); + } + + public static ConcurrentUUID2ReferenceChainedHashTable createWithExpected(final int expected) { + return createWithExpected(expected, DEFAULT_LOAD_FACTOR); + } + + public static ConcurrentUUID2ReferenceChainedHashTable createWithExpected(final int expected, final float loadFactor) { + final int capacity = (int)Math.ceil((double)expected / (double)loadFactor); + return createWithCapacity(capacity, loadFactor); + } + + protected static int getHash(final UUID key) { + return (int)HashUtil.mix(key.getMostSignificantBits() ^ key.getLeastSignificantBits()); + } + + public final float getLoadFactor() { + return this.loadFactor; + } + + protected static TableEntry getAtIndexVolatile(final TableEntry[] table, final int index) { + //noinspection unchecked + return (TableEntry)TableEntry.TABLE_ENTRY_ARRAY_HANDLE.getVolatile(table, index); + } + + protected static void setAtIndexRelease(final TableEntry[] table, final int index, final TableEntry value) { + TableEntry.TABLE_ENTRY_ARRAY_HANDLE.setRelease(table, index, value); + } + + protected static void setAtIndexVolatile(final TableEntry[] table, final int index, final TableEntry value) { + TableEntry.TABLE_ENTRY_ARRAY_HANDLE.setVolatile(table, index, value); + } + + protected static TableEntry compareAndExchangeAtIndexVolatile(final TableEntry[] table, final int index, + final TableEntry expect, final TableEntry update) { + //noinspection unchecked + return (TableEntry)TableEntry.TABLE_ENTRY_ARRAY_HANDLE.compareAndExchange(table, index, expect, update); + } + + /** + * Returns the possible node associated with the key, or {@code null} if there is no such node. + */ + protected final TableEntry getNode(final UUID key) { + final int hash = getHash(key); + + TableEntry[] table = this.table; + for (;;) { + TableEntry node = getAtIndexVolatile(table, hash & (table.length - 1)); + + if (node == null) { + return node; + } + + if (node.resize) { + // noinspection unchecked + table = (TableEntry[])node.getValuePlain(); + continue; + } + + for (; node != null; node = node.getNextVolatile()) { + if (node.key.equals(key)) { + return node; + } + } + + return node; + } + } + + /** + * Returns the currently mapped value associated with the specified key, or {@code null} if there is none. + */ + public V get(final UUID key) { + final TableEntry node = this.getNode(key); + return node == null ? null : node.getValueVolatile(); + } + + /** + * Returns the currently mapped value associated with the specified key, or the specified default value if there is none. + */ + public V getOrDefault(final UUID key, final V defaultValue) { + final TableEntry node = this.getNode(key); + if (node == null) { + return defaultValue; + } + + final V ret = node.getValueVolatile(); + if (ret == null) { + return defaultValue; + } + + return ret; + } + + /** + * Returns whether the specified key is mapped to some value. + */ + public boolean containsKey(final UUID key) { + return this.get(key) != null; + } + + /** + * Returns whether the specified value has a key mapped to it. + */ + public boolean containsValue(final V value) { + Validate.notNull(value, "Value cannot be null"); + + final NodeIterator iterator = new NodeIterator<>(this.table); + + TableEntry node; + while ((node = iterator.findNext()) != null) { + if (node.getValueAcquire() == value) { + return true; + } + } + + return false; + } + + /** + * Returns the number of mappings in this map. + */ + public int size() { + final long ret = this.size.sum(); + + if (ret < 0L) { + return 0; + } + if (ret > (long)Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + + return (int)ret; + } + + /** + * Returns whether this map has no mappings. + */ + public boolean isEmpty() { + return this.size.sum() <= 0L; + } + + /** + * Adds count to size and checks threshold for resizing + */ + protected final void addSize(final long count) { + this.size.add(count); + + final int threshold = this.getThresholdAcquire(); + + if (threshold < 0L) { + return; + } + + final long sum = this.size.sum(); + + if (sum < (long)threshold) { + return; + } + + if (threshold != this.compareExchangeThresholdVolatile(threshold, THRESHOLD_RESIZING)) { + return; + } + + this.resize(sum); + } + + /** + * Resizes table + */ + private void resize(final long sum) { + int capacity; + + final double targetD = ((double)sum / (double)this.loadFactor) + 1.0; + if (targetD >= (double)MAXIMUM_CAPACITY) { + capacity = MAXIMUM_CAPACITY; + } else { + capacity = (int)Math.ceil(targetD); + capacity = IntegerUtil.roundCeilLog2(capacity); + if (capacity > MAXIMUM_CAPACITY) { + capacity = MAXIMUM_CAPACITY; + } + } + + // noinspection unchecked + final TableEntry[] newTable = new TableEntry[capacity]; + // noinspection unchecked + final TableEntry resizeNode = new TableEntry<>(null, (V)newTable, true); + + final TableEntry[] oldTable = this.table; + + final int capOldShift = IntegerUtil.floorLog2(oldTable.length); + final int capDiffShift = IntegerUtil.floorLog2(capacity) - capOldShift; + + if (capDiffShift == 0) { + throw new IllegalStateException("Resizing to same size"); + } + + // noinspection unchecked + final TableEntry[] work = new TableEntry[1 << capDiffShift]; + + for (int i = 0, len = oldTable.length; i < len; ++i) { + TableEntry binNode = getAtIndexVolatile(oldTable, i); + + for (;;) { + if (binNode == null) { + if (null == (binNode = compareAndExchangeAtIndexVolatile(oldTable, i, null, resizeNode))) { + break; + } + } + + synchronized (binNode) { + if (binNode != (binNode = getAtIndexVolatile(oldTable, i))) { + continue; + } + + TableEntry next = binNode.getNextPlain(); + + if (next == null) { + newTable[getHash(binNode.key) & (capacity - 1)] = binNode; + } else { + Arrays.fill(work, null); + + for (TableEntry curr = binNode; curr != null; curr = curr.getNextPlain()) { + final int newTableIdx = getHash(curr.key) & (capacity - 1); + final int workIdx = newTableIdx >>> capOldShift; + + final TableEntry replace = new TableEntry<>(curr.key, curr.getValuePlain()); + + final TableEntry workNode = work[workIdx]; + work[workIdx] = replace; + + if (workNode == null) { + newTable[newTableIdx] = replace; + continue; + } else { + workNode.setNextPlain(replace); + continue; + } + } + } + + setAtIndexRelease(oldTable, i, resizeNode); + break; + } + } + } + + final int newThreshold; + if (capacity == MAXIMUM_CAPACITY) { + newThreshold = THRESHOLD_NO_RESIZE; + } else { + newThreshold = getTargetThreshold(capacity, loadFactor); + } + + this.table = newTable; + this.setThresholdVolatile(newThreshold); + } + + /** + * Subtracts count from size + */ + protected final void subSize(final long count) { + this.size.add(-count); + } + + /** + * Atomically updates the value associated with {@code key} to {@code value}, or inserts a new mapping. + */ + public V put(final UUID key, final V value) { + Validate.notNull(value, "Value may not be null"); + + final int hash = getHash(key); + + TableEntry[] table = this.table; + table_loop: + for (;;) { + final int index = hash & (table.length - 1); + + TableEntry node = getAtIndexVolatile(table, index); + node_loop: + for (;;) { + if (node == null) { + if (null == (node = compareAndExchangeAtIndexVolatile(table, index, null, new TableEntry<>(key, value)))) { + this.addSize(1L); + return null; + } + } + + if (node.resize) { + // noinspection unchecked + table = (TableEntry[])node.getValuePlain(); + continue table_loop; + } + + synchronized (node) { + if (node != (node = getAtIndexVolatile(table, index))) { + continue node_loop; + } + TableEntry prev = null; + for (; node != null; prev = node, node = node.getNextPlain()) { + if (node.key.equals(key)) { + final V ret = node.getValuePlain(); + node.setValueVolatile(value); + return ret; + } + } + + prev.setNextRelease(new TableEntry<>(key, value)); + } + + this.addSize(1L); + return null; + } + } + } + + /** + * Atomically inserts a new mapping if and only if {@code key} is not currently mapped. + */ + public V putIfAbsent(final UUID key, final V value) { + Validate.notNull(value, "Value may not be null"); + + final int hash = getHash(key); + + TableEntry[] table = this.table; + table_loop: + for (;;) { + final int index = hash & (table.length - 1); + + TableEntry node = getAtIndexVolatile(table, index); + node_loop: + for (;;) { + if (node == null) { + if (null == (node = compareAndExchangeAtIndexVolatile(table, index, null, new TableEntry<>(key, value)))) { + this.addSize(1L); + return null; + } + } + + if (node.resize) { + // noinspection unchecked + table = (TableEntry[])node.getValuePlain(); + continue table_loop; + } + + if (node.key.equals(key)) { + final V ret = node.getValueVolatile(); + if (ret != null) { + return ret; + } + } + + synchronized (node) { + if (node != (node = getAtIndexVolatile(table, index))) { + continue node_loop; + } + TableEntry prev = null; + for (; node != null; prev = node, node = node.getNextPlain()) { + if (node.key.equals(key)) { + return node.getValuePlain(); + } + } + + prev.setNextRelease(new TableEntry<>(key, value)); + } + + this.addSize(1L); + return null; + } + } + } + + /** + * Atomically updates the value associated with {@code key} to {@code value}, or does nothing if not mapped. + */ + public V replace(final UUID key, final V value) { + Validate.notNull(value, "Value may not be null"); + + final int hash = getHash(key); + + TableEntry[] table = this.table; + table_loop: + for (;;) { + final int index = hash & (table.length - 1); + + TableEntry node = getAtIndexVolatile(table, index); + node_loop: + for (;;) { + if (node == null) { + return null; + } + + if (node.resize) { + // noinspection unchecked + table = (TableEntry[])node.getValuePlain(); + continue table_loop; + } + + synchronized (node) { + if (node != (node = getAtIndexVolatile(table, index))) { + continue node_loop; + } + + for (; node != null; node = node.getNextPlain()) { + if (node.key.equals(key)) { + final V ret = node.getValuePlain(); + node.setValueVolatile(value); + return ret; + } + } + } + + return null; + } + } + } + + /** + * Atomically updates the value if the currently associated value is reference equal to {@code expect}. + */ + public V replace(final UUID key, final V expect, final V update) { + Validate.notNull(expect, "Expect may not be null"); + Validate.notNull(update, "Update may not be null"); + + final int hash = getHash(key); + + TableEntry[] table = this.table; + table_loop: + for (;;) { + final int index = hash & (table.length - 1); + + TableEntry node = getAtIndexVolatile(table, index); + node_loop: + for (;;) { + if (node == null) { + return null; + } + + if (node.resize) { + // noinspection unchecked + table = (TableEntry[])node.getValuePlain(); + continue table_loop; + } + + synchronized (node) { + if (node != (node = getAtIndexVolatile(table, index))) { + continue node_loop; + } + + for (; node != null; node = node.getNextPlain()) { + if (node.key.equals(key)) { + final V ret = node.getValuePlain(); + + if (ret != expect) { + return ret; + } + + node.setValueVolatile(update); + return ret; + } + } + } + + return null; + } + } + } + + /** + * Atomically removes the mapping for the specified key. + */ + public V remove(final UUID key) { + final int hash = getHash(key); + + TableEntry[] table = this.table; + table_loop: + for (;;) { + final int index = hash & (table.length - 1); + + TableEntry node = getAtIndexVolatile(table, index); + node_loop: + for (;;) { + if (node == null) { + return null; + } + + if (node.resize) { + // noinspection unchecked + table = (TableEntry[])node.getValuePlain(); + continue table_loop; + } + + boolean removed = false; + V ret = null; + + synchronized (node) { + if (node != (node = getAtIndexVolatile(table, index))) { + continue node_loop; + } + + TableEntry prev = null; + + for (; node != null; prev = node, node = node.getNextPlain()) { + if (node.key.equals(key)) { + ret = node.getValuePlain(); + removed = true; + + if (prev == null) { + setAtIndexRelease(table, index, node.getNextPlain()); + } else { + prev.setNextRelease(node.getNextPlain()); + } + + break; + } + } + } + + if (removed) { + this.subSize(1L); + } + + return ret; + } + } + } + + /** + * Atomically removes the mapping if it is mapped to {@code expect}. + */ + public V remove(final UUID key, final V expect) { + final int hash = getHash(key); + + TableEntry[] table = this.table; + table_loop: + for (;;) { + final int index = hash & (table.length - 1); + + TableEntry node = getAtIndexVolatile(table, index); + node_loop: + for (;;) { + if (node == null) { + return null; + } + + if (node.resize) { + // noinspection unchecked + table = (TableEntry[])node.getValuePlain(); + continue table_loop; + } + + boolean removed = false; + V ret = null; + + synchronized (node) { + if (node != (node = getAtIndexVolatile(table, index))) { + continue node_loop; + } + + TableEntry prev = null; + + for (; node != null; prev = node, node = node.getNextPlain()) { + if (node.key.equals(key)) { + ret = node.getValuePlain(); + if (ret == expect) { + removed = true; + + if (prev == null) { + setAtIndexRelease(table, index, node.getNextPlain()); + } else { + prev.setNextRelease(node.getNextPlain()); + } + } + break; + } + } + } + + if (removed) { + this.subSize(1L); + } + + return ret; + } + } + } + + /** + * Removes at least all entries currently mapped at the beginning of this call. + */ + public void clear() { + final NodeIterator nodeIterator = new NodeIterator<>(this.table); + + TableEntry node; + while ((node = nodeIterator.findNext()) != null) { + this.remove(node.key); + } + } + + /** + * Returns an iterator over the entries in this map. + */ + public Iterator> entryIterator() { + return new EntryIterator<>(this); + } + + @Override + public final Iterator> iterator() { + return this.entryIterator(); + } + + /** + * Returns an iterator over the keys in this map. + */ + public Iterator keyIterator() { + return new KeyIterator<>(this); + } + + /** + * Returns an iterator over the values in this map. + */ + public Iterator valueIterator() { + return new ValueIterator<>(this); + } + + public Collection values() { + final Values values = this.values; + if (values != null) { + return values; + } + return this.values = new Values<>(this); + } + + public Set> entrySet() { + final EntrySet entrySet = this.entrySet; + if (entrySet != null) { + return entrySet; + } + return this.entrySet = new EntrySet<>(this); + } + + /** + * See {@link java.util.concurrent.ConcurrentMap#computeIfAbsent(Object, Function)} + *

+ * This function is a "functional methods" as defined by {@link ConcurrentUUID2ReferenceChainedHashTable}. + *

+ */ + public V computeIfAbsent(final UUID key, final Function function) { + Validate.notNull(function, "Function may not be null"); + + final int hash = getHash(key); + + TableEntry[] table = this.table; + table_loop: + for (;;) { + final int index = hash & (table.length - 1); + + TableEntry node = getAtIndexVolatile(table, index); + node_loop: + for (;;) { + V ret = null; + if (node == null) { + final TableEntry insert = new TableEntry<>(key, null); + + boolean added = false; + + synchronized (insert) { + if (null == (node = compareAndExchangeAtIndexVolatile(table, index, null, insert))) { + try { + ret = function.apply(key); + } catch (final Throwable throwable) { + setAtIndexVolatile(table, index, null); + ThrowUtil.throwUnchecked(throwable); + // unreachable + return null; + } + + if (ret == null) { + setAtIndexVolatile(table, index, null); + return null; + } else { + // volatile ordering ensured by addSize(), but we need release here + // to ensure proper ordering with reads and other writes + insert.setValueRelease(ret); + added = true; + } + } // else: node != null, fall through + } + + if (added) { + this.addSize(1L); + return ret; + } + } + + if (node.resize) { + // noinspection unchecked + table = (TableEntry[])node.getValuePlain(); + continue table_loop; + } + + // optimise ifAbsent calls: check if first node is key before attempting lock acquire + if (node.key.equals(key)) { + ret = node.getValueVolatile(); + if (ret != null) { + return ret; + } // else: fall back to lock to read the node + } + + boolean added = false; + + synchronized (node) { + if (node != (node = getAtIndexVolatile(table, index))) { + continue node_loop; + } + // plain reads are fine during synchronised access, as we are the only writer + TableEntry prev = null; + for (; node != null; prev = node, node = node.getNextPlain()) { + if (node.key.equals(key)) { + ret = node.getValuePlain(); + return ret; + } + } + + final V computed = function.apply(key); + if (computed != null) { + // volatile ordering ensured by addSize(), but we need release here + // to ensure proper ordering with reads and other writes + prev.setNextRelease(new TableEntry<>(key, computed)); + ret = computed; + added = true; + } + } + + if (added) { + this.addSize(1L); + } + + return ret; + } + } + } + + // Iterator implementations (similar to original but with UUID instead of long) + protected static final class EntryIterator extends BaseIteratorImpl> { + public EntryIterator(final ConcurrentUUID2ReferenceChainedHashTable map) { + super(map); + } + @Override public TableEntry next() { return this.nextNode(); } + @Override public void forEachRemaining(final Consumer> action) { + Validate.notNull(action, "Action may not be null"); + while (this.hasNext()) { action.accept(this.next()); } + } + } + + protected static final class KeyIterator extends BaseIteratorImpl { + public KeyIterator(final ConcurrentUUID2ReferenceChainedHashTable map) { super(map); } + @Override public UUID next() { return this.nextNode().key; } + @Override public void forEachRemaining(final Consumer action) { + Validate.notNull(action, "Action may not be null"); + while (this.hasNext()) { action.accept(this.next()); } + } + } + + protected static final class ValueIterator extends BaseIteratorImpl { + public ValueIterator(final ConcurrentUUID2ReferenceChainedHashTable map) { super(map); } + @Override public V next() { return this.nextNode().getValueVolatile(); } + @Override public void forEachRemaining(final Consumer action) { + Validate.notNull(action, "Action may not be null"); + while (this.hasNext()) { action.accept(this.next()); } + } + } + + protected static abstract class BaseIteratorImpl extends NodeIterator implements Iterator { + protected final ConcurrentUUID2ReferenceChainedHashTable map; + protected TableEntry lastReturned; + protected TableEntry nextToReturn; + + protected BaseIteratorImpl(final ConcurrentUUID2ReferenceChainedHashTable map) { + super(map.table); + this.map = map; + } + + @Override public final boolean hasNext() { + if (this.nextToReturn != null) return true; + return (this.nextToReturn = this.findNext()) != null; + } + + protected final TableEntry nextNode() throws NoSuchElementException { + TableEntry ret = this.nextToReturn; + if (ret != null) { + this.lastReturned = ret; + this.nextToReturn = null; + return ret; + } + ret = this.findNext(); + if (ret != null) { + this.lastReturned = ret; + return ret; + } + throw new NoSuchElementException(); + } + + @Override public final void remove() { + final TableEntry lastReturned = this.lastReturned; + if (lastReturned == null) throw new NoSuchElementException(); + this.lastReturned = null; + this.map.remove(lastReturned.key); + } + + @Override public abstract T next() throws NoSuchElementException; + @Override public abstract void forEachRemaining(final Consumer action); + } + + protected static class NodeIterator { + // Implementation similar to original but with UUID keys + protected TableEntry[] currentTable; + protected ResizeChain resizeChain; + protected TableEntry last; + protected int nextBin; + protected int increment; + + protected NodeIterator(final TableEntry[] baseTable) { + this.currentTable = baseTable; + this.increment = 1; + } + + private TableEntry[] pullResizeChain(final int index) { + final ResizeChain resizeChain = this.resizeChain; + if (resizeChain == null) { + this.currentTable = null; + return null; + } + + final ResizeChain prevChain = resizeChain.prev; + this.resizeChain = prevChain; + if (prevChain == null) { + this.currentTable = null; + return null; + } + + final TableEntry[] newTable = prevChain.table; + int newIdx = index & (newTable.length - 1); + + final ResizeChain nextPrevChain = prevChain.prev; + final int increment = nextPrevChain == null ? 1 : nextPrevChain.table.length; + + newIdx += increment; + this.increment = increment; + this.nextBin = newIdx; + this.currentTable = newTable; + + return newTable; + } + + private TableEntry[] pushResizeChain(final TableEntry[] table, final TableEntry entry) { + final ResizeChain chain = this.resizeChain; + + if (chain == null) { + // noinspection unchecked + final TableEntry[] nextTable = (TableEntry[])entry.getValuePlain(); + final ResizeChain oldChain = new ResizeChain<>(table, null, null); + final ResizeChain currChain = new ResizeChain<>(nextTable, oldChain, null); + oldChain.next = currChain; + + this.increment = table.length; + this.resizeChain = currChain; + this.currentTable = nextTable; + + return nextTable; + } else { + ResizeChain currChain = chain.next; + if (currChain == null) { + // noinspection unchecked + final TableEntry[] ret = (TableEntry[])entry.getValuePlain(); + currChain = new ResizeChain<>(ret, chain, null); + chain.next = currChain; + + this.increment = table.length; + this.resizeChain = currChain; + this.currentTable = ret; + + return ret; + } else { + this.increment = table.length; + this.resizeChain = currChain; + return this.currentTable = currChain.table; + } + } + } + + protected final TableEntry findNext() { + for (;;) { + final TableEntry last = this.last; + if (last != null) { + final TableEntry next = last.getNextVolatile(); + if (next != null) { + this.last = next; + if (next.getValuePlain() == null) continue; + return next; + } + } + + TableEntry[] table = this.currentTable; + if (table == null) return null; + + int idx = this.nextBin; + int increment = this.increment; + for (;;) { + if (idx >= table.length) { + table = this.pullResizeChain(idx); + idx = this.nextBin; + increment = this.increment; + if (table != null) continue; + this.last = null; + return null; + } + + final TableEntry entry = getAtIndexVolatile(table, idx); + if (entry == null) { + idx += increment; + continue; + } + + if (entry.resize) { + table = this.pushResizeChain(table, entry); + increment = this.increment; + continue; + } + + this.last = entry; + this.nextBin = idx + increment; + if (entry.getValuePlain() != null) return entry; + break; + } + } + } + + protected static final class ResizeChain { + public final TableEntry[] table; + public final ResizeChain prev; + public ResizeChain next; + + public ResizeChain(final TableEntry[] table, final ResizeChain prev, final ResizeChain next) { + this.table = table; + this.prev = prev; + this.next = next; + } + } + } + + protected static abstract class BaseCollection implements Collection { + protected final ConcurrentUUID2ReferenceChainedHashTable map; + + protected BaseCollection(final ConcurrentUUID2ReferenceChainedHashTable map) { + this.map = map; + } + + @Override public int size() { return this.map.size(); } + @Override public boolean isEmpty() { return this.map.isEmpty(); } + @Override public void forEach(final Consumer action) { this.iterator().forEachRemaining(action); } + + private List asList() { + final List ret = new ArrayList<>(this.map.size()); + for (final E element : this) ret.add(element); + return ret; + } + + @Override public Object[] toArray() { return this.asList().toArray(); } + @Override public T[] toArray(final T[] a) { return this.asList().toArray(a); } + + @Override public boolean containsAll(final Collection collection) { + for (final Object value : collection) { + if (!this.contains(value)) return false; + } + return true; + } + + @Override public boolean add(final E value) { throw new UnsupportedOperationException(); } + @Override public boolean remove(final Object value) { throw new UnsupportedOperationException(); } + @Override public boolean addAll(final Collection collection) { throw new UnsupportedOperationException(); } + @Override public boolean removeAll(final Collection collection) { throw new UnsupportedOperationException(); } + @Override public boolean removeIf(final Predicate filter) { throw new UnsupportedOperationException(); } + @Override public boolean retainAll(final Collection collection) { throw new UnsupportedOperationException(); } + @Override public void clear() { throw new UnsupportedOperationException(); } + } + + protected static class Values extends BaseCollection { + public Values(final ConcurrentUUID2ReferenceChainedHashTable map) { super(map); } + @Override public boolean contains(final Object value) { return this.map.containsValue((V)value); } + @Override public Iterator iterator() { return this.map.valueIterator(); } + } + + protected static class EntrySet extends BaseCollection> implements Set> { + protected EntrySet(final ConcurrentUUID2ReferenceChainedHashTable map) { super(map); } + @Override public boolean contains(final Object value) { + if (!(value instanceof TableEntry entry)) return false; + final V mapped = this.map.get((UUID)entry.getKey()); + return mapped != null && mapped == value; + } + @Override public Iterator> iterator() { return this.map.entryIterator(); } + } + + public static final class TableEntry { + private static final VarHandle TABLE_ENTRY_ARRAY_HANDLE = ConcurrentUtil.getArrayHandle(TableEntry[].class); + + private final boolean resize; + private final UUID key; + + private volatile V value; + private static final VarHandle VALUE_HANDLE = ConcurrentUtil.getVarHandle(TableEntry.class, "value", Object.class); + + private V getValuePlain() { return (V)VALUE_HANDLE.get(this); } + private V getValueAcquire() { return (V)VALUE_HANDLE.getAcquire(this); } + private V getValueVolatile() { return (V)VALUE_HANDLE.getVolatile(this); } + private void setValuePlain(final V value) { VALUE_HANDLE.set(this, (Object)value); } + private void setValueRelease(final V value) { VALUE_HANDLE.setRelease(this, (Object)value); } + private void setValueVolatile(final V value) { VALUE_HANDLE.setVolatile(this, (Object)value); } + + private volatile TableEntry next; + private static final VarHandle NEXT_HANDLE = ConcurrentUtil.getVarHandle(TableEntry.class, "next", TableEntry.class); + + private TableEntry getNextPlain() { return (TableEntry)NEXT_HANDLE.get(this); } + private TableEntry getNextVolatile() { return (TableEntry)NEXT_HANDLE.getVolatile(this); } + private void setNextPlain(final TableEntry next) { NEXT_HANDLE.set(this, next); } + private void setNextRelease(final TableEntry next) { NEXT_HANDLE.setRelease(this, next); } + private void setNextVolatile(final TableEntry next) { NEXT_HANDLE.setVolatile(this, next); } + + public TableEntry(final UUID key, final V value) { + this.resize = false; + this.key = key; + this.setValuePlain(value); + } + + public TableEntry(final UUID key, final V value, final boolean resize) { + this.resize = resize; + this.key = key; + this.setValuePlain(value); + } + + public UUID getKey() { return this.key; } + public V getValue() { return this.getValueVolatile(); } + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Direction.java b/core/src/main/java/net/momirealms/craftengine/core/util/Direction.java index 636d84a0b..8e8e44da1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Direction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Direction.java @@ -131,10 +131,10 @@ public enum Direction { public static Direction[] orderedByNearest(AbstractEntity entity) { float f = entity.xRot() * ((float)Math.PI / 180F); float f1 = -entity.yRot() * ((float)Math.PI / 180F); - float sin = MCUtils.sin(f); - float cos = MCUtils.cos(f); - float sin1 = MCUtils.sin(f1); - float cos1 = MCUtils.cos(f1); + float sin = MiscUtils.sin(f); + float cos = MiscUtils.cos(f); + float sin1 = MiscUtils.sin(f1); + float cos1 = MiscUtils.cos(f1); boolean flag = sin1 > 0.0F; boolean flag1 = sin < 0.0F; boolean flag2 = cos1 > 0.0F; diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/FriendlyByteBuf.java b/core/src/main/java/net/momirealms/craftengine/core/util/FriendlyByteBuf.java index 68e30c8a2..b4b339108 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/FriendlyByteBuf.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/FriendlyByteBuf.java @@ -14,6 +14,7 @@ import it.unimi.dsi.fastutil.ints.IntList; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.registry.Registry; import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.sparrow.nbt.NBT; import net.momirealms.sparrow.nbt.Tag; import org.jetbrains.annotations.NotNull; @@ -76,7 +77,7 @@ public class FriendlyByteBuf extends ByteBuf { public > C readCollection(IntFunction collectionFactory, Reader reader) { int i = this.readVarInt(); C collection = collectionFactory.apply(i); - for(int j = 0; j < i; ++j) { + for (int j = 0; j < i; ++j) { collection.add(reader.apply(this)); } return collection; @@ -93,7 +94,7 @@ public class FriendlyByteBuf extends ByteBuf { public T[] readArray(Reader reader, Class type) { int i = this.readVarInt(); T[] array = (T[]) Array.newInstance(type, i); - for(int j = 0; j < i; ++j) { + for (int j = 0; j < i; ++j) { array[j] = reader.apply(this); } return array; @@ -101,7 +102,7 @@ public class FriendlyByteBuf extends ByteBuf { public void writeArray(T[] array, Writer writer) { this.writeVarInt(array.length); - for(T t : array) { + for (T t : array) { writer.accept(this, t); } } @@ -351,7 +352,7 @@ public class FriendlyByteBuf extends ByteBuf { } public long[] readFixedSizeLongArray(long[] output) { - for(int i = 0; i < output.length; ++i) { + for (int i = 0; i < output.length; ++i) { output[i] = this.readLong(); } return output; @@ -473,12 +474,12 @@ public class FriendlyByteBuf extends ByteBuf { public void writeHolderSet(Either, Key> holderSet) { holderSet.ifLeft( - ints -> { - writeVarInt(ints.size() + 1); - for (Integer anInt : ints) { - writeVarInt(anInt); + ints -> { + writeVarInt(ints.size() + 1); + for (Integer anInt : ints) { + writeVarInt(anInt); + } } - } ).ifRight(key -> { writeVarInt(0); writeKey(key); @@ -584,7 +585,7 @@ public class FriendlyByteBuf extends ByteBuf { } public BitSet readFixedBitSet(int size) { - byte[] byteArray = new byte[MCUtils.positiveCeilDiv(size, 8)]; + byte[] byteArray = new byte[MiscUtils.positiveCeilDiv(size, 8)]; this.readBytes(byteArray); return BitSet.valueOf(byteArray); } @@ -594,18 +595,62 @@ public class FriendlyByteBuf extends ByteBuf { throw new EncoderException("BitSet length exceeds expected size (" + bitSet.length() + " > " + size + ")"); } byte[] byteArray = bitSet.toByteArray(); - this.writeBytes(Arrays.copyOf(byteArray, MCUtils.positiveCeilDiv(size, 8))); + this.writeBytes(Arrays.copyOf(byteArray, MiscUtils.positiveCeilDiv(size, 8))); } @SuppressWarnings("unchecked") public > T readEnumConstant(Class enumClass) { - return (T)((Enum[])enumClass.getEnumConstants())[this.readVarInt()]; + return (T) ((Enum[]) enumClass.getEnumConstants())[this.readVarInt()]; } public FriendlyByteBuf writeEnumConstant(Enum instance) { return this.writeVarInt(instance.ordinal()); } + public Vec3d readLpVec3() { + int unsignedByte = this.readUnsignedByte(); + if (unsignedByte == 0) { + return Vec3d.ZERO; + } else { + int unsignedByte1 = this.readUnsignedByte(); + long unsignedInt = this.readUnsignedInt(); + long l = unsignedInt << 16 | (long) (unsignedByte1 << 8) | (long) unsignedByte; + long l1 = unsignedByte & 3; + if ((unsignedByte & 4) == 4) { + l1 |= ((long) this.readVarInt() & 4294967295L) << 2; + } + return new Vec3d( + (Math.min((double) ((l >> 3) & 32767L), (double) 32766.0F) * (double) 2.0F / (double) 32766.0F - (double) 1.0F) * (double) l1, + (Math.min((double) ((l >> 18) & 32767L), (double) 32766.0F) * (double) 2.0F / (double) 32766.0F - (double) 1.0F) * (double) l1, + (Math.min((double) ((l >> 33) & 32767L), (double) 32766.0F) * (double) 2.0F / (double) 32766.0F - (double) 1.0F) * (double) l1 + ); + } + } + + public void writeLpVec3(Vec3d vec3) { + double d = Double.isNaN(vec3.x) ? (double) 0.0F : Math.clamp(vec3.x, -1.7179869183E10, 1.7179869183E10); + double d1 = Double.isNaN(vec3.y) ? (double) 0.0F : Math.clamp(vec3.y, -1.7179869183E10, 1.7179869183E10); + double d2 = Double.isNaN(vec3.z) ? (double) 0.0F : Math.clamp(vec3.z, -1.7179869183E10, 1.7179869183E10); + double max = MiscUtils.absMax(d, MiscUtils.absMax(d1, d2)); + if (max < 3.051944088384301E-5) { + this.writeByte(0); + } else { + long l = MiscUtils.ceilLong(max); + boolean flag = (l & 3L) != l; + long l1 = flag ? l & 3L | 4L : l; + long l2 = (Math.round(((d / (double) l) * (double) 0.5F + (double) 0.5F) * (double) 32766.0F)) << 3; + long l3 = (Math.round(((d1 / (double) l) * (double) 0.5F + (double) 0.5F) * (double) 32766.0F)) << 18; + long l4 = (Math.round(((d2 / (double) l) * (double) 0.5F + (double) 0.5F) * (double) 32766.0F)) << 33; + long l5 = l1 | l2 | l3 | l4; + this.writeByte((byte) ((int) l5)); + this.writeByte((byte) ((int) (l5 >> 8))); + this.writeInt((int) (l5 >> 16)); + if (flag) { + this.writeVarInt((int) (l >> 2)); + } + } + } + @FunctionalInterface public interface Writer extends BiConsumer { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/GsonHelper.java b/core/src/main/java/net/momirealms/craftengine/core/util/GsonHelper.java index 975fcfee0..beb6c42a1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/GsonHelper.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/GsonHelper.java @@ -11,25 +11,19 @@ import java.nio.file.Path; import java.util.List; import java.util.Map; -public class GsonHelper { - private final Gson gson; +public final class GsonHelper { + private static final Gson GSON; - public GsonHelper() { - this.gson = new GsonBuilder() + private GsonHelper() {} + + static { + GSON = new GsonBuilder() .disableHtmlEscaping() .create(); } - public Gson getGson() { - return gson; - } - public static Gson get() { - return SingletonHolder.INSTANCE.getGson(); - } - - private static class SingletonHolder { - private static final GsonHelper INSTANCE = new GsonHelper(); + return GSON; } public static void writeJsonFile(JsonElement json, Path path) throws IOException { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/HorizontalDirection.java b/core/src/main/java/net/momirealms/craftengine/core/util/HorizontalDirection.java index cc8143e12..d0a6ec27c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/HorizontalDirection.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/HorizontalDirection.java @@ -3,10 +3,26 @@ package net.momirealms.craftengine.core.util; import org.jetbrains.annotations.NotNull; public enum HorizontalDirection { - NORTH, - SOUTH, - WEST, - EAST; + NORTH(0, -1), + SOUTH(0, 1), + WEST(-1, 0), + EAST(1, 0); + + private final int adjX; + private final int adjZ; + + HorizontalDirection(int adjX, int adjZ) { + this.adjX = adjX; + this.adjZ = adjZ; + } + + public int stepX() { + return this.adjX; + } + + public int stepZ() { + return this.adjZ; + } public Direction toDirection() { return switch (this) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Instrument.java b/core/src/main/java/net/momirealms/craftengine/core/util/Instrument.java index 7c1f60dad..a43d10a2a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Instrument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Instrument.java @@ -34,4 +34,6 @@ public enum Instrument { public String id() { return id; } + + public static final Instrument[] VALUES = Instrument.values(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Int2ObjectBiMap.java b/core/src/main/java/net/momirealms/craftengine/core/util/Int2ObjectBiMap.java index 297d49600..5eeb3c669 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Int2ObjectBiMap.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Int2ObjectBiMap.java @@ -121,12 +121,12 @@ public class Int2ObjectBiMap implements IndexedIterable { } private int getIdealIndex(@Nullable K value) { - return (MCUtils.idealHash(System.identityHashCode(value)) & Integer.MAX_VALUE) % this.values.length; + return (MiscUtils.idealHash(System.identityHashCode(value)) & Integer.MAX_VALUE) % this.values.length; } private int findIndex(@Nullable K value, int id) { int i; - for(i = id; i < this.values.length; ++i) { + for (i = id; i < this.values.length; ++i) { if (this.values[i] == value) { return i; } @@ -136,7 +136,7 @@ public class Int2ObjectBiMap implements IndexedIterable { } } - for(i = 0; i < id; ++i) { + for (i = 0; i < id; ++i) { if (this.values[i] == value) { return i; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/IntIdentityList.java b/core/src/main/java/net/momirealms/craftengine/core/util/IntIdentityList.java index a60be053e..10df6acd1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/IntIdentityList.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/IntIdentityList.java @@ -1,22 +1,16 @@ package net.momirealms.craftengine.core.util; -import it.unimi.dsi.fastutil.ints.IntArrayList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Iterator; -import java.util.List; +import java.util.NoSuchElementException; public class IntIdentityList implements IndexedIterable { private final int size; - private final List list; public IntIdentityList(int size) { this.size = size; - list = new IntArrayList(size); - for (int i = 0; i < size; i++) { - list.add(i); - } } @Override @@ -36,6 +30,29 @@ public class IntIdentityList implements IndexedIterable { @Override public @NotNull Iterator iterator() { - return list.iterator(); + return new IntIterator(size); } -} + + private static class IntIterator implements Iterator { + private final int size; + private int current; + + public IntIterator(int size) { + this.size = size; + this.current = 0; + } + + @Override + public boolean hasNext() { + return current < size; + } + + @Override + public Integer next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return current++; + } + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ListMonitor.java b/core/src/main/java/net/momirealms/craftengine/core/util/ListMonitor.java index bddcd6a34..3e37d53b5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/ListMonitor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ListMonitor.java @@ -13,14 +13,13 @@ public class ListMonitor implements List { private final List list; private final Consumer addConsumer; private final Consumer removeConsumer; - public ListMonitor(List list, Consumer addConsumer, Consumer removeConsumer) { - for (T key : list) { - addConsumer.accept(key); - } this.list = list; this.addConsumer = addConsumer; this.removeConsumer = removeConsumer; + for (T key : list) { + this.addConsumer.accept(key); + } } public List list() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MCUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/MCUtils.java deleted file mode 100644 index 95e1621f7..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MCUtils.java +++ /dev/null @@ -1,280 +0,0 @@ -package net.momirealms.craftengine.core.util; - -import com.google.common.collect.Iterators; -import org.jetbrains.annotations.Nullable; - -import java.util.Iterator; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Predicate; - -public class MCUtils { - - private MCUtils() {} - - public static final float DEG_TO_RAD = ((float)Math.PI / 180F); - - private static final int[] MULTIPLY_DE_BRUIJN_BIT_POSITION = new int[]{0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9}; - private static final float[] SIN = make(new float[65536], (sineTable) -> { - for(int i = 0; i < sineTable.length; ++i) { - sineTable[i] = (float) Math.sin((double) i * Math.PI * 2.0 / 65536.0); - } - }); - - public static int fastFloor(double value) { - int truncated = (int) value; - return value < (double) truncated ? truncated - 1 : truncated; - } - - public static int fastFloor(float value) { - int truncated = (int) value; - return value < (double) truncated ? truncated - 1 : truncated; - } - - public static int murmurHash3Mixer(int value) { - value ^= value >>> 16; - value *= -2048144789; - value ^= value >>> 13; - value *= -1028477387; - return value ^ value >>> 16; - } - - public static int ceil(double value) { - int i = (int)value; - return value > (double)i ? i + 1 : i; - } - - public static boolean isPowerOfTwo(int value) { - return value != 0 && (value & value - 1) == 0; - } - - public static int smallestEncompassingPowerOfTwo(int value) { - int i = value - 1; - i |= i >> 1; - i |= i >> 2; - i |= i >> 4; - i |= i >> 8; - i |= i >> 16; - return i + 1; - } - - public static int ceilLog2(int value) { - value = isPowerOfTwo(value) ? value : smallestEncompassingPowerOfTwo(value); - return MULTIPLY_DE_BRUIJN_BIT_POSITION[(int)((long)value * 125613361L >> 27) & 31]; - } - - public static int positiveCeilDiv(int a, int b) { - return -Math.floorDiv(-a, b); - } - - public static int idealHash(int value) { - value ^= value >>> 16; - value *= -2048144789; - value ^= value >>> 13; - value *= -1028477387; - value ^= value >>> 16; - return value; - } - - public static long getUnsignedDivisorMagic(final long divisor, final int bits) { - return ((1L << bits) - 1L) / divisor + 1L; - } - - public static T make(T object, Consumer initializer) { - initializer.accept(object); - return object; - } - - public static Predicate allOf() { - return o -> true; - } - - @SuppressWarnings("unchecked") - public static Predicate allOf(Predicate a) { - return (Predicate) a; - } - - public static Predicate allOf(Predicate a, Predicate b) { - return o -> a.test(o) && b.test(o); - } - - public static Predicate allOf(Predicate a, Predicate b, Predicate c) { - return o -> a.test(o) && b.test(o) && c.test(o); - } - - public static Predicate allOf(Predicate a, Predicate b, Predicate c, Predicate d) { - return o -> a.test(o) && b.test(o) && c.test(o) && d.test(o); - } - - public static Predicate allOf(Predicate a, Predicate b, Predicate c, Predicate d, Predicate e) { - return o -> a.test(o) && b.test(o) && c.test(o) && d.test(o) && e.test(o); - } - - @SafeVarargs - public static Predicate allOf(Predicate... predicates) { - return o -> { - for (Predicate predicate : predicates) { - if (!predicate.test(o)) { - return false; - } - } - return true; - }; - } - - public static Predicate allOf(List> predicates) { - return switch (predicates.size()) { - case 0 -> allOf(); - case 1 -> allOf((Predicate) predicates.get(0)); - case 2 -> allOf((Predicate) predicates.get(0), (Predicate) predicates.get(1)); - case 3 -> allOf((Predicate) predicates.get(0), (Predicate) predicates.get(1), (Predicate) predicates.get(2)); - case 4 -> allOf( - (Predicate) predicates.get(0), - (Predicate) predicates.get(1), - (Predicate) predicates.get(2), - (Predicate) predicates.get(3) - ); - case 5 -> allOf( - (Predicate) predicates.get(0), - (Predicate) predicates.get(1), - (Predicate) predicates.get(2), - (Predicate) predicates.get(3), - (Predicate) predicates.get(4) - ); - default -> { - @SuppressWarnings("unchecked") - Predicate[] predicates2 = predicates.toArray(Predicate[]::new); - yield allOf(predicates2); - } - }; - } - - public static Predicate anyOf() { - return o -> false; - } - - @SuppressWarnings("unchecked") - public static Predicate anyOf(Predicate a) { - return (Predicate) a; - } - - public static Predicate anyOf(Predicate a, Predicate b) { - return o -> a.test(o) || b.test(o); - } - - public static Predicate anyOf(Predicate a, Predicate b, Predicate c) { - return o -> a.test(o) || b.test(o) || c.test(o); - } - - public static Predicate anyOf(Predicate a, Predicate b, Predicate c, Predicate d) { - return o -> a.test(o) || b.test(o) || c.test(o) || d.test(o); - } - - public static Predicate anyOf(Predicate a, Predicate b, Predicate c, Predicate d, Predicate e) { - return o -> a.test(o) || b.test(o) || c.test(o) || d.test(o) || e.test(o); - } - - @SafeVarargs - public static Predicate anyOf(Predicate... predicates) { - return o -> { - for (Predicate predicate : predicates) { - if (predicate.test(o)) { - return true; - } - } - return false; - }; - } - - public static Predicate anyOf(List> predicates) { - return switch (predicates.size()) { - case 0 -> anyOf(); - case 1 -> anyOf((Predicate) predicates.get(0)); - case 2 -> anyOf((Predicate) predicates.get(0), (Predicate) predicates.get(1)); - case 3 -> anyOf((Predicate) predicates.get(0), (Predicate) predicates.get(1), (Predicate) predicates.get(2)); - case 4 -> anyOf( - (Predicate) predicates.get(0), - (Predicate) predicates.get(1), - (Predicate) predicates.get(2), - (Predicate) predicates.get(3) - ); - case 5 -> anyOf( - (Predicate) predicates.get(0), - (Predicate) predicates.get(1), - (Predicate) predicates.get(2), - (Predicate) predicates.get(3), - (Predicate) predicates.get(4) - ); - default -> { - @SuppressWarnings("unchecked") - Predicate[] predicates2 = predicates.toArray(Predicate[]::new); - yield anyOf(predicates2); - } - }; - } - - public static T findPreviousInIterable(Iterable iterable, @Nullable T object) { - Iterator iterator = iterable.iterator(); - T previous = null; - while (iterator.hasNext()) { - T current = iterator.next(); - if (current == object) { - if (previous == null) { - previous = iterator.hasNext() ? Iterators.getLast(iterator) : object; - } - break; - } - previous = current; - } - return previous; - } - - public static float sin(float value) { - return SIN[(int) (value * 10430.378F) & '\uffff']; - } - - public static float cos(float value) { - return SIN[(int)(value * 10430.378F + 16384.0F) & '\uffff']; - } - - public static float sqrt(float value) { - return (float)Math.sqrt(value); - } - - public static T findNextInIterable(Iterable iterable, @Nullable T object) { - Iterator iterator = iterable.iterator(); - T next = iterator.next(); - if (object != null) { - T current = next; - while (current != object) { - if (iterator.hasNext()) { - current = iterator.next(); - } - } - if (iterator.hasNext()) { - return iterator.next(); - } - } - return next; - } - - public static byte packDegrees(float degrees) { - return (byte) fastFloor(degrees * 256.0F / 360.0F); - } - - public static float unpackDegrees(byte degrees) { - return (float) (degrees * 360) / 256.0F; - } - - public static int clamp(int value, int min, int max) { - return Math.min(Math.max(value, min), max); - } - - public static float clamp(float value, float min, float max) { - return value < min ? min : Math.min(value, max); - } - - public static double clamp(double value, double min, double max) { - return value < min ? min : Math.min(value, max); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersion.java b/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersion.java index 2e20c0a66..80fc10b38 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersion.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersion.java @@ -22,6 +22,7 @@ public final class MinecraftVersion implements Comparable { PACK_FORMATS.put(1_21_06, 63); PACK_FORMATS.put(1_21_07, 64); PACK_FORMATS.put(1_21_08, 64); + PACK_FORMATS.put(1_21_09, 69); PACK_FORMATS.put(1_99_99, 1000); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersions.java b/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersions.java index 2f28c73ad..b9c6980cc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersions.java @@ -19,5 +19,6 @@ public final class MinecraftVersions { public static final MinecraftVersion V1_21_6 = new MinecraftVersion("1.21.6"); public static final MinecraftVersion V1_21_7 = new MinecraftVersion("1.21.7"); public static final MinecraftVersion V1_21_8 = new MinecraftVersion("1.21.8"); + public static final MinecraftVersion V1_21_9 = new MinecraftVersion("1.21.9"); public static final MinecraftVersion FUTURE = new MinecraftVersion("1.99.99"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java index ed0c90d9c..2e28001c2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java @@ -1,14 +1,302 @@ package net.momirealms.craftengine.core.util; -import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import org.joml.Quaternionf; -import org.joml.Vector3f; +import com.google.common.collect.Iterators; +import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; public class MiscUtils { + private MiscUtils() { + } - private MiscUtils() {} + public static final float DEG_TO_RAD = ((float) Math.PI / 180F); + + private static final int[] MULTIPLY_DE_BRUIJN_BIT_POSITION = new int[]{0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9}; + private static final float[] SIN = init(new float[65536], (sineTable) -> { + for (int i = 0; i < sineTable.length; ++i) { + sineTable[i] = (float) Math.sin((double) i * Math.PI * 2.0 / 65536.0); + } + }); + + public static int fastFloor(double value) { + int truncated = (int) value; + return value < (double) truncated ? truncated - 1 : truncated; + } + + public static int fastFloor(float value) { + int truncated = (int) value; + return value < (double) truncated ? truncated - 1 : truncated; + } + + public static int lerpDiscrete(float delta, int start, int end) { + int i = end - start; + return start + fastFloor(delta * (float) (i - 1)) + (delta > 0.0F ? 1 : 0); + } + + public static int murmurHash3Mixer(int value) { + value ^= value >>> 16; + value *= -2048144789; + value ^= value >>> 13; + value *= -1028477387; + return value ^ value >>> 16; + } + + public static int ceil(double value) { + int i = (int) value; + return value > (double) i ? i + 1 : i; + } + + public static boolean isPowerOfTwo(int value) { + return value != 0 && (value & value - 1) == 0; + } + + public static int smallestEncompassingPowerOfTwo(int value) { + int i = value - 1; + i |= i >> 1; + i |= i >> 2; + i |= i >> 4; + i |= i >> 8; + i |= i >> 16; + return i + 1; + } + + public static int ceilLog2(int value) { + value = isPowerOfTwo(value) ? value : smallestEncompassingPowerOfTwo(value); + return MULTIPLY_DE_BRUIJN_BIT_POSITION[(int) ((long) value * 125613361L >> 27) & 31]; + } + + public static int positiveCeilDiv(int a, int b) { + return -Math.floorDiv(-a, b); + } + + public static int idealHash(int value) { + value ^= value >>> 16; + value *= -2048144789; + value ^= value >>> 13; + value *= -1028477387; + value ^= value >>> 16; + return value; + } + + public static long getUnsignedDivisorMagic(final long divisor, final int bits) { + return ((1L << bits) - 1L) / divisor + 1L; + } + + public static T init(T object, Consumer initializer) { + initializer.accept(object); + return object; + } + + public static T make(final T object, Function initializer) { + return initializer.apply(object); + } + + public static Predicate allOf() { + return o -> true; + } + + @SuppressWarnings("unchecked") + public static Predicate allOf(Predicate a) { + return (Predicate) a; + } + + public static Predicate allOf(Predicate a, Predicate b) { + return o -> a.test(o) && b.test(o); + } + + public static Predicate allOf(Predicate a, Predicate b, Predicate c) { + return o -> a.test(o) && b.test(o) && c.test(o); + } + + public static Predicate allOf(Predicate a, Predicate b, Predicate c, Predicate d) { + return o -> a.test(o) && b.test(o) && c.test(o) && d.test(o); + } + + public static Predicate allOf(Predicate a, Predicate b, Predicate c, Predicate d, Predicate e) { + return o -> a.test(o) && b.test(o) && c.test(o) && d.test(o) && e.test(o); + } + + @SafeVarargs + public static Predicate allOf(Predicate... predicates) { + return o -> { + for (Predicate predicate : predicates) { + if (!predicate.test(o)) { + return false; + } + } + return true; + }; + } + + public static Predicate allOf(List> predicates) { + return switch (predicates.size()) { + case 0 -> allOf(); + case 1 -> allOf((Predicate) predicates.get(0)); + case 2 -> allOf((Predicate) predicates.get(0), (Predicate) predicates.get(1)); + case 3 -> + allOf((Predicate) predicates.get(0), (Predicate) predicates.get(1), (Predicate) predicates.get(2)); + case 4 -> allOf( + (Predicate) predicates.get(0), + (Predicate) predicates.get(1), + (Predicate) predicates.get(2), + (Predicate) predicates.get(3) + ); + case 5 -> allOf( + (Predicate) predicates.get(0), + (Predicate) predicates.get(1), + (Predicate) predicates.get(2), + (Predicate) predicates.get(3), + (Predicate) predicates.get(4) + ); + default -> { + @SuppressWarnings("unchecked") + Predicate[] predicates2 = predicates.toArray(Predicate[]::new); + yield allOf(predicates2); + } + }; + } + + public static Predicate anyOf() { + return o -> false; + } + + @SuppressWarnings("unchecked") + public static Predicate anyOf(Predicate a) { + return (Predicate) a; + } + + public static Predicate anyOf(Predicate a, Predicate b) { + return o -> a.test(o) || b.test(o); + } + + public static Predicate anyOf(Predicate a, Predicate b, Predicate c) { + return o -> a.test(o) || b.test(o) || c.test(o); + } + + public static Predicate anyOf(Predicate a, Predicate b, Predicate c, Predicate d) { + return o -> a.test(o) || b.test(o) || c.test(o) || d.test(o); + } + + public static Predicate anyOf(Predicate a, Predicate b, Predicate c, Predicate d, Predicate e) { + return o -> a.test(o) || b.test(o) || c.test(o) || d.test(o) || e.test(o); + } + + @SafeVarargs + public static Predicate anyOf(Predicate... predicates) { + return o -> { + for (Predicate predicate : predicates) { + if (predicate.test(o)) { + return true; + } + } + return false; + }; + } + + public static Predicate anyOf(List> predicates) { + return switch (predicates.size()) { + case 0 -> anyOf(); + case 1 -> anyOf((Predicate) predicates.get(0)); + case 2 -> anyOf((Predicate) predicates.get(0), (Predicate) predicates.get(1)); + case 3 -> + anyOf((Predicate) predicates.get(0), (Predicate) predicates.get(1), (Predicate) predicates.get(2)); + case 4 -> anyOf( + (Predicate) predicates.get(0), + (Predicate) predicates.get(1), + (Predicate) predicates.get(2), + (Predicate) predicates.get(3) + ); + case 5 -> anyOf( + (Predicate) predicates.get(0), + (Predicate) predicates.get(1), + (Predicate) predicates.get(2), + (Predicate) predicates.get(3), + (Predicate) predicates.get(4) + ); + default -> { + @SuppressWarnings("unchecked") + Predicate[] predicates2 = predicates.toArray(Predicate[]::new); + yield anyOf(predicates2); + } + }; + } + + public static T findPreviousInIterable(Iterable iterable, @Nullable T object) { + Iterator iterator = iterable.iterator(); + T previous = null; + while (iterator.hasNext()) { + T current = iterator.next(); + if (current == object) { + if (previous == null) { + previous = iterator.hasNext() ? Iterators.getLast(iterator) : object; + } + break; + } + previous = current; + } + return previous; + } + + public static float sin(float value) { + return SIN[(int) (value * 10430.378F) & '\uffff']; + } + + public static float cos(float value) { + return SIN[(int) (value * 10430.378F + 16384.0F) & '\uffff']; + } + + public static float sqrt(float value) { + return (float) Math.sqrt(value); + } + + public static T findNextInIterable(Iterable iterable, @Nullable T object) { + Iterator iterator = iterable.iterator(); + T next = iterator.next(); + if (object != null) { + T current = next; + while (current != object) { + if (iterator.hasNext()) { + current = iterator.next(); + } + } + if (iterator.hasNext()) { + return iterator.next(); + } + } + return next; + } + + public static byte packDegrees(float degrees) { + return (byte) fastFloor(degrees * 256.0F / 360.0F); + } + + public static float unpackDegrees(byte degrees) { + return (float) (degrees * 360) / 256.0F; + } + + public static int clamp(int value, int min, int max) { + return Math.min(Math.max(value, min), max); + } + + public static float clamp(float value, float min, float max) { + return value < min ? min : Math.min(value, max); + } + + public static double clamp(double value, double min, double max) { + return value < min ? min : Math.min(value, max); + } + + public static double absMax(double x, double y) { + return Math.max(Math.abs(x), Math.abs(y)); + } + + public static long ceilLong(double value) { + long l = (long) value; + return value > (double) l ? l + 1L : l; + } @SuppressWarnings("unchecked") public static Map castToMap(Object obj, boolean allowNull) { @@ -78,42 +366,6 @@ public class MiscUtils { return List.of(); } - public static Vector3f getAsVector3f(Object o, String option) { - if (o == null) return new Vector3f(); - if (o instanceof List list && list.size() == 3) { - return new Vector3f(Float.parseFloat(list.get(0).toString()), Float.parseFloat(list.get(1).toString()), Float.parseFloat(list.get(2).toString())); - } else { - String stringFormat = o.toString(); - String[] split = stringFormat.split(","); - if (split.length == 3) { - return new Vector3f(Float.parseFloat(split[0]), Float.parseFloat(split[1]), Float.parseFloat(split[2])); - } else if (split.length == 1) { - return new Vector3f(Float.parseFloat(split[0])); - } else { - throw new LocalizedResourceConfigException("warning.config.type.vector3f", stringFormat, option); - } - } - } - - public static Quaternionf getAsQuaternionf(Object o, String option) { - if (o == null) return new Quaternionf(); - if (o instanceof List list && list.size() == 4) { - return new Quaternionf(Float.parseFloat(list.get(0).toString()), Float.parseFloat(list.get(1).toString()), Float.parseFloat(list.get(2).toString()), Float.parseFloat(list.get(3).toString())); - } else { - String stringFormat = o.toString(); - String[] split = stringFormat.split(","); - if (split.length == 4) { - return new Quaternionf(Float.parseFloat(split[0]), Float.parseFloat(split[1]), Float.parseFloat(split[2]), Float.parseFloat(split[3])); - } else if (split.length == 3) { - return QuaternionUtils.toQuaternionf((float) Math.toRadians(Float.parseFloat(split[2])), (float) Math.toRadians(Float.parseFloat(split[1])), (float) Math.toRadians(Float.parseFloat(split[0]))); - } else if (split.length == 1) { - return QuaternionUtils.toQuaternionf(0, (float) -Math.toRadians(Float.parseFloat(split[0])), 0); - } else { - throw new LocalizedResourceConfigException("warning.config.type.quaternionf", stringFormat, option); - } - } - } - @SuppressWarnings("unchecked") public static void deepMergeMaps(Map baseMap, Map mapToMerge) { for (Map.Entry entry : mapToMerge.entrySet()) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/QuaternionUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/QuaternionUtils.java index 96c2081b7..98eaeb41d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/QuaternionUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/QuaternionUtils.java @@ -21,12 +21,12 @@ public class QuaternionUtils { } public static Quaternionf toQuaternionf(float yaw, float pitch, float roll) { - float cy = MCUtils.cos(yaw * 0.5f); - float sy = MCUtils.sin(yaw * 0.5f); - float cp = MCUtils.cos(pitch * 0.5f); - float sp = MCUtils.sin(pitch * 0.5f); - float cr = MCUtils.cos(roll * 0.5f); - float sr = MCUtils.sin(roll * 0.5f); + float cy = MiscUtils.cos(yaw * 0.5f); + float sy = MiscUtils.sin(yaw * 0.5f); + float cp = MiscUtils.cos(pitch * 0.5f); + float sp = MiscUtils.sin(pitch * 0.5f); + float cr = MiscUtils.cos(roll * 0.5f); + float sr = MiscUtils.sin(roll * 0.5f); float w = cr * cp * cy + sr * sp * sy; float x = sr * cp * cy - cr * sp * sy; float y = cr * sp * cy + sr * cp * sy; diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/RandomUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/RandomUtils.java index b659d4c28..7904d1776 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/RandomUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/RandomUtils.java @@ -22,6 +22,10 @@ public final class RandomUtils { return ThreadLocalRandom.current().nextBoolean(); } + public static long generateRandomLong() { + return ThreadLocalRandom.current().nextLong(); + } + public static double triangle(double mode, double deviation) { return mode + deviation * (generateRandomDouble(0,1) - generateRandomDouble(0,1)); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ReflectionUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/ReflectionUtils.java index fa7b61ce5..703571af4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/ReflectionUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ReflectionUtils.java @@ -111,6 +111,22 @@ public class ReflectionUtils { return null; } + @Nullable + public static Field getStaticDeclaredField(final Class clazz, final Class type, final int index) { + int i = 0; + for (final Field field : clazz.getDeclaredFields()) { + if (field.getType() == type) { + if (Modifier.isStatic(field.getModifiers())) { + if (index == i) { + return setAccessible(field); + } + i++; + } + } + } + return null; + } + @Nullable public static Field getDeclaredField(final Class clazz, final Class type, int index) { int i = 0; diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java index 9f240f7dd..ee688e805 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java @@ -1,10 +1,16 @@ package net.momirealms.craftengine.core.util; import com.mojang.datafixers.util.Either; +import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; +import net.momirealms.craftengine.core.world.Vec3d; import org.jetbrains.annotations.Nullable; +import org.joml.Quaternionf; +import org.joml.Vector3f; +import java.nio.file.Path; import java.util.*; import java.util.function.Function; import java.util.function.Supplier; @@ -17,6 +23,13 @@ public final class ResourceConfigUtils { return raw != null ? function.apply(raw) : defaultValue; } + public static String getAsString(@Nullable Object raw) { + if (raw == null) { + return null; + } + return raw.toString(); + } + public static > E getAsEnum(Object o, Class clazz, E defaultValue) { if (o == null) { return defaultValue; @@ -223,4 +236,85 @@ public final class ResourceConfigUtils { } throw new LocalizedResourceConfigException("warning.config.type.map", String.valueOf(obj), option); } + + public static Vector3f getAsVector3f(Object o, String option) { + if (o == null) return new Vector3f(); + if (o instanceof List list && list.size() == 3) { + return new Vector3f(Float.parseFloat(list.get(0).toString()), Float.parseFloat(list.get(1).toString()), Float.parseFloat(list.get(2).toString())); + } else { + String stringFormat = o.toString(); + String[] split = stringFormat.split(","); + if (split.length == 3) { + return new Vector3f(Float.parseFloat(split[0]), Float.parseFloat(split[1]), Float.parseFloat(split[2])); + } else if (split.length == 1) { + return new Vector3f(Float.parseFloat(split[0])); + } else { + throw new LocalizedResourceConfigException("warning.config.type.vector3f", stringFormat, option); + } + } + } + + public static Quaternionf getAsQuaternionf(Object o, String option) { + if (o == null) return new Quaternionf(); + if (o instanceof List list && list.size() == 4) { + return new Quaternionf(Float.parseFloat(list.get(0).toString()), Float.parseFloat(list.get(1).toString()), Float.parseFloat(list.get(2).toString()), Float.parseFloat(list.get(3).toString())); + } else { + String stringFormat = o.toString(); + String[] split = stringFormat.split(","); + if (split.length == 4) { + return new Quaternionf(Float.parseFloat(split[0]), Float.parseFloat(split[1]), Float.parseFloat(split[2]), Float.parseFloat(split[3])); + } else if (split.length == 3) { + return QuaternionUtils.toQuaternionf((float) Math.toRadians(Float.parseFloat(split[2])), (float) Math.toRadians(Float.parseFloat(split[1])), (float) Math.toRadians(Float.parseFloat(split[0]))); + } else if (split.length == 1) { + return QuaternionUtils.toQuaternionf(0, (float) -Math.toRadians(Float.parseFloat(split[0])), 0); + } else { + throw new LocalizedResourceConfigException("warning.config.type.quaternionf", stringFormat, option); + } + } + } + + public static Vec3d getAsVec3d(Object o, String option) { + if (o == null) return new Vec3d(0, 0, 0); + if (o instanceof List list && list.size() == 3) { + return new Vec3d(Double.parseDouble(list.get(0).toString()), Double.parseDouble(list.get(1).toString()), Double.parseDouble(list.get(2).toString())); + } else { + String stringFormat = o.toString(); + String[] split = stringFormat.split(","); + if (split.length == 3) { + return new Vec3d(Double.parseDouble(split[0]), Double.parseDouble(split[1]), Double.parseDouble(split[2])); + } else if (split.length == 1) { + double d = Double.parseDouble(split[0]); + return new Vec3d(d, d, d); + } else { + throw new LocalizedResourceConfigException("warning.config.type.vec3d", stringFormat, option); + } + } + } + + public static void runCatching(Path configPath, String node, Runnable runnable, Supplier config) { + try { + runnable.run(); + } catch (LocalizedException e) { + printWarningRecursively(e, configPath, node); + } catch (Exception e) { + String message = "Unexpected error loading file " + configPath + " - '" + node + "'."; + if (config != null) { + message += " Configuration details: " + config.get(); + } + CraftEngine.instance().logger().warn(message, e); + } + } + + private static void printWarningRecursively(LocalizedException e, Path path, String node) { + for (Throwable t : e.getSuppressed()) { + if (t instanceof LocalizedException suppressed) { + printWarningRecursively(suppressed, path, node); + } + } + if (e instanceof LocalizedResourceConfigException exception) { + exception.setPath(path); + exception.setNode(node); + } + TranslationManager.instance().log(e.node(), e.arguments()); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SectionPosUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/SectionPosUtils.java index 4640a4140..8d2871260 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SectionPosUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SectionPosUtils.java @@ -29,7 +29,7 @@ public class SectionPosUtils { return nearby; } - public static Map toMap(Set sections, int minLightSection, int maxLightSection) { + public static Map toMap(Collection sections, int minLightSection, int maxLightSection) { int nBits = maxLightSection - minLightSection; Map nearby = new Long2ObjectOpenHashMap<>(Math.max(8, sections.size() / 2), 0.5f); for (SectionPos section : sections) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SetMonitor.java b/core/src/main/java/net/momirealms/craftengine/core/util/SetMonitor.java new file mode 100644 index 000000000..39a3fa4ac --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SetMonitor.java @@ -0,0 +1,99 @@ +package net.momirealms.craftengine.core.util; + +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; +import java.util.function.Consumer; + +public class SetMonitor implements Set { + private final Set set; + private final Consumer addConsumer; + private final Consumer removeConsumer; + + public SetMonitor(Set set, Consumer addConsumer, Consumer removeConsumer) { + this.set = set; + this.addConsumer = addConsumer; + this.removeConsumer = removeConsumer; + for (E element : set) { + this.addConsumer.accept(element); + } + } + + @Override + public boolean add(E e) { + this.addConsumer.accept(e); + return this.set.add(e); + } + + @Override + public boolean remove(Object o) { + this.removeConsumer.accept(o); + return this.set.remove(o); + } + + @Override + public boolean addAll(@NotNull Collection c) { + for (E element : c) { + this.addConsumer.accept(element); + } + return this.set.addAll(c); + } + + @Override + public boolean removeAll(@NotNull Collection c) { + for (Object o : c) { + this.removeConsumer.accept(o); + } + return this.set.removeAll(c); + } + + @Override + public void clear() { + for (E element : this.set) { + this.removeConsumer.accept(element); + } + this.set.clear(); + } + + @Override + public int size() { + return this.set.size(); + } + + @Override + public boolean isEmpty() { + return this.set.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return this.set.contains(o); + } + + @Override + public @NotNull Iterator iterator() { + return this.set.iterator(); + } + + @Override + public @NotNull Object @NotNull [] toArray() { + return this.set.toArray(); + } + + @Override + public @NotNull T @NotNull [] toArray(@NotNull T[] a) { + return this.set.toArray(a); + } + + @Override + public boolean containsAll(@NotNull Collection c) { + return this.set.containsAll(c); + } + + @Override + public boolean retainAll(@NotNull Collection c) { + return this.set.retainAll(c); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/StringUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/StringUtils.java index 4c44e0054..a399dd63c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/StringUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/StringUtils.java @@ -1,5 +1,7 @@ package net.momirealms.craftengine.core.util; +import java.nio.charset.StandardCharsets; + public final class StringUtils { private StringUtils() {} @@ -39,4 +41,12 @@ public final class StringUtils { } return new String(chars); } + + public static String fromBytes(byte[] bytes, int index) { + byte[] decodedBytes = new byte[bytes.length]; + for (int i = 0; i < bytes.length; i++) { + decodedBytes[i] = (byte) (bytes[i] ^ ((byte) index)); + } + return new String(decodedBytes, StandardCharsets.UTF_8); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java b/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java index 6b36f6bc3..2112ecd15 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java @@ -33,6 +33,7 @@ public class VersionHelper { private static final boolean v1_21_6; private static final boolean v1_21_7; private static final boolean v1_21_8; + private static final boolean v1_21_9; static { try (InputStream inputStream = Class.forName("net.minecraft.obfuscate.DontObfuscate").getResourceAsStream("/version.json")) { @@ -68,6 +69,7 @@ public class VersionHelper { v1_21_6 = version >= 12106; v1_21_7 = version >= 12107; v1_21_8 = version >= 12108; + v1_21_9 = version >= 12109; majorVersion = major; minorVersion = minor; @@ -239,4 +241,8 @@ public class VersionHelper { public static boolean isOrAbove1_21_8() { return v1_21_8; } + + public static boolean isOrAbove1_21_9() { + return v1_21_9; + } } \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/BlockPos.java b/core/src/main/java/net/momirealms/craftengine/core/world/BlockPos.java index 686b5e95d..86905697d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/BlockPos.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/BlockPos.java @@ -1,9 +1,10 @@ package net.momirealms.craftengine.core.world; import net.momirealms.craftengine.core.util.Direction; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; public class BlockPos extends Vec3i { + public static final BlockPos ZERO = new BlockPos(0, 0, 0); public BlockPos(int x, int y, int z) { super(x, y, z); @@ -22,7 +23,7 @@ public class BlockPos extends Vec3i { } public static BlockPos fromVec3d(Vec3d vec) { - return new BlockPos(MCUtils.fastFloor(vec.x), MCUtils.fastFloor(vec.y), MCUtils.fastFloor(vec.z)); + return new BlockPos(MiscUtils.fastFloor(vec.x), MiscUtils.fastFloor(vec.y), MiscUtils.fastFloor(vec.z)); } public static BlockPos of(long packedPos) { @@ -54,4 +55,48 @@ public class BlockPos extends Vec3i { public BlockPos offset(int x, int y, int z) { return x == 0 && y == 0 && z == 0 ? this : new BlockPos(this.x() + x, this.y() + y, this.z() + z); } + + public BlockPos immutable() { + return this; + } + + @Override + public BlockPos north() { + return new BlockPos(this.x(), this.y(), this.z() - 1); + } + + @Override + public BlockPos north(int distance) { + return distance == 0 ? this.immutable() : new BlockPos(this.x(), this.y(), this.z() - distance); + } + + @Override + public BlockPos south() { + return new BlockPos(this.x(), this.y(), this.z() + 1); + } + + @Override + public BlockPos south(int distance) { + return distance == 0 ? this.immutable() : new BlockPos(this.x(), this.y(), this.z() + distance); + } + + @Override + public BlockPos west() { + return new BlockPos(this.x() - 1, this.y(), this.z()); + } + + @Override + public BlockPos west(int distance) { + return distance == 0 ? this.immutable() : new BlockPos(this.x() - distance, this.y(), this.z()); + } + + @Override + public BlockPos east() { + return new BlockPos(this.x() + 1, this.y(), this.z()); + } + + @Override + public BlockPos east(int distance) { + return distance == 0 ? this.immutable() : new BlockPos(this.x() + distance, this.y(), this.z()); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java b/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java index 62dfa77da..60098466b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java @@ -2,34 +2,44 @@ package net.momirealms.craftengine.core.world; import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable; import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.entity.tick.TickingBlockEntity; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask; +import net.momirealms.craftengine.core.util.BlockEntityTickersList; import net.momirealms.craftengine.core.world.chunk.CEChunk; import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor; import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; import org.jetbrains.annotations.Nullable; import java.io.IOException; -import java.util.Collection; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; public abstract class CEWorld { public static final String REGION_DIRECTORY = "craftengine"; - protected final World world; + public final World world; protected final ConcurrentLong2ReferenceChainedHashTable loadedChunkMap; protected final WorldDataStorage worldDataStorage; protected final WorldHeight worldHeightAccessor; - protected final Set updatedSectionSet = ConcurrentHashMap.newKeySet(128); - - private CEChunk lastChunk; - private long lastChunkPos; + protected List pendingLightSections = new ArrayList<>(); + protected final Set lightSections = ConcurrentHashMap.newKeySet(128); + protected final BlockEntityTickersList tickingSyncBlockEntities = new BlockEntityTickersList(); + protected final List pendingSyncTickingBlockEntities = new ArrayList<>(); + protected final BlockEntityTickersList tickingAsyncBlockEntities = new BlockEntityTickersList(); + protected final List pendingAsyncTickingBlockEntities = new ArrayList<>(); + protected volatile boolean isTickingSyncBlockEntities = false; + protected volatile boolean isTickingAsyncBlockEntities = false; + protected volatile boolean isUpdatingLights = false; + protected SchedulerTask syncTickTask; + protected SchedulerTask asyncTickTask; public CEWorld(World world, StorageAdaptor adaptor) { this.world = world; this.loadedChunkMap = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(1024, 0.5f); this.worldDataStorage = adaptor.adapt(world); this.worldHeightAccessor = world.worldHeight(); - this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS; } public CEWorld(World world, WorldDataStorage dataStorage) { @@ -37,7 +47,32 @@ public abstract class CEWorld { this.loadedChunkMap = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(1024, 0.5f); this.worldDataStorage = dataStorage; this.worldHeightAccessor = world.worldHeight(); - this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS; + } + + public void setTicking(boolean ticking) { + if (ticking) { + if (this.syncTickTask == null || this.syncTickTask.cancelled()) { + this.syncTickTask = CraftEngine.instance().scheduler().sync().runRepeating(this::syncTick, 1, 1); + } + if (this.asyncTickTask == null || this.asyncTickTask.cancelled()) { + this.asyncTickTask = CraftEngine.instance().scheduler().sync().runAsyncRepeating(this::asyncTick, 1, 1); + } + } else { + if (this.syncTickTask != null && !this.syncTickTask.cancelled()) { + this.syncTickTask.cancel(); + } + if (this.asyncTickTask != null && !this.asyncTickTask.cancelled()) { + this.asyncTickTask.cancel(); + } + } + } + + public String name() { + return this.world.name(); + } + + public UUID uuid() { + return this.world.uuid(); } public void save() { @@ -45,7 +80,7 @@ public abstract class CEWorld { for (ConcurrentLong2ReferenceChainedHashTable.TableEntry entry : this.loadedChunkMap.entrySet()) { CEChunk chunk = entry.getValue(); if (chunk.dirty()) { - worldDataStorage.writeChunkAt(new ChunkPos(entry.getKey()), chunk); + this.worldDataStorage.writeChunkAt(new ChunkPos(entry.getKey()), chunk); chunk.setDirty(false); } } @@ -55,7 +90,7 @@ public abstract class CEWorld { } public World world() { - return world; + return this.world; } public boolean isChunkLoaded(final long chunkPos) { @@ -68,15 +103,11 @@ public abstract class CEWorld { public void removeLoadedChunk(CEChunk chunk) { this.loadedChunkMap.remove(chunk.chunkPos().longKey()); - if (this.lastChunk == chunk) { - this.lastChunk = null; - this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS; - } } @Nullable public CEChunk getChunkAtIfLoaded(long chunkPos) { - return getChunkAtIfLoadedMainThread(chunkPos); + return this.loadedChunkMap.get(chunkPos); } @Nullable @@ -85,27 +116,11 @@ public abstract class CEWorld { } @Nullable - public CEChunk getChunkAtIfLoadedMainThread(long chunkPos) { - if (chunkPos == this.lastChunkPos) { - return this.lastChunk; - } - CEChunk chunk = this.loadedChunkMap.get(chunkPos); - if (chunk != null) { - this.lastChunk = chunk; - this.lastChunkPos = chunkPos; - } - return chunk; + public CEChunk getChunkAtIfLoaded(ChunkPos chunkPos) { + return getChunkAtIfLoaded(chunkPos.longKey); } @Nullable - public CEChunk getChunkAtIfLoadedMainThread(int x, int z) { - return getChunkAtIfLoadedMainThread(ChunkPos.asLong(x, z)); - } - - public WorldHeight worldHeight() { - return worldHeightAccessor; - } - public ImmutableBlockState getBlockStateAtIfLoaded(int x, int y, int z) { CEChunk chunk = getChunkAtIfLoaded(x >> 4, z >> 4); if (chunk == null) { @@ -114,6 +129,7 @@ public abstract class CEWorld { return chunk.getBlockState(x, y, z); } + @Nullable public ImmutableBlockState getBlockStateAtIfLoaded(BlockPos blockPos) { CEChunk chunk = getChunkAtIfLoaded(blockPos.x() >> 4, blockPos.z() >> 4); if (chunk == null) { @@ -123,7 +139,7 @@ public abstract class CEWorld { } public boolean setBlockStateAtIfLoaded(BlockPos blockPos, ImmutableBlockState blockState) { - if (worldHeightAccessor.isOutsideBuildHeight(blockPos)) { + if (this.worldHeightAccessor.isOutsideBuildHeight(blockPos)) { return false; } CEChunk chunk = getChunkAtIfLoaded(blockPos.x() >> 4, blockPos.z() >> 4); @@ -134,17 +150,105 @@ public abstract class CEWorld { return true; } + @Nullable + public BlockEntity getBlockEntityAtIfLoaded(BlockPos blockPos) { + if (this.worldHeightAccessor.isOutsideBuildHeight(blockPos)) { + return null; + } + CEChunk chunk = getChunkAtIfLoaded(blockPos.x() >> 4, blockPos.z() >> 4); + if (chunk == null) { + return null; + } + return chunk.getBlockEntity(blockPos, true); + } + public WorldDataStorage worldDataStorage() { return worldDataStorage; } - public void sectionLightUpdated(SectionPos pos) { - this.updatedSectionSet.add(pos); - } - public void sectionLightUpdated(Collection pos) { - this.updatedSectionSet.addAll(pos); + if (this.isUpdatingLights) { + this.pendingLightSections.addAll(pos); + } else { + this.lightSections.addAll(pos); + } } - public abstract void tick(); + public WorldHeight worldHeight() { + return this.worldHeightAccessor; + } + + public void syncTick() { + this.tickSyncBlockEntities(); + if (!Config.asyncLightUpdate()) { + this.updateLight(); + } + } + + public void asyncTick() { + this.tickAsyncBlockEntities(); + if (Config.asyncLightUpdate()) { + this.updateLight(); + } + } + + public abstract void updateLight(); + + public void addSyncBlockEntityTicker(TickingBlockEntity ticker) { + if (this.isTickingSyncBlockEntities) { + this.pendingSyncTickingBlockEntities.add(ticker); + } else { + this.tickingSyncBlockEntities.add(ticker); + } + } + + public void addAsyncBlockEntityTicker(TickingBlockEntity ticker) { + if (this.isTickingAsyncBlockEntities) { + this.pendingAsyncTickingBlockEntities.add(ticker); + } else { + this.tickingAsyncBlockEntities.add(ticker); + } + } + + protected void tickSyncBlockEntities() { + this.isTickingSyncBlockEntities = true; + if (!this.pendingSyncTickingBlockEntities.isEmpty()) { + this.tickingSyncBlockEntities.addAll(this.pendingSyncTickingBlockEntities); + this.pendingSyncTickingBlockEntities.clear(); + } + if (!this.tickingSyncBlockEntities.isEmpty()) { + Object[] entities = this.tickingSyncBlockEntities.elements(); + for (int i = 0, size = this.tickingSyncBlockEntities.size(); i < size; i++) { + TickingBlockEntity entity = (TickingBlockEntity) entities[i]; + if (entity.isValid()) { + entity.tick(); + } else { + this.tickingSyncBlockEntities.markAsRemoved(i); + } + } + this.tickingSyncBlockEntities.removeMarkedEntries(); + } + this.isTickingSyncBlockEntities = false; + } + + protected void tickAsyncBlockEntities() { + this.isTickingAsyncBlockEntities = true; + if (!this.pendingAsyncTickingBlockEntities.isEmpty()) { + this.tickingAsyncBlockEntities.addAll(this.pendingAsyncTickingBlockEntities); + this.pendingAsyncTickingBlockEntities.clear(); + } + if (!this.tickingAsyncBlockEntities.isEmpty()) { + Object[] entities = this.tickingAsyncBlockEntities.elements(); + for (int i = 0, size = this.tickingAsyncBlockEntities.size(); i < size; i++) { + TickingBlockEntity entity = (TickingBlockEntity) entities[i]; + if (entity.isValid()) { + entity.tick(); + } else { + this.tickingAsyncBlockEntities.markAsRemoved(i); + } + } + this.tickingAsyncBlockEntities.removeMarkedEntries(); + } + this.isTickingAsyncBlockEntities = false; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/ChunkPos.java b/core/src/main/java/net/momirealms/craftengine/core/world/ChunkPos.java index a053afd79..32eb4788f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/ChunkPos.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/ChunkPos.java @@ -65,10 +65,23 @@ public class ChunkPos { return longKey; } + public ChunkPos[] adjacentChunkPos() { + return adjacentChunkPos(this); + } + public static long asLong(int chunkX, int chunkZ) { return (long) chunkX & 4294967295L | ((long) chunkZ & 4294967295L) << 32; } + public static ChunkPos[] adjacentChunkPos(ChunkPos chunkPos) { + return new ChunkPos[] { + new ChunkPos(chunkPos.x, chunkPos.z - 1), + new ChunkPos(chunkPos.x, chunkPos.z + 1), + new ChunkPos(chunkPos.x + 1, chunkPos.z), + new ChunkPos(chunkPos.x - 1, chunkPos.z) + }; + } + @Override public final boolean equals(Object o) { if (o == null) return false; diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/EntityHitResult.java b/core/src/main/java/net/momirealms/craftengine/core/world/EntityHitResult.java index 36c0f2e96..6944337ed 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/EntityHitResult.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/EntityHitResult.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.core.world; import net.momirealms.craftengine.core.util.Direction; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; public class EntityHitResult { private final Direction direction; @@ -23,9 +23,9 @@ public class EntityHitResult { } private BlockPos getBlockPos() { - int x = MCUtils.fastFloor(this.position.x); - int y = MCUtils.fastFloor(this.position.y); - int z = MCUtils.fastFloor(this.position.z); + int x = MiscUtils.fastFloor(this.position.x); + int y = MiscUtils.fastFloor(this.position.y); + int z = MiscUtils.fastFloor(this.position.z); if (this.direction == Direction.UP) { if (this.position.y % 1 == 0) { y--; diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/ExistingBlock.java b/core/src/main/java/net/momirealms/craftengine/core/world/ExistingBlock.java index 947bc664d..d6fac39cf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/ExistingBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/ExistingBlock.java @@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.world; import net.momirealms.craftengine.core.block.BlockStateWrapper; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; -import net.momirealms.craftengine.core.block.state.StatePropertyAccessor; +import net.momirealms.craftengine.core.block.StatePropertyAccessor; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.NotNull; diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/SectionPos.java b/core/src/main/java/net/momirealms/craftengine/core/world/SectionPos.java index 3897374da..fac06da92 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/SectionPos.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/SectionPos.java @@ -13,4 +13,8 @@ public class SectionPos extends Vec3i { public static SectionPos of(BlockPos pos) { return new SectionPos(pos.x() >> 4, pos.y() >> 4, pos.z() >> 4); } + + public static int sectionRelative(int rel) { + return rel & 15; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/Vec3d.java b/core/src/main/java/net/momirealms/craftengine/core/world/Vec3d.java index 1a367fc03..e24e99037 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/Vec3d.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/Vec3d.java @@ -1,8 +1,9 @@ package net.momirealms.craftengine.core.world; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; public class Vec3d implements Position { + public static final Vec3d ZERO = new Vec3d(0, 0, 0); public final double x; public final double y; public final double z; @@ -14,7 +15,7 @@ public class Vec3d implements Position { } public Vec3d toCenter() { - return new Vec3d(MCUtils.fastFloor(x) + 0.5, MCUtils.fastFloor(y) + 0.5, MCUtils.fastFloor(z) + 0.5); + return new Vec3d(MiscUtils.fastFloor(x) + 0.5, MiscUtils.fastFloor(y) + 0.5, MiscUtils.fastFloor(z) + 0.5); } public Vec3d add(Vec3d vec) { @@ -45,6 +46,13 @@ public class Vec3d implements Position { return atLowerCornerWithOffset(vec, 0.5, deltaY, 0.5); } + public static double distanceToSqr(Vec3d vec1, Vec3d vec2) { + double dx = vec2.x - vec1.x; + double dy = vec2.y - vec1.y; + double dz = vec2.z - vec1.z; + return dx * dx + dy * dy + dz * dz; + } + @Override public double x() { return x; diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/Vec3i.java b/core/src/main/java/net/momirealms/craftengine/core/world/Vec3i.java index 7d49c6aaa..82b2e4bbb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/Vec3i.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/Vec3i.java @@ -47,12 +47,12 @@ public class Vec3i implements Comparable { @Override public boolean equals(Object object) { - return this == object || object instanceof Vec3i vec3i && this.x() == vec3i.x() && this.y() == vec3i.y() && this.z() == vec3i.z(); + return this == object || object instanceof Vec3i vec3i && this.x == vec3i.x && this.y == vec3i.y && this.z == vec3i.z; } @Override public int hashCode() { - return (this.y() + this.z() * 31) * 31 + this.x(); + return (this.y + this.z * 31) * 31 + this.x; } @Override @@ -92,4 +92,37 @@ public class Vec3i implements Comparable { return this.y() - vec3i.y(); } } + + public Vec3i north() { + return this.north(1); + } + + public Vec3i north(int distance) { + return this.relative(Direction.NORTH, distance); + } + + public Vec3i south() { + return this.south(1); + } + + public Vec3i south(int distance) { + return this.relative(Direction.SOUTH, distance); + } + + public Vec3i west() { + return this.west(1); + } + + public Vec3i west(int distance) { + return this.relative(Direction.WEST, distance); + } + + public Vec3i east() { + return this.east(1); + } + + public Vec3i east(int distance) { + return this.relative(Direction.EAST, distance); + } + } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/World.java b/core/src/main/java/net/momirealms/craftengine/core/world/World.java index a7d960454..6bf1e2eed 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/World.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/World.java @@ -1,20 +1,26 @@ package net.momirealms.craftengine.core.world; import net.momirealms.craftengine.core.block.BlockStateWrapper; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.sound.SoundSource; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.world.particle.ParticleData; +import net.momirealms.craftengine.core.world.particle.ParticleType; import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; import java.nio.file.Path; +import java.util.List; import java.util.UUID; public interface World { + CEWorld storageWorld(); + Object platformWorld(); Object serverWorld(); @@ -29,6 +35,10 @@ public interface World { void setBlockAt(int x, int y, int z, BlockStateWrapper blockState, int flags); + default void setBlockAt(int x, int y, int z, ImmutableBlockState blockState, int flags) { + this.setBlockAt(x, y, z, blockState.customBlockState(), flags); + } + String name(); Path directory(); @@ -49,7 +59,9 @@ public interface World { void levelEvent(int id, BlockPos pos, int data); - void spawnParticle(Position location, Key particle, int count, double xOffset, double yOffset, double zOffset, double speed, @Nullable ParticleData extraData, @NotNull Context context); + void spawnParticle(Position location, ParticleType particle, int count, double xOffset, double yOffset, double zOffset, double speed, @Nullable ParticleData extraData, @NotNull Context context); long time(); + + List getTrackedBy(ChunkPos pos); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java index fb00cc0dc..d48e09472 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java @@ -1,38 +1,61 @@ package net.momirealms.craftengine.core.world.chunk; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import net.momirealms.craftengine.core.block.BlockEntityState; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.entity.render.ConstantBlockEntityRenderer; +import net.momirealms.craftengine.core.block.entity.render.DynamicBlockEntityRenderer; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; +import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; +import net.momirealms.craftengine.core.block.entity.tick.*; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.world.*; +import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntityRendererSerializer; +import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntitySerializer; +import net.momirealms.sparrow.nbt.ListTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Map; +import java.util.*; +import java.util.concurrent.locks.ReentrantReadWriteLock; public class CEChunk { - private boolean loaded; - private final CEWorld world; - private final ChunkPos chunkPos; - private final CESection[] sections; - private final WorldHeight worldHeightAccessor; - private final Map blockEntities; - private boolean dirty; + public final CEWorld world; + public final ChunkPos chunkPos; + public final CESection[] sections; + public final WorldHeight worldHeightAccessor; + public final Map blockEntities; // 从区域线程上访问,安全 + public final Map tickingSyncBlockEntitiesByPos; // 从区域线程上访问,安全 + public final Map tickingAsyncBlockEntitiesByPos; // 从区域线程上访问,安全 + public final Map constantBlockEntityRenderers; // 会从区域线程上读写,netty线程上读取 + public final Map dynamicBlockEntityRenderers; // 会从区域线程上读写,netty线程上读取 + private final ReentrantReadWriteLock renderLock = new ReentrantReadWriteLock(); + private volatile boolean dirty; + private volatile boolean loaded; public CEChunk(CEWorld world, ChunkPos chunkPos) { this.world = world; this.chunkPos = chunkPos; this.worldHeightAccessor = world.worldHeight(); this.sections = new CESection[this.worldHeightAccessor.getSectionsCount()]; - this.blockEntities = new Int2ObjectOpenHashMap<>(16, 0.5f); + this.blockEntities = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.dynamicBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.tickingSyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.tickingAsyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); this.fillEmptySection(); } - public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, Map blockEntities) { + public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, @Nullable ListTag blockEntitiesTag, @Nullable ListTag itemDisplayBlockRenders) { this.world = world; this.chunkPos = chunkPos; - this.blockEntities = blockEntities; this.worldHeightAccessor = world.worldHeight(); + this.dynamicBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.tickingSyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.tickingAsyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); int sectionCount = this.worldHeightAccessor.getSectionsCount(); this.sections = new CESection[sectionCount]; if (sections != null) { @@ -44,10 +67,291 @@ public class CEChunk { } } this.fillEmptySection(); + if (blockEntitiesTag != null) { + this.blockEntities = new Object2ObjectOpenHashMap<>(Math.max(blockEntitiesTag.size(), 10), 0.5f); + List blockEntities = DefaultBlockEntitySerializer.deserialize(this, blockEntitiesTag); + for (BlockEntity blockEntity : blockEntities) { + this.setBlockEntity(blockEntity); + } + } else { + this.blockEntities = new Object2ObjectOpenHashMap<>(10, 0.5f); + } + if (itemDisplayBlockRenders != null) { + this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(Math.max(itemDisplayBlockRenders.size(), 10), 0.5f); + List blockEntityRendererPoses = DefaultBlockEntityRendererSerializer.deserialize(this.chunkPos, itemDisplayBlockRenders); + for (BlockPos pos : blockEntityRendererPoses) { + this.addConstantBlockEntityRenderer(pos); + } + } else { + this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); + } } - public Map blockEntities() { - return this.blockEntities; + public void spawnBlockEntities(Player player) { + try { + this.renderLock.readLock().lock(); + for (ConstantBlockEntityRenderer renderer : this.constantBlockEntityRenderers.values()) { + renderer.show(player); + } + for (DynamicBlockEntityRenderer renderer : this.dynamicBlockEntityRenderers.values()) { + renderer.show(player); + } + } finally { + this.renderLock.readLock().unlock(); + } + } + + public void despawnBlockEntities(Player player) { + try { + this.renderLock.readLock().lock(); + for (ConstantBlockEntityRenderer renderer : this.constantBlockEntityRenderers.values()) { + renderer.hide(player); + } + for (DynamicBlockEntityRenderer renderer : this.dynamicBlockEntityRenderers.values()) { + renderer.hide(player); + } + } finally { + this.renderLock.readLock().unlock(); + } + } + + public void addConstantBlockEntityRenderer(BlockPos pos) { + this.addConstantBlockEntityRenderer(pos, this.getBlockState(pos)); + } + + public void addConstantBlockEntityRenderer(BlockPos pos, ImmutableBlockState state) { + BlockEntityElementConfig[] renderers = state.constantRenderers(); + if (renderers != null && renderers.length > 0) { + BlockEntityElement[] elements = new BlockEntityElement[renderers.length]; + World wrappedWorld = this.world.world(); + for (int i = 0; i < elements.length; i++) { + elements[i] = renderers[i].create(wrappedWorld, pos); + } + ConstantBlockEntityRenderer renderer = new ConstantBlockEntityRenderer(elements); + for (Player player : getTrackedBy()) { + renderer.show(player); + } + try { + this.renderLock.writeLock().lock(); + this.constantBlockEntityRenderers.put(pos, renderer); + } finally { + this.renderLock.writeLock().unlock(); + } + } + } + + public void removeConstantBlockEntityRenderer(BlockPos pos) { + try { + this.renderLock.writeLock().lock(); + ConstantBlockEntityRenderer removed = this.constantBlockEntityRenderers.remove(pos); + if (removed != null) { + for (Player player : getTrackedBy()) { + removed.hide(player); + } + } + } finally { + this.renderLock.writeLock().unlock(); + } + } + + private void removeDynamicBlockEntityRenderer(BlockPos pos) { + try { + this.renderLock.writeLock().lock(); + DynamicBlockEntityRenderer renderer = this.dynamicBlockEntityRenderers.remove(pos); + if (renderer != null) { + for (Player player : getTrackedBy()) { + renderer.hide(player); + } + } + } finally { + this.renderLock.writeLock().unlock(); + } + } + + public void addBlockEntity(BlockEntity blockEntity) { + this.setBlockEntity(blockEntity); + this.replaceOrCreateTickingBlockEntity(blockEntity); + this.createDynamicBlockEntityRenderer(blockEntity); + } + + public void removeBlockEntity(BlockPos blockPos) { + BlockEntity removedBlockEntity = this.blockEntities.remove(blockPos); + if (removedBlockEntity != null) { + removedBlockEntity.setValid(false); + } + this.removeBlockEntityTicker(blockPos); + this.removeDynamicBlockEntityRenderer(blockPos); + } + + public void activateAllBlockEntities() { + for (BlockEntity blockEntity : this.blockEntities.values()) { + blockEntity.setValid(true); + this.replaceOrCreateTickingBlockEntity(blockEntity); + this.createDynamicBlockEntityRenderer(blockEntity); + } + for (ConstantBlockEntityRenderer renderer : this.constantBlockEntityRenderers.values()) { + renderer.activate(); + } + } + + public List getTrackedBy() { + return this.world.world.getTrackedBy(this.chunkPos); + } + + public void deactivateAllBlockEntities() { + this.blockEntities.values().forEach(e -> e.setValid(false)); + this.constantBlockEntityRenderers.values().forEach(ConstantBlockEntityRenderer::deactivate); + this.dynamicBlockEntityRenderers.clear(); + this.tickingSyncBlockEntitiesByPos.values().forEach((ticker) -> ticker.setTicker(DummyTickingBlockEntity.INSTANCE)); + this.tickingSyncBlockEntitiesByPos.clear(); + this.tickingAsyncBlockEntitiesByPos.values().forEach((ticker) -> ticker.setTicker(DummyTickingBlockEntity.INSTANCE)); + this.tickingAsyncBlockEntitiesByPos.clear(); + } + + @SuppressWarnings("unchecked") + public void replaceOrCreateTickingBlockEntity(T blockEntity) { + ImmutableBlockState blockState = blockEntity.blockState(); + EntityBlockBehavior blockBehavior = blockState.behavior().getEntityBehavior(); + if (blockBehavior == null) { + this.removeBlockEntityTicker(blockEntity.pos()); + } else { + BlockEntityTicker syncTicker = (BlockEntityTicker) blockBehavior.createSyncBlockEntityTicker(this.world, blockState, blockEntity.type()); + if (syncTicker != null) { + this.tickingSyncBlockEntitiesByPos.compute(blockEntity.pos(), ((pos, previousTicker) -> { + TickingBlockEntity newTicker = new TickingBlockEntityImpl<>(this, blockEntity, syncTicker); + if (previousTicker != null) { + previousTicker.setTicker(newTicker); + return previousTicker; + } else { + ReplaceableTickingBlockEntity replaceableTicker = new ReplaceableTickingBlockEntity(newTicker); + this.world.addSyncBlockEntityTicker(replaceableTicker); + return replaceableTicker; + } + })); + } else { + this.removeSyncBlockEntityTicker(blockEntity.pos()); + } + BlockEntityTicker asyncTicker = (BlockEntityTicker) blockBehavior.createAsyncBlockEntityTicker(this.world, blockState, blockEntity.type()); + if (asyncTicker != null) { + this.tickingAsyncBlockEntitiesByPos.compute(blockEntity.pos(), ((pos, previousTicker) -> { + TickingBlockEntity newTicker = new TickingBlockEntityImpl<>(this, blockEntity, asyncTicker); + if (previousTicker != null) { + previousTicker.setTicker(newTicker); + return previousTicker; + } else { + ReplaceableTickingBlockEntity replaceableTicker = new ReplaceableTickingBlockEntity(newTicker); + this.world.addAsyncBlockEntityTicker(replaceableTicker); + return replaceableTicker; + } + })); + } else { + this.removeAsyncBlockEntityTicker(blockEntity.pos()); + } + } + } + + public void createDynamicBlockEntityRenderer(T blockEntity) { + DynamicBlockEntityRenderer renderer = blockEntity.blockEntityRenderer(); + if (renderer != null) { + DynamicBlockEntityRenderer previous; + try { + this.renderLock.writeLock().lock(); + previous = this.dynamicBlockEntityRenderers.put(blockEntity.pos(), renderer); + } finally { + this.renderLock.writeLock().unlock(); + } + if (previous != null) { + if (previous == renderer) { + return; + } + for (Player player : getTrackedBy()) { + previous.hide(player); + renderer.show(player); + } + } else { + for (Player player : getTrackedBy()) { + renderer.show(player); + } + } + } else { + this.removeDynamicBlockEntityRenderer(blockEntity.pos()); + } + } + + private void removeSyncBlockEntityTicker(BlockPos pos) { + ReplaceableTickingBlockEntity e1 = this.tickingSyncBlockEntitiesByPos.remove(pos); + if (e1 != null) { + e1.setTicker(DummyTickingBlockEntity.INSTANCE); + } + } + + private void removeAsyncBlockEntityTicker(BlockPos pos) { + ReplaceableTickingBlockEntity e2 = this.tickingAsyncBlockEntitiesByPos.remove(pos); + if (e2 != null) { + e2.setTicker(DummyTickingBlockEntity.INSTANCE); + } + } + + private void removeBlockEntityTicker(BlockPos pos) { + removeSyncBlockEntityTicker(pos); + removeAsyncBlockEntityTicker(pos); + } + + public void setBlockEntity(BlockEntity blockEntity) { + BlockPos pos = blockEntity.pos(); + ImmutableBlockState blockState = this.getBlockState(pos); + if (!blockState.hasBlockEntity()) { + Debugger.BLOCK_ENTITY.debug(() -> "Failed to add invalid block entity " + blockEntity.saveAsTag() + " at " + pos); + return; + } + // 设置方块实体所在世界 + blockEntity.setWorld(this.world); + blockEntity.setValid(true); + BlockEntity previous = this.blockEntities.put(pos, blockEntity); + // 标记旧的方块实体无效 + if (previous != null && previous != blockEntity) { + previous.setValid(false); + } + } + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos, boolean create) { + BlockEntity blockEntity = this.blockEntities.get(pos); + if (blockEntity == null) { + if (create) { + blockEntity = createBlockEntity(pos); + if (blockEntity != null) { + this.addBlockEntity(blockEntity); + } + } + } else { + if (!blockEntity.isValid()) { + this.blockEntities.remove(pos); + return null; + } + } + return blockEntity; + } + + private BlockEntity createBlockEntity(BlockPos pos) { + ImmutableBlockState blockState = this.getBlockState(pos); + if (!blockState.hasBlockEntity()) { + return null; + } + return Objects.requireNonNull(blockState.behavior().getEntityBehavior()).createBlockEntity(pos, blockState); + } + + public Collection blockEntities() { + return Collections.unmodifiableCollection(this.blockEntities.values()); + } + + public List constantBlockEntityRenderers() { + try { + this.renderLock.readLock().lock(); + return new ArrayList<>(this.constantBlockEntityRenderers.keySet()); + } finally { + this.renderLock.readLock().unlock(); + } } public boolean dirty() { @@ -69,9 +373,9 @@ public class CEChunk { } private void fillEmptySection() { - for (int i = 0; i < sections.length; ++i) { - if (sections[i] == null) { - sections[i] = new CESection(world.worldHeight().getSectionYFromSectionIndex(i), + for (int i = 0; i < this.sections.length; ++i) { + if (this.sections[i] == null) { + this.sections[i] = new CESection(this.world.worldHeight().getSectionYFromSectionIndex(i), new PalettedContainer<>(null, EmptyBlock.STATE, PalettedContainer.PaletteProvider.CUSTOM_BLOCK_STATE)); } } @@ -93,17 +397,17 @@ public class CEChunk { } } - @Nullable + @NotNull public ImmutableBlockState getBlockState(BlockPos pos) { return getBlockState(pos.x(), pos.y(), pos.z()); } - @Nullable + @NotNull public ImmutableBlockState getBlockState(int x, int y, int z) { int index = sectionIndex(SectionPos.blockToSectionCoord(y)); CESection section = this.sections[index]; if (section == null) { - return null; + return EmptyBlock.STATE; } return section.getBlockState((y & 15) << 8 | (z & 15) << 4 | x & 15); } @@ -128,32 +432,34 @@ public class CEChunk { @NotNull public CEWorld world() { - return world; + return this.world; } @NotNull public ChunkPos chunkPos() { - return chunkPos; + return this.chunkPos; } @NotNull public CESection[] sections() { - return sections; + return this.sections; } public boolean isLoaded() { - return loaded; + return this.loaded; } public void load() { if (this.loaded) return; this.world.addLoadedChunk(this); + this.activateAllBlockEntities(); this.loaded = true; } public void unload() { if (!this.loaded) return; this.world.removeLoadedChunk(this); + this.deactivateAllBlockEntities(); this.loaded = false; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CESection.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CESection.java index a7f9553ab..344509b5c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CESection.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CESection.java @@ -9,8 +9,8 @@ public class CESection { public static final int SECTION_HEIGHT = 16; public static final int SECTION_SIZE = SECTION_WIDTH * SECTION_WIDTH * SECTION_HEIGHT; - private final int sectionY; - private final PalettedContainer statesContainer; + public final int sectionY; + public final PalettedContainer statesContainer; public CESection(int sectionY, PalettedContainer statesContainer) { this.sectionY = sectionY; @@ -53,6 +53,6 @@ public class CESection { } public int sectionY() { - return sectionY; + return this.sectionY; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/ChunkStatus.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/ChunkStatus.java new file mode 100644 index 000000000..744068aa6 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/ChunkStatus.java @@ -0,0 +1,7 @@ +package net.momirealms.craftengine.core.world.chunk; + +public class ChunkStatus { + + public ChunkStatus() { + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PalettedContainer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PalettedContainer.java index 211addb11..9ce501846 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PalettedContainer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PalettedContainer.java @@ -7,7 +7,7 @@ import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.FriendlyByteBuf; import net.momirealms.craftengine.core.util.IndexedIterable; -import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.VersionHelper; import org.jetbrains.annotations.Nullable; @@ -269,7 +269,7 @@ public class PalettedContainer implements PaletteResizeListener, ReadableC case 0 -> new DataProvider<>(SINGULAR, bits); case 1, 2, 3, 4 -> new DataProvider<>(ARRAY, 4); case 5, 6, 7, 8 -> new DataProvider<>(BI_MAP, bits); - default -> new DataProvider<>(PaletteProvider.ID_LIST, MCUtils.ceilLog2(idList.size())); + default -> new DataProvider<>(PaletteProvider.ID_LIST, MiscUtils.ceilLog2(idList.size())); }; } }; @@ -278,7 +278,7 @@ public class PalettedContainer implements PaletteResizeListener, ReadableC return switch (bits) { case 0 -> new DataProvider<>(SINGULAR, bits); case 1, 2, 3 -> new DataProvider<>(ARRAY, bits); - default -> new DataProvider<>(PaletteProvider.ID_LIST, MCUtils.ceilLog2(idList.size())); + default -> new DataProvider<>(PaletteProvider.ID_LIST, MiscUtils.ceilLog2(idList.size())); }; } }; @@ -300,7 +300,7 @@ public class PalettedContainer implements PaletteResizeListener, ReadableC public abstract DataProvider createDataProvider(IndexedIterable idList, int bits); int getBits(IndexedIterable idList, int size) { - int i = MCUtils.ceilLog2(size); + int i = MiscUtils.ceilLog2(size); DataProvider dataProvider = this.createDataProvider(idList, i); return dataProvider.factory() == ID_LIST ? i : dataProvider.bits(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/packet/MCSection.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/packet/MCSection.java index d0931a2ee..81948b867 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/packet/MCSection.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/packet/MCSection.java @@ -3,13 +3,12 @@ package net.momirealms.craftengine.core.world.chunk.packet; import net.momirealms.craftengine.core.util.FriendlyByteBuf; import net.momirealms.craftengine.core.util.IndexedIterable; import net.momirealms.craftengine.core.world.chunk.PalettedContainer; -import net.momirealms.craftengine.core.world.chunk.ReadableContainer; public class MCSection { private short nonEmptyBlockCount; private final PalettedContainer serverBlockStateContainer; private final IndexedIterable clientBlockStateList; - private ReadableContainer biomeContainer; + private PalettedContainer biomeContainer; public MCSection(IndexedIterable clientBlockStateList, IndexedIterable serverBlockStateList, IndexedIterable biomeList) { this.serverBlockStateContainer = new PalettedContainer<>(serverBlockStateList, 0, PalettedContainer.PaletteProvider.BLOCK_STATE); @@ -42,4 +41,8 @@ public class MCSection { public PalettedContainer blockStateContainer() { return serverBlockStateContainer; } + + public PalettedContainer biomeContainer() { + return biomeContainer; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntityRendererSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntityRendererSerializer.java new file mode 100644 index 000000000..cf89f584e --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntityRendererSerializer.java @@ -0,0 +1,35 @@ +package net.momirealms.craftengine.core.world.chunk.serialization; + +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.ChunkPos; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.ListTag; + +import java.util.ArrayList; +import java.util.List; + +public final class DefaultBlockEntityRendererSerializer { + + public static List deserialize(ChunkPos chunkPos, ListTag blockEntitiesTag) { + List blockEntities = new ArrayList<>(blockEntitiesTag.size()); + for (int i = 0; i < blockEntitiesTag.size(); i++) { + CompoundTag tag = blockEntitiesTag.getCompound(i); + BlockPos blockPos = BlockEntity.readPosAndVerify(tag, chunkPos); + blockEntities.add(blockPos); + } + return blockEntities; + } + + public static ListTag serialize(List poses) { + ListTag listTag = new ListTag(); + for (BlockPos pos : poses) { + CompoundTag tag = new CompoundTag(); + tag.putInt("x", pos.x()); + tag.putInt("y", pos.y()); + tag.putInt("z", pos.z()); + listTag.add(tag); + } + return listTag; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java index 8f8be31d9..b72ebf984 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java @@ -1,48 +1,50 @@ package net.momirealms.craftengine.core.world.chunk.serialization; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.momirealms.craftengine.core.block.BlockEntityState; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.entity.BlockEntityType; +import net.momirealms.craftengine.core.plugin.logger.Debugger; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.chunk.CEChunk; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.ListTag; -import org.jetbrains.annotations.ApiStatus; -import java.util.Map; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; public final class DefaultBlockEntitySerializer { - @ApiStatus.Experimental - public static ListTag serialize(Map tiles) { + public static ListTag serialize(Collection entities) { ListTag result = new ListTag(); - Map nbtToPosMap = new Object2ObjectOpenHashMap<>(Math.max(tiles.size(), 10), 0.75f); - for (Map.Entry entry : tiles.entrySet()) { - int pos = entry.getKey(); - CompoundTag tag = entry.getValue().nbt(); - int[] previous = nbtToPosMap.computeIfAbsent(tag, k -> new int[] {pos}); - int[] newPoses = new int[previous.length + 1]; - System.arraycopy(previous, 0, newPoses, 0, previous.length); - newPoses[previous.length] = pos; - nbtToPosMap.put(tag, newPoses); - } - for (Map.Entry entry : nbtToPosMap.entrySet()) { - CompoundTag blockEntityTag = new CompoundTag(); - blockEntityTag.put("data", entry.getKey()); - blockEntityTag.putIntArray("pos", entry.getValue()); - result.add(blockEntityTag); - } - return result; - } - - @ApiStatus.Experimental - public static Map deserialize(ListTag tag) { - Map result = new Object2ObjectOpenHashMap<>(Math.max(tag.size(), 16), 0.5f); - for (int i = 0; i < tag.size(); i++) { - CompoundTag blockEntityTag = tag.getCompound(i); - CompoundTag data = blockEntityTag.getCompound("data"); - int[] pos = blockEntityTag.getIntArray("pos"); - for (int j = 0; j < pos.length; j++) { - result.put(j, new BlockEntityState(data)); + for (BlockEntity entity : entities) { + if (entity.isValid()) { + result.add(entity.saveAsTag()); } } return result; } + + public static List deserialize(CEChunk chunk, ListTag tag) { + List blockEntities = new ArrayList<>(tag.size()); + for (int i = 0; i < tag.size(); i++) { + CompoundTag data = tag.getCompound(i); + Key id = Key.of(data.getString("id")); + BlockEntityType type = BuiltInRegistries.BLOCK_ENTITY_TYPE.getValue(id); + if (type == null) { + Debugger.BLOCK_ENTITY.debug(() -> "Unknown block entity type: " + id); + } else { + BlockPos pos = BlockEntity.readPosAndVerify(data, chunk.chunkPos()); + ImmutableBlockState blockState = chunk.getBlockState(pos); + if (blockState.blockEntityType() == type) { + BlockEntity blockEntity = type.factory().create(pos, blockState); + blockEntity.loadCustomData(data); + blockEntities.add(blockEntity); + } + } + } + return blockEntities; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java index 1a44e0efc..5c4395082 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java @@ -9,8 +9,6 @@ import net.momirealms.sparrow.nbt.ListTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Optional; - public final class DefaultChunkSerializer { @Nullable @@ -28,7 +26,14 @@ public final class DefaultChunkSerializer { if (sections.isEmpty()) return null; CompoundTag chunkNbt = new CompoundTag(); chunkNbt.put("sections", sections); - chunkNbt.put("block_entities", DefaultBlockEntitySerializer.serialize(chunk.blockEntities())); + ListTag blockEntities = DefaultBlockEntitySerializer.serialize(chunk.blockEntities()); + if (!blockEntities.isEmpty()) { + chunkNbt.put("block_entities", blockEntities); + } + ListTag blockEntityRenders = DefaultBlockEntityRendererSerializer.serialize(chunk.constantBlockEntityRenderers()); + if (!blockEntityRenders.isEmpty()) { + chunkNbt.put("block_entity_renderers", blockEntityRenders); + } return chunkNbt; } @@ -46,7 +51,8 @@ public final class DefaultChunkSerializer { } } } - ListTag blockEntities = Optional.ofNullable(chunkNbt.getList("block_entities")).orElse(new ListTag()); - return new CEChunk(world, pos, sectionArray, DefaultBlockEntitySerializer.deserialize(blockEntities)); + ListTag blockEntities = chunkNbt.getList("block_entities"); + ListTag itemDisplayBlockRenders = chunkNbt.getList("block_entity_renderers"); + return new CEChunk(world, pos, sectionArray, blockEntities, itemDisplayBlockRenders); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java index 36d420260..991797da3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java @@ -74,9 +74,8 @@ public final class DefaultSectionSerializer { key = Key.of(id); } Holder owner = BuiltInRegistries.BLOCK.get(key).orElseGet(() -> { - Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.BLOCK).registerForHolder( - ResourceKey.create(BuiltInRegistries.BLOCK.key().location(), key)); - InactiveCustomBlock inactiveBlock = new InactiveCustomBlock(key, holder); + Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.BLOCK).registerForHolder(ResourceKey.create(BuiltInRegistries.BLOCK.key().location(), key)); + InactiveCustomBlock inactiveBlock = new InactiveCustomBlock(holder); holder.bindValue(inactiveBlock); return holder; }); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java b/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java index 931692fef..9a837a003 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.world.collision; import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.EntityHitResult; import net.momirealms.craftengine.core.world.Vec3d; import org.jetbrains.annotations.Nullable; @@ -34,6 +35,17 @@ public class AABB { this.maxZ = Math.max(pos1.z, pos2.z); } + public AABB(BlockPos pos) { + this(pos.x(), pos.y(), pos.z(), pos.x() + 1, pos.y() + 1, pos.z() + 1); + } + + public double distanceToSqr(Vec3d vec) { + double x = Math.max(Math.max(this.minX - vec.x, vec.x - this.maxX), 0.0F); + double y = Math.max(Math.max(this.minY - vec.y, vec.y - this.maxY), 0.0F); + double z = Math.max(Math.max(this.minZ - vec.z, vec.z - this.maxZ), 0.0F); + return x * x + y * y + z * z; + } + public static AABB fromInteraction(Vec3d pos, double width, double height) { return new AABB( pos.x - width / 2, diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/particle/ParticleConfig.java b/core/src/main/java/net/momirealms/craftengine/core/world/particle/ParticleConfig.java new file mode 100644 index 000000000..0621016d6 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/particle/ParticleConfig.java @@ -0,0 +1,102 @@ +package net.momirealms.craftengine.core.world.particle; + +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; +import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.Map; +import java.util.Optional; + +public class ParticleConfig { + public final ParticleType particleType; + public final NumberProvider x; + public final NumberProvider y; + public final NumberProvider z; + public final NumberProvider count; + public final NumberProvider xOffset; + public final NumberProvider yOffset; + public final NumberProvider zOffset; + public final NumberProvider speed; + public final ParticleData particleData; + + public ParticleConfig(ParticleType particleType, NumberProvider x, NumberProvider y, NumberProvider z, NumberProvider count, NumberProvider xOffset, NumberProvider yOffset, NumberProvider zOffset, NumberProvider speed, ParticleData particleData) { + this.particleType = particleType; + this.x = x; + this.y = y; + this.z = z; + this.count = count; + this.xOffset = xOffset; + this.yOffset = yOffset; + this.zOffset = zOffset; + this.speed = speed; + this.particleData = particleData; + } + + public static ParticleConfig fromMap$function(Map arguments) { + Key particleType = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("particle"), "warning.config.function.particle.missing_particle")); + NumberProvider x = NumberProviders.fromObject(arguments.getOrDefault("x", "")); + NumberProvider y = NumberProviders.fromObject(arguments.getOrDefault("y", "")); + NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "")); + NumberProvider count = NumberProviders.fromObject(arguments.getOrDefault("count", 1)); + NumberProvider xOffset = NumberProviders.fromObject(arguments.getOrDefault("offset-x", 0)); + NumberProvider yOffset = NumberProviders.fromObject(arguments.getOrDefault("offset-y", 0)); + NumberProvider zOffset = NumberProviders.fromObject(arguments.getOrDefault("offset-z", 0)); + NumberProvider speed = NumberProviders.fromObject(arguments.getOrDefault("speed", 0)); + return new ParticleConfig(CraftEngine.instance().platform().getParticleType(particleType), x, y, z, count, xOffset, yOffset, zOffset, speed, Optional.ofNullable(ParticleDataTypes.TYPES.get(particleType)).map(it -> it.apply(arguments)).orElse(null)); + } + + public static ParticleConfig fromMap$blockEntity(Map arguments) { + Key particleType = Key.of(arguments.getOrDefault("particle", "flame").toString()); + NumberProvider x = NumberProviders.fromObject(arguments.getOrDefault("x", 0)); + NumberProvider y = NumberProviders.fromObject(arguments.getOrDefault("y", 0)); + NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", 0)); + NumberProvider count = NumberProviders.fromObject(arguments.getOrDefault("count", 1)); + NumberProvider xOffset = NumberProviders.fromObject(arguments.getOrDefault("offset-x", 0)); + NumberProvider yOffset = NumberProviders.fromObject(arguments.getOrDefault("offset-y", 0)); + NumberProvider zOffset = NumberProviders.fromObject(arguments.getOrDefault("offset-z", 0)); + NumberProvider speed = NumberProviders.fromObject(arguments.getOrDefault("speed", 0)); + return new ParticleConfig(CraftEngine.instance().platform().getParticleType(particleType), x, y, z, count, xOffset, yOffset, zOffset, speed, Optional.ofNullable(ParticleDataTypes.TYPES.get(particleType)).map(it -> it.apply(arguments)).orElse(null)); + } + + public ParticleType particleType() { + return particleType; + } + + public NumberProvider x() { + return x; + } + + public NumberProvider y() { + return y; + } + + public NumberProvider z() { + return z; + } + + public NumberProvider count() { + return count; + } + + public NumberProvider xOffset() { + return xOffset; + } + + public NumberProvider yOffset() { + return yOffset; + } + + public NumberProvider zOffset() { + return zOffset; + } + + public NumberProvider speed() { + return speed; + } + + public ParticleData particleData() { + return particleData; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/particle/ParticleDataTypes.java b/core/src/main/java/net/momirealms/craftengine/core/world/particle/ParticleDataTypes.java new file mode 100644 index 000000000..094a83daa --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/particle/ParticleDataTypes.java @@ -0,0 +1,77 @@ +package net.momirealms.craftengine.core.world.particle; + +import net.momirealms.craftengine.core.block.BlockStateWrapper; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; +import net.momirealms.craftengine.core.util.Color; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.LazyReference; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +public final class ParticleDataTypes { + public static final Map, ParticleData>> TYPES = new HashMap<>(); + + static { + registerParticleData(map -> new BlockStateData( + LazyReference.lazyReference(new Supplier<>() { + final String blockState = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("block-state"), "warning.config.function.particle.missing_block_state"); + @Override + public BlockStateWrapper get() { + return CraftEngine.instance().blockManager().createBlockState(this.blockState); + } + })), + ParticleTypes.BLOCK, ParticleTypes.FALLING_DUST, ParticleTypes.DUST_PILLAR, ParticleTypes.BLOCK_CRUMBLE, ParticleTypes.BLOCK_MARKER); + registerParticleData(map -> new ColorData( + Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("color"), "warning.config.function.particle.missing_color").split(","))), + ParticleTypes.ENTITY_EFFECT, ParticleTypes.TINTED_LEAVES); + registerParticleData(map -> new JavaTypeData( + ResourceConfigUtils.getAsFloat(map.get("charge"), "charge")), + ParticleTypes.SCULK_CHARGE); + registerParticleData(map -> new JavaTypeData( + ResourceConfigUtils.getAsInt(map.get("shriek"), "shriek")), + ParticleTypes.SHRIEK); + registerParticleData(map -> new DustData( + Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("color"), "warning.config.function.particle.missing_color").split(",")), + ResourceConfigUtils.getAsFloat(map.getOrDefault("scale", 1), "scale")), + ParticleTypes.DUST); + registerParticleData(map -> new DustTransitionData( + Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("from"), "warning.config.function.particle.missing_from").split(",")), + Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("to"), "warning.config.function.particle.missing_to").split(",")), + ResourceConfigUtils.getAsFloat(map.getOrDefault("scale", 1), "scale")), + ParticleTypes.DUST_COLOR_TRANSITION); + registerParticleData(map -> new ItemStackData( + LazyReference.lazyReference(new Supplier<>() { + final Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("item"), "warning.config.function.particle.missing_item")); + @Override + public Item get() { + return CraftEngine.instance().itemManager().createWrappedItem(this.itemId, null); + } + }) + ), + ParticleTypes.ITEM); + registerParticleData(map -> new VibrationData( + NumberProviders.fromObject(map.getOrDefault("target-x", 0)), + NumberProviders.fromObject(map.getOrDefault("target-y", 0)), + NumberProviders.fromObject(map.getOrDefault("target-z", 0)), + NumberProviders.fromObject(map.getOrDefault("arrival-time", 10))), + ParticleTypes.VIBRATION); + registerParticleData(map -> new TrailData( + NumberProviders.fromObject(map.getOrDefault("target-x", 0)), + NumberProviders.fromObject(map.getOrDefault("target-y", 0)), + NumberProviders.fromObject(map.getOrDefault("target-z", 0)), + Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("color"), "warning.config.function.particle.missing_color").split(",")), + NumberProviders.fromObject(map.getOrDefault("duration", 10))), + ParticleTypes.TRAIL); + } + + public static void registerParticleData(java.util.function.Function, ParticleData> function, Key... types) { + for (Key type : types) { + TYPES.put(type, function); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/particle/ParticleType.java b/core/src/main/java/net/momirealms/craftengine/core/world/particle/ParticleType.java new file mode 100644 index 000000000..1a0a59998 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/particle/ParticleType.java @@ -0,0 +1,10 @@ +package net.momirealms.craftengine.core.world.particle; + +import net.momirealms.craftengine.core.util.Key; + +public interface ParticleType { + + Key type(); + + Object platformParticle(); +} diff --git a/gradle.properties b/gradle.properties index 2bc460d2f..f633a3e61 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,9 +2,9 @@ org.gradle.jvmargs=-Xmx1G # Project settings # Rule: [major update].[feature update].[bug fix] -project_version=0.0.62.6 -config_version=45 -lang_version=25 +project_version=0.0.63.9 +config_version=47 +lang_version=32 project_group=net.momirealms latest_supported_version=1.21.8 @@ -15,8 +15,8 @@ supported_languages=en,zh_cn,zh_tw,es,tr,de,ru_ru paper_version=1.21.8 jetbrains_annotations_version=26.0.2 slf4j_version=2.0.17 -log4j_version=2.24.3 -gson_version=2.11.0 +log4j_version=2.25.2 +gson_version=2.13.2 asm_version=9.8 asm_commons_version=9.8 jar_relocator_version=1.7 @@ -29,34 +29,34 @@ cloud_paper_version=2.0.0-beta.11 cloud_minecraft_extras_version=2.0.0-beta.11 boosted_yaml_version=1.3.7 bstats_version=3.1.0 -caffeine_version=3.2.0 +caffeine_version=3.2.2 placeholder_api_version=2.11.6 vault_version=1.7 -guava_version=33.4.6-jre +guava_version=33.5.0-jre lz4_version=1.8.0 geantyref_version=1.3.16 -zstd_version=1.5.7-2 -commons_io_version=2.18.0 +zstd_version=1.5.7-4 +commons_io_version=2.20.0 commons_imaging_version=1.0.0-alpha6 -commons_lang3_version=3.17.0 +commons_lang3_version=3.19.0 sparrow_nbt_version=0.9.4 sparrow_util_version=0.51 -fastutil_version=8.5.15 -netty_version=4.1.124.Final +fastutil_version=8.5.16 +netty_version=4.1.127.Final joml_version=1.10.8 datafixerupper_version=8.0.16 mojang_brigadier_version=1.0.18 byte_buddy_version=1.17.5 ahocorasick_version=0.6.3 -snake_yaml_version=2.4 -anti_grief_version=0.19 -nms_helper_version=1.0.67 +snake_yaml_version=2.5 +anti_grief_version=0.20 +nms_helper_version=1.0.98 evalex_version=3.5.0 reactive_streams_version=1.0.4 -amazon_awssdk_version=2.31.23 +amazon_awssdk_version=2.34.5 amazon_awssdk_eventstream_version=1.0.1 -jimfs_version=1.3.0 -authlib_version=6.0.58 +jimfs_version=1.3.1 +authlib_version=7.0.60 concurrent_util_version=0.0.3 # Proxy settings diff --git a/libs/worldguard-bukkit-7.0.14-dist.jar b/libs/worldguard-bukkit-7.0.14-dist.jar new file mode 100644 index 000000000..69d590e5e Binary files /dev/null and b/libs/worldguard-bukkit-7.0.14-dist.jar differ