diff --git a/README.md b/README.md index 10bf509de..836dcd8f0 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ repositories { ``` ```kotlin dependencies { - compileOnly("net.momirealms:craft-engine-core:0.0.61") - compileOnly("net.momirealms:craft-engine-bukkit:0.0.61") + compileOnly("net.momirealms:craft-engine-core:0.0.63") + compileOnly("net.momirealms:craft-engine-bukkit:0.0.63") } ``` \ No newline at end of file diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts index c9bf92f91..ec25aa893 100644 --- a/bukkit/build.gradle.kts +++ b/bukkit/build.gradle.kts @@ -4,11 +4,11 @@ plugins { } repositories { + mavenCentral() maven("https://jitpack.io/") maven("https://repo.momirealms.net/releases/") maven("https://libraries.minecraft.net/") maven("https://repo.papermc.io/repository/maven-public/") - mavenCentral() } dependencies { @@ -60,6 +60,8 @@ dependencies { compileOnly("org.ahocorasick:ahocorasick:${rootProject.properties["ahocorasick_version"]}") // authlib compileOnly("com.mojang:authlib:${rootProject.properties["authlib_version"]}") + // concurrentutil + compileOnly("ca.spottedleaf:concurrentutil:${rootProject.properties["concurrent_util_version"]}") } java { @@ -99,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 110a0b229..520da0749 100644 --- a/bukkit/compatibility/build.gradle.kts +++ b/bukkit/compatibility/build.gradle.kts @@ -14,6 +14,8 @@ repositories { maven("https://nexus.neetgames.com/repository/maven-releases/") // mcmmo 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 { @@ -67,6 +69,16 @@ dependencies { compileOnly("com.willfp:libreforge:4.58.1") // AureliumSkills 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 5e752460f..c4ca0328e 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 @@ -1,19 +1,18 @@ package net.momirealms.craftengine.bukkit.compatibility; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; -import net.momirealms.craftengine.bukkit.compatibility.item.CustomFishingSource; -import net.momirealms.craftengine.bukkit.compatibility.item.MMOItemsSource; -import net.momirealms.craftengine.bukkit.compatibility.item.MythicMobsSource; -import net.momirealms.craftengine.bukkit.compatibility.item.NeigeItemsSource; +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; @@ -23,9 +22,12 @@ import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; 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.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; @@ -120,6 +122,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 @@ -249,6 +268,22 @@ public class BukkitCompatibilityManager implements CompatibilityManager { itemManager.registerExternalItemSource(new CustomFishingSource()); logHook("CustomFishing"); } + if (this.isPluginEnabled("Zaphkiel")) { + 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/item/ZaphkielSource.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/ZaphkielSource.java new file mode 100644 index 000000000..3c9329b11 --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/ZaphkielSource.java @@ -0,0 +1,33 @@ +package net.momirealms.craftengine.bukkit.compatibility.item; + +import ink.ptms.zaphkiel.Zaphkiel; +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 javax.annotation.Nullable; +import java.util.Optional; + +/** + * @author iiabc + * @since 2025/8/30 09:39 + */ +public class ZaphkielSource implements ExternalItemSource { + + @Override + public String plugin() { + return "zaphkiel"; + } + + @Override + public @Nullable ItemStack build(String id, ItemBuildContext context) { + Player player = Optional.ofNullable(context.player()).map(it -> (Player) it.platformPlayer()).orElse(null); + return Zaphkiel.INSTANCE.api().getItemManager().generateItemStack(id, player); + } + + @Override + public String id(ItemStack item) { + return Zaphkiel.INSTANCE.api().getItemHandler().getItemId(item); + } +} 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..dbb2f00db 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 @@ -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(); 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/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/WorldEditBlockRegister.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/WorldEditBlockRegister.java index 5b702192b..be5b1f2ed 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/WorldEditBlockRegister.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/WorldEditBlockRegister.java @@ -90,7 +90,7 @@ public class WorldEditBlockRegister { if (state == null) return null; try { - String id = state.customBlockState().handle().toString(); + String id = state.customBlockState().literalObject().toString(); int first = id.indexOf('{'); int last = id.indexOf('}'); if (first != -1 && last != -1 && last > first) { diff --git a/bukkit/loader/build.gradle.kts b/bukkit/loader/build.gradle.kts index 29d893949..11ecaf4e1 100644 --- a/bukkit/loader/build.gradle.kts +++ b/bukkit/loader/build.gradle.kts @@ -21,6 +21,9 @@ dependencies { implementation(project(":bukkit:compatibility:legacy")) implementation(project(":common-files")) + // concurrentutil + implementation(files("${rootProject.rootDir}/libs/concurrentutil-${rootProject.properties["concurrent_util_version"]}.jar")) + implementation("net.momirealms:sparrow-util:${rootProject.properties["sparrow_util_version"]}") implementation("net.momirealms:antigrieflib:${rootProject.properties["anti_grief_version"]}") implementation("net.momirealms:craft-engine-nms-helper:${rootProject.properties["nms_helper_version"]}") @@ -47,7 +50,7 @@ bukkit { name = "CraftEngine" apiVersion = "1.20" authors = listOf("XiaoMoMi") - contributors = listOf("jhqwqmc", "iqtesterrr", "WhiteProject1", "Catnies", "xiaozhangup", "TamashiiMon") + contributors = listOf("https://github.com/Xiao-MoMi/craft-engine/graphs/contributors") softDepend = listOf("PlaceholderAPI", "WorldEdit", "FastAsyncWorldEdit", "Skript") foliaSupported = true } @@ -77,5 +80,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("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/build.gradle.kts b/bukkit/paper-loader/build.gradle.kts index 35ced8a05..6572d2c56 100644 --- a/bukkit/paper-loader/build.gradle.kts +++ b/bukkit/paper-loader/build.gradle.kts @@ -23,6 +23,9 @@ dependencies { implementation(project(":bukkit:compatibility:legacy")) implementation(project(":common-files")) + // concurrentutil + implementation(files("${rootProject.rootDir}/libs/concurrentutil-${rootProject.properties["concurrent_util_version"]}.jar")) + implementation("net.momirealms:sparrow-util:${rootProject.properties["sparrow_util_version"]}") implementation("net.momirealms:antigrieflib:${rootProject.properties["anti_grief_version"]}") implementation("net.momirealms:craft-engine-nms-helper-mojmap:${rootProject.properties["nms_helper_version"]}") @@ -50,7 +53,7 @@ paper { name = "CraftEngine" apiVersion = "1.20" authors = listOf("XiaoMoMi") - contributors = listOf("jhqwqmc", "iqtesterrr", "WhiteProject1", "Catnies", "xiaozhangup", "TamashiiMon") + contributors = listOf("https://github.com/Xiao-MoMi/craft-engine/graphs/contributors") foliaSupported = true serverDependencies { register("PlaceholderAPI") { @@ -82,6 +85,10 @@ paper { register("MMOItems") { required = false } 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 } @@ -149,5 +156,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("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..05364cd61 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()) { + 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..c037f46c3 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,25 @@ 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.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,6 +41,70 @@ public class BukkitAdvancementManager extends AbstractAdvancementManager { return this.advancementParser; } + @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 implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"advancements", "advancement"}; 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 3986509cf..6dcd0167a 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 @@ -4,7 +4,7 @@ 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.BukkitBlockInWorld; +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; @@ -29,8 +29,8 @@ public final class BukkitAdaptors { return new BukkitEntity(entity); } - public static BukkitBlockInWorld adapt(final Block block) { - return new BukkitBlockInWorld(block); + public static BukkitExistingBlock adapt(final Block block) { + return new BukkitExistingBlock(block); } public static Location toLocation(WorldPosition 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 ffddee806..93ec4c025 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 * @@ -126,7 +144,7 @@ public final class CraftEngineBlocks { boolean success; Object worldServer = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()); Object blockPos = FastNMS.INSTANCE.constructor$BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); - Object blockState = block.customBlockState().handle(); + Object blockState = block.customBlockState().literalObject(); Object oldBlockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(worldServer, blockPos); success = FastNMS.INSTANCE.method$LevelWriter$setBlock(worldServer, blockPos, blockState, option.flags()); if (success) { @@ -188,9 +206,10 @@ 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); @@ -249,6 +268,6 @@ public final class CraftEngineBlocks { */ @NotNull public static BlockData getBukkitBlockData(@NotNull ImmutableBlockState blockState) { - return BlockStateUtils.fromBlockData(blockState.customBlockState().handle()); + return BlockStateUtils.fromBlockData(blockState.customBlockState().literalObject()); } } 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/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/api/event/CraftEngineReloadEvent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/CraftEngineReloadEvent.java index 58d5dea02..949299b48 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/CraftEngineReloadEvent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/CraftEngineReloadEvent.java @@ -8,9 +8,17 @@ import org.jetbrains.annotations.NotNull; public class CraftEngineReloadEvent extends Event { private static final HandlerList HANDLER_LIST = new HandlerList(); private final BukkitCraftEngine plugin; + private static boolean firstFlag = true; + private final boolean isFirstReload; public CraftEngineReloadEvent(BukkitCraftEngine plugin) { this.plugin = plugin; + this.isFirstReload = firstFlag; + firstFlag = false; + } + + public boolean isFirstReload() { + return this.isFirstReload; } public BukkitCraftEngine plugin() { 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 7052fc484..1daab5e9d 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 @@ -9,7 +9,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.*; -import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; +import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.entity.player.InteractionHand; @@ -82,7 +82,7 @@ public final class BlockEventListener implements Listener { try { Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(ownerBlock); Object placeSound = CoreReflections.field$SoundType$placeSound.get(soundType); - player.playSound(block.getLocation(), FastNMS.INSTANCE.field$SoundEvent$location(placeSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f); + 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); } @@ -99,7 +99,7 @@ public final class BlockEventListener implements Listener { 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(), FastNMS.INSTANCE.field$SoundEvent$location(placeSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f); + 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); } @@ -124,7 +124,7 @@ public final class BlockEventListener implements Listener { Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled); optionalCustomItem.get().execute( PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block)) .withParameter(DirectContextParameters.POSITION, position) .withParameter(DirectContextParameters.PLAYER, serverPlayer) .withParameter(DirectContextParameters.EVENT, cancellable) @@ -156,7 +156,7 @@ public final class BlockEventListener implements Listener { // execute functions Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled); PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block)) .withParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, state) .withParameter(DirectContextParameters.EVENT, cancellable) .withParameter(DirectContextParameters.POSITION, position) @@ -174,12 +174,15 @@ public final class BlockEventListener implements Listener { // 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); } ContextHolder lootContext = ContextHolder.builder() - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block)) .withParameter(DirectContextParameters.POSITION, position) .withParameter(DirectContextParameters.PLAYER, serverPlayer) .withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, ItemUtils.isEmpty(itemInHand) ? null : itemInHand).build(); @@ -198,7 +201,7 @@ public final class BlockEventListener implements Listener { try { Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(ownerBlock); Object breakSound = CoreReflections.field$SoundType$breakSound.get(soundType); - block.getWorld().playSound(block.getLocation(), FastNMS.INSTANCE.field$SoundEvent$location(breakSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f); + 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); } @@ -224,7 +227,7 @@ public final class BlockEventListener implements Listener { WorldPosition position = new WorldPosition(world, location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5); ContextHolder.Builder builder = ContextHolder.builder() .withParameter(DirectContextParameters.POSITION, position) - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)); + .withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block)); for (LootTable lootTable : it.lootTables()) { for (Item item : lootTable.getRandomItems(builder.build(), world, null)) { world.dropItemNaturally(position, item); @@ -250,7 +253,7 @@ public final class BlockEventListener implements Listener { state.owner().value().execute(PlayerOptionalContext.of(BukkitAdaptors.adapt(player), ContextHolder.builder() .withParameter(DirectContextParameters.EVENT, cancellable) .withParameter(DirectContextParameters.POSITION, new WorldPosition(new BukkitWorld(event.getWorld()), LocationUtils.toVec3d(location))) - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block)) .withParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, state) ), EventTrigger.STEP); if (cancellable.isCancelled()) { 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 d3c7709fd..a63b539be 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 @@ -76,7 +76,6 @@ public final class BukkitBlockManager extends AbstractBlockManager { private List blockRegisterOrder = new ObjectArrayList<>(); // Event listeners private BlockEventListener blockEventListener; - private FallingBlockRemoveListener fallingBlockRemoveListener; // cached tag packet private Object cachedUpdateTagsPacket; @@ -101,7 +100,6 @@ public final class BukkitBlockManager extends AbstractBlockManager { 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(); @@ -123,9 +121,6 @@ public final class BukkitBlockManager extends AbstractBlockManager { @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 @@ -145,7 +140,6 @@ public final class BukkitBlockManager extends AbstractBlockManager { public void disable() { this.unload(); HandlerList.unregisterAll(this.blockEventListener); - if (this.fallingBlockRemoveListener != null) HandlerList.unregisterAll(this.fallingBlockRemoveListener); } @Override @@ -181,14 +175,14 @@ public final class BukkitBlockManager extends AbstractBlockManager { @Nullable @Override - public BlockStateWrapper createPackedBlockState(String blockState) { + public BlockStateWrapper createBlockState(String blockState) { ImmutableBlockState state = BlockStateParser.deserialize(blockState); if (state != null) { return state.customBlockState(); } try { BlockData blockData = Bukkit.createBlockData(blockState); - return BlockStateUtils.toPackedBlockState(blockData); + return BlockStateUtils.toBlockStateWrapper(blockData); } catch (IllegalArgumentException e) { return null; } @@ -220,7 +214,7 @@ public final class BukkitBlockManager extends AbstractBlockManager { 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()); + throw new LocalizedResourceConfigException("warning.config.block.state.bind_failed", state.toString(), previous.toString(), BlockStateUtils.getBlockOwnerIdFromState(previous.customBlockState().literalObject()).toString()); } this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()] = state; this.tempBlockAppearanceConvertor.put(state.customBlockState().registryId(), state.vanillaBlockState().registryId()); @@ -231,7 +225,7 @@ public final class BukkitBlockManager extends AbstractBlockManager { @Override public Key getBlockOwnerId(BlockStateWrapper state) { - return BlockStateUtils.getBlockOwnerIdFromState(state.handle()); + return BlockStateUtils.getBlockOwnerIdFromState(state.literalObject()); } @Override @@ -258,9 +252,9 @@ public final class BukkitBlockManager extends AbstractBlockManager { int size = RegistryUtils.currentBlockRegistrySize(); BlockStateWrapper[] states = new BlockStateWrapper[size]; for (int i = 0; i < size; i++) { - states[i] = BlockStateWrapper.create(BlockStateUtils.idToBlockState(i), i, BlockStateUtils.isVanillaBlock(i)); + states[i] = new BukkitBlockStateWrapper(BlockStateUtils.idToBlockState(i), i); } - BlockRegistryMirror.init(states, BlockStateWrapper.vanilla(MBlocks.STONE$defaultState, BlockStateUtils.blockStateToId(MBlocks.STONE$defaultState))); + BlockRegistryMirror.init(states, new BukkitBlockStateWrapper(MBlocks.STONE$defaultState, BlockStateUtils.blockStateToId(MBlocks.STONE$defaultState))); } private void registerEmptyBlock() { 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 new file mode 100644 index 000000000..cafb79317 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockStateWrapper.java @@ -0,0 +1,23 @@ +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 14ae935fc..ae91184a2 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 @@ -39,7 +39,7 @@ public final class BukkitCustomBlock extends AbstractCustomBlock { @NotNull Key id, @NotNull Holder.Reference holder, @NotNull Map> properties, - @NotNull Map appearances, + @NotNull Map appearances, @NotNull Map variantMapper, @NotNull BlockSettings settings, @NotNull Map>> events, @@ -76,13 +76,13 @@ public final class BukkitCustomBlock extends AbstractCustomBlock { 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!"); + CraftEngine.instance().logger().warn("Could not find vanilla visual block state 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!"); + CraftEngine.instance().logger().warn("Could not find real block state for " + immutableBlockState + ". This might cause errors!"); continue; } - DelegatingBlockState nmsState = (DelegatingBlockState) immutableBlockState.customBlockState().handle(); + DelegatingBlockState nmsState = (DelegatingBlockState) immutableBlockState.customBlockState().literalObject(); nmsState.setBlockState(immutableBlockState); BlockSettings settings = immutableBlockState.settings(); @@ -98,10 +98,10 @@ public final class BukkitCustomBlock extends AbstractCustomBlock { 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().handle()) : settings.canOcclude().asBoolean(); + 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().handle()) : settings.useShapeForLightOcclusion().asBoolean(); + 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); @@ -111,7 +111,7 @@ public final class BukkitCustomBlock extends AbstractCustomBlock { // set parent block properties DelegatingBlock nmsBlock = (DelegatingBlock) BlockStateUtils.getBlockOwner(nmsState); ObjectHolder shapeHolder = nmsBlock.shapeDelegate(); - shapeHolder.bindValue(new BukkitBlockShape(immutableBlockState.vanillaBlockState().handle(), Optional.ofNullable(immutableBlockState.settings().supportShapeBlockState()).map(it -> { + 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)) { @@ -128,6 +128,9 @@ public final class BukkitCustomBlock extends AbstractCustomBlock { 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); @@ -137,7 +140,7 @@ public final class BukkitCustomBlock extends AbstractCustomBlock { } // modify cache if (VersionHelper.isOrAbove1_21_2()) { - int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$lightBlock.getInt(immutableBlockState.vanillaBlockState().handle()); + 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 @@ -146,11 +149,11 @@ public final class BukkitCustomBlock extends AbstractCustomBlock { } 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().handle())); + 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().handle())); + 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 @@ -159,7 +162,7 @@ public final class BukkitCustomBlock extends AbstractCustomBlock { } 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().handle()))); + 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); @@ -197,7 +200,7 @@ public final class BukkitCustomBlock extends AbstractCustomBlock { public static class BuilderImpl implements Builder { protected final Key id; protected Map> properties; - protected Map appearances; + protected Map appearances; protected Map variantMapper; protected BlockSettings settings; protected List> behavior; @@ -215,7 +218,7 @@ public final class BukkitCustomBlock extends AbstractCustomBlock { } @Override - public Builder appearances(Map appearances) { + public Builder appearances(Map appearances) { this.appearances = appearances; return this; } 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/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/BukkitBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehavior.java index bd8b082c9..ea36861b8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehavior.java @@ -31,11 +31,11 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior { Direction.Axis axis = blockState.get(axisProperty); return switch (rotation) { case COUNTERCLOCKWISE_90, CLOCKWISE_90 -> switch (axis) { - case X -> blockState.with(axisProperty, Direction.Axis.Z).customBlockState().handle(); - case Z -> blockState.with(axisProperty, Direction.Axis.X).customBlockState().handle(); - default -> blockState.customBlockState().handle(); + case X -> blockState.with(axisProperty, Direction.Axis.Z).customBlockState().literalObject(); + case Z -> blockState.with(axisProperty, Direction.Axis.X).customBlockState().literalObject(); + default -> blockState.customBlockState().literalObject(); }; - default -> blockState.customBlockState().handle(); + default -> blockState.customBlockState().literalObject(); }; }; }); @@ -45,7 +45,7 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior { Property directionProperty = (Property) property; behavior.rotateFunction = (thisBlock, blockState, rotation) -> blockState.with(directionProperty, rotation.rotate(blockState.get(directionProperty).toDirection()).toHorizontalDirection()) - .customBlockState().handle(); + .customBlockState().literalObject(); behavior.mirrorFunction = (thisBlock, blockState, mirror) -> { Rotation rotation = mirror.getRotation(blockState.get(directionProperty).toDirection()); return behavior.rotateFunction.rotate(thisBlock, blockState, rotation); @@ -55,7 +55,7 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior { Property directionProperty = (Property) property; behavior.rotateFunction = (thisBlock, blockState, rotation) -> blockState.with(directionProperty, rotation.rotate(blockState.get(directionProperty))) - .customBlockState().handle(); + .customBlockState().literalObject(); behavior.mirrorFunction = (thisBlock, blockState, mirror) -> { Rotation rotation = mirror.getRotation(blockState.get(directionProperty)); return behavior.rotateFunction.rotate(thisBlock, blockState, rotation); @@ -68,7 +68,7 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior { Property directionProperty = (Property) property; behavior.rotateFunction = (thisBlock, blockState, rotation) -> blockState.with(directionProperty, rotation.rotate(blockState.get(directionProperty).toDirection()).toHorizontalDirection()) - .customBlockState().handle(); + .customBlockState().literalObject(); behavior.mirrorFunction = (thisBlock, blockState, mirror) -> { Rotation rotation = mirror.getRotation(blockState.get(directionProperty).toDirection()); return behavior.rotateFunction.rotate(thisBlock, blockState, rotation); @@ -139,7 +139,7 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior { if (optionalCustomState.isEmpty()) return CoreReflections.instance$ItemStack$EMPTY; ImmutableBlockState immutableBlockState = optionalCustomState.get(); if (immutableBlockState.get(this.waterloggedProperty)) { - FastNMS.INSTANCE.method$LevelWriter$setBlock(world, pos, immutableBlockState.with(this.waterloggedProperty, false).customBlockState().handle(), 3); + FastNMS.INSTANCE.method$LevelWriter$setBlock(world, pos, immutableBlockState.with(this.waterloggedProperty, false).customBlockState().literalObject(), 3); return FastNMS.INSTANCE.constructor$ItemStack(MItems.WATER_BUCKET, 1); } return CoreReflections.instance$ItemStack$EMPTY; @@ -154,7 +154,7 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior { ImmutableBlockState immutableBlockState = optionalCustomState.get(); Object fluidType = FastNMS.INSTANCE.method$FluidState$getType(args[3]); if (!immutableBlockState.get(this.waterloggedProperty) && fluidType == MFluids.WATER) { - FastNMS.INSTANCE.method$LevelWriter$setBlock(args[0], args[1], immutableBlockState.with(this.waterloggedProperty, true).customBlockState().handle(), 3); + FastNMS.INSTANCE.method$LevelWriter$setBlock(args[0], args[1], immutableBlockState.with(this.waterloggedProperty, true).customBlockState().literalObject(), 3); FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleFluidTick(args[0], args[1], fluidType, 5); return true; } @@ -182,6 +182,6 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior { if (optionalCustomState.isEmpty()) return false; BlockStateWrapper vanillaState = optionalCustomState.get().vanillaBlockState(); if (vanillaState == null) return false; - return FastNMS.INSTANCE.method$BlockStateBase$isPathFindable(vanillaState.handle(), VersionHelper.isOrAbove1_20_5() ? null : args[1], VersionHelper.isOrAbove1_20_5() ? null : args[2], args[isPathFindable$type]); + return FastNMS.INSTANCE.method$BlockStateBase$isPathFindable(vanillaState.literalObject(), VersionHelper.isOrAbove1_20_5() ? null : args[1], VersionHelper.isOrAbove1_20_5() ? null : args[2], args[isPathFindable$type]); } } 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 e4b503afb..f6cc1c58e 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 @@ -27,6 +27,15 @@ public class BukkitBlockBehaviors extends BlockBehaviors { public static final Key STAIRS_BLOCK = Key.from("craftengine:stairs_block"); 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 void init() { register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE); @@ -52,5 +61,14 @@ public class BukkitBlockBehaviors extends BlockBehaviors { register(STAIRS_BLOCK, StairsBlockBehavior.FACTORY); 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); } } 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 d535f0030..f20eab56d 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 @@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.block.behavior; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.BlockTags; +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; @@ -76,10 +77,7 @@ public class BushBlockBehavior extends AbstractCanSurviveBlockBehavior { @SuppressWarnings("DuplicatedCode") @Override protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws Exception { - int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos); - int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos); - int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos); - Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x, y - 1, z); + Object belowPos = LocationUtils.below(blockPos); Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos); return mayPlaceOn(belowState, world, belowPos); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ChangeOverTimeBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ChangeOverTimeBlockBehavior.java new file mode 100644 index 000000000..b6a40cf8f --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ChangeOverTimeBlockBehavior.java @@ -0,0 +1,46 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; +import net.momirealms.craftengine.core.block.*; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +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; +import java.util.Optional; +import java.util.concurrent.Callable; + +public class ChangeOverTimeBlockBehavior extends BukkitBlockBehavior { + public static final Factory FACTORY = new Factory(); + private final float changeSpeed; + private final Key nextBlock; + + public ChangeOverTimeBlockBehavior(CustomBlock customBlock, float changeSpeed, Key nextBlock) { + super(customBlock); + this.changeSpeed = changeSpeed; + this.nextBlock = nextBlock; + } + + @Override + public void randomTick(Object thisBlock, Object[] args, Callable superMethod) throws ReflectiveOperationException { + if (RandomUtils.generateRandomFloat(0F, 1F) >= this.changeSpeed) return; + Optional nextState = BukkitBlockManager.instance().blockById(this.nextBlock) + .map(CustomBlock::defaultState) + .map(ImmutableBlockState::customBlockState) + .map(BlockStateWrapper::literalObject); + if (nextState.isEmpty()) return; + CraftBukkitReflections.method$CraftEventFactory$handleBlockFormEvent.invoke(null, args[1], args[2], nextState.get(), UpdateOption.UPDATE_ALL.flags()); + } + + public static class Factory implements BlockBehaviorFactory { + + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + float changeSpeed = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("change-speed", 0.05688889F), "change-speed"); + Key nextBlock = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.getOrDefault("next-block", "minecraft:air"), "warning.config.block.behavior.change_over_time.missing_next_block")); + return new ChangeOverTimeBlockBehavior(block, changeSpeed, nextBlock); + } + } +} 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 f1dc2529e..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 @@ -48,7 +48,7 @@ public class ConcretePowderBlockBehavior extends BukkitBlockBehavior { Optional optionalCustomBlock = BukkitBlockManager.instance().blockById(this.targetBlock); if (optionalCustomBlock.isPresent()) { CustomBlock customBlock = optionalCustomBlock.get(); - this.defaultBlockState = customBlock.defaultState().customBlockState().handle(); + this.defaultBlockState = customBlock.defaultState().customBlockState().literalObject(); this.defaultImmutableBlockState = customBlock.defaultState(); } else { CraftEngine.instance().logger().warn("Failed to create solid block " + this.targetBlock + " in ConcretePowderBlockBehavior"); @@ -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 a2b0b3c0f..7e0746ead 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 @@ -94,7 +94,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior { BlockStateUtils.getOptionalCustomBlockState(state).ifPresent(customState -> { int age = this.getAge(customState); if (age < this.ageProperty.max && RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) { - FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, customState.with(this.ageProperty, age + 1).customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, customState.with(this.ageProperty, age + 1).customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); } }); } @@ -133,7 +133,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior { if (isMaxAge(state)) return InteractionResult.PASS; boolean sendSwing = false; - Object visualState = state.vanillaBlockState().handle(); + Object visualState = state.vanillaBlockState().literalObject(); Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState); if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState); @@ -156,7 +156,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior { } ImmutableBlockState customState = optionalCustomState.get(); boolean sendParticles = false; - Object visualState = customState.vanillaBlockState().handle(); + Object visualState = customState.vanillaBlockState().literalObject(); Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState); if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, pos, visualState); @@ -180,7 +180,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior { if (i > maxAge) { i = maxAge; } - FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, customState.with(this.ageProperty, i).customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, customState.with(this.ageProperty, i).customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); if (sendParticles) { world.spawnParticle(ParticleUtils.HAPPY_VILLAGER, x + 0.5, y + 0.5, z + 0.5, 15, 0.25, 0.25, 0.25); } 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..228bab889 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DirectionalAttachedBlockBehavior.java @@ -0,0 +1,107 @@ +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.item.context.BlockPlaceContext; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +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.world.BlockPos; +import net.momirealms.craftengine.core.world.World; + +import java.util.Map; +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; + + public DirectionalAttachedBlockBehavior(CustomBlock customBlock, Property facingProperty, boolean isSixDirection) { + super(customBlock); + this.facingProperty = facingProperty; + this.isSixDirection = isSixDirection; + } + + @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); + } + + @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.surface_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.surface_attached.missing_facing"); + } + return new DirectionalAttachedBlockBehavior(block, facing, isDirection); + } + } +} 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 768f01adf..864563451 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 @@ -107,7 +107,7 @@ public class DoorBlockBehavior extends AbstractCanSurviveBlockBehavior { return MBlocks.AIR$defaultState; } if (neighborState.get(anotherDoorBehavior.get().halfProperty) != half) { - return neighborState.with(anotherDoorBehavior.get().halfProperty, half).customBlockState().handle(); + return neighborState.with(anotherDoorBehavior.get().halfProperty, half).customBlockState().literalObject(); } return MBlocks.AIR$defaultState; } else { @@ -245,7 +245,7 @@ public class DoorBlockBehavior extends AbstractCanSurviveBlockBehavior { public void setOpen(@Nullable Player player, Object serverLevel, ImmutableBlockState state, BlockPos pos, boolean isOpen) { if (isOpen(state) != isOpen) { org.bukkit.World world = FastNMS.INSTANCE.method$Level$getCraftWorld(serverLevel); - FastNMS.INSTANCE.method$LevelWriter$setBlock(serverLevel, LocationUtils.toBlockPos(pos), state.with(this.openProperty, isOpen).customBlockState().handle(), UpdateOption.builder().updateImmediate().updateClients().build().flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(serverLevel, LocationUtils.toBlockPos(pos), state.with(this.openProperty, isOpen).customBlockState().literalObject(), UpdateOption.builder().updateImmediate().updateClients().build().flags()); world.sendGameEvent(player == null ? null : (org.bukkit.entity.Player) player.platformPlayer(), isOpen ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, new Vector(pos.x(), pos.y(), pos.z())); SoundData soundData = isOpen ? this.openSound : this.closeSound; if (soundData != null) { @@ -307,7 +307,7 @@ public class DoorBlockBehavior extends AbstractCanSurviveBlockBehavior { ); } } - FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, customState.with(this.poweredProperty, flag).with(this.openProperty, flag).customBlockState().handle(), UpdateOption.Flags.UPDATE_CLIENTS); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, customState.with(this.poweredProperty, flag).with(this.openProperty, flag).customBlockState().literalObject(), UpdateOption.Flags.UPDATE_CLIENTS); } } 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 016979338..14305b479 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 @@ -63,7 +63,7 @@ public class DoubleHighBlockBehavior extends BukkitBlockBehavior { World level = context.getLevel(); BlockPos anotherHalfPos = context.getClickedPos().relative(Direction.UP); BlockStateWrapper blockStateWrapper = state.with(this.halfProperty, DoubleBlockHalf.UPPER).customBlockState(); - FastNMS.INSTANCE.method$LevelWriter$setBlock(level.serverWorld(), LocationUtils.toBlockPos(anotherHalfPos), blockStateWrapper.handle(), UpdateOption.Flags.UPDATE_CLIENTS); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level.serverWorld(), LocationUtils.toBlockPos(anotherHalfPos), blockStateWrapper.literalObject(), UpdateOption.Flags.UPDATE_CLIENTS); } @Override 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..e41956aec 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 @@ -10,11 +10,7 @@ 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.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; @@ -67,7 +63,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,27 +72,17 @@ 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) { + 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)); world.playBlockSound(position, customState.settings().sounds().destroySound()); } } 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 7040f8b58..99f897792 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 @@ -113,7 +113,7 @@ public class FenceGateBlockBehavior extends BukkitBlockBehavior { if (relativeStateIsWall) { // TODO: 连接原版方块 } - return customState.with(this.inWallProperty, flag).customBlockState().handle(); + return customState.with(this.inWallProperty, flag).customBlockState().literalObject(); } @Override @@ -148,7 +148,7 @@ public class FenceGateBlockBehavior extends BukkitBlockBehavior { private void playerToggle(UseOnContext context, ImmutableBlockState state) { Player player = context.getPlayer(); this.toggle(state, context.getLevel(), context.getClickedPos(), player); - if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().handle()), context.getHitResult(), (Item) context.getItem())) { + if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().literalObject()), context.getHitResult(), (Item) context.getItem())) { player.swingHand(context.getHand()); } } @@ -223,7 +223,7 @@ public class FenceGateBlockBehavior extends BukkitBlockBehavior { this.playSound(LocationUtils.fromBlockPos(blockPos), world, hasSignal); } - FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, customState.with(this.poweredProperty, hasSignal).customBlockState().handle(), UpdateOption.Flags.UPDATE_CLIENTS); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, customState.with(this.poweredProperty, hasSignal).customBlockState().literalObject(), UpdateOption.Flags.UPDATE_CLIENTS); } private void toggle(ImmutableBlockState state, World world, BlockPos pos, @Nullable Player player) { @@ -240,7 +240,7 @@ public class FenceGateBlockBehavior extends BukkitBlockBehavior { } newState = blockState.with(this.openProperty, true); } - FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); boolean open = isOpen(newState); ((org.bukkit.World) world.platformWorld()).sendGameEvent( player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null, 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 d4009daae..20833c67c 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 @@ -8,7 +8,7 @@ import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.FeatureUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.util.ParticleUtils; -import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; +import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.core.block.BlockBehavior; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; @@ -59,7 +59,7 @@ public class GrassBlockBehavior extends BukkitBlockBehavior { } boolean sendParticles = false; ImmutableBlockState customState = optionalCustomState.get(); - Object visualState = customState.vanillaBlockState().handle(); + Object visualState = customState.vanillaBlockState().literalObject(); Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState); if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, blockPos, visualState); @@ -86,12 +86,12 @@ public class GrassBlockBehavior extends BukkitBlockBehavior { if (ItemUtils.isEmpty(item) || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || context.getPlayer().isAdventureMode()) return InteractionResult.PASS; BlockPos pos = context.getClickedPos(); - BukkitBlockInWorld upper = (BukkitBlockInWorld) context.getLevel().getBlockAt(pos.x(), pos.y() + 1, pos.z()); + BukkitExistingBlock upper = (BukkitExistingBlock) context.getLevel().getBlockAt(pos.x(), pos.y() + 1, pos.z()); Block block = upper.block(); if (!block.isEmpty()) return InteractionResult.PASS; boolean sendSwing = false; - Object visualState = state.vanillaBlockState().handle(); + Object visualState = state.vanillaBlockState().literalObject(); Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState); if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState); 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 c6e645afd..594962aca 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 @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.block.behavior; import net.momirealms.craftengine.bukkit.nms.FastNMS; +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; @@ -20,10 +21,7 @@ public class HangingBlockBehavior extends BushBlockBehavior { @Override protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException { - int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos); - int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos); - int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos); - Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x, y + 1, z); + Object belowPos = LocationUtils.above(blockPos); Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos); return mayPlaceOn(belowState, world, belowPos); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LampBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LampBlockBehavior.java index 21398bb83..432837793 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LampBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LampBlockBehavior.java @@ -44,7 +44,7 @@ public class LampBlockBehavior extends BukkitBlockBehavior { if (FastNMS.INSTANCE.method$CraftEventFactory$callRedstoneChange(world, blockPos, 0, 15).getNewCurrent() != 15) { return; } - FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, customState.cycle(this.litProperty).customBlockState().handle(), 2); + FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, customState.cycle(this.litProperty).customBlockState().literalObject(), 2); } } @@ -64,7 +64,7 @@ public class LampBlockBehavior extends BukkitBlockBehavior { if (FastNMS.INSTANCE.method$CraftEventFactory$callRedstoneChange(world, blockPos, 0, 15).getNewCurrent() != 15) { return; } - FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, customState.cycle(this.litProperty).customBlockState().handle(), 2); + FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, customState.cycle(this.litProperty).customBlockState().literalObject(), 2); } } } 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 7db9e3abb..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 @@ -86,10 +86,10 @@ public class LeavesBlockBehavior extends BukkitBlockBehavior { LeavesBlockBehavior behavior = optionalBehavior.get(); ImmutableBlockState newState = behavior.updateDistance(customState, level, blockPos); if (newState != customState) { - if (blockState == newState.customBlockState().handle()) { + if (blockState == newState.customBlockState().literalObject()) { CoreReflections.method$BlockStateBase$updateNeighbourShapes.invoke(blockState, level, blockPos, UpdateOption.UPDATE_ALL.flags(), 512); } else { - FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, newState.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, newState.customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); } } } @@ -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 9f3e513c7..da6eb9d78 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 @@ -105,7 +105,6 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior { if (signalForState == 0) { this.checkPressed(args[3], args[1], args[2], state, signalForState, thisBlock); } else { - // todo 为什么 FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(args[1], args[2], thisBlock, this.pressedTime); } } @@ -122,7 +121,7 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior { private Object setSignalForState(Object state, int strength) { Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(state); if (optionalCustomState.isEmpty()) return state; - return optionalCustomState.get().with(this.poweredProperty, strength > 0).customBlockState().handle(); + return optionalCustomState.get().with(this.poweredProperty, strength > 0).customBlockState().literalObject(); } private void checkPressed(@Nullable Object entity, Object level, Object pos, Object state, int currentSignal, Object thisBlock) { @@ -189,7 +188,7 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior { Object pos = args[2]; Object newState = args[3]; boolean movedByPiston = (boolean) args[4]; - if (!movedByPiston && !FastNMS.INSTANCE.method$BlockStateBase$is(state, FastNMS.INSTANCE.method$BlockState$getBlock(newState))) { + if (!movedByPiston && !FastNMS.INSTANCE.method$BlockStateBase$isBlock(state, FastNMS.INSTANCE.method$BlockState$getBlock(newState))) { if (this.getSignalForState(state) > 0) { this.updateNeighbours(level, pos, thisBlock); } 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 324c4752b..c56fa4050 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 @@ -114,7 +114,7 @@ public class SaplingBlockBehavior extends BukkitBlockBehavior { } ImmutableBlockState customState = optionalCustomState.get(); boolean sendParticles = false; - Object visualState = customState.vanillaBlockState().handle(); + Object visualState = customState.vanillaBlockState().literalObject(); Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState); if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, blockPos, visualState); @@ -151,7 +151,7 @@ public class SaplingBlockBehavior extends BukkitBlockBehavior { if (ItemUtils.isEmpty(item) || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || context.getPlayer().isAdventureMode()) return InteractionResult.PASS; boolean sendSwing = false; - Object visualState = state.vanillaBlockState().handle(); + Object visualState = state.vanillaBlockState().literalObject(); Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState); if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState); 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..d52bda8a4 --- /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 createBlockEntityTicker(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..0a7933cad --- /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.MCUtils; +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 MCUtils.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 = MCUtils.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/SofaBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SofaBlockBehavior.java new file mode 100644 index 000000000..7d7498c36 --- /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.state.properties.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/StackableBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java index bb7552b74..6556345dc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java @@ -68,7 +68,7 @@ public class StackableBlockBehavior extends BukkitBlockBehavior { private void updateStackableBlock(ImmutableBlockState state, BlockPos pos, World world, Item item, Player player, InteractionHand hand) { ImmutableBlockState nextStage = state.cycle(this.amountProperty); Location location = new Location((org.bukkit.World) world.platformWorld(), pos.x(), pos.y(), pos.z()); - FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), nextStage.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), nextStage.customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); if (this.stackSound != null) { world.playBlockSound(new Vec3d(location.getX(), location.getY(), location.getZ()), this.stackSound); } 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 64fc2c627..2ad532eed 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 @@ -65,7 +65,7 @@ public class StairsBlockBehavior extends BukkitBlockBehavior { Direction direction = DirectionUtils.fromNMSDirection(VersionHelper.isOrAbove1_21_2() ? args[4] : args[1]); StairsShape stairsShape = getStairsShape(customState, level, LocationUtils.fromBlockPos(blockPos)); return direction.axis().isHorizontal() - ? customState.with(this.shapeProperty, stairsShape).customBlockState().handle() + ? customState.with(this.shapeProperty, stairsShape).customBlockState().literalObject() : superMethod.call(); } 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..577c167c0 --- /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(arguments.getOrDefault("can-open-with-hand", false), "can-open-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 5f16405dd..71db218b5 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 @@ -118,7 +118,7 @@ public class TrapDoorBlockBehavior extends BukkitBlockBehavior { private void playerToggle(UseOnContext context, ImmutableBlockState state) { Player player = context.getPlayer(); this.toggle(state, context.getLevel(), context.getClickedPos(), player); - if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().handle()), context.getHitResult(), (Item) context.getItem())) { + if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().literalObject()), context.getHitResult(), (Item) context.getItem())) { player.swingHand(context.getHand()); } } @@ -195,7 +195,7 @@ public class TrapDoorBlockBehavior extends BukkitBlockBehavior { this.playSound(LocationUtils.fromBlockPos(blockPos), world, hasSignal); } - FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, customState.with(this.poweredProperty, hasSignal).customBlockState().handle(), UpdateOption.Flags.UPDATE_CLIENTS); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, customState.with(this.poweredProperty, hasSignal).customBlockState().literalObject(), UpdateOption.Flags.UPDATE_CLIENTS); if (this.waterloggedProperty != null && customState.get(this.waterloggedProperty)) { FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleFluidTick(level, blockPos, MFluids.WATER, 5); } @@ -203,7 +203,7 @@ public class TrapDoorBlockBehavior extends BukkitBlockBehavior { private void toggle(ImmutableBlockState state, World world, BlockPos pos, @Nullable Player player) { ImmutableBlockState newState = state.cycle(this.openProperty); - FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); boolean open = newState.get(this.openProperty); ((org.bukkit.World) world.platformWorld()).sendGameEvent( player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null, 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..2ab839d6b 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,26 @@ 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); + } +} \ No newline at end of file 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 b5d2d9c9a..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 @@ -66,13 +66,13 @@ public class VerticalCropBlockBehavior extends BukkitBlockBehavior { if (age >= this.ageProperty.max || RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) { Object nextPos = this.direction ? LocationUtils.above(blockPos) : LocationUtils.below(blockPos); if (VersionHelper.isOrAbove1_21_5()) { - CraftBukkitReflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, nextPos, super.customBlock.defaultState().customBlockState().handle(), UpdateOption.UPDATE_ALL.flags()); + CraftBukkitReflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, nextPos, super.customBlock.defaultState().customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); } else { - CraftBukkitReflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, nextPos, super.customBlock.defaultState().customBlockState().handle()); + CraftBukkitReflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, nextPos, super.customBlock.defaultState().customBlockState().literalObject()); } - FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, currentState.with(this.ageProperty, this.ageProperty.min).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, currentState.with(this.ageProperty, this.ageProperty.min).customBlockState().literalObject(), UpdateOption.UPDATE_NONE.flags()); } else if (RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) { - FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, currentState.with(this.ageProperty, age + 1).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, currentState.with(this.ageProperty, age + 1).customBlockState().literalObject(), UpdateOption.UPDATE_NONE.flags()); } } } @@ -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..84a2d19fb --- /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 createBlockEntityTicker(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..f571661e8 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/WallTorchParticleBlockEntity.java @@ -0,0 +1,55 @@ +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.Direction; +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/BukkitEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitEntity.java index 2193b86f9..caf677a3a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitEntity.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitEntity.java @@ -1,6 +1,6 @@ package net.momirealms.craftengine.bukkit.entity; -import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.bukkit.util.EntityUtils; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.entity.AbstractEntity; import net.momirealms.craftengine.core.util.Direction; @@ -68,7 +68,7 @@ public class BukkitEntity extends AbstractEntity { @Override public Key type() { - return KeyUtils.namespacedKey2Key(literalObject().getType().getKey()); + return EntityUtils.getEntityType(literalObject()); } @Override 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/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..5f72065db 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 @@ -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/item/BukkitItemManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java index c21500bb8..2318e20a9 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 @@ -21,7 +21,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.*; @@ -335,7 +334,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/LegacyNetworkItemHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/LegacyNetworkItemHandler.java index aa98ccc73..7c6df9cdd 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/LegacyNetworkItemHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/LegacyNetworkItemHandler.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.bukkit.item; -import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.CustomItem; @@ -11,8 +10,11 @@ import net.momirealms.craftengine.core.item.modifier.ArgumentsModifier; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +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.NetworkTextReplaceContext; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.ListTag; @@ -61,7 +63,7 @@ public final class LegacyNetworkItemHandler implements NetworkItemHandler> optionalCustomItem = wrapped.getCustomItem(); if (optionalCustomItem.isEmpty()) { if (!Config.interceptItem()) return Optional.empty(); - return new OtherItem(wrapped, false).process(); + return new OtherItem(wrapped, false).process(NetworkTextReplaceContext.of(player)); } else { BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get(); Object serverItem = FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject()); @@ -71,7 +73,7 @@ public final class LegacyNetworkItemHandler implements NetworkItemHandler item, BiConsumer callback) { + public static boolean processCustomName(Item item, BiConsumer callback, Context context) { Optional optionalCustomName = item.customNameJson(); if (optionalCustomName.isPresent()) { String line = optionalCustomName.get(); - Map tokens = CraftEngine.instance().fontManager().matchTags(line); + Map tokens = CraftEngine.instance().fontManager().matchTags(line); if (!tokens.isEmpty()) { - item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens))); + item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context))); callback.accept("display.Name", NetworkItemHandler.pack(Operation.ADD, new StringTag(line))); return true; } @@ -125,18 +127,18 @@ public final class LegacyNetworkItemHandler implements NetworkItemHandler item, BiConsumer callback) { + private static boolean processLore(Item item, BiConsumer callback, Context context) { Optional> optionalLore = item.loreJson(); if (optionalLore.isPresent()) { boolean changed = false; List lore = optionalLore.get(); List newLore = new ArrayList<>(lore.size()); for (String line : lore) { - Map tokens = CraftEngine.instance().fontManager().matchTags(line); + Map tokens = CraftEngine.instance().fontManager().matchTags(line); if (tokens.isEmpty()) { newLore.add(line); } else { - newLore.add(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens))); + newLore.add(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context))); changed = true; } } @@ -164,11 +166,11 @@ public final class LegacyNetworkItemHandler implements NetworkItemHandler> process() { - if (processLore(this.item, (s, c) -> networkTag().put(s, c))) { + public Optional> process(Context context) { + if (processLore(this.item, (s, c) -> networkTag().put(s, c), context)) { this.globalChanged = true; } - if (processCustomName(this.item, (s, c) -> networkTag().put(s, c))) { + if (processCustomName(this.item, (s, c) -> networkTag().put(s, c), context)) { this.globalChanged = true; } if (this.globalChanged) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ModernNetworkItemHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ModernNetworkItemHandler.java index a1be6a2f5..41bad6664 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ModernNetworkItemHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ModernNetworkItemHandler.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.bukkit.item; -import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.*; @@ -8,8 +7,11 @@ import net.momirealms.craftengine.core.item.modifier.ArgumentsModifier; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +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.NetworkTextReplaceContext; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.sparrow.nbt.CompoundTag; @@ -64,7 +66,7 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler> optionalCustomItem = wrapped.getCustomItem(); if (optionalCustomItem.isEmpty()) { if (!Config.interceptItem()) return Optional.empty(); - return new OtherItem(wrapped, false).process(); + return new OtherItem(wrapped, false).process(NetworkTextReplaceContext.of(player)); } else { BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get(); Object serverItem = FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject()); @@ -74,7 +76,7 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler tag); - else processLegacyItemName(wrapped, () -> tag); + if (VersionHelper.isOrAbove1_21_5()) processModernItemName(wrapped, () -> tag, context); + else processLegacyItemName(wrapped, () -> tag, context); } if (!tag.containsKey(ComponentIds.CUSTOM_NAME)) { - if (VersionHelper.isOrAbove1_21_5()) processModernCustomName(wrapped, () -> tag); - else processLegacyCustomName(wrapped, () -> tag); + if (VersionHelper.isOrAbove1_21_5()) processModernCustomName(wrapped, () -> tag, context); + else processLegacyCustomName(wrapped, () -> tag, context); } if (!tag.containsKey(ComponentIds.LORE)) { - if (VersionHelper.isOrAbove1_21_5()) processModernLore(wrapped, () -> tag); - else processLegacyLore(wrapped, () -> tag); + if (VersionHelper.isOrAbove1_21_5()) processModernLore(wrapped, () -> tag, context); + else processLegacyLore(wrapped, () -> tag, context); } } if (tag.isEmpty()) { @@ -120,18 +122,18 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler item, Supplier tag) { + public static boolean processLegacyLore(Item item, Supplier tag, Context context) { Optional> optionalLore = item.loreJson(); if (optionalLore.isPresent()) { boolean changed = false; List lore = optionalLore.get(); List newLore = new ArrayList<>(lore.size()); for (String line : lore) { - Map tokens = CraftEngine.instance().fontManager().matchTags(line); + Map tokens = CraftEngine.instance().fontManager().matchTags(line); if (tokens.isEmpty()) { newLore.add(line); } else { - newLore.add(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens))); + newLore.add(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context))); changed = true; } } @@ -148,13 +150,13 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler item, Supplier tag) { + public static boolean processLegacyCustomName(Item item, Supplier tag, Context context) { Optional optionalCustomName = item.customNameJson(); if (optionalCustomName.isPresent()) { String line = optionalCustomName.get(); - Map tokens = CraftEngine.instance().fontManager().matchTags(line); + Map tokens = CraftEngine.instance().fontManager().matchTags(line); if (!tokens.isEmpty()) { - item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens))); + item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context))); tag.get().put(ComponentIds.CUSTOM_NAME, NetworkItemHandler.pack(Operation.ADD, new StringTag(line))); return true; } @@ -162,13 +164,13 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler item, Supplier tag) { + public static boolean processLegacyItemName(Item item, Supplier tag, Context context) { Optional optionalItemName = item.itemNameJson(); if (optionalItemName.isPresent()) { String line = optionalItemName.get(); - Map tokens = CraftEngine.instance().fontManager().matchTags(line); + Map tokens = CraftEngine.instance().fontManager().matchTags(line); if (!tokens.isEmpty()) { - item.itemNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens))); + item.itemNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context))); tag.get().put(ComponentIds.ITEM_NAME, NetworkItemHandler.pack(Operation.ADD, new StringTag(line))); return true; } @@ -176,33 +178,33 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler item, Supplier tag) { + public static boolean processModernItemName(Item item, Supplier tag, Context context) { Tag nameTag = item.getSparrowNBTComponent(ComponentTypes.ITEM_NAME); if (nameTag == null) return false; String tagStr = nameTag.getAsString(); - Map tokens = CraftEngine.instance().fontManager().matchTags(tagStr); + Map tokens = CraftEngine.instance().fontManager().matchTags(tagStr); if (!tokens.isEmpty()) { - item.setNBTComponent(ComponentKeys.ITEM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens))); + item.setNBTComponent(ComponentKeys.ITEM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens, context))); tag.get().put(ComponentIds.ITEM_NAME, NetworkItemHandler.pack(Operation.ADD, nameTag)); return true; } return false; } - public static boolean processModernCustomName(Item item, Supplier tag) { + public static boolean processModernCustomName(Item item, Supplier tag, Context context) { Tag nameTag = item.getSparrowNBTComponent(ComponentTypes.CUSTOM_NAME); if (nameTag == null) return false; String tagStr = nameTag.getAsString(); - Map tokens = CraftEngine.instance().fontManager().matchTags(tagStr); + Map tokens = CraftEngine.instance().fontManager().matchTags(tagStr); if (!tokens.isEmpty()) { - item.setNBTComponent(ComponentKeys.CUSTOM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens))); + item.setNBTComponent(ComponentKeys.CUSTOM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens, context))); tag.get().put(ComponentIds.CUSTOM_NAME, NetworkItemHandler.pack(Operation.ADD, nameTag)); return true; } return false; } - public static boolean processModernLore(Item item, Supplier tagSupplier) { + public static boolean processModernLore(Item item, Supplier tagSupplier, Context context) { Tag loreTag = item.getSparrowNBTComponent(ComponentTypes.LORE); boolean changed = false; if (!(loreTag instanceof ListTag listTag)) { @@ -211,11 +213,11 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler tokens = CraftEngine.instance().fontManager().matchTags(tagStr); + Map tokens = CraftEngine.instance().fontManager().matchTags(tagStr); if (tokens.isEmpty()) { newLore.add(tag); } else { - newLore.add(AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(tag), tokens))); + newLore.add(AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(tag), tokens, context))); changed = true; } } @@ -238,20 +240,20 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler> process() { + public Optional> process(Context context) { if (VersionHelper.isOrAbove1_21_5()) { - if (processModernLore(this.item, this::getOrCreateTag)) + if (processModernLore(this.item, this::getOrCreateTag, context)) this.globalChanged = true; - if (processModernCustomName(this.item, this::getOrCreateTag)) + if (processModernCustomName(this.item, this::getOrCreateTag, context)) this.globalChanged = true; - if (processModernItemName(this.item, this::getOrCreateTag)) + if (processModernItemName(this.item, this::getOrCreateTag, context)) this.globalChanged = true; } else { - if (processLegacyLore(this.item, this::getOrCreateTag)) + if (processLegacyLore(this.item, this::getOrCreateTag, context)) this.globalChanged = true; - if (processLegacyCustomName(this.item, this::getOrCreateTag)) + if (processLegacyCustomName(this.item, this::getOrCreateTag, context)) this.globalChanged = true; - if (processLegacyItemName(this.item, this::getOrCreateTag)) + if (processLegacyItemName(this.item, this::getOrCreateTag, context)) this.globalChanged = true; } if (this.globalChanged) { 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 0b8574385..e928f9233 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 @@ -6,7 +6,7 @@ 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.util.*; -import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; +import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.UpdateOption; @@ -82,12 +82,12 @@ public class AxeItemBehavior extends ItemBehavior { CompoundTag compoundTag = customState.propertiesNbt(); ImmutableBlockState newState = newCustomBlock.getBlockState(compoundTag); - BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos()); + BukkitExistingBlock clicked = (BukkitExistingBlock) context.getLevel().getBlockAt(context.getClickedPos()); org.bukkit.entity.Player bukkitPlayer = null; if (player != null) { bukkitPlayer = ((org.bukkit.entity.Player) player.platformPlayer()); // Call bukkit event - EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, clicked.block(), BlockStateUtils.fromBlockData(newState.customBlockState().handle())); + EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, clicked.block(), BlockStateUtils.fromBlockData(newState.customBlockState().literalObject())); if (EventUtils.fireAndCheckCancel(event)) { return InteractionResult.FAIL; } @@ -98,7 +98,7 @@ public class AxeItemBehavior extends ItemBehavior { if (ItemUtils.isEmpty(item)) return InteractionResult.FAIL; BlockPos pos = context.getClickedPos(); context.getLevel().playBlockSound(Vec3d.atCenterOf(pos), AXE_STRIP_SOUND, 1, 1); - FastNMS.INSTANCE.method$LevelWriter$setBlock(context.getLevel().serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().handle(), UpdateOption.UPDATE_ALL_IMMEDIATE.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(context.getLevel().serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().literalObject(), UpdateOption.UPDATE_ALL_IMMEDIATE.flags()); clicked.block().getWorld().sendGameEvent(bukkitPlayer, GameEvent.BLOCK_CHANGE, new Vector(pos.x(), pos.y(), pos.z())); Material material = MaterialUtils.getMaterial(item.vanillaId()); if (bukkitPlayer != null) { @@ -106,7 +106,7 @@ public class AxeItemBehavior extends ItemBehavior { // resend swing if it's not interactable on client side if (!InteractUtils.isInteractable( - bukkitPlayer, BlockStateUtils.fromBlockData(customState.vanillaBlockState().handle()), + bukkitPlayer, BlockStateUtils.fromBlockData(customState.vanillaBlockState().literalObject()), context.getHitResult(), item ) || player.isSecondaryUseActive()) { player.swingHand(context.getHand()); 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 faa5afefc..faf8bbc09 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 @@ -9,7 +9,7 @@ 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.*; -import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; +import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.UpdateOption; @@ -73,7 +73,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { return InteractionResult.FAIL; } if (!context.canPlace()) { - return InteractionResult.FAIL; + return InteractionResult.PASS; } Player player = context.getPlayer(); @@ -89,7 +89,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { ImmutableBlockState blockStateToPlace = getPlacementState(context, block); if (blockStateToPlace == null) { - return InteractionResult.FAIL; + return InteractionResult.PASS; } BlockPos againstPos = context.getAgainstPos(); @@ -111,7 +111,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { } else { ImmutableBlockState customState = optionalCustomState.get(); // custom block - if (!AdventureModeUtils.canPlace(context.getItem(), context.getLevel(), againstPos, Config.simplifyAdventurePlaceCheck() ? customState.vanillaBlockState().handle() : againstBlockState)) { + if (!AdventureModeUtils.canPlace(context.getItem(), context.getLevel(), againstPos, Config.simplifyAdventurePlaceCheck() ? customState.vanillaBlockState().literalObject() : againstBlockState)) { return InteractionResult.FAIL; } } @@ -157,7 +157,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { WorldPosition position = new WorldPosition(context.getLevel(), pos.x() + 0.5, pos.y() + 0.5, pos.z() + 0.5); Cancellable dummy = Cancellable.dummy(); PlayerOptionalContext functionContext = PlayerOptionalContext.of(player, ContextHolder.builder() - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(bukkitBlock)) + .withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(bukkitBlock)) .withParameter(DirectContextParameters.POSITION, position) .withParameter(DirectContextParameters.EVENT, dummy) .withParameter(DirectContextParameters.HAND, context.getHand()) @@ -196,7 +196,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { try { Player cePlayer = context.getPlayer(); Object player = cePlayer != null ? cePlayer.serverPlayer() : null; - Object blockState = state.customBlockState().handle(); + Object blockState = state.customBlockState().literalObject(); Object blockPos = LocationUtils.toBlockPos(context.getClickedPos()); Object voxelShape; if (VersionHelper.isOrAbove1_21_6()) { 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 79292cfda..271930a23 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 void init() { register(EMPTY, EmptyItemBehavior.FACTORY); @@ -22,5 +23,6 @@ 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); } } 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 480b4db98..e4d92d241 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 @@ -5,7 +5,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.EventUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; +import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; @@ -38,7 +38,7 @@ public class CompostableItemBehavior extends ItemBehavior { @SuppressWarnings("UnstableApiUsage") @Override public InteractionResult useOnBlock(UseOnContext context) { - BukkitBlockInWorld block = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos()); + BukkitExistingBlock block = (BukkitExistingBlock) context.getLevel().getBlockAt(context.getClickedPos()); BlockData blockData = block.block().getBlockData(); Object blockOwner = BlockStateUtils.getBlockOwner(BlockStateUtils.blockDataToBlockState(blockData)); if (blockOwner != MBlocks.COMPOSTER) return InteractionResult.PASS; 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 5365f9692..2be578ff4 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 @@ -6,7 +6,7 @@ import net.momirealms.craftengine.bukkit.util.BlockStateUtils; 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.BukkitBlockInWorld; +import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.item.Item; @@ -40,7 +40,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior { if (player == null) return InteractionResult.PASS; BlockPos clickedPos = context.getClickedPos(); - BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(clickedPos); + BukkitExistingBlock clicked = (BukkitExistingBlock) context.getLevel().getBlockAt(clickedPos); Block block = clicked.block(); BlockPos firePos = clickedPos.relative(context.getClickedFace()); Direction direction = context.getHorizontalDirection(); @@ -77,10 +77,10 @@ public class FlintAndSteelItemBehavior extends ItemBehavior { // 点击对象为自定义方块 ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId); // 原版外观也可燃 - if (BlockStateUtils.isBurnable(immutableBlockState.vanillaBlockState().handle())) { + if (BlockStateUtils.isBurnable(immutableBlockState.vanillaBlockState().literalObject())) { return InteractionResult.PASS; } - BlockData vanillaBlockState = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().handle()); + BlockData vanillaBlockState = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().literalObject()); // 点击的是方块上面,则只需要判断shift和可交互 if (direction == Direction.UP) { // 客户端层面必须可交互 @@ -95,7 +95,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior { } else { // 玩家觉得自定义方块不可燃,且点击了侧面,那么就要判断火源下方的方块是否可燃,如果不可燃,则补发声音 BlockPos belowFirePos = firePos.relative(Direction.DOWN); - BukkitBlockInWorld belowFireBlock = (BukkitBlockInWorld) context.getLevel().getBlockAt(belowFirePos); + BukkitExistingBlock belowFireBlock = (BukkitExistingBlock) context.getLevel().getBlockAt(belowFirePos); boolean belowCanBurn; try { Block belowBlock = belowFireBlock.block(); @@ -134,7 +134,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior { for (Direction dir : Direction.values()) { if (dir == relativeDirection) continue; BlockPos relPos = firePos.relative(dir); - BukkitBlockInWorld nearByBlock = (BukkitBlockInWorld) context.getLevel().getBlockAt(relPos); + BukkitExistingBlock nearByBlock = (BukkitExistingBlock) context.getLevel().getBlockAt(relPos); BlockData nearbyBlockData = nearByBlock.block().getBlockData(); Object nearbyBlockState = BlockStateUtils.blockDataToBlockState(nearbyBlockData); int stateID = BlockStateUtils.blockStateToId(nearbyBlockState); 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..051838931 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,11 +47,15 @@ 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$BlockHitResul$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 { 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..53767eeaf --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/WallBlockItemBehavior.java @@ -0,0 +1,56 @@ +package net.momirealms.craftengine.bukkit.item.behavior; + +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +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 net.momirealms.craftengine.core.util.MiscUtils; + +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, 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) { + 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)); + } + 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/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/ItemEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java index db9aff35a..ce1500a38 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 @@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.item.listener; import io.papermc.paper.event.block.CompostItemEvent; import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.api.event.CustomBlockInteractEvent; +import net.momirealms.craftengine.bukkit.entity.BukkitEntity; import net.momirealms.craftengine.bukkit.item.BukkitCustomItem; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; @@ -10,7 +11,7 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.*; -import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; +import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior; @@ -96,9 +97,10 @@ public class ItemEventListener implements Listener { Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled); PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() .withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand) - .withParameter(DirectContextParameters.EVENT, cancellable) - .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(event.getRightClicked().getLocation())) .withParameter(DirectContextParameters.HAND, hand) + .withParameter(DirectContextParameters.EVENT, cancellable) + .withParameter(DirectContextParameters.ENTITY, new BukkitEntity(entity)) + .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(event.getRightClicked().getLocation())) ); CustomItem customItem = optionalCustomItem.get(); customItem.execute(context, EventTrigger.RIGHT_CLICK); @@ -162,7 +164,7 @@ public class ItemEventListener implements Listener { // fix client side issues if (action.isRightClick() && hitResult != null && - InteractUtils.willConsume(player, BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().handle()), hitResult, itemInHand)) { + InteractUtils.willConsume(player, BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().literalObject()), hitResult, itemInHand)) { player.updateInventory(); //PlayerUtils.resendItemInHand(player); } @@ -171,7 +173,7 @@ public class ItemEventListener implements Listener { // run custom functions CustomBlock customBlock = immutableBlockState.owner().value(); PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block)) .withParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, immutableBlockState) .withParameter(DirectContextParameters.HAND, hand) .withParameter(DirectContextParameters.EVENT, dummy) @@ -253,13 +255,13 @@ public class ItemEventListener implements Listener { if (immutableBlockState != null) { // client won't have sounds if the clientside block is interactable // so we should check and resend sounds on BlockPlaceEvent - BlockData craftBlockData = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().handle()); + BlockData craftBlockData = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().literalObject()); if (InteractUtils.isInteractable(player, craftBlockData, hitResult, itemInHand)) { if (!serverPlayer.isSecondaryUseActive()) { serverPlayer.setResendSound(); } } else { - if (BlockStateUtils.isReplaceable(immutableBlockState.customBlockState().handle()) && !BlockStateUtils.isReplaceable(immutableBlockState.vanillaBlockState().handle())) { + if (BlockStateUtils.isReplaceable(immutableBlockState.customBlockState().literalObject()) && !BlockStateUtils.isReplaceable(immutableBlockState.vanillaBlockState().literalObject())) { serverPlayer.setResendSwing(); } } @@ -316,7 +318,7 @@ public class ItemEventListener implements Listener { if (serverPlayer.isSecondaryUseActive() || !InteractUtils.isInteractable(player, blockData, hitResult, itemInHand)) { Cancellable dummy = Cancellable.dummy(); PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block)) .withOptionalParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, immutableBlockState) .withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand) .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(block.getLocation())) @@ -337,7 +339,7 @@ public class ItemEventListener implements Listener { if (hasCustomItem && action == Action.LEFT_CLICK_BLOCK) { Cancellable dummy = Cancellable.dummy(); PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block)) .withOptionalParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, immutableBlockState) .withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand) .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(block.getLocation())) 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 519734e35..b738734a3 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 @@ -3,7 +3,6 @@ package net.momirealms.craftengine.bukkit.item.recipe; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import io.papermc.paper.potion.PotionMix; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; @@ -69,58 +68,35 @@ public class BukkitRecipeManager extends AbstractRecipeManager { return recipe; }; - static { - try { - Key dyeRecipeId = Key.from("armor_dye"); - MINECRAFT_RECIPE_REMOVER.accept(dyeRecipeId); - MINECRAFT_RECIPE_ADDER.apply(dyeRecipeId, RecipeInjector.createCustomDyeRecipe(dyeRecipeId)); - Key repairRecipeId = Key.from("repair_item"); - MINECRAFT_RECIPE_REMOVER.accept(repairRecipeId); - MINECRAFT_RECIPE_ADDER.apply(repairRecipeId, RecipeInjector.createRepairItemRecipe(repairRecipeId)); - Key fireworkStarFadeRecipeId = Key.from("firework_star_fade"); - MINECRAFT_RECIPE_REMOVER.accept(fireworkStarFadeRecipeId); - MINECRAFT_RECIPE_ADDER.apply(fireworkStarFadeRecipeId, RecipeInjector.createFireworkStarFadeRecipe(fireworkStarFadeRecipeId)); - } catch (ReflectiveOperationException e) { - throw new ReflectionInitException("Failed to inject special recipes", e); - } - } - - private static final List MODIFIED_INGREDIENTS = new ArrayList<>(); private static final Map, Object>> ADD_RECIPE_FOR_MINECRAFT_RECIPE_HOLDER = Map.of( RecipeSerializers.SHAPED, recipe -> { CustomShapedRecipe shapedRecipe = (CustomShapedRecipe) recipe; Object mcRecipe = FastNMS.INSTANCE.createShapedRecipe(shapedRecipe); - modifyShapedRecipeIngredients(shapedRecipe, mcRecipe); return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); }, RecipeSerializers.SHAPELESS, recipe -> { CustomShapelessRecipe shapelessRecipe = (CustomShapelessRecipe) recipe; Object mcRecipe = FastNMS.INSTANCE.createShapelessRecipe(shapelessRecipe); - modifyShapelessRecipeIngredients(shapelessRecipe, mcRecipe); return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); }, RecipeSerializers.SMELTING, recipe -> { CustomSmeltingRecipe smeltingRecipe = (CustomSmeltingRecipe) recipe; Object mcRecipe = FastNMS.INSTANCE.createSmeltingRecipe(smeltingRecipe); - modifyCookingRecipeIngredient(smeltingRecipe, mcRecipe); return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); }, RecipeSerializers.BLASTING, recipe -> { CustomBlastingRecipe blastingRecipe = (CustomBlastingRecipe) recipe; Object mcRecipe = FastNMS.INSTANCE.createBlastingRecipe(blastingRecipe); - modifyCookingRecipeIngredient(blastingRecipe, mcRecipe); return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); }, RecipeSerializers.SMOKING, recipe -> { CustomSmokingRecipe smokingRecipe = (CustomSmokingRecipe) recipe; Object mcRecipe = FastNMS.INSTANCE.createSmokingRecipe(smokingRecipe); - modifyCookingRecipeIngredient(smokingRecipe, mcRecipe); return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); }, RecipeSerializers.CAMPFIRE_COOKING, recipe -> { CustomCampfireRecipe campfireRecipe = (CustomCampfireRecipe) recipe; Object mcRecipe = FastNMS.INSTANCE.createCampfireRecipe(campfireRecipe); - modifyCookingRecipeIngredient(campfireRecipe, mcRecipe); return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); }, RecipeSerializers.STONECUTTING, recipe -> { @@ -219,18 +195,19 @@ public class BukkitRecipeManager extends AbstractRecipeManager { } } - private static List getIngredientLooks(List holders) { + public static List getIngredientLooks(List holders) { List itemStacks = new ArrayList<>(); 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 { Item barrier = BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, null); assert barrier != null; barrier.customNameJson(AdventureHelper.componentToJson(Component.text(holder.key().asString()).color(NamedTextColor.RED))); + itemStacks.add(barrier.getLiteralObject()); } } return itemStacks; @@ -245,9 +222,9 @@ public class BukkitRecipeManager extends AbstractRecipeManager { Ingredient actualIngredient = actualIngredients.get(i); List items = getIngredientLooks(actualIngredient.items()); if (VersionHelper.isOrAbove1_21_4()) { - CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (Set) new ObjectOpenHashSet<>(items)); + CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (Set) new CustomIngredientSet(items, actualIngredient)); } else if (VersionHelper.isOrAbove1_21_2()) { - CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (List) items); + CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (List) new CustomIngredientList(items, actualIngredient)); } else { Object itemStackArray = Array.newInstance(CoreReflections.clazz$ItemStack, items.size()); for (int j = 0; j < items.size(); j++) { @@ -255,7 +232,6 @@ public class BukkitRecipeManager extends AbstractRecipeManager { } CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (Object) itemStackArray); } - MODIFIED_INGREDIENTS.add(ingredient); } } @@ -360,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)); @@ -484,6 +460,22 @@ public class BukkitRecipeManager extends AbstractRecipeManager { this.plugin.logger().warn("Failed to register recipe " + recipe.id().toString(), e); } } + + // 重新注入特殊配方 + try { + Key dyeRecipeId = Key.from("armor_dye"); + MINECRAFT_RECIPE_REMOVER.accept(dyeRecipeId); + MINECRAFT_RECIPE_ADDER.apply(dyeRecipeId, RecipeInjector.createCustomDyeRecipe(dyeRecipeId)); + Key repairRecipeId = Key.from("repair_item"); + MINECRAFT_RECIPE_REMOVER.accept(repairRecipeId); + MINECRAFT_RECIPE_ADDER.apply(repairRecipeId, RecipeInjector.createRepairItemRecipe(repairRecipeId)); + Key fireworkStarFadeRecipeId = Key.from("firework_star_fade"); + MINECRAFT_RECIPE_REMOVER.accept(fireworkStarFadeRecipeId); + MINECRAFT_RECIPE_ADDER.apply(fireworkStarFadeRecipeId, RecipeInjector.createFireworkStarFadeRecipe(fireworkStarFadeRecipeId)); + } catch (ReflectiveOperationException e) { + throw new ReflectionInitException("Failed to inject special recipes", e); + } + try { // give flags back on 1.21.2+ if (VersionHelper.isOrAbove1_21_2() && this.stolenFeatureFlagSet != null) { @@ -498,20 +490,6 @@ public class BukkitRecipeManager extends AbstractRecipeManager { // send to players CoreReflections.methodHandle$DedicatedPlayerList$reloadRecipes.invokeExact(CraftBukkitReflections.methodHandle$CraftServer$playerListGetter.invokeExact(Bukkit.getServer())); - - // now we need to remove the fake `exact` choices - if (VersionHelper.isOrAbove1_21_4()) { - for (Object ingredient : MODIFIED_INGREDIENTS) { - CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (Set) null); - } - } else if (VersionHelper.isOrAbove1_21_2()) { - for (Object ingredient : MODIFIED_INGREDIENTS) { - CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (List) null); - } - } - - // clear cache - MODIFIED_INGREDIENTS.clear(); } catch (Throwable e) { this.plugin.logger().warn("Failed to run delayed recipe tasks", e); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/CustomIngredientList.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/CustomIngredientList.java new file mode 100644 index 000000000..6b7332486 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/CustomIngredientList.java @@ -0,0 +1,28 @@ +package net.momirealms.craftengine.bukkit.item.recipe; + +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.core.item.recipe.Ingredient; +import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; + +public class CustomIngredientList extends ArrayList { + private final Ingredient ingredient; + + public CustomIngredientList(@NotNull Collection c, Ingredient ingredient) { + super(c); + this.ingredient = ingredient; + } + + @Override + public boolean contains(Object o) { + if (o == null || FastNMS.INSTANCE.method$ItemStack$isEmpty(o)) { + return false; + } + return this.ingredient.test(UniqueIdItem.of(BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(o)))); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/CustomIngredientSet.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/CustomIngredientSet.java new file mode 100644 index 000000000..c7201607a --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/CustomIngredientSet.java @@ -0,0 +1,28 @@ +package net.momirealms.craftengine.bukkit.item.recipe; + +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.core.item.recipe.Ingredient; +import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.HashSet; + +public class CustomIngredientSet extends HashSet { + private final Ingredient ingredient; + + public CustomIngredientSet(@NotNull Collection c, Ingredient ingredient) { + super(c); + this.ingredient = ingredient; + } + + @Override + public boolean contains(Object o) { + if (o == null || FastNMS.INSTANCE.method$ItemStack$isEmpty(o)) { + return false; + } + return this.ingredient.test(UniqueIdItem.of(BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(o)))); + } +} 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 5b6359d3f..59894f3e4 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,6 @@ 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.util.*; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -270,6 +269,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(); @@ -284,6 +284,10 @@ public class RecipeEventListener implements Listener { } // 如果第二个物品是附魔书,那么忽略 if (wrappedSecond.vanillaId().equals(ItemKeys.ENCHANTED_BOOK)) { + // 禁止不可附魔的物品被附魔书附魔 + if (firstCustom.isPresent() && !firstCustom.get().settings().canEnchant()) { + event.setResult(null); + } return; } @@ -313,12 +317,23 @@ public class RecipeEventListener implements Listener { return; } - // 如果禁止在铁砧使用两个相同物品修复 - firstCustom.ifPresent(it -> { - if (it.settings().canRepair() == Tristate.FALSE) { + if (firstCustom.isPresent()) { + CustomItem firstCustomItem = firstCustom.get(); + if (firstCustomItem.settings().repairable().anvilCombine() == Tristate.FALSE) { event.setResult(null); + return; } - }); + + Item wrappedResult = BukkitItemManager.instance().wrap(event.getResult()); + if (!firstCustomItem.settings().canEnchant()) { + Object previousEnchantment = wrappedFirst.getExactComponent(ComponentTypes.ENCHANTMENTS); + if (previousEnchantment != null) { + wrappedResult.setExactComponent(ComponentTypes.ENCHANTMENTS, previousEnchantment); + } else { + wrappedResult.resetComponent(ComponentTypes.ENCHANTMENTS); + } + } + } } /* @@ -356,7 +371,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; } @@ -477,14 +492,12 @@ 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)) { return; } - if (event.getResult() == null) { - return; - } Item wrappedFirst = BukkitItemManager.instance().wrap(first); wrappedFirst.getCustomItem().ifPresent(item -> { if (!item.settings().renameable()) { @@ -599,7 +612,36 @@ public class RecipeEventListener implements Listener { 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()) { + inventory.setResult(craftingTableRecipe.assembleVisual(input, ItemBuildContext.of(serverPlayer))); + } else { + inventory.setResult(craftingTableRecipe.assemble(input, ItemBuildContext.of(serverPlayer))); + } + } + + @EventHandler(ignoreCancelled = true) + public void onCraftingFinish(CraftItemEvent event) { + if (!Config.enableRecipeSystem()) return; + org.bukkit.inventory.Recipe recipe = event.getRecipe(); + if (!(recipe instanceof CraftingRecipe craftingRecipe)) return; + Key recipeId = Key.of(craftingRecipe.getKey().namespace(), craftingRecipe.getKey().value()); + Optional> optionalRecipe = this.recipeManager.recipeById(recipeId); + // 也许是其他插件注册的配方,直接无视 + if (optionalRecipe.isEmpty()) { + return; + } + CraftingInventory inventory = event.getInventory(); + 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, ItemBuildContext.of(serverPlayer))); } private CraftingInput getCraftingInput(CraftingInventory inventory) { @@ -651,7 +693,7 @@ public class RecipeEventListener implements Listener { 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.of(BukkitAdaptors.adapt(player))); event.setResult(result); } else { event.setResult(null); @@ -675,7 +717,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..68205240c 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 @@ -55,7 +55,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(); 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..6633b1fa3 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; @@ -72,26 +73,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()); + } } } @@ -185,6 +190,7 @@ public class BukkitCraftEngine extends CraftEngine { BukkitBlockBehaviors.init(); BukkitItemBehaviors.init(); BukkitHitBoxTypes.init(); + BukkitBlockEntityElementConfigs.init(); PacketConsumers.initEntities(RegistryUtils.currentEntityTypeRegistrySize()); super.packManager = new BukkitPackManager(this); super.senderFactory = new BukkitSenderFactory(this); 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..9d80faf29 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,7 +55,8 @@ 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) )); final LegacyPaperCommandManager manager = (LegacyPaperCommandManager) getCommandManager(); manager.settings().set(ManagerSetting.ALLOW_UNSAFE_REGISTRATION, true); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGetBlockInternalIdCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGetBlockInternalIdCommand.java index 03e90d034..f08c257c0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGetBlockInternalIdCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGetBlockInternalIdCommand.java @@ -38,7 +38,7 @@ public class DebugGetBlockInternalIdCommand 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/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..fb01ccc33 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,12 +1,14 @@ 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.*; @@ -18,16 +20,20 @@ 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.event.player.PlayerQuitEvent; import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.MenuType; public class BukkitGuiManager implements GuiManager, Listener { 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 @@ -83,8 +89,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 +98,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 +114,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 +125,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 83b94ad9f..f7105c15e 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 @@ -23,6 +23,7 @@ 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; @@ -34,7 +35,6 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Field; import java.util.concurrent.Callable; -import java.util.function.BiConsumer; import java.util.function.Function; public final class BlockGenerator { @@ -63,6 +63,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")) @@ -90,12 +91,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)) @@ -106,54 +116,21 @@ public final class BlockGenerator { .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$randomTick)) .intercept(MethodDelegation.to(RandomTickInterceptor.INSTANCE)) // onPlace - .method(ElementMatchers.takesArguments(5) - .and(ElementMatchers.takesArgument(0, CoreReflections.clazz$BlockState)) - .and(ElementMatchers.takesArgument(1, CoreReflections.clazz$Level)) - .and(ElementMatchers.takesArgument(2, CoreReflections.clazz$BlockPos)) - .and(ElementMatchers.takesArgument(3, CoreReflections.clazz$BlockState)) - .and(ElementMatchers.takesArgument(4, boolean.class)) - .and(ElementMatchers.named("onPlace").or(ElementMatchers.named("a"))) - ) + .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$onPlace)) .intercept(MethodDelegation.to(OnPlaceInterceptor.INSTANCE)) // onBrokenAfterFall - .method(ElementMatchers.takesArguments(3) - .and(ElementMatchers.takesArgument(0, CoreReflections.clazz$Level)) - .and(ElementMatchers.takesArgument(1, CoreReflections.clazz$BlockPos)) - .and(ElementMatchers.takesArgument(2, CoreReflections.clazz$FallingBlockEntity)) - ) + .method(ElementMatchers.is(CoreReflections.method$Fallable$onBrokenAfterFall)) .intercept(MethodDelegation.to(OnBrokenAfterFallInterceptor.INSTANCE)) // onLand - .method(ElementMatchers.takesArguments(5) - .and(ElementMatchers.takesArgument(0, CoreReflections.clazz$Level)) - .and(ElementMatchers.takesArgument(1, CoreReflections.clazz$BlockPos)) - .and(ElementMatchers.takesArgument(2, CoreReflections.clazz$BlockState)) - .and(ElementMatchers.takesArgument(3, CoreReflections.clazz$BlockState)) - .and(ElementMatchers.takesArgument(4, CoreReflections.clazz$FallingBlockEntity)) - ) + .method(ElementMatchers.is(CoreReflections.method$Fallable$onLand)) .intercept(MethodDelegation.to(OnLandInterceptor.INSTANCE)) // canSurvive - .method(ElementMatchers.takesArguments(3) - .and(ElementMatchers.takesArgument(0, CoreReflections.clazz$BlockState)) - .and(ElementMatchers.takesArgument(1, CoreReflections.clazz$LevelReader)) - .and(ElementMatchers.takesArgument(2, CoreReflections.clazz$BlockPos)) + .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$canSurvive) ) .intercept(MethodDelegation.to(CanSurviveInterceptor.INSTANCE)) // updateShape - .method(ElementMatchers.returns(CoreReflections.clazz$BlockState) - .and(ElementMatchers.takesArgument(0, CoreReflections.clazz$BlockState)) - // LevelReader 1.21.3+ // 1.20-1.12.2 - .and(ElementMatchers.takesArgument(1, CoreReflections.clazz$LevelReader).or(ElementMatchers.takesArgument(1, CoreReflections.clazz$Direction))) - .and(ElementMatchers.named("updateShape").or(ElementMatchers.named("a")))) + .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$updateShape)) .intercept(MethodDelegation.to(UpdateShapeInterceptor.INSTANCE)) - // onExplosionHit 1.21+ - .method(ElementMatchers.returns(void.class) - .and(ElementMatchers.takesArgument(0, CoreReflections.clazz$BlockState)) - .and(ElementMatchers.takesArgument(1, VersionHelper.isOrAbove1_21_2() ? CoreReflections.clazz$ServerLevel : CoreReflections.clazz$Level)) - .and(ElementMatchers.takesArgument(2, CoreReflections.clazz$BlockPos)) - .and(ElementMatchers.takesArgument(3, CoreReflections.clazz$Explosion)) - .and(ElementMatchers.takesArgument(4, BiConsumer.class)) - ) - .intercept(MethodDelegation.to(OnExplosionHitInterceptor.INSTANCE)) // neighborChanged .method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$neighborChanged)) .intercept(MethodDelegation.to(NeighborChangedInterceptor.INSTANCE)) @@ -183,16 +160,29 @@ 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)) + ; + // 1.21.5+ if (CoreReflections.method$BlockBehaviour$affectNeighborsAfterRemoval != null) { - builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$affectNeighborsAfterRemoval)) + builder = builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$affectNeighborsAfterRemoval)) .intercept(MethodDelegation.to(AffectNeighborsAfterRemovalInterceptor.INSTANCE)); } + // 1.20-1.21.4 if (CoreReflections.method$BlockBehaviour$onRemove != null) { - builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$onRemove)) + builder = builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$onRemove)) .intercept(MethodDelegation.to(OnRemoveInterceptor.INSTANCE)); } - + // 1.21+ + if (CoreReflections.method$BlockBehaviour$onExplosionHit != null) { + builder = builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$onExplosionHit)) + .intercept(MethodDelegation.to(OnExplosionHitInterceptor.INSTANCE)); + } Class clazz$CraftEngineBlock = builder.make().load(BlockGenerator.class.getClassLoader()).getLoaded(); constructor$CraftEngineBlock = MethodHandles.publicLookup().in(clazz$CraftEngineBlock) .findConstructor(clazz$CraftEngineBlock, MethodType.methodType(void.class, CoreReflections.clazz$BlockBehaviour$Properties)) @@ -469,6 +459,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(); @@ -504,7 +539,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); @@ -672,4 +707,40 @@ public final class BlockGenerator { } } } -} + + 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); + } + } + } +} \ 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 33367919e..825f90a50 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 @@ -109,7 +109,7 @@ public final class BlockStateGenerator { if (optionalPlayer != null && settings.requireCorrectTool()) { if (item.isEmpty()) return List.of(); if (!settings.isCorrectTool(item.id()) && - (!settings.respectToolComponent() || !FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(tool, state.customBlockState().handle()))) { + (!settings.respectToolComponent() || !FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(tool, state.customBlockState().literalObject()))) { return List.of(); } } @@ -121,8 +121,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); @@ -177,7 +179,7 @@ public final class BlockStateGenerator { if (state == null) return thisObj; Property waterloggedProperty = (Property) state.owner().value().getProperty("waterlogged"); if (waterloggedProperty == null) return thisObj; - return state.with(waterloggedProperty, (boolean) args[1]).customBlockState().handle(); + return state.with(waterloggedProperty, (boolean) args[1]).customBlockState().literalObject(); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/LootEntryInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/LootEntryInjector.java index e2f1f4267..7002c9084 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/LootEntryInjector.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/LootEntryInjector.java @@ -10,6 +10,8 @@ import java.util.Set; public final class LootEntryInjector { + private LootEntryInjector() {} + public static void init() throws ReflectiveOperationException { Object registry = MBuiltInRegistries.LOOT_POOL_ENTRY_TYPE; CoreReflections.field$MappedRegistry$frozen.set(registry, false); 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 1fdc089d0..6a79e1032 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,17 @@ 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 +35,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,52 +216,103 @@ 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.handle(), 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().handle(), newState, x, y, z); - } else { - // 自定义块到自定义块 - updateLight$complex(holder, immutableBlockState.vanillaBlockState().handle(), 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(); + private 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)) { @@ -270,8 +323,8 @@ 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(); + 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; Object blockPos = LocationUtils.toBlockPos(x, y, z); Object serverWorld = world.world().serverWorld(); // 如果客户端新状态和服务端新状态光照属性不同 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 e98de26d8..f97d36b3e 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 @@ -11,8 +11,9 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIdFinder; 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.payload.PayloadHelper; +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.LeavesReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.LibraryReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; @@ -105,6 +106,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes this.packetIds = VersionHelper.isOrAbove1_20_5() ? new PacketIds1_20_5() : new PacketIds1_20(); // register packet handlers this.registerPacketHandlers(); + PayloadHelper.registerDataTypes(); // set up packet senders this.packetConsumer = FastNMS.INSTANCE::method$Connection$send; this.packetsConsumer = ((connection, packets, sendListener) -> { @@ -202,7 +204,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes 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.CUSTOM_PAYLOAD_1_20_2, VersionHelper.isOrAbove1_20_2() ? NetworkReflections.clazz$ServerboundCustomPayloadPacket : null); 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); @@ -213,6 +215,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes 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()); @@ -244,6 +247,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes 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()); + registerC2SByteBufPacketConsumer(PacketConsumers.CUSTOM_PAYLOAD_1_20, VersionHelper.isOrAbove1_20_2() ? -1 : this.packetIds.serverboundCustomPayloadPacket()); } @EventHandler(priority = EventPriority.LOWEST) 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 index 2fc91c8af..b8671f959 100644 --- 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 @@ -11,7 +11,6 @@ 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; @@ -29,8 +28,8 @@ 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.PayloadHelper; 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; @@ -38,6 +37,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityType 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.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; @@ -56,16 +56,18 @@ 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.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 net.momirealms.craftengine.core.world.BlockHitResult; -import net.momirealms.craftengine.core.world.BlockPos; -import net.momirealms.craftengine.core.world.EntityHitResult; -import net.momirealms.craftengine.core.world.WorldEvents; +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; @@ -73,6 +75,7 @@ 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; @@ -140,13 +143,13 @@ public class PacketConsumers { 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(AbstractMinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.COMMAND_BLOCK_MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.FURNACE_MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.HOPPER_MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.SPAWNER_MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE); - ADD_ENTITY_HANDLERS[MEntityTypes.TNT_MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.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); @@ -235,7 +238,7 @@ public class PacketConsumers { int[] newMappingsMOD = Arrays.copyOf(newMappings, registrySize); for (Map.Entry entry : map.entrySet()) { newMappings[entry.getKey()] = entry.getValue(); - if (BlockStateUtils.isVanillaBlock(entry.getKey())) { + if (BlockStateUtils.isVanillaBlock((int) entry.getKey())) { newMappingsMOD[entry.getKey()] = entry.getValue(); } } @@ -259,6 +262,32 @@ public class PacketConsumers { return MOD_BLOCK_STATE_MAPPINGS[stateId]; } + public static final BiConsumer FORGET_LEVEL_CHUNK = (user, event) -> { + try { + 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); + } + } + } 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; @@ -308,7 +337,7 @@ public class PacketConsumers { 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 mcSection = new MCSection(user.clientBlockList(), SERVER_BLOCK_LIST, BIOME_LIST); mcSection.readPacket(friendlyByteBuf); PalettedContainer container = mcSection.blockStateContainer(); Palette palette = container.data().palette(); @@ -331,7 +360,7 @@ public class PacketConsumers { 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 mcSection = new MCSection(user.clientBlockList(), SERVER_BLOCK_LIST, BIOME_LIST); mcSection.readPacket(friendlyByteBuf); PalettedContainer container = mcSection.blockStateContainer(); Palette palette = container.data().palette(); @@ -350,6 +379,9 @@ public class PacketConsumers { } buffer = newBuf.array(); } + + // 开始修改 + event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); buf.writeInt(chunkX); @@ -378,7 +410,16 @@ public class PacketConsumers { buf.writeBitSet(emptyBlockYMask); buf.writeByteArrayList(skyUpdates); buf.writeByteArrayList(blockUpdates); - event.setChanged(true); + + 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); + } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundLevelChunkWithLightPacket", e); } @@ -493,24 +534,24 @@ public class PacketConsumers { 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()); + 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)), false); + 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)), false); - buf.writeNbt(tokens3.isEmpty() ? suffix : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(suffix), tokens3)), false); + 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); } @@ -544,11 +585,12 @@ public class PacketConsumers { newEntries.add(entry); } else { String json = ComponentUtils.minecraftToJson(mcComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + 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))); + 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; } @@ -578,24 +620,25 @@ public class PacketConsumers { 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); + 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))); + 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))); - buf.writeUtf(tokens3.isEmpty() ? suffix : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(suffix), tokens3))); + 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); } @@ -612,7 +655,7 @@ public class PacketConsumers { int actionType = buf.readVarInt(); if (actionType == 0) { String json = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) return; float health = buf.readFloat(); int color = buf.readVarInt(); @@ -623,21 +666,21 @@ public class PacketConsumers { buf.writeVarInt(event.packetID()); buf.writeUUID(uuid); buf.writeVarInt(actionType); - buf.writeUtf(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens))); + 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); + 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))); + 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); @@ -653,7 +696,7 @@ public class PacketConsumers { if (actionType == 0) { Tag nbt = buf.readNbt(false); if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); if (tokens.isEmpty()) return; float health = buf.readFloat(); int color = buf.readVarInt(); @@ -664,7 +707,7 @@ public class PacketConsumers { buf.writeVarInt(event.packetID()); buf.writeUUID(uuid); buf.writeVarInt(actionType); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens)), false); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); buf.writeFloat(health); buf.writeVarInt(color); buf.writeVarInt(division); @@ -672,14 +715,14 @@ public class PacketConsumers { } else if (actionType == 3) { Tag nbt = buf.readNbt(false); if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + 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)), false); + 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); @@ -695,14 +738,14 @@ public class PacketConsumers { if (mode != 0 && mode != 2) return; String displayName = buf.readUtf(); int renderType = buf.readVarInt(); - Map tokens = CraftEngine.instance().fontManager().matchTags(displayName); + 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))); + 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); @@ -723,19 +766,19 @@ public class PacketConsumers { if (optionalNumberFormat) { int format = buf.readVarInt(); if (format == 0) { - Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); + 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)), false); + 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()); + Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); if (tokens.isEmpty()) return; Tag style = buf.readNbt(false); event.setChanged(true); @@ -743,7 +786,7 @@ public class PacketConsumers { buf.writeVarInt(event.packetID()); buf.writeUtf(objective); buf.writeByte(mode); - buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens)), false); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); buf.writeVarInt(renderType); buf.writeBoolean(true); buf.writeVarInt(1); @@ -751,29 +794,29 @@ public class PacketConsumers { } 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()); + 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)), false); + 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)), false); + 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()); + 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)), false); + buf.writeNbt(AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(displayName), tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user))), false); buf.writeVarInt(renderType); buf.writeBoolean(false); } @@ -787,13 +830,13 @@ public class PacketConsumers { try { FriendlyByteBuf buf = event.getBuffer(); String jsonOrPlainString = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(jsonOrPlainString); + 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))); + 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); @@ -806,13 +849,13 @@ public class PacketConsumers { FriendlyByteBuf buf = event.getBuffer(); Tag nbt = buf.readNbt(false); if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + 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)), false); + 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); @@ -824,12 +867,12 @@ public class PacketConsumers { try { FriendlyByteBuf buf = event.getBuffer(); String json = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + 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))); + 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); } @@ -841,12 +884,12 @@ public class PacketConsumers { FriendlyByteBuf buf = event.getBuffer(); Tag nbt = buf.readNbt(false); if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + 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)), false); + 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); } @@ -857,12 +900,12 @@ public class PacketConsumers { try { FriendlyByteBuf buf = event.getBuffer(); String json = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + 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))); + 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); } @@ -874,12 +917,12 @@ public class PacketConsumers { FriendlyByteBuf buf = event.getBuffer(); Tag nbt = buf.readNbt(false); if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + 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)), false); + 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); } @@ -890,12 +933,12 @@ public class PacketConsumers { try { FriendlyByteBuf buf = event.getBuffer(); String json = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + 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))); + 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); } @@ -907,12 +950,12 @@ public class PacketConsumers { FriendlyByteBuf buf = event.getBuffer(); Tag nbt = buf.readNbt(false); if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + 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)), false); + 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); } @@ -924,14 +967,15 @@ public class PacketConsumers { 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); + 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()); - buf.writeUtf(tokens1.isEmpty() ? json1 : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json1), tokens1))); - buf.writeUtf(tokens2.isEmpty() ? json2 : AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json2), tokens2))); + 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); } @@ -945,14 +989,15 @@ public class PacketConsumers { 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()); + 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()); - buf.writeNbt(tokens1.isEmpty() ? nbt1 : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt1), tokens1)), false); - buf.writeNbt(tokens2.isEmpty() ? nbt2 : AdventureHelper.componentToTag(AdventureHelper.replaceText(AdventureHelper.tagToComponent(nbt2), tokens2)), false); + 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); } @@ -965,14 +1010,14 @@ public class PacketConsumers { int containerId = buf.readVarInt(); int type = buf.readVarInt(); String json = buf.readUtf(); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + 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))); + 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); } @@ -986,13 +1031,13 @@ public class PacketConsumers { int type = buf.readVarInt(); Tag nbt = buf.readNbt(false); if (nbt == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); + 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)), false); + 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); } @@ -1168,7 +1213,7 @@ public class PacketConsumers { if (player.isAdventureMode()) { if (Config.simplifyAdventureBreakCheck()) { ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId); - if (!player.canBreak(pos, state.vanillaBlockState().handle())) { + if (!player.canBreak(pos, state.vanillaBlockState().literalObject())) { player.preventMiningBlock(); return; } @@ -1195,17 +1240,17 @@ public class PacketConsumers { try { BukkitServerPlayer player = (BukkitServerPlayer) user; String name = (String) NetworkReflections.methodHandle$ServerboundHelloPacket$nameGetter.invokeExact(packet); - player.setName(name); + player.setUnverifiedName(name); if (VersionHelper.isOrAbove1_20_2()) { UUID uuid = (UUID) NetworkReflections.methodHandle$ServerboundHelloPacket$uuidGetter.invokeExact(packet); - player.setUUID(uuid); + player.setUnverifiedUUID(uuid); } else { @SuppressWarnings("unchecked") Optional uuid = (Optional) NetworkReflections.methodHandle$ServerboundHelloPacket$uuidGetter.invokeExact(packet); if (uuid.isPresent()) { - player.setUUID(uuid.get()); + player.setUnverifiedUUID(uuid.get()); } else { - player.setUUID(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8))); + player.setUnverifiedUUID(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8))); } } } catch (Throwable e) { @@ -1256,6 +1301,7 @@ public class PacketConsumers { 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"); } @@ -1336,7 +1382,7 @@ public class PacketConsumers { Key itemId = state.settings().itemId(); // no item available if (itemId == null) return; - Object vanillaBlock = FastNMS.INSTANCE.method$BlockState$getBlock(state.vanillaBlockState().handle()); + 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()); @@ -1563,6 +1609,11 @@ public class PacketConsumers { 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; @@ -1596,6 +1647,7 @@ public class PacketConsumers { 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(); @@ -1615,6 +1667,11 @@ public class PacketConsumers { 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; @@ -1896,55 +1953,46 @@ public class PacketConsumers { return hasIllegal ? Pair.of(true, new String(newCodepoints, 0, newCodepoints.length)) : Pair.of(false, original); } - public static final TriConsumer CUSTOM_PAYLOAD = (user, event, packet) -> { + public static final TriConsumer CUSTOM_PAYLOAD_1_20_2 = (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)) { + if (VersionHelper.isOrAbove1_20_5() && NetworkReflections.clazz$DiscardedPayload.isInstance(payload)) { clientPayload = DiscardedPayload.from(payload); - } else if (!VersionHelper.isOrAbove1_20_5() && NetworkReflections.clazz$UnknownPayload.isInstance(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; - 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()); - } - } + if (clientPayload == null || !clientPayload.channel().equals(NetworkManager.MOD_CHANNEL_KEY)) return; + PayloadHelper.handleReceiver(clientPayload, user); } catch (Throwable e) { CraftEngine.instance().logger().warn("Failed to handle ServerboundCustomPayloadPacket", e); } }; + public static final BiConsumer CUSTOM_PAYLOAD_1_20 = (user, event) -> { + try { + 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); + } catch (Exception 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(user, event); + handler.handleSetEntityData(serverPlayer, event); return; } if (Config.interceptEntityName()) { @@ -1958,12 +2006,10 @@ public class PacketConsumers { if (optionalTextComponent.isEmpty()) continue; Object textComponent = optionalTextComponent.get(); String json = ComponentUtils.minecraftToJson(textComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) continue; Component component = AdventureHelper.jsonToComponent(json); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } + 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; @@ -1985,6 +2031,7 @@ public class PacketConsumers { 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(); @@ -1997,12 +2044,10 @@ public class PacketConsumers { } outside: if (displayName != null) { - Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); + Map tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString()); if (tokens.isEmpty()) break outside; Component component = AdventureHelper.tagToComponent(displayName); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } + component = AdventureHelper.replaceText(component, tokens, NetworkTextReplaceContext.of(serverPlayer)); displayName = AdventureHelper.componentToTag(component); isChanged = true; } @@ -2020,13 +2065,11 @@ public class PacketConsumers { } else if (format == 2) { fixed = buf.readNbt(false); if (fixed == null) return; - Map tokens = CraftEngine.instance().fontManager().matchTags(fixed.getAsString()); + Map tokens = CraftEngine.instance().fontManager().matchTags(fixed.getAsString()); if (tokens.isEmpty() && !isChanged) return; if (!tokens.isEmpty()) { Component component = AdventureHelper.tagToComponent(fixed); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } + component = AdventureHelper.replaceText(component, tokens, NetworkTextReplaceContext.of(serverPlayer)); fixed = AdventureHelper.componentToTag(component); isChanged = true; } @@ -2457,6 +2500,18 @@ public class PacketConsumers { // 防止后续加入的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.loginFailed")); + return; + } + if (Config.debugResourcePack()) { + TranslationManager.instance().log("warning.network.resource_pack.unverified_uuid", user.name(), user.uuid().toString()); + } + } + // 取消 ClientboundFinishConfigurationPacket,让客户端发呆,并结束掉当前的进入世界任务 event.setCancelled(true); try { @@ -2506,8 +2561,8 @@ public class PacketConsumers { 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()); + user.setVerifiedName(gameProfile.getName()); + user.setVerifiedUUID(gameProfile.getId()); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundLoginFinishedPacket", e); } @@ -2569,11 +2624,12 @@ public class PacketConsumers { 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((BukkitServerPlayer) user); + holder.applyClientboundData(serverPlayer); return holder; }); Set removed = buf.readCollection(Sets::newLinkedHashSetWithExpectedSize, FriendlyByteBuf::readKey); 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 bef170327..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 @@ -67,4 +67,8 @@ public interface PacketIds { int serverboundInteractPacket(); int clientboundUpdateRecipesPacket(); + + int clientboundForgetLevelChunkPacket(); + + int serverboundCustomPayloadPacket(); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ArmorStandPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ArmorStandPacketHandler.java index d7aa19418..672394811 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ArmorStandPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ArmorStandPacketHandler.java @@ -4,11 +4,13 @@ import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; -import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.FriendlyByteBuf; @@ -21,7 +23,7 @@ public class ArmorStandPacketHandler implements EntityPacketHandler { public static final ArmorStandPacketHandler INSTANCE = new ArmorStandPacketHandler(); @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { if (!Config.interceptArmorStand()) { return; } @@ -38,12 +40,9 @@ public class ArmorStandPacketHandler implements EntityPacketHandler { if (optionalTextComponent.isEmpty()) continue; Object textComponent = optionalTextComponent.get(); String json = ComponentUtils.minecraftToJson(textComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) continue; - Component component = AdventureHelper.jsonToComponent(json); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } + Component component = AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of(user)); 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; 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 3f9d13d5d..953f45bf5 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 @@ -7,11 +7,13 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; -import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.FriendlyByteBuf; @@ -23,7 +25,7 @@ public class BlockDisplayPacketHandler implements EntityPacketHandler { public static final BlockDisplayPacketHandler INSTANCE = new BlockDisplayPacketHandler(); @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); boolean isChanged = false; @@ -52,12 +54,9 @@ public class BlockDisplayPacketHandler implements EntityPacketHandler { if (optionalTextComponent.isEmpty()) continue; Object textComponent = optionalTextComponent.get(); String json = ComponentUtils.minecraftToJson(textComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) continue; - Component component = AdventureHelper.jsonToComponent(json); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } + Component component = AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of(user)); Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue( entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component)) 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 17bba65f8..80a4c20cd 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 @@ -5,10 +5,10 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.EntityDataUtils; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; -import net.momirealms.craftengine.core.plugin.network.NetWorkUser; import net.momirealms.craftengine.core.util.FriendlyByteBuf; import org.bukkit.inventory.ItemStack; @@ -20,7 +20,7 @@ public class CommonItemPacketHandler implements EntityPacketHandler { private static long lastWarningTime = 0; @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); boolean isChanged = false; 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 2d1f7f43a..3d5561c01 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 @@ -7,11 +7,13 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; -import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.FriendlyByteBuf; @@ -23,7 +25,7 @@ public class EndermanPacketHandler implements EntityPacketHandler { public static final EndermanPacketHandler INSTANCE = new EndermanPacketHandler(); @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); boolean isChanged = false; @@ -54,12 +56,9 @@ public class EndermanPacketHandler implements EntityPacketHandler { if (optionalTextComponent.isEmpty()) continue; Object textComponent = optionalTextComponent.get(); String json = ComponentUtils.minecraftToJson(textComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) continue; - Component component = AdventureHelper.jsonToComponent(json); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } + Component component = AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of(user)); Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue( entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component)) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemDisplayPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemDisplayPacketHandler.java index 77dff78ab..a84770a74 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemDisplayPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemDisplayPacketHandler.java @@ -4,9 +4,9 @@ import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; -import net.momirealms.craftengine.core.plugin.network.NetWorkUser; import net.momirealms.craftengine.core.util.FriendlyByteBuf; import org.bukkit.inventory.ItemStack; @@ -17,7 +17,7 @@ public class ItemDisplayPacketHandler implements EntityPacketHandler { public static final ItemDisplayPacketHandler INSTANCE = new ItemDisplayPacketHandler(); @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); boolean isChanged = false; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemFramePacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemFramePacketHandler.java index 25c4f3871..eeefbcc48 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemFramePacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemFramePacketHandler.java @@ -5,10 +5,10 @@ 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.user.BukkitServerPlayer; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; -import net.momirealms.craftengine.core.plugin.network.NetWorkUser; import net.momirealms.craftengine.core.util.FriendlyByteBuf; import org.bukkit.inventory.ItemStack; @@ -20,7 +20,7 @@ public class ItemFramePacketHandler implements EntityPacketHandler { private static long lastWarningTime = 0; @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); boolean isChanged = false; @@ -41,7 +41,7 @@ public class ItemFramePacketHandler 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/AbstractMinecartPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/MinecartPacketHandler.java similarity index 84% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/AbstractMinecartPacketHandler.java rename to bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/MinecartPacketHandler.java index 7e087f395..653b1f18a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/AbstractMinecartPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/MinecartPacketHandler.java @@ -7,11 +7,14 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.FriendlyByteBuf; import net.momirealms.craftengine.core.util.VersionHelper; @@ -20,12 +23,12 @@ import java.util.List; import java.util.Map; import java.util.Optional; -public class AbstractMinecartPacketHandler implements EntityPacketHandler { - public static final AbstractMinecartPacketHandler INSTANCE = new AbstractMinecartPacketHandler(); - private static final BlockStateHandler HANDLER = VersionHelper.isOrAbove1_21_3() ? BlockStateHandler_1_21_3.INSTANCE : BlockStateHandler_1_20.INSTANCE; +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; @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); boolean isChanged = false; @@ -33,7 +36,7 @@ public class AbstractMinecartPacketHandler implements EntityPacketHandler { for (int i = 0; i < packedItems.size(); i++) { Object packedItem = packedItems.get(i); int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); - Object blockState = HANDLER.handle(user, packedItem, entityDataId); + Object blockState = BLOCK_STATE_HANDLER.handle(user, packedItem, entityDataId); if (blockState != null) { packedItems.set(i, blockState); isChanged = true; @@ -43,12 +46,9 @@ public class AbstractMinecartPacketHandler implements EntityPacketHandler { if (optionalTextComponent.isEmpty()) continue; Object textComponent = optionalTextComponent.get(); String json = ComponentUtils.minecraftToJson(textComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) continue; - Component component = AdventureHelper.jsonToComponent(json); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } + Component component = AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of(user)); Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue( entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component)) 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 f352c9620..ef3178e21 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 @@ -7,11 +7,13 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; -import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.FriendlyByteBuf; @@ -23,7 +25,7 @@ public class PrimedTNTPacketHandler implements EntityPacketHandler { public static final PrimedTNTPacketHandler INSTANCE = new PrimedTNTPacketHandler(); @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); boolean isChanged = false; @@ -52,12 +54,9 @@ public class PrimedTNTPacketHandler implements EntityPacketHandler { if (optionalTextComponent.isEmpty()) continue; Object textComponent = optionalTextComponent.get(); String json = ComponentUtils.minecraftToJson(textComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) continue; - Component component = AdventureHelper.jsonToComponent(json); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } + Component component = AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of(user)); Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue( entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component)) 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 9c217435d..6e697af25 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 @@ -6,6 +6,7 @@ import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.injector.ProtectedFieldVisitor; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta; import net.momirealms.craftengine.core.item.CustomItem; import net.momirealms.craftengine.core.item.Item; @@ -38,7 +39,7 @@ public class ProjectilePacketHandler implements EntityPacketHandler { } @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); event.setChanged(true); @@ -111,7 +112,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); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/TextDisplayPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/TextDisplayPacketHandler.java index a02346b58..8b7fc31b9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/TextDisplayPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/TextDisplayPacketHandler.java @@ -5,11 +5,13 @@ import net.momirealms.craftengine.bukkit.entity.data.TextDisplayEntityData; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; -import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.FriendlyByteBuf; @@ -20,7 +22,7 @@ public class TextDisplayPacketHandler implements EntityPacketHandler { public static final TextDisplayPacketHandler INSTANCE = new TextDisplayPacketHandler(); @Override - public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { if (!Config.interceptTextDisplay()) { return; } @@ -35,12 +37,9 @@ public class TextDisplayPacketHandler implements EntityPacketHandler { Object textComponent = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); if (textComponent == CoreReflections.instance$Component$empty) break; String json = ComponentUtils.minecraftToJson(textComponent); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); + Map tokens = CraftEngine.instance().fontManager().matchTags(json); if (tokens.isEmpty()) continue; - Component component = AdventureHelper.jsonToComponent(json); - for (Map.Entry token : tokens.entrySet()) { - component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); - } + Component component = AdventureHelper.replaceText(AdventureHelper.jsonToComponent(json), tokens, NetworkTextReplaceContext.of(user)); Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, ComponentUtils.adventureToMinecraft(component))); isChanged = true; 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 34981a06b..99eb1c449 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 @@ -169,4 +169,14 @@ public class PacketIds1_20 implements PacketIds { public int clientboundUpdateAdvancementsPacket() { return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundUpdateAdvancementsPacket); } + + @Override + public int clientboundForgetLevelChunkPacket() { + return PacketIdFinder.clientboundByClazz(NetworkReflections.clazz$ClientboundForgetLevelChunkPacket); + } + + @Override + public int serverboundCustomPayloadPacket() { + return PacketIdFinder.serverboundByClazz(NetworkReflections.clazz$ServerboundCustomPayloadPacket); + } } 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 216b11d56..70596861c 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 @@ -168,4 +168,14 @@ public class PacketIds1_20_5 implements PacketIds { public int serverboundInteractPacket() { return PacketIdFinder.serverboundByName("minecraft:interact"); } + + @Override + public int clientboundForgetLevelChunkPacket() { + return PacketIdFinder.clientboundByName("minecraft:forget_level_chunk"); + } + + @Override + public int serverboundCustomPayloadPacket() { + return PacketIdFinder.serverboundByName("custom_payload"); + } } 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..e2840c816 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientBlockStateSizePacket.java @@ -0,0 +1,40 @@ +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/LeavesReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/leaves/LeavesReflections.java similarity index 97% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/LeavesReflections.java rename to bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/leaves/LeavesReflections.java index b7b89995c..dd5522890 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/LeavesReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/leaves/LeavesReflections.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft; +package net.momirealms.craftengine.bukkit.plugin.reflection.leaves; //import net.momirealms.craftengine.core.util.MiscUtils; //import net.momirealms.craftengine.core.util.ReflectionUtils; 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 bff6a601b..48eb0072e 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 @@ -469,17 +469,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); } @@ -917,7 +917,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; @@ -1181,6 +1181,17 @@ public final class CoreReflections { ) ); + public static final Class clazz$ServerLevel = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "server.level.WorldServer", + "server.level.ServerLevel" + ) + ); + + public static final Class clazz$Explosion = requireNonNull( + ReflectionUtils.getClazz(BukkitReflectionUtils.assembleMCClass("world.level.Explosion")) + ); + public static final Constructor constructor$StateDefinition$Builder = requireNonNull( ReflectionUtils.getTheOnlyConstructor(clazz$StateDefinition$Builder) ); @@ -1451,6 +1462,18 @@ public final class CoreReflections { ReflectionUtils.getInstanceDeclaredField(clazz$BlockBehaviour, float.class, 0) ); + public static final Field field$BlockBehaviour$friction = requireNonNull( + ReflectionUtils.getInstanceDeclaredField(clazz$BlockBehaviour, float.class, 1) + ); + + public static final Field field$BlockBehaviour$speedFactor = requireNonNull( + ReflectionUtils.getInstanceDeclaredField(clazz$BlockBehaviour, float.class, 2) + ); + + public static final Field field$BlockBehaviour$jumpFactor = requireNonNull( + ReflectionUtils.getInstanceDeclaredField(clazz$BlockBehaviour, float.class, 3) + ); + public static final Field field$BlockBehaviour$soundType = requireNonNull( ReflectionUtils.getInstanceDeclaredField(clazz$BlockBehaviour, clazz$SoundType, 0) ); @@ -1547,6 +1570,15 @@ 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( + 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) ); @@ -1651,6 +1683,15 @@ public final class CoreReflections { ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, clazz$BlockState, clazz$BlockState, clazz$Direction, clazz$BlockState, clazz$LevelAccessor, clazz$BlockPos, clazz$BlockPos) ); + public static final Method method$BlockBehaviour$canSurvive = requireNonNull( + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, boolean.class, clazz$BlockState, clazz$LevelReader, clazz$BlockPos) + ); + + 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() + ); + public static final Class clazz$Fallable = requireNonNull( ReflectionUtils.getClazz(BukkitReflectionUtils.assembleMCClass("world.level.block.Fallable")) ); @@ -1673,6 +1714,14 @@ public final class CoreReflections { ) ); + public static final Method method$Fallable$onLand = requireNonNull( + ReflectionUtils.getMethod(clazz$Fallable, void.class, clazz$Level, clazz$BlockPos, clazz$BlockState, clazz$BlockState, clazz$FallingBlockEntity) + ); + + public static final Method method$Fallable$onBrokenAfterFall = requireNonNull( + ReflectionUtils.getMethod(clazz$Fallable, void.class, clazz$Level, clazz$BlockPos, clazz$FallingBlockEntity) + ); + public static final Method method$FallingBlockEntity$fall = requireNonNull( ReflectionUtils.getStaticMethod(clazz$FallingBlockEntity, clazz$FallingBlockEntity, clazz$Level, clazz$BlockPos, clazz$BlockState) ); @@ -2274,12 +2323,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) ); @@ -2957,13 +3024,6 @@ public final class CoreReflections { ReflectionUtils.getInstanceDeclaredField(clazz$ServerPlayer, clazz$ServerGamePacketListenerImpl, 0) ); - public static final Class clazz$ServerLevel = requireNonNull( - BukkitReflectionUtils.findReobfOrMojmapClass( - "server.level.WorldServer", - "server.level.ServerLevel" - ) - ); - public static final Method method$ServerLevel$getNoiseBiome = requireNonNull( ReflectionUtils.getMethod(clazz$ServerLevel, clazz$Holder, int.class, int.class, int.class) ); @@ -3446,6 +3506,11 @@ public final class CoreReflections { ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, new String[]{"randomTick", "b"}, clazz$BlockState, clazz$ServerLevel, clazz$BlockPos, clazz$RandomSource) ); + public static final Method method$BlockBehaviour$onPlace = requireNonNull( + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, new String[]{"onPlace", VersionHelper.isOrAbove1_21_5() ? "a" : "b"}, + clazz$BlockState, clazz$Level, clazz$BlockPos, clazz$BlockState, boolean.class) + ); + public static final Class clazz$InsideBlockEffectApplier = BukkitReflectionUtils.findReobfOrMojmapClass( "world.entity.InsideBlockEffectApplier", "world.entity.InsideBlockEffectApplier" @@ -3603,20 +3668,15 @@ public final class CoreReflections { ) ); - public static final Class clazz$Explosion = requireNonNull( - ReflectionUtils.getClazz( - BukkitReflectionUtils.assembleMCClass("world.level.Explosion") - ) - ); - // 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); } @@ -3993,18 +4053,15 @@ public final class CoreReflections { ReflectionUtils.getStaticMethod(clazz$ArmorTrim, Optional.class, clazz$RegistryAccess, clazz$ItemStack); public static final Method method$BlockBehaviour$spawnAfterBreak = requireNonNull( - ReflectionUtils.getDeclaredMethod( - clazz$BlockBehaviour, void.class, clazz$BlockState, clazz$ServerLevel, clazz$BlockPos, clazz$ItemStack, boolean.class - ) + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, clazz$BlockState, clazz$ServerLevel, clazz$BlockPos, clazz$ItemStack, boolean.class) ); // 1.20~1.21.4 public static final Method method$BlockBehaviour$onRemove = MiscUtils.requireNonNullIf( - ReflectionUtils.getDeclaredMethod( - clazz$BlockBehaviour, void.class, clazz$BlockState, clazz$Level, clazz$BlockPos, clazz$BlockState, boolean.class - ), + ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, new String[]{"a", "onRemove"}, clazz$BlockState, clazz$Level, clazz$BlockPos, clazz$BlockState, boolean.class), !VersionHelper.isOrAbove1_21_5() ); + public static final Object instance$CollisionContext$empty; static { @@ -4148,4 +4205,188 @@ 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); } 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 5bb4bd47d..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) ); @@ -1255,7 +1254,17 @@ public final class NetworkReflections { // 1.20.2+ public static final Constructor constructor$DiscardedPayload = Optional.ofNullable(clazz$DiscardedPayload) - .map(ReflectionUtils::getTheOnlyConstructor) + .map(it -> { + if (VersionHelper.isOrAbove1_20_5()) { + Constructor constructor1 = ReflectionUtils.getConstructor(it, CoreReflections.clazz$ResourceLocation, ByteBuf.class); + if (constructor1 != null) { + return constructor1; + } + return ReflectionUtils.getConstructor(it, CoreReflections.clazz$ResourceLocation, byte[].class); + } else { + return ReflectionUtils.getConstructor(it, CoreReflections.clazz$ResourceLocation); + } + }) .orElse(null); public static final Class clazz$ClientboundContainerSetContentPacket = requireNonNull( @@ -1625,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") ), @@ -1633,18 +1642,18 @@ 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) - .map(ReflectionUtils::getTheOnlyConstructor) + public static final Constructor constructor$ServerboundCustomPayloadPacket$UnknownPayload = Optional.ofNullable(clazz$ServerboundCustomPayloadPacket$UnknownPayload) + .map(it -> ReflectionUtils.getConstructor(it, CoreReflections.clazz$ResourceLocation, ByteBuf.class)) .orElse(null); // 1.21.5+ @@ -1655,4 +1664,17 @@ public final class NetworkReflections { ), VersionHelper.isOrAbove1_21_5() ); + + public static final Class clazz$ClientboundForgetLevelChunkPacket = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "network.protocol.game.PacketPlayOutUnloadChunk", + "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 bf6afab4b..c08d6af2d 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,28 @@ 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 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,15 +34,19 @@ 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.*; 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.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; @@ -64,6 +71,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; @@ -95,6 +104,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.vanillaStateSize()); // cache if player can break blocks private boolean clientSideCanBreak = true; // prevent AFK players from consuming too much CPU resource on predicting @@ -108,8 +118,10 @@ public class BukkitServerPlayer extends Player { private double cachedInteractionRange; // cooldown data private CooldownData cooldownData; - - private final Map entityTypeView = new ConcurrentHashMap<>(); + // tracked chunks + private ConcurrentLong2ReferenceChainedHashTable trackedChunks; + // entity view + private Map entityTypeView; public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) { this.channel = channel; @@ -129,8 +141,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) { @@ -177,6 +193,26 @@ public class BukkitServerPlayer extends Player { return platformPlayer().isSneaking(); } + @Override + public boolean isSwimming() { + return platformPlayer().isSwimming(); + } + + @Override + public boolean isClimbing() { + return platformPlayer().isClimbing(); + } + + @Override + public boolean isGliding() { + return platformPlayer().isGliding(); + } + + @Override + public boolean isFlying() { + return platformPlayer().isFlying(); + } + @Override public GameMode gameMode() { return switch (platformPlayer().getGameMode()) { @@ -203,6 +239,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)); @@ -283,22 +324,46 @@ 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); @@ -351,8 +416,8 @@ public class BukkitServerPlayer extends Player { Object responsePacket; if (VersionHelper.isOrAbove1_20_2()) { Object dataPayload; - if (NetworkReflections.clazz$UnknownPayload != null) { - dataPayload = NetworkReflections.constructor$UnknownPayload.newInstance(channelResourceLocation, Unpooled.wrappedBuffer(data)); + if (!VersionHelper.isOrAbove1_20_5()) { + dataPayload = NetworkReflections.constructor$ServerboundCustomPayloadPacket$UnknownPayload.newInstance(channelResourceLocation, Unpooled.wrappedBuffer(data)); } else if (DiscardedPayload.useNewMethod) { dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelResourceLocation, data); } else { @@ -438,7 +503,7 @@ 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) { @@ -459,14 +524,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); @@ -474,11 +558,7 @@ 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 (!isCorrectTool) { - progress *= (10f / 3f); - } + // 如果自定义方块在服务端侧未使用正确的工具,那么需要还原挖掘速度 if (!BlockStateUtils.isCorrectTool(customState, tool)) { progress *= customState.settings().incorrectToolSpeed(); } @@ -520,7 +600,7 @@ public class BukkitServerPlayer extends Player { if (custom && getDestroyProgress(state, pos) >= 1f) { BlockStateWrapper vanillaBlockState = immutableBlockState.vanillaBlockState(); // if it's not an instant break on client side, we should resend level event - if (vanillaBlockState != null && getDestroyProgress(vanillaBlockState.handle(), pos) < 1f) { + if (vanillaBlockState != null && getDestroyProgress(vanillaBlockState.literalObject(), pos) < 1f) { Object levelEventPacket = FastNMS.INSTANCE.constructor$ClientboundLevelEventPacket( WorldEvents.BLOCK_BREAK_EFFECT, LocationUtils.toBlockPos(pos), BlockStateUtils.blockStateToId(state), false); sendPacket(levelEventPacket, false); @@ -682,7 +762,7 @@ public class BukkitServerPlayer extends Player { // for simplified adventure break, switch mayBuild temporarily if (isAdventureMode() && Config.simplifyAdventureBreakCheck()) { // check the appearance state - if (canBreak(hitPos, customState.vanillaBlockState().handle())) { + if (canBreak(hitPos, customState.vanillaBlockState().literalObject())) { // Error might occur so we use try here try { FastNMS.INSTANCE.field$Player$mayBuild(serverPlayer, true); @@ -886,14 +966,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()) { @@ -935,8 +1027,28 @@ public class BukkitServerPlayer extends Player { } @Override - public void performCommand(String command) { - platformPlayer().performCommand(command); + public void performCommand(String command, boolean asOp) { + org.bukkit.entity.Player player = platformPlayer(); + if (asOp) { + boolean isOp = player.isOp(); + player.setOp(true); + try { + player.performCommand(command); + } catch (Throwable t) { + this.plugin.logger().warn("Failed to perform command '" + command + "' for " + this.name() + " as operator", t); + } + player.setOp(isOp); + } else { + player.performCommand(command); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void performCommandAsEvent(String command) { + String formattedCommand = command.startsWith("/") ? command : "/" + command; + PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(platformPlayer(), formattedCommand); + Bukkit.getPluginManager().callEvent(event); } @Override @@ -948,11 +1060,6 @@ public class BukkitServerPlayer extends Player { } } - @Override - public boolean isFlying() { - return platformPlayer().isFlying(); - } - @Override public int foodLevel() { return platformPlayer().getFoodLevel(); @@ -996,4 +1103,35 @@ public class BukkitServerPlayer extends Player { public CooldownData cooldown() { return this.cooldownData; } + + @Override + public boolean isChunkTracked(long chunkPos) { + return this.trackedChunks.containsKey(chunkPos); + } + + @Override + 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); + } } 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 5728c14df..3d3a592f7 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,5 +1,6 @@ package net.momirealms.craftengine.bukkit.util; +import net.momirealms.craftengine.bukkit.block.BukkitBlockStateWrapper; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.ReflectionInitException; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; @@ -42,10 +43,10 @@ public final class BlockStateUtils { hasInit = true; } - public static BlockStateWrapper toPackedBlockState(BlockData blockData) { + public static BlockStateWrapper toBlockStateWrapper(BlockData blockData) { Object state = blockDataToBlockState(blockData); int id = blockStateToId(state); - return BlockStateWrapper.create(state, id, isVanillaBlock(id)); + return new BukkitBlockStateWrapper(state, id); } public static boolean isCorrectTool(@NotNull ImmutableBlockState state, @Nullable Item itemInHand) { @@ -53,7 +54,7 @@ public final class BlockStateUtils { if (settings.requireCorrectTool()) { if (itemInHand == null || itemInHand.isEmpty()) return false; return settings.isCorrectTool(itemInHand.id()) || - (settings.respectToolComponent() && FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(itemInHand.getLiteralObject(), state.customBlockState().handle())); + (settings.respectToolComponent() && FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(itemInHand.getLiteralObject(), state.customBlockState().literalObject())); } return true; } @@ -145,8 +146,6 @@ public final class BlockStateUtils { } public static Object getBlockState(Block block) { - Object worldServer = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(block.getWorld()); - Object blockPos = LocationUtils.toBlockPos(block.getX(), block.getY(), block.getZ()); - return FastNMS.INSTANCE.method$BlockGetter$getBlockState(worldServer, blockPos); + return FastNMS.INSTANCE.method$BlockGetter$getBlockState(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(block.getWorld()), LocationUtils.toBlockPos(block.getX(), block.getY(), block.getZ())); } } 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/InteractUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java index d16537def..dee6874b7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java @@ -169,6 +169,7 @@ public final class InteractUtils { registerInteraction(BlockKeys.DAMAGED_ANVIL, (player, item, blockState, result) -> true); registerInteraction(BlockKeys.FURNACE, (player, item, blockState, result) -> true); registerInteraction(BlockKeys.CRAFTING_TABLE, (player, item, blockState, result) -> true); + registerInteraction(BlockKeys.CARTOGRAPHY_TABLE, (player, item, blockState, result) -> true); registerInteraction(BlockKeys.STONECUTTER, (player, item, blockState, result) -> true); registerInteraction(BlockKeys.SMITHING_TABLE, (player, item, blockState, result) -> true); registerInteraction(BlockKeys.LOOM, (player, item, blockState, result) -> true); 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/ParticleUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java index 0703d45ea..61ef8bc5e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java @@ -47,7 +47,7 @@ public final class ParticleUtils { public static Object toBukkitParticleData(ParticleData particleData, Context context, World world, double x, double y, double z) { return switch (particleData) { - case BlockStateData data -> BlockStateUtils.fromBlockData(data.blockState().handle()); + case BlockStateData data -> BlockStateUtils.fromBlockData(data.blockState().literalObject()); case ColorData data -> ColorUtils.toBukkit(data.color()); case DustData data -> new Particle.DustOptions(ColorUtils.toBukkit(data.color()), data.size()); case DustTransitionData data -> new Particle.DustTransition(ColorUtils.toBukkit(data.from()), ColorUtils.toBukkit(data.to()), data.size()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/SoundUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/SoundUtils.java index 9f21529df..64ff47e55 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/SoundUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/SoundUtils.java @@ -37,6 +37,7 @@ public final class SoundUtils { case HOSTILE -> SoundCategory.HOSTILE; case NEUTRAL -> SoundCategory.NEUTRAL; case WEATHER -> SoundCategory.WEATHER; + case UI -> SoundCategory.UI; }; } } 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/BukkitBlockInWorld.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitExistingBlock.java similarity index 66% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitBlockInWorld.java rename to bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitExistingBlock.java index 6a9ce89a4..38dd8db86 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitBlockInWorld.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitExistingBlock.java @@ -7,18 +7,25 @@ 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.item.context.BlockPlaceContext; -import net.momirealms.craftengine.core.world.BlockInWorld; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.world.ExistingBlock; import net.momirealms.craftengine.core.world.World; import org.bukkit.Location; import org.bukkit.block.Block; +import org.jetbrains.annotations.NotNull; -public class BukkitBlockInWorld implements BlockInWorld { +import java.util.Optional; + +public class BukkitExistingBlock implements ExistingBlock { private final Block block; - public BukkitBlockInWorld(Block block) { + public BukkitExistingBlock(Block block) { this.block = block; } @@ -44,6 +51,22 @@ public class BukkitBlockInWorld implements BlockInWorld { return FastNMS.INSTANCE.method$FluidState$getType(fluidData) == MFluids.WATER; } + @Override + public @NotNull StatePropertyAccessor createStatePropertyAccessor() { + return FastNMS.INSTANCE.createStatePropertyAccessor(BlockStateUtils.getBlockState(this.block)); + } + + @Override + public boolean isCustom() { + return CraftEngineBlocks.isCustomBlock(this.block); + } + + @Override + public @NotNull BlockStateWrapper blockState() { + Object blockState = BlockStateUtils.getBlockState(this.block); + return BlockRegistryMirror.stateByRegistryId(BlockStateUtils.blockStateToId(blockState)); + } + @Override public int x() { return this.block.getX(); @@ -59,6 +82,16 @@ public class BukkitBlockInWorld implements BlockInWorld { return this.block.getZ(); } + @Override + public Key id() { + Object blockState = BlockStateUtils.getBlockState(this.block); + Optional optionalCustomBlockState = BlockStateUtils.getOptionalCustomBlockState(blockState); + if (optionalCustomBlockState.isPresent()) { + return optionalCustomBlockState.get().owner().value().id(); + } + return BlockStateUtils.getBlockOwnerIdFromState(blockState); + } + @Override public World world() { return new BukkitWorld(this.block.getWorld()); 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 af2d6c269..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,14 +50,15 @@ 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; } @Override - public BlockInWorld getBlockAt(int x, int y, int z) { - return new BukkitBlockInWorld(platformWorld().getBlockAt(x, y, z)); + public ExistingBlock getBlockAt(int x, int y, int z) { + return new BukkitExistingBlock(platformWorld().getBlockAt(x, y, z)); } @Override @@ -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())); @@ -116,11 +123,31 @@ public class BukkitWorld implements World { public void setBlockAt(int x, int y, int z, BlockStateWrapper blockState, int flags) { Object worldServer = serverWorld(); Object blockPos = FastNMS.INSTANCE.constructor$BlockPos(x, y, z); - FastNMS.INSTANCE.method$LevelWriter$setBlock(worldServer, blockPos, blockState.handle(), flags); + FastNMS.INSTANCE.method$LevelWriter$setBlock(worldServer, blockPos, blockState.literalObject(), flags); } @Override 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 45d45bf9d..2a90f6333 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 @@ -8,7 +8,7 @@ import net.momirealms.craftengine.bukkit.util.BlockStateUtils; 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; @@ -29,31 +29,23 @@ import org.bukkit.event.world.*; import org.jetbrains.annotations.NotNull; import java.io.IOException; -import java.util.HashMap; -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 HashMap<>(); + 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 +67,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 +88,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 +111,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 +124,8 @@ public class BukkitWorldManager implements WorldManager, Listener { } } this.worlds.clear(); + this.lastWorld = null; + this.lastWorldUUID = null; } @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) @@ -166,33 +135,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 +177,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 +213,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()); } @@ -311,7 +258,7 @@ public class BukkitWorldManager implements WorldManager, Listener { for (int y = 0; y < 16; y++) { ImmutableBlockState customState = ceSection.getBlockState(x, y, z); if (!customState.isEmpty() && customState.vanillaBlockState() != null) { - FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, customState.vanillaBlockState().handle(), false); + FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, customState.vanillaBlockState().literalObject(), false); unsaved = true; } } @@ -398,7 +345,7 @@ public class BukkitWorldManager implements WorldManager, Listener { 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().handle(), false); + FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, customState.customBlockState().literalObject(), false); } } } 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 index 1573d2fbf..2c6f2c87d 100644 --- a/common-files/src/main/resources/additional-real-blocks.yml +++ b/common-files/src/main/resources/additional-real-blocks.yml @@ -26,7 +26,7 @@ minecraft:cherry_sapling: 1 minecraft:anvil: 2 minecraft:chipped_anvil: 2 minecraft:damaged_anvil: 2 -minecraft:sugarcane: 14 +minecraft:sugar_cane: 14 minecraft:iron_trapdoor: 32 minecraft:acacia_trapdoor: 32 minecraft:oak_trapdoor: 32 @@ -80,4 +80,8 @@ 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 +minecraft:warped_fence_gate: 16 +minecraft:barrier: 128 +minecraft:white_bed: 1 +minecraft:redstone_torch: 1 +minecraft:redstone_wall_torch: 4 \ 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..3a2b6c8de 100644 --- a/common-files/src/main/resources/commands.yml +++ b/common-files/src/main/resources/commands.yml @@ -198,11 +198,18 @@ 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_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 27ead33b2..cb1226ba1 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -29,7 +29,6 @@ resource-pack: seed: 0 # 0 = random seed fake-directory: false escape-unicode: false - break-json: false resource-location: enable: true random-namespace: @@ -41,7 +40,6 @@ resource-pack: anti-unzip: false random-atlas: images-per-canvas: 32 # 0 = disable - use-double: true # Sometimes, some vanilla files that have been overwritten might be mistakenly obfuscated. # Please add the ignored textures/models/sounds here. bypass-textures: @@ -67,10 +65,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 @@ -100,6 +98,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 @@ -190,16 +190,6 @@ block: extended-interaction-range: 0.5 furniture: - # Automatically remove outdated furniture entities when a chunk is loaded. - handle-invalid-furniture-on-chunk-load: - # Enable/disable the cleanup system - enable: false - # Removes the specified invalid furniture - remove: - - "xxx:invalid_furniture" - # Converts the invalid furniture to a valid one - convert: - "namespace:furniture_a": "namespace:furniture_b" # Hide technical entities used for storing furniture metadata. # NOTE: # - These are INVISIBLE entities used internally for tracking furniture states @@ -211,10 +201,14 @@ furniture: collision-entity-type: interaction emoji: - chat: true - book: true - anvil: true - sign: true + # Contexts where emoji parsing is enabled + contexts: + chat: true + book: true + anvil: true + sign: true + # Prevent lag or oversized packet when processing emoji-heavy content + max-emojis-per-parse: 16 image: # Block image tags using minecraft:default font in these interfaces @@ -366,15 +360,11 @@ gui: brewing: title: "" -performance: - # Maximum chain update depth when fixing client visuals - max-note-block-chain-update-limit: 48 - # Prevent lag or oversized packet when processing emoji-heavy content - max-emojis-per-parse: 16 - 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 @@ -389,7 +379,7 @@ chunk-system: # Settings for injection injection: # Requires a restart to apply. - # SECTION: Inject the LevelChunkSection + # SECTION: Inject the LevelChunkSection (Use this if you have installed both FastAsyncWorldEdit and Axiom) # PALETTE: Inject the PalettedContainer target: PALETTE # Enables faster injection method @@ -420,6 +410,16 @@ chunk-system: # server's CraftEngine internal data. Enabling this option will synchronize the data when the chunk is loaded. # (This option only slightly impacts performance, which has been fully optimized, so you don't need to worry too much.) sync-custom-blocks-on-chunk-load: false + # This system processes any invalid block when a chunk is loaded. + process-invalid-blocks: + enable: false + remove: [] + convert: {} + # This system processes any invalid furniture when a chunk is loaded. + process-invalid-furniture: + enable: false + remove: [] + convert: {} # Enables or disables debug mode debug: 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..84b5e29e0 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/amethyst_torch.yml @@ -0,0 +1,170 @@ +items: + default:amethyst_torch: + material: nether_brick + custom-model-data: 3020 + 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 + custom-model-data: 3021 + 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 + custom-model-data: 3022 + 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: + id: 0 + 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 + id: 0 + facing=east: + appearance: east + id: 1 + facing=west: + appearance: west + id: 2 + facing=south: + appearance: south + id: 3 +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..2ba57608c --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/chessboard_block.yml @@ -0,0 +1,84 @@ +items: + default:chessboard_block: + material: nether_brick + custom-model-data: 3000 + 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: + 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..9a22722b8 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/chinese_lantern.yml @@ -0,0 +1,51 @@ +items: + default:chinese_lantern: + material: nether_brick + custom-model-data: 3001 + 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 +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..c0fe30805 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/copper_coil.yml @@ -0,0 +1,80 @@ +items: + default:copper_coil: + material: nether_brick + custom-model-data: 3002 + 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 +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..6ccd4a673 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/ender_pearl_flower.yml @@ -0,0 +1,145 @@ +items: + default:ender_pearl_flower_seeds: + material: nether_brick + custom-model-data: 3003 + 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: + 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 +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..4777b653c --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/fairy_flower.yml @@ -0,0 +1,52 @@ +items: + default:fairy_flower: + material: nether_brick + custom-model-data: 3004 + 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: + 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 \ 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..6a43fc1cf --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/flame_cane.yml @@ -0,0 +1,112 @@ +items: + default:flame_cane: + material: nether_brick + custom-model-data: 3005 + 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: + 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 +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..1bab9bb39 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/gunpowder_block.yml @@ -0,0 +1,91 @@ +items: + default:gunpowder_block: + material: nether_brick + custom-model-data: 3006 + 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: 3007 + 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 +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/netherite_anvil.yml b/common-files/src/main/resources/resources/default/configuration/blocks/netherite_anvil.yml new file mode 100644 index 000000000..add02ff3b --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/netherite_anvil.yml @@ -0,0 +1,94 @@ +items: + default:netherite_anvil: + material: nether_brick + custom-model-data: 3008 + 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 + 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 +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 98% 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 a47b29498..fe8e46bd6 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 @@ -363,7 +363,7 @@ items: template: - default:sound/wood overrides: - push-reaction: DESTROY + push-reaction: destroy map-color: 2 instrument: bass hardness: 3.0 @@ -588,7 +588,7 @@ items: - default:hardness/planks overrides: burnable: true - push-reaction: DESTROY + push-reaction: destroy map-color: 2 instrument: bass tags: @@ -643,6 +643,17 @@ recipes: result: id: default:palm_trapdoor count: 2 + default:palm_door: + type: shaped + pattern: + - AA + - AA + - AA + ingredients: + A: default:palm_planks + result: + id: default:palm_door + count: 3 default:palm_fence_gate: type: shaped pattern: @@ -683,4 +694,4 @@ recipes: A: default:palm_planks result: id: default:palm_stairs - count: 4 \ No newline at end of file + count: 4 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..9d5b8a502 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/pebble.yml @@ -0,0 +1,130 @@ +items: + default:pebble: + material: nether_brick + custom-model-data: 3009 + 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 +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..f444576b6 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/reed.yml @@ -0,0 +1,42 @@ +items: + default:reed: + material: nether_brick + custom-model-data: 3010 + 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: + id: 1 + state: sugar_cane:1 + 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..99d1a28e3 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/safe_block.yml @@ -0,0 +1,136 @@ +items: + default:safe_block: + material: nether_brick + custom-model-data: 3011 + 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 + 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: + state: note_block:22 + 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: + state: note_block:23 + 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: + state: note_block:24 + model: + path: minecraft:block/custom/safe_block + north_open: + state: note_block:25 + model: + path: minecraft:block/custom/safe_block_open + south: + state: note_block:26 + model: + path: minecraft:block/custom/safe_block + y: 180 + south_open: + state: note_block:27 + model: + path: minecraft:block/custom/safe_block_open + y: 180 + west: + state: note_block:28 + model: + path: minecraft:block/custom/safe_block + y: 270 + west_open: + state: note_block:29 + model: + path: minecraft:block/custom/safe_block_open + y: 270 + variants: + facing=east,open=false: + appearance: east + id: 22 + facing=east,open=true: + appearance: east_open + id: 23 + facing=north,open=false: + appearance: north + id: 24 + facing=north,open=true: + appearance: north_open + id: 25 + facing=south,open=false: + appearance: south + id: 26 + facing=south,open=true: + appearance: south_open + id: 27 + facing=west,open=false: + appearance: west + id: 28 + facing=west,open=true: + appearance: west_open + id: 29 +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..1f4a86917 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/sofa.yml @@ -0,0 +1,185 @@ +items: + default:sleeper_sofa: + material: nether_brick + custom-model-data: 3012 + 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 + 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: + id: 0 + state: white_bed[facing=west,occupied=false,part=foot] + entity-renderer: + item: default:sleeper_sofa + default:sofa_inner: + material: nether_brick + custom-model-data: 3013 + model: + type: minecraft:model + path: minecraft:item/custom/sofa_inner + default:sofa: + material: nether_brick + custom-model-data: 3014 + 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 + id: 0 + facing=east,shape=inner_right: + appearance: facing=east,shape=inner_right + id: 1 + facing=east,shape=straight: + appearance: facing=east,shape=straight + id: 2 + facing=north,shape=inner_left: + appearance: facing=north,shape=inner_left + id: 3 + facing=north,shape=inner_right: + appearance: facing=north,shape=inner_right + id: 4 + facing=north,shape=straight: + appearance: facing=north,shape=straight + id: 5 + facing=south,shape=inner_left: + appearance: facing=south,shape=inner_left + id: 6 + facing=south,shape=inner_right: + appearance: facing=south,shape=inner_right + id: 7 + facing=south,shape=straight: + appearance: facing=south,shape=straight + id: 8 + facing=west,shape=inner_left: + appearance: facing=west,shape=inner_left + id: 9 + facing=west,shape=inner_right: + appearance: facing=west,shape=inner_right + id: 10 + facing=west,shape=straight: + appearance: facing=west,shape=straight + id: 11 \ 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..8802410b6 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/blocks/table_lamp.yml @@ -0,0 +1,135 @@ +items: + default:table_lamp: + material: nether_brick + custom-model-data: 3015 + 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 + behaviors: + - type: toggleable_lamp_block + can-open-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 + id: 12 + facing=north,lit=false: + appearance: north_off + id: 13 + facing=south,lit=false: + appearance: south_off + id: 14 + facing=west,lit=false: + appearance: west_off + id: 15 + facing=east,lit=true: + appearance: east_on + id: 16 + settings: + luminance: 15 + facing=north,lit=true: + appearance: north_on + id: 17 + settings: + luminance: 15 + facing=south,lit=true: + appearance: south_on + id: 18 + settings: + luminance: 15 + facing=west,lit=true: + appearance: west_on + id: 19 + settings: + luminance: 15 + default:table_lamp_on: + material: nether_brick + custom-model-data: 3016 + 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 97% 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..067cc0a09 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,7 @@ items: default:topaz_ore: material: nether_brick - custom-model-data: 1010 + custom-model-data: 3017 data: item-name: model: @@ -14,7 +14,7 @@ items: block: default:topaz_ore default:deepslate_topaz_ore: material: nether_brick - custom-model-data: 1011 + custom-model-data: 3018 data: item-name: model: @@ -27,7 +27,7 @@ items: block: default:deepslate_topaz_ore default:topaz: material: nether_brick - custom-model-data: 1012 + custom-model-data: 3019 settings: anvil-repair-item: - target: 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..55610a412 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: @@ -49,15 +48,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 +68,14 @@ 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 \ 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..49c0ca5c6 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: 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..e5eb93aa2 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/furniture/bench.yml @@ -0,0 +1,44 @@ +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 \ 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..2a008d707 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/furniture/flower_basket.yml @@ -0,0 +1,107 @@ +items: + default:flower_basket: + material: nether_brick + custom-model-data: 2001 + 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: 2002 + model: + type: minecraft:model + path: minecraft:item/custom/flower_basket_ground + default:flower_basket_wall: + material: nether_brick + custom-model-data: 2003 + model: + type: minecraft:model + path: minecraft:item/custom/flower_basket_wall + default:flower_basket_ceiling: + material: nether_brick + custom-model-data: 2004 + 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..f835379c3 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/furniture/wooden_chair.yml @@ -0,0 +1,41 @@ +items: + default:wooden_chair: + material: nether_brick + custom-model-data: 2005 + 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..fa91ebbb0 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,13 @@ 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 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 +104,78 @@ i18n: item.cap: 鸭舌帽 item.flower_basket: 花篮 item.chessboard_block: 棋盘方块 + item.safe_block: 保险柜 + item.sofa: 沙发 + item.amethyst_torch: 紫水晶火把 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 + 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: 紫水晶火把 \ No newline at end of file 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..ab797c99a --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/items/cap.yml @@ -0,0 +1,16 @@ +items: + 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 \ 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..6ec20e315 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/items/flame_elytra.yml @@ -0,0 +1,17 @@ +items: + $$>=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 \ 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..effc996c8 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/items/gui_head.yml @@ -0,0 +1,31 @@ +items: + 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 \ 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..bde573c46 --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/items/topaz_armor.yml @@ -0,0 +1,104 @@ +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 +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 67% 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..5102ab3f6 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,35 +1,4 @@ -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 @@ -236,94 +205,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 +271,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..e2c881a6c 100644 --- a/common-files/src/main/resources/resources/default/configuration/templates.yml +++ b/common-files/src/main/resources/resources/default/configuration/templates.yml @@ -747,7 +747,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 +763,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 +780,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 +798,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 +814,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 +830,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: @@ -3142,7 +3142,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/mushroom_1.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/mushroom_1.json new file mode 100644 index 000000000..f70b470fe --- /dev/null +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/mushroom_1.json @@ -0,0 +1,74 @@ +{ + "texture_size": [32, 32], + "textures": { + "0": "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] + } + } +} \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/mushroom_2.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/mushroom_2.json new file mode 100644 index 000000000..5c75b3c6e --- /dev/null +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/mushroom_2.json @@ -0,0 +1,151 @@ +{ + "textures": { + "0": "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] + } + } +} \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/mushroom_3.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/mushroom_3.json new file mode 100644 index 000000000..79459425c --- /dev/null +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/block/custom/mushroom_3.json @@ -0,0 +1,86 @@ +{ + "textures": { + "0": "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] + } + } +} \ 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/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/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/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/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/translations/de.yml b/common-files/src/main/resources/translations/de.yml index 3e4619163..f22dc1863 100644 --- a/common-files/src/main/resources/translations/de.yml +++ b/common-files/src/main/resources/translations/de.yml @@ -1,418 +1,426 @@ -# Nicht ändern +# Bitte nicht ändern lang-version: "${lang_version}" exception.invalid_syntax: "Ungültige Syntax. Korrekte Syntax: " exception.invalid_argument: "Ungültiges Argument. Grund: " -exception.invalid_sender: " ist nicht berechtigt, diesen Befehl auszuführen. Muss vom Typ " -exception.unexpected: "Beim Versuch, diesen Befehl auszuführen, ist ein interner Fehler aufgetreten" -exception.no_permission: "Es tut mir leid, aber Sie haben keine Berechtigung, diesen Befehl auszuführen" +exception.invalid_sender: " darf diesen Befehl nicht ausführen. Muss vom Typ sein" +exception.unexpected: "Ein interner Fehler ist beim Ausführen dieses Befehls aufgetreten" +exception.no_permission: "Entschuldigung, aber du hast keine Berechtigung, diesen Befehl auszuführen" exception.no_such_command: "Unbekannter Befehl." -argument.entity.notfound.player: "Spieler nicht gefunden: " -argument.entity.notfound.entity: "Entität nicht gefunden: " +argument.entity.notfound.player: "" +argument.entity.notfound.entity: "" argument.parse.failure.time: "'' ist kein gültiges Zeitformat" -argument.parse.failure.material: "'' ist kein gültiger Materialname" +argument.parse.failure.material: "'' ist kein gültiger Material-Name" argument.parse.failure.enchantment: "'' ist keine gültige Verzauberung" argument.parse.failure.offlineplayer: "Kein Spieler für die Eingabe '' gefunden" argument.parse.failure.player: "Kein Spieler für die Eingabe '' gefunden" argument.parse.failure.world: "'' ist keine gültige Minecraft-Welt" -argument.parse.failure.location.invalid_format: "'' ist kein gültiger Ort. Erforderliches Format ist ' " -argument.parse.failure.location.mixed_local_absolute: "Lokale und absolute Koordinaten können nicht gemischt werden. (Entweder alle Koordinaten verwenden '^' oder keine)" +argument.parse.failure.location.invalid_format: "'' ist keine gültige Koordinate. Benötigtes Format ist ' " +argument.parse.failure.location.mixed_local_absolute: "Lokale und absolute Koordinaten können nicht gemischt werden. (entweder alle Koordinaten verwenden '^' oder keine)" argument.parse.failure.namespacedkey.namespace: "Ungültiger Namespace ''. Muss [a-z0-9._-] sein" -argument.parse.failure.namespacedkey.key: "Ungültiger Schlüssel ''. Muss [a-z0-9/._-] sein" +argument.parse.failure.namespacedkey.key: "Ungültiger Key ''. Muss [a-z0-9/._-] sein" argument.parse.failure.namespacedkey.need_namespace: "Ungültige Eingabe '', erfordert einen expliziten Namespace" -argument.parse.failure.boolean: "Konntest keinen booleschen Wert aus '' parsen" +argument.parse.failure.boolean: "Boolean konnte aus '' nicht geparst werden" argument.parse.failure.number: "'' ist keine gültige Zahl im Bereich von bis " argument.parse.failure.char: "'' ist kein gültiges Zeichen" argument.parse.failure.string: "'' ist kein gültiger String vom Typ " argument.parse.failure.uuid: "'' ist keine gültige UUID" -argument.parse.failure.enum: "'' ist keiner der folgenden Werte: " +argument.parse.failure.enum: "'' ist nicht einer der folgenden Werte: " argument.parse.failure.regex: "'' stimmt nicht mit '' überein" -argument.parse.failure.flag.unknown: "Unbekanntes Flag ''" -argument.parse.failure.flag.duplicate_flag: "Doppeltes Flag ''" +argument.parse.failure.flag.unknown: "Unbekannter Flag ''" +argument.parse.failure.flag.duplicate_flag: "Doppelter Flag ''" argument.parse.failure.flag.no_flag_started: "Kein Flag gestartet. Weiß nicht, was mit '' zu tun ist" argument.parse.failure.flag.missing_argument: "Fehlendes Argument für ''" -argument.parse.failure.flag.no_permission: "Sie haben keine Berechtigung, '' zu verwenden" +argument.parse.failure.flag.no_permission: "Du hast keine Berechtigung, '' zu verwenden" argument.parse.failure.color: "'' ist keine gültige Farbe" -argument.parse.failure.duration: "'' ist kein gültiges Zeitdauerformat" +argument.parse.failure.duration: "'' ist kein gültiges Zeitdauer-Format" argument.parse.failure.aggregate.missing: "Fehlende Komponente ''" argument.parse.failure.aggregate.failure: "Ungültige Komponente '': " argument.parse.failure.either: "Konnte oder aus '' nicht auflösen" argument.parse.failure.namedtextcolor: "'' ist keine benannte Textfarbe" -command.reload.config.success: "Konfigurationen in ms neu geladen. (Async: ms | Sync: ms)" -command.reload.config.failure: "Neuladen der Konfiguration fehlgeschlagen. Überprüfen Sie die Konsolenprotokolle." -command.reload.pack.success: "Ressourcenpaket in ms neu geladen." -command.reload.pack.failure: "Neuladen des Ressourcenpakets fehlgeschlagen. Überprüfen Sie die Konsolenprotokolle." -command.reload.all.success: "Neu laden in ms abgeschlossen. (Async: ms | Sync: ms | Pack: ms)" -command.reload.all.failure: "Neu laden fehlgeschlagen. Überprüfen Sie die Konsolenprotokolle." -command.item.get.success: "Sie haben erhalten" +command.reload.config.success: "Configs in ms neugeladen. (Async: ms | Sync: ms)" +command.reload.config.failure: "Neuladen der Config fehlgeschlagen. Überprüfe die Konsolen-Logs." +command.reload.pack.success: "Resource Pack in ms neugeladen." +command.reload.pack.failure: "Neuladen des Resource Packs fehlgeschlagen. Überprüfe die Konsolen-Logs." +command.reload.all.success: "Neuladen in ms abgeschlossen. (Async: ms | Sync: ms | Pack: ms)" +command.reload.all.failure: "Neuladen fehlgeschlagen. Überprüfe die Konsolen-Logs." +command.item.get.success: " erhalten" command.item.get.failure.not_exist: "'>" command.item.give.success.single: "':'':''>" command.item.give.success.multiple: "':'':''>" command.item.give.failure.not_exist: "'>" -command.search_recipe.not_found: "Kein Rezept für diesen Item gefunden" +command.search_recipe.not_found: "Kein Rezept für dieses Item gefunden" command.search_usage.not_found: "Keine Verwendung für dieses Item gefunden" -command.search_recipe.no_item: "Bitte halten Sie ein Item, bevor Sie diesen Befehl ausführen" -command.search_usage.no_item: "Bitte halten Sie ein Item, bevor Sie diesen Befehl ausführen" -command.totem_animation.failure.not_totem: "Item '' ist kein minecraft:totem_of_undying" -command.resource.enable.success: "Ressource aktiviert. Führen Sie /ce reload all aus, um die Änderungen anzuwenden" -command.resource.enable.failure.unknown: "Unbekannte Ressource " -command.resource.disable.success: "Ressource deaktiviert. Führen Sie /ce reload all aus, um die Änderungen anzuwenden" -command.resource.disable.failure.unknown: "Unbekannte Ressource " -command.resource.list: "Aktivierte Ressourcen(): Deaktivierte Ressourcen(): " -command.upload.failure.not_supported: "Die aktuelle Hosting-Methode '' unterstützt das Hochladen von Ressourcenpaketen nicht." -command.upload.on_progress: "Upload-Vorgang gestartet. Überprüfen Sie die Konsole für weitere Informationen." -command.send_resource_pack.success.single: "Ressourcenpaket an gesendet." -command.send_resource_pack.success.multiple: "Ressourcenpakete an Spieler gesendet." -warning.config.pack.duplicated_files: "Duplizierte Dateien gefunden. Bitte beheben Sie diese im Abschnitt 'resource-pack.duplicated-files-handler' in config.yml." -warning.config.yaml.duplicated_key: "Problem in Datei gefunden - Duplizierter Schlüssel '' in Zeile gefunden, dies kann zu unerwarteten Ergebnissen führen." -warning.config.yaml.inconsistent_value_type: "Fehler in der Datei - Der duplizierte Schlüssel '' wurde in Zeile mit einem anderen Werttyp gefunden. Dies könnte zu unerwarteten Ergebnissen führen." -warning.config.type.int: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Kann '' nicht in den Integer-Typ für Option '' umwandeln." -warning.config.type.float: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Kann '' nicht in den Float-Typ für Option '' umwandeln." -warning.config.type.double: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Kann '' nicht in den Double-Typ für Option '' umwandeln." -warning.config.type.quaternionf: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Kann '' nicht in den Quaternionf-Typ für Option '' umwandeln." -warning.config.type.vector3f: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Kann '' nicht in den Vector3f-Typ für Option '' umwandeln." -warning.config.type.boolean: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Kann '' nicht in den Booleschen Typ für Option '' umwandeln." -warning.config.type.snbt.invalid_syntax: "Problem in Datei gefunden - Die Konfiguration '' verwendet einen ungültigen SNBT-Syntax ''." -warning.config.number.missing_type: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'type'-Argument für das Zahlenargument." -warning.config.number.invalid_type: "Problem in Datei gefunden - Die Konfiguration '' verwendet einen ungültigen Zahlargumenttyp ''." -warning.config.number.missing_argument: "Problem in Datei gefunden - Der Konfiguration '' fehlt das Argument für 'number'." -warning.config.number.invalid_format: "Problem in Datei gefunden - Die Konfiguration '' verwendet ein ungültiges Zahlenformat ''." -warning.config.number.fixed.missing_value: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'value'-Argument für die 'constant'-Zahl." -warning.config.number.fixed.invalid_value: "Problem in Datei gefunden - Die Konfiguration '' verwendet das ungültige 'value'-Argument '' für die 'constant'-Zahl." -warning.config.number.expression.missing_expression: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'expression'-Argument für die 'expression'-Zahl." -warning.config.number.uniform.missing_min: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'min'-Argument für die 'uniform'-Zahl." -warning.config.number.uniform.missing_max: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'max'-Argument für die 'uniform'-Zahl." -warning.config.condition.all_of.missing_terms: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'terms'-Argument für die 'all_of'-Bedingung." -warning.config.condition.all_of.invalid_terms_type: "Problem in Datei gefunden - Die Konfiguration '' hat eine falsch konfigurierte 'all_of'-Bedingung, 'terms' sollte eine Kartenliste sein, aktueller Typ: ''." -warning.config.condition.any_of.missing_terms: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'terms'-Argument für die 'any_of'-Bedingung." -warning.config.condition.any_of.invalid_terms_type: "Problem in Datei gefunden - Die Konfiguration '' hat eine falsch konfigurierte 'any_of'-Bedingung, 'terms' sollte eine Kartenliste sein, aktueller Typ: ''." -warning.config.condition.inverted.missing_term: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'term'-Argument für die 'inverted'-Bedingung." -warning.config.condition.inverted.invalid_term_type: "Problem in Datei gefunden - Die Konfiguration '' hat eine falsch konfigurierte 'inverted'-Bedingung, 'term' sollte ein Konfigurationsabschnitt sein, aktueller Typ: ''." -warning.config.condition.enchantment.missing_predicate: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'predicate'-Argument für die 'enchantment'-Bedingung." -warning.config.condition.enchantment.invalid_predicate: "Problem in Datei gefunden - Die Konfiguration '' verwendet ein ungültiges Verzauberungs-'predicate'-Argument ''." -warning.config.condition.match_block_property.missing_properties: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'properties'-Argument für die 'match_block_property'-Bedingung." -warning.config.condition.match_item.missing_id: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'id'-Argument für die 'match_item'-Bedingung." -warning.config.condition.table_bonus.missing_enchantment: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'enchantment'-Argument für die 'table_bonus'-Bedingung." -warning.config.condition.table_bonus.missing_chances: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'chances'-Argument für die 'table_bonus'-Bedingung." -warning.config.condition.permission.missing_permission: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'permission'-Argument für die 'permission'-Bedingung." -warning.config.condition.string_equals.missing_value1: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'value1'-Argument für die 'string_equals'-Bedingung." -warning.config.condition.string_equals.missing_value2: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'value2'-Argument für die 'string_equals'-Bedingung." -warning.config.condition.string_contains.missing_value1: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'value1'-Argument für die 'string_contains'-Bedingung." -warning.config.condition.string_contains.missing_value2: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'value2'-Argument für die 'string_contains'-Bedingung." -warning.config.condition.string_regex.missing_value: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'value'-Argument für die 'string_regex'-Bedingung." -warning.config.condition.string_regex.missing_regex: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'regex'-Argument für die 'string_regex'-Bedingung." -warning.config.condition.expression.missing_expression: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'expression'-Argument für die 'expression'-Bedingung." -warning.config.condition.is_null.missing_argument: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'argument'-Argument für die 'is_null'-Bedingung." -warning.config.condition.hand.missing_hand: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'hand'-Argument für die 'hand'-Bedingung." -warning.config.condition.hand.invalid_hand: "Problem in Datei gefunden - Die Konfiguration '' verwendet ein ungültiges 'hand'-Argument '' für die 'hand'-Bedingung. Erlaubte Handtypen: []" -warning.config.condition.on_cooldown.missing_id: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'id'-Argument für die 'on_cooldown'-Bedingung." -warning.config.structure.not_section: "Problem in Datei gefunden - Die Konfiguration '' wird als Konfigurationsabschnitt erwartet, ist aber tatsächlich ein(e) ''." -warning.config.image.duplicate: "Problem in Datei gefunden - Dupliziertes Bild ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." -warning.config.image.missing_height: "Problem in Datei gefunden - Dem Bild '' fehlt das erforderliche 'height'-Argument." -warning.config.image.height_ascent_conflict: "Problem in Datei gefunden - Das Bild '' verletzt die Bitmap-Bildregel: 'height'-Argument '' sollte nicht niedriger als 'ascent'-Argument '' sein." -warning.config.image.missing_file: "Problem in Datei gefunden - Dem Bild '' fehlt das erforderliche 'file'-Argument." -warning.config.image.invalid_file_chars: "Problem in Datei gefunden - Das Bild '' hat ein 'file'-Argument '', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." -warning.config.image.invalid_font_chars: "Problem in Datei gefunden - Das Bild '' hat ein 'font'-Argument '', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." -warning.config.image.missing_char: "Problem in Datei gefunden - Dem Bild '' fehlt das erforderliche 'char'-Argument." -warning.config.image.codepoint_conflict: "Problem in Datei gefunden - Das Bild '' verwendet ein Zeichen '()' in Schriftart , das von einem anderen Bild '' verwendet wurde." -warning.config.image.invalid_codepoint_grid: "Problem in Datei gefunden - Bild '' hat ein ungültiges 'chars'-Codepunktgitter." -warning.config.image.invalid_char: "Problem in Datei gefunden - Bild '' hat einen Zeichenparameter, der kombinierende Zeichen enthält, was zu einer Bildaufteilung führen kann." -warning.config.image.invalid_hex_value: "Problem in Datei gefunden - Das Bild '' verwendet ein Unicode-Zeichen '', das kein gültiger Hexadezimalwert (Basis 16) ist." -warning.config.recipe.duplicate: "Problem in Datei gefunden - Dupliziertes Rezept ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." -warning.config.recipe.missing_type: "Problem in Datei gefunden - Dem Rezept '' fehlt das erforderliche 'type'-Argument." -warning.config.recipe.invalid_type: "Problem in Datei gefunden - Das Rezept '' verwendet einen ungültigen Rezepttyp ''." -warning.config.recipe.invalid_ingredient: "Issue found in file - The recipe '' is using an invalid ingredient ''." -warning.config.recipe.invalid_result: "Issue found in file - The recipe '' is using an invalid result ''." -warning.config.recipe.missing_ingredient: "Problem in Datei gefunden - Dem Kochrezept '' fehlt das erforderliche 'ingredient'-Argument." -warning.config.recipe.missing_result: "Problem in Datei gefunden - Dem Rezept '' fehlt das erforderliche 'result'-Argument." -warning.config.recipe.result.missing_id: "Problem in Datei gefunden - Dem Rezept '' fehlt das erforderliche Argument 'id' für das Rezeptresultat." -warning.config.recipe.crafting.invalid_category: "Problem in Datei gefunden - Das Herstellungsrezept '' verwendet eine ungültige Kategorie ''. Erlaubte Kategorien: []." -warning.config.recipe.cooking.invalid_category: "Problem in Datei gefunden - Das Kochrezept '' verwendet eine ungültige Kategorie ''. Erlaubte Kategorien: []." -warning.config.recipe.shaped.missing_pattern: "Problem in Datei gefunden - Dem geformten Rezept '' fehlt das erforderliche Argument 'pattern'." -warning.config.recipe.shaped.invalid_pattern: "Problem in Datei gefunden - Das geformte Rezept '' verwendet ein ungültiges Muster ''." -warning.config.recipe.shaped.invalid_symbol: "Problem in Datei gefunden - Das geformte Rezept '' verwendet ein ungültiges Symbol '' im Muster." -warning.config.recipe.smithing_transform.post_processor.missing_type: "Problem in Datei gefunden - Dem Schmiedetransformationsrezept '' fehlt das erforderliche Argument 'type' für einen der Post-Prozessoren." -warning.config.recipe.smithing_transform.post_processor.invalid_type: "Problem in Datei gefunden - Das Schmiedetransformationsrezept '' verwendet einen ungültigen Post-Prozessor-Typ ''." -warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components: "Problem in Datei gefunden - Dem Schmiedetransformationsrezept '' fehlt das erforderliche Argument 'components' für die Post-Prozessoren 'keep_components'." -warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "Problem in Datei gefunden - Dem Schmiedetransformationsrezept '' fehlt das erforderliche Argument 'tags' für die Post-Prozessoren 'keep_tags'." -warning.config.i18n.unknown_locale: "Problem in Datei gefunden - Unbekanntes Gebietsschema ''." -warning.config.template.duplicate: "Problem in Datei gefunden - Dupliziertes Template ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." -warning.config.template.invalid: "Problem in Datei gefunden - Die Konfiguration '' verwendet ein ungültiges Template ''." -warning.config.template.argument.self_increase_int.invalid_range: "Problem in Datei gefunden - Das Template '' verwendet einen 'from'-Wert '', der größer ist als der 'to'-Wert '' im 'self_increase_int'-Argument." -warning.config.template.argument.list.invalid_type: "Problem in Datei gefunden - Das Template '' verwendet ein 'list'-Argument, das eine 'List' als Argument erwartet, während das Eingabeargument ein(e) '' ist." -warning.config.template.argument.default_value.invalid_syntax: "Problem in Datei gefunden - Das Template '' verwendet einen ungültigen Standardwert '' für das Argument ''." -warning.config.vanilla_loot.missing_type: "Problem in Datei gefunden - Dem Vanilla-Beute '' fehlt das erforderliche 'type'-Argument." -warning.config.vanilla_loot.invalid_type: "Problem in Datei gefunden - Der Vanilla-Beute '' verwendet einen ungültigen Typ ''. Erlaubte Typen: []." -warning.config.vanilla_loot.block.invalid_target: "Problem in Datei gefunden - Ungültiges Blockziel '' in Vanilla-Beute ''." -warning.config.sound.duplicate: "Problem in Datei gefunden - Duplizierter Sound ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." -warning.config.sound.missing_sounds: "Problem in Datei gefunden - Dem Sound '' fehlt das erforderliche 'sounds'-Argument." -warning.config.sound.missing_name: "Problem in Datei gefunden - Dem Sound '' fehlt das erforderliche 'name'-Argument." -warning.config.jukebox_song.duplicate: "Problem in Datei gefunden - Dupliziertes Jukebox-Lied ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." -warning.config.jukebox_song.missing_sound: "Problem in Datei gefunden - Dem Jukebox-Lied '' fehlt das erforderliche 'sound'-Argument." -warning.config.furniture.duplicate: "Problem in Datei gefunden - Dupliziertes Möbelstück ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." -warning.config.furniture.missing_placement: "Problem in Datei gefunden - Dem Möbelstück '' fehlt das erforderliche 'placement'-Argument." -warning.config.furniture.element.missing_item: "Problem in Datei gefunden - Dem Möbelstück '' fehlt das erforderliche 'item'-Argument für eines seiner Elemente." -warning.config.furniture.settings.unknown: "Problem in Datei gefunden - Das Möbelstück '' verwendet einen unbekannten Einstellungstyp ''." -warning.config.furniture.hitbox.invalid_type: "Problem in Datei gefunden - Das Möbelstück '' verwendet einen ungültigen Hitbox-Typ ''." -warning.config.furniture.hitbox.custom.invalid_entity: "Problem in Datei gefunden - Das Möbelstück '' verwendet eine benutzerdefinierte Hitbox mit ungültigem Entitätstyp ''." -warning.config.item.duplicate: "Problem in Datei gefunden - Dupliziertes Item ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." -warning.config.item.settings.unknown: "Problem in Datei gefunden - Das Item '' verwendet einen unbekannten Einstellungstyp ''." -warning.config.item.settings.invulnerable.invalid_damage_source: "Problem in Datei gefunden - Das Item '' verwendet eine unbekannte Schadensquelle ''. Erlaubte Quellen: []." -warning.config.item.settings.equippable.missing_slot: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'slot'-Argument für die 'equippable'-Einstellung." -warning.config.item.missing_material: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'material'-Argument." -warning.config.item.invalid_material: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Materialtyp ''." -warning.config.item.invalid_custom_model_data: "Problem in Datei gefunden - Das Item '' verwendet negative benutzerdefinierte Modelldaten '', die ungültig sind." -warning.config.item.bad_custom_model_data: "Problem in Datei gefunden - Das Item '' verwendet benutzerdefinierte Modelldaten '', die zu groß sind. Es wird empfohlen, einen Wert unter 16.777.216 zu verwenden." -warning.config.item.custom_model_data_conflict: "Problem in Datei gefunden - Das Item '' verwendet benutzerdefinierte Modelldaten '', die bereits von Item '' belegt sind." -warning.config.item.invalid_component: "Problem in Datei gefunden - Das Item '' verwendet einen nicht existierenden Komponententyp ''." -warning.config.item.missing_model_id: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'custom-model-data' oder 'item-model'-Argument." -warning.config.item.missing_model: "Problem in Datei gefunden - Das Item '' fehlt der erforderliche 'model'-Abschnitt für die Unterstützung von 1.21.4+-Ressourcenpaketen." -warning.config.item.behavior.missing_type: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'type'-Argument für sein Itemsverhalten." -warning.config.item.behavior.invalid_type: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Itemsverhaltenstyp ''." -warning.config.item.behavior.block.missing_block: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'block'-Argument für das 'block_item'-Verhalten." -warning.config.item.behavior.furniture.missing_furniture: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'furniture'-Argument für das 'furniture_item'-Verhalten." -warning.config.item.behavior.liquid_collision.missing_block: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'block'-Argument für das 'liquid_collision_block_item'-Verhalten." -warning.config.item.legacy_model.missing_path: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'path'-Argument für das Legacy-Modell." -warning.config.item.legacy_model.overrides.missing_path: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'path'-Argument für Legacy-Modell-Überschreibungen." -warning.config.item.legacy_model.overrides.missing_predicate: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'predicate'-Argument für Legacy-Modell-Überschreibungen." -warning.config.item.legacy_model.cannot_convert: "Problem in Datei gefunden - Kann 1.21.4+-Gegenstände für Item '' nicht in das Legacy-Format konvertieren. Bitte erstellen Sie den Abschnitt 'legacy-model' für diesen Item manuell." -warning.config.item.model.invalid_type: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Modelltyp ''." -warning.config.item.model.tint.missing_type: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'type'-Argument für die Tönung." -warning.config.item.model.tint.invalid_type: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Tönungstyp ''." -warning.config.item.model.tint.constant.missing_value: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'value'-Argument für die konstante Tönung." -warning.config.item.model.tint.grass.invalid_temp: "Problem in Datei gefunden - Das Item '' verwendet eine ungültige Temperatur '' für die Grastönung, die zwischen 0 und 1 liegen sollte." -warning.config.item.model.tint.grass.invalid_downfall: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Niederschlag '' für die Grastönung, der zwischen 0 und 1 liegen sollte." -warning.config.item.model.tint.invalid_value: "Problem in Datei gefunden - Das Item '' verwendet eine ungültige Tönung ''." -warning.config.item.model.base.missing_path: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'path'-Argument für das Modell 'minecraft:model'." -warning.config.item.model.base.invalid_path: "Problem in Datei gefunden - Das Item '' hat ein ungültiges 'path'-Argument '' für das Modell 'minecraft:model', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." -warning.config.item.model.condition.missing_property: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:condition'." -warning.config.item.model.condition.invalid_property: "Problem in Datei gefunden - Das Item '' verwendet eine ungültige Eigenschaft '' für das Modell 'minecraft:condition'." -warning.config.item.model.condition.missing_on_true: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'on-true'-Argument für das Modell 'minecraft:condition'." -warning.config.item.model.condition.missing_on_false: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'on-false'-Argument für das Modell 'minecraft:condition'." -warning.config.item.model.condition.keybind.missing_keybind: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'keybind'-Argument für die Eigenschaft 'minecraft:keybind_down'." -warning.config.item.model.condition.component.missing_predicate: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'predicate'-Argument für die Eigenschaft 'minecraft:has_component'." -warning.config.item.model.condition.component.missing_value: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'value'-Argument für die Eigenschaft 'minecraft:has_component'." -warning.config.item.model.condition.has_component.missing_component: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'component'-Argument für die Eigenschaft 'minecraft:has_component'." -warning.config.item.model.composite.missing_models: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'models'-Argument für das Modell 'minecraft:composite'." -warning.config.item.model.range_dispatch.missing_property: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:range_dispatch'." -warning.config.item.model.range_dispatch.invalid_property: "Problem in Datei gefunden - Das Item '' verwendet eine ungültige Eigenschaft '' für das Modell 'minecraft:range_dispatch'." -warning.config.item.model.range_dispatch.missing_entries: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'entries'-Argument für das Modell 'minecraft:composite'." -warning.config.item.model.range_dispatch.entry.missing_model: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'model'-Argument für einen der Einträge im Modell 'minecraft:composite'." -warning.config.item.model.range_dispatch.compass.missing_target: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'target'-Argument für die Eigenschaft 'minecraft:compass'." -warning.config.item.model.range_dispatch.time.missing_source: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'source'-Argument für die Eigenschaft 'minecraft:time'." -warning.config.item.model.select.missing_property: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:select'." -warning.config.item.model.select.invalid_property: "Problem in Datei gefunden - Das Item '' verwendet eine ungültige Eigenschaft '' für das Modell 'minecraft:select'." -warning.config.item.model.select.missing_cases: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'cases'-Argument für das Modell 'minecraft:select'." -warning.config.item.model.select.case.missing_when: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'when'-Argument für einen der Fälle im Modell 'minecraft:select'." -warning.config.item.model.select.case.missing_model: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'model'-Argument für einen der Fälle im Modell 'minecraft:select'." -warning.config.item.model.select.component.missing_component: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'component'-Argument für die Eigenschaft 'minecraft:component'." -warning.config.item.model.select.block_state.missing_property: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'block-state-property'-Argument für die Eigenschaft 'minecraft:block_state'." -warning.config.item.model.select.local_time.missing_pattern: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'pattern'-Argument für die Eigenschaft 'minecraft:local_time'." -warning.config.item.model.special.missing_type: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'type'-Argument für das Modell 'minecraft:special'." -warning.config.item.model.special.missing_path: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'path'-Argument für das Modell 'minecraft:special'." -warning.config.item.model.special.invalid_path: "Problem in Datei gefunden - Das Item '' hat ein ungültiges 'path'-Argument '' für das Modell 'minecraft:special', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." -warning.config.item.model.special.invalid_type: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Typ '' für das Modell 'minecraft:special'." -warning.config.item.model.special.banner.missing_color: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'color'-Argument für das Spezialmodell 'minecraft:banner'." -warning.config.item.model.special.bed.missing_texture: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:bed'." -warning.config.item.model.special.sign.missing_wood_type: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'wood-type'-Argument für das Spezialmodell 'minecraft:hanging_sign'/'minecraft:standing_sign'." -warning.config.item.model.special.sign.missing_texture: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:hanging_sign'/'minecraft:standing_sign'." -warning.config.item.model.special.chest.missing_texture: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:chest'." -warning.config.item.model.special.chest.invalid_openness: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen 'openness'-Wert '' für das Spezialmodell 'minecraft:chest'. Gültiger Bereich '0~1.'" -warning.config.item.model.special.shulker_box.missing_texture: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:shulker_box'." -warning.config.item.model.special.shulker_box.invalid_openness: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen 'openness'-Wert '' für das Spezialmodell 'minecraft:shulker_box'. Gültiger Bereich '0~1.'" -warning.config.item.model.special.head.missing_kind: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'kind'-Argument für das Spezialmodell 'minecraft:head'." -warning.config.block.duplicate: "Problem in Datei gefunden - Duplizierter Block ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." -warning.config.block.missing_state: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'state'-Argument." -warning.config.block.state.property.missing_type: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'type'-Argument für die Eigenschaft ''." -warning.config.block.state.property.invalid_type: "Problem in Datei gefunden - Der Block '' verwendet das ungültige 'type'-Argument '' für die Eigenschaft ''." -warning.config.block.state.property.integer.invalid_range: "Problem in Datei gefunden - Der Block '' verwendet das ungültige 'range'-Argument '' für die Integer-Eigenschaft ''. Korrekte Syntax: 1~2." -warning.config.block.state.property.invalid_format: "Problem in Datei gefunden - Der Block '' verwendet ein ungültiges Blockzustandsformat ''." -warning.config.block.state.missing_real_id: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'id'-Argument für 'state'. 'id' ist die serverseitige Block-ID, die für jeden Blockzustandstyp eindeutig ist. Wenn Sie einen serverseitigen Block mit 'note_block' und ID 30 erstellen, wäre die echte Block-ID 'craftengine:note_block_30'." -warning.config.block.state.missing_state: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'state'-Argument für 'state'." -warning.config.block.state.missing_properties: "Problem in Datei gefunden - Dem Block '' fehlt der erforderliche 'properties'-Abschnitt für 'states'." -warning.config.block.state.missing_appearances: "Problem in Datei gefunden - Dem Block '' fehlt der erforderliche 'appearances'-Abschnitt für 'states'." -warning.config.block.state.missing_variants: "Problem in Datei gefunden - Dem Block '' fehlt der erforderliche 'variants'-Abschnitt für 'states'." -warning.config.block.state.variant.missing_appearance: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'appearance'-Argument für Variante ''." -warning.config.block.state.variant.invalid_appearance: "Problem in Datei gefunden - Der Block '' hat einen Fehler, dass die Variante '' ein nicht existierendes Erscheinungsbild '' verwendet." -warning.config.block.state.invalid_vanilla: "Problem in Datei gefunden - Der Block '' verwendet einen ungültigen Vanilla-Blockzustand ''." -warning.config.block.state.unavailable_vanilla: "Problem in Datei gefunden - Der Block '' verwendet einen nicht verfügbaren Vanilla-Blockzustand ''. Bitte geben Sie diesen Zustand in mappings.yml frei." -warning.config.block.state.invalid_vanilla_id: "Problem in Datei gefunden - Der Block '' verwendet einen Vanilla-Blockzustand '', der den verfügbaren Slot-Bereich '0~' überschreitet." -warning.config.block.state.conflict: "Problem in Datei gefunden - Der Block '' verwendet einen Vanilla-Blockzustand '', der bereits von '' belegt ist." -warning.config.block.state.bind_failed: "Problem in Datei gefunden - Der Block '' konnte den echten Blockzustand für '' nicht binden, da der Zustand von '' belegt ist." -warning.config.block.state.invalid_real_id: "Problem in Datei gefunden - Der Block '' verwendet einen echten Blockzustand '', der den verfügbaren Slot-Bereich '0~' überschreitet. Erwägen Sie, weitere echte Zustände in 'additional-real-blocks.yml' hinzuzufügen, wenn die Slots aufgebraucht sind." -warning.config.block.state.model.missing_path: "Problem in Datei gefunden - Dem 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 illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." -warning.config.block.settings.unknown: "Problem in Datei gefunden - Der Block '' verwendet einen unbekannten Einstellungstyp ''." -warning.config.block.behavior.missing_type: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'type'-Argument für sein Blockverhalten." -warning.config.block.behavior.invalid_type: "Problem in Datei gefunden - Der Block '' verwendet einen ungültigen Blockverhaltenstyp ''." -warning.config.block.behavior.concrete.missing_solid: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'solid-block'-Option für das 'concrete_block'-Verhalten." -warning.config.block.behavior.crop.missing_age: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'age'-Eigenschaft für das 'crop_block'-Verhalten." -warning.config.block.behavior.sugar_cane.missing_age: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'age'-Eigenschaft für das 'sugar_cane_block'-Verhalten." -warning.config.block.behavior.leaves.missing_persistent: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'persistent'-Eigenschaft für das 'leaves_block'-Verhalten." -warning.config.block.behavior.leaves.missing_distance: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'distance'-Eigenschaft für das 'leaves_block'-Verhalten." -warning.config.block.behavior.lamp.missing_lit: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'lit'-Eigenschaft für das 'lamp_block'-Verhalten." -warning.config.block.behavior.sapling.missing_stage: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'stage'-Eigenschaft für das 'sapling_block'-Verhalten." -warning.config.block.behavior.sapling.missing_feature: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'feature'-Argument für das 'sapling_block'-Verhalten." -warning.config.block.behavior.strippable.missing_stripped: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'stripped'-Argument für das 'strippable_block'-Verhalten." -warning.config.block.behavior.door.missing_half: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'half'-Eigenschaft für das 'door_block'-Verhalten." -warning.config.block.behavior.door.missing_facing: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'facing'-Eigenschaft für das 'door_block'-Verhalten." -warning.config.block.behavior.door.missing_hinge: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'hinge'-Eigenschaft für das 'door_block'-Verhalten." -warning.config.block.behavior.door.missing_open: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'open'-Eigenschaft für das 'door_block'-Verhalten." -warning.config.block.behavior.door.missing_powered: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'powered'-Eigenschaft für das 'door_block'-Verhalten." -warning.config.block.behavior.trapdoor.missing_half: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'half'-Eigenschaft für das 'trapdoor_block'-Verhalten." -warning.config.block.behavior.trapdoor.missing_facing: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'facing'-Eigenschaft für das 'trapdoor_block'-Verhalten." -warning.config.block.behavior.trapdoor.missing_open: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'open'-Eigenschaft für das 'trapdoor_block'-Verhalten." -warning.config.block.behavior.trapdoor.missing_powered: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'powered'-Eigenschaft für das 'trapdoor_block'-Verhalten." -warning.config.block.behavior.stackable.missing_property: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche ''-Eigenschaft für das 'stackable_block'-Verhalten." -warning.config.block.behavior.stackable.missing_items: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'items'-Argument für das 'stackable_block'-Verhalten." -warning.config.block.behavior.fence_gate.missing_facing: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'facing'-Argument für das 'fence_gate_block'-Verhalten." -warning.config.block.behavior.fence_gate.missing_in_wall: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'in_wall'-Argument für das 'fence_gate_block'-Verhalten." -warning.config.block.behavior.fence_gate.missing_open: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'powered'-Argument für das 'fence_gate_block'-Verhalten." -warning.config.block.behavior.fence_gate.missing_powered: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'open'-Argument für das 'fence_gate_block'-Verhalten." -warning.config.block.behavior.trapdoor.missing_type: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'type'-Eigenschaft für das 'slab_block'-Verhalten." -warning.config.block.behavior.stairs.missing_facing: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'facing'-Eigenschaft für das 'stairs_block'-Verhalten." -warning.config.block.behavior.stairs.missing_half: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'half'-Eigenschaft für das 'stairs_block'-Verhalten." -warning.config.block.behavior.stairs.missing_shape: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'shape'-Eigenschaft für das 'stairs_block'-Verhalten." -warning.config.block.behavior.pressure_plate.missing_powered: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'powered'-Eigenschaft für das 'pressure_plate_block'-Verhalten." -warning.config.model.generation.missing_parent: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'parent'-Argument im Abschnitt 'generation'." -warning.config.model.generation.invalid_display_position: "Problem in Datei gefunden - Die Konfiguration '' verwendet eine ungültige Anzeigeposition '' im Abschnitt 'generation.display'. Erlaubte Anzeigepositionen: []" -warning.config.model.generation.invalid_gui_light: "Problem in Datei gefunden - Die Konfiguration '' verwendet eine ungültige GUI-Lichtoption '' im Abschnitt 'generation'. Erlaubte GUI-Lichtoptionen: []" -warning.config.model.generation.conflict: "Problem in Datei gefunden - Fehler beim Generieren des Modells für '', da zwei oder mehr Konfigurationen versuchen, verschiedene JSON-Modelle mit demselben Pfad zu generieren: ''." -warning.config.model.generation.texture.invalid: "Problem in Datei gefunden - Die Konfiguration '' hat eine Textur '' mit Pfad '', die illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." -warning.config.model.generation.parent.invalid: "Problem in Datei gefunden - Die Konfiguration '' hat ein 'parent'-Argument '', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." -warning.config.emoji.missing_keywords: "Problem in Datei gefunden - Dem Emoji '' fehlt das erforderliche 'keywords'-Argument." -warning.config.emoji.duplicate: "Problem in Datei gefunden - Dupliziertes Emoji ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +command.search_recipe.no_item: "Bitte halte ein Item in der Hand, bevor du diesen Befehl ausführst" +command.search_usage.no_item: "Bitte halte ein Item in der Hand, bevor du diesen Befehl ausführst" +command.totem_animation.failure.not_totem: "Item '' ist nicht minecraft:totem_of_undying" +command.resource.enable.success: "Resource aktiviert. Führe /ce reload all aus, um die Änderungen zu übernehmen" +command.resource.enable.failure.unknown: "Unbekannte Resource " +command.resource.disable.success: "Resource deaktiviert. Führe /ce reload all aus, um die Änderungen zu übernehmen" +command.resource.disable.failure.unknown: "Unbekannte Resource " +command.resource.list: "Aktivierte Resources (): Deaktivierte Resources (): " +command.upload.failure.not_supported: "Die aktuelle Hosting-Methode '' unterstützt das Hochladen von Resource Packs nicht." +command.upload.on_progress: "Upload-Fortschritt gestartet. Überprüfe die Konsole für weitere Informationen." +command.send_resource_pack.success.single: "Resource Pack an gesendet." +command.send_resource_pack.success.multiple: "Resource Packs an Spieler gesendet." +warning.config.pack.duplicated_files: "Doppelte Dateien gefunden. Bitte löse dies über den 'resource-pack.duplicated-files-handler' Abschnitt in der config.yml." +warning.config.yaml.duplicated_key: "Problem in Datei gefunden - Doppelter Key '' in Zeile gefunden, dies könnte zu unerwarteten Ergebnissen führen." +warning.config.yaml.inconsistent_value_type: "Problem in Datei gefunden - Doppelter Key '' in Zeile mit unterschiedlichen Wertetypen gefunden, dies könnte zu unerwarteten Ergebnissen führen." +warning.config.type.int: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: '' kann nicht in den Integer-Typ für Option '' umgewandelt werden." +warning.config.type.boolean: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: '' kann nicht in den Boolean-Typ für Option '' umgewandelt werden." +warning.config.type.float: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: '' kann nicht in den Float-Typ für Option '' umgewandelt werden." +warning.config.type.double: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: '' kann nicht in den Double-Typ für Option '' umgewandelt werden." +warning.config.type.quaternionf: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: '' kann nicht in den Quaternionf-Typ für Option '' umgewandelt werden." +warning.config.type.vector3f: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: '' kann nicht in den Vector3f-Typ für Option '' umgewandelt werden." +warning.config.type.map: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: '' kann nicht in den Map-Typ für Option '' umgewandelt werden." +warning.config.type.snbt.invalid_syntax: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Ungültige SNBT-Syntax ''." +warning.config.number.missing_type: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'type'-Argument für das Zahlenargument." +warning.config.number.invalid_type: "Problem in Datei gefunden - Die Config '' verwendet einen ungültigen Zahlenargument-Typ ''." +warning.config.number.missing_argument: "Problem in Datei gefunden - Bei der Config '' fehlt das Argument für 'number'." +warning.config.number.invalid_format: "Problem in Datei gefunden - Die Config '' verwendet ein ungültiges Zahlenformat ''." +warning.config.number.fixed.missing_value: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'value'-Argument für die 'constant' Zahl." +warning.config.number.fixed.invalid_value: "Problem in Datei gefunden - Die Config '' verwendet das ungültige 'value'-Argument '' für die 'constant' Zahl." +warning.config.number.expression.missing_expression: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'expression'-Argument für die 'expression' Zahl." +warning.config.number.uniform.missing_min: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'min'-Argument für die 'uniform' Zahl." +warning.config.number.uniform.missing_max: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'max'-Argument für die 'uniform' Zahl." +warning.config.condition.all_of.missing_terms: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'terms'-Argument für die 'all_of'-Bedingung." +warning.config.condition.all_of.invalid_terms_type: "Problem in Datei gefunden - Die Config '' hat eine falsch konfigurierte 'all_of'-Bedingung, 'terms' sollte eine Map-Liste sein, aktueller Typ: ''." +warning.config.condition.any_of.missing_terms: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'terms'-Argument für die 'any_of'-Bedingung." +warning.config.condition.any_of.invalid_terms_type: "Problem in Datei gefunden - Die Config '' hat eine falsch konfigurierte 'any_of'-Bedingung, 'terms' sollte eine Map-Liste sein, aktueller Typ: ''." +warning.config.condition.inverted.missing_term: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'term'-Argument für die 'inverted'-Bedingung." +warning.config.condition.inverted.invalid_term_type: "Problem in Datei gefunden - Die Config '' hat eine falsch konfigurierte 'inverted'-Bedingung, 'term' sollte ein Config-Abschnitt sein, aktueller Typ: ''." +warning.config.condition.enchantment.missing_predicate: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'predicate'-Argument für die 'enchantment'-Bedingung." +warning.config.condition.enchantment.invalid_predicate: "Problem in Datei gefunden - Die Config '' verwendet ein ungültiges 'predicate'-Argument '' für die 'enchantment'-Bedingung." +warning.config.condition.match_block_property.missing_properties: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'properties'-Argument für die 'match_block_property'-Bedingung." +warning.config.condition.match_item.missing_id: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'id'-Argument für die 'match_item'-Bedingung." +warning.config.condition.table_bonus.missing_enchantment: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'enchantment'-Argument für die 'table_bonus'-Bedingung." +warning.config.condition.table_bonus.missing_chances: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'chances'-Argument für die 'table_bonus'-Bedingung." +warning.config.condition.permission.missing_permission: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'permission'-Argument für die 'permission'-Bedingung." +warning.config.condition.string_equals.missing_value1: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'value1'-Argument für die 'string_equals'-Bedingung." +warning.config.condition.string_equals.missing_value2: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'value2'-Argument für die 'string_equals'-Bedingung." +warning.config.condition.string_contains.missing_value1: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'value1'-Argument für die 'string_contains'-Bedingung." +warning.config.condition.string_contains.missing_value2: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'value2'-Argument für die 'string_contains'-Bedingung." +warning.config.condition.string_regex.missing_value: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'value'-Argument für die 'string_regex'-Bedingung." +warning.config.condition.string_regex.missing_regex: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'regex'-Argument für die 'string_regex'-Bedingung." +warning.config.condition.expression.missing_expression: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'expression'-Argument für die 'expression'-Bedingung." +warning.config.condition.is_null.missing_argument: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'argument'-Argument für die 'is_null'-Bedingung." +warning.config.condition.hand.missing_hand: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'hand'-Argument für die 'hand'-Bedingung." +warning.config.condition.hand.invalid_hand: "Problem in Datei gefunden - Die Config '' verwendet ein ungültiges 'hand'-Argument '' für die 'hand'-Bedingung. Erlaubte Hand-Typen: []" +warning.config.condition.on_cooldown.missing_id: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'id'-Argument für die 'on_cooldown'-Bedingung." +warning.config.structure.not_section: "Problem in Datei gefunden - Die Config '' wird als Config-Abschnitt erwartet, ist aber tatsächlich ein(e) ''." +warning.config.image.duplicate: "Problem in Datei gefunden - Doppeltes Image ''. Bitte prüfe, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.image.missing_height: "Problem in Datei gefunden - Beim Image '' fehlt das erforderliche 'height'-Argument." +warning.config.image.height_ascent_conflict: "Problem in Datei gefunden - Das Image '' verletzt die Bitmap-Image-Regel: 'height'-Argument '' sollte nicht niedriger sein als das 'ascent'-Argument ''." +warning.config.image.missing_file: "Problem in Datei gefunden - Beim Image '' fehlt das erforderliche 'file'-Argument." +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.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." +warning.config.recipe.duplicate: "Problem in Datei gefunden - Doppeltes Recipe ''. Bitte prüfe, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.recipe.missing_type: "Problem in Datei gefunden - Beim Recipe '' fehlt das erforderliche 'type'-Argument." +warning.config.recipe.invalid_type: "Problem in Datei gefunden - Das Recipe '' verwendet einen ungültigen Recipe-Typ ''." +warning.config.recipe.invalid_ingredient: "Problem in Datei gefunden - Das Recipe '' verwendet eine ungültige Zutat (ingredient) ''." +warning.config.recipe.invalid_result: "Problem in Datei gefunden - Das Recipe '' verwendet ein ungültiges Ergebnis (result) ''." +warning.config.recipe.missing_ingredient: "Problem in Datei gefunden - Beim Cooking-Recipe '' fehlt das erforderliche 'ingredient'-Argument." +warning.config.recipe.missing_result: "Problem in Datei gefunden - Beim Recipe '' fehlt das erforderliche 'result'-Argument." +warning.config.recipe.result.missing_id: "Problem in Datei gefunden - Beim Recipe '' fehlt das erforderliche Argument 'id' für das Recipe-Ergebnis." +warning.config.recipe.crafting.invalid_category: "Problem in Datei gefunden - Das Crafting-Recipe '' verwendet eine ungültige Kategorie ''. Erlaubte Kategorien: []." +warning.config.recipe.cooking.invalid_category: "Problem in Datei gefunden - Das Cooking-Recipe '' verwendet eine ungültige Kategorie ''. Erlaubte Kategorien: []." +warning.config.recipe.shaped.missing_pattern: "Problem in Datei gefunden - Beim Shaped-Recipe '' fehlt das erforderliche Argument 'pattern'." +warning.config.recipe.shaped.invalid_pattern: "Problem in Datei gefunden - Das Shaped-Recipe '' verwendet ein ungültiges Pattern ''." +warning.config.recipe.shaped.invalid_symbol: "Problem in Datei gefunden - Das Shaped-Recipe '' verwendet ein ungültiges Symbol '' im Pattern." +warning.config.recipe.smithing_transform.post_processor.missing_type: "Problem in Datei gefunden - Beim Smithing-Transform-Recipe '' fehlt das erforderliche Argument 'type' für einen der Post-Processors." +warning.config.recipe.smithing_transform.post_processor.invalid_type: "Problem in Datei gefunden - Das Smithing-Transform-Recipe '' verwendet einen ungültigen Post-Processor-Typ ''." +warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components: "Problem in Datei gefunden - Beim Smithing-Transform-Recipe '' fehlt das erforderliche Argument 'components' für Post-Processors 'keep_components'." +warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "Problem in Datei gefunden - Beim Smithing-Transform-Recipe '' fehlt das erforderliche Argument 'tags' für Post-Processors 'keep_tags'." +warning.config.recipe.smithing_transform.missing_base: "Problem in Datei gefunden - Beim Smithing-Transform-Recipe '' fehlt das erforderliche 'base'-Argument." +warning.config.recipe.smithing_trim.missing_base: "Problem in Datei gefunden - Beim Smithing-Trim-Recipe '' fehlt das erforderliche 'base'-Argument." +warning.config.recipe.smithing_trim.missing_template_type: "Problem in Datei gefunden - Beim Smithing-Trim-Recipe '' fehlt das erforderliche 'template-type'-Argument." +warning.config.recipe.smithing_trim.missing_addition: "Problem in Datei gefunden - Beim Smithing-Trim-Recipe '' fehlt das erforderliche 'addition'-Argument." +warning.config.recipe.smithing_trim.missing_pattern: "Problem in Datei gefunden - Beim Smithing-Trim-Recipe '' fehlt das erforderliche 'pattern'-Argument." +warning.config.recipe.brewing.missing_container: "Problem in Datei gefunden - Beim Brewing-Recipe '' fehlt das erforderliche 'container'-Argument." +warning.config.recipe.brewing.missing_ingredient: "Problem in Datei gefunden - Beim Brewing-Recipe '' fehlt das erforderliche 'ingredient'-Argument." +warning.config.recipe.result.post_processor.missing_type: "Problem in Datei gefunden - Beim Recipe '' fehlt das erforderliche 'type'-Argument für Result-Post-Processors." +warning.config.recipe.result.post_processor.invalid_type: "Problem in Datei gefunden - Das Recipe '' verwendet einen ungültigen Result-Post-Processor-Typ ''." +warning.config.i18n.unknown_locale: "Problem in Datei gefunden - Unbekannte Locale ''." +warning.config.template.duplicate: "Problem in Datei gefunden - Doppeltes Template ''. Bitte prüfe, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.template.invalid: "Problem in Datei gefunden - Die Config '' verwendet ein ungültiges Template ''." +warning.config.template.argument.self_increase_int.invalid_range: "Problem in Datei gefunden - Das Template '' verwendet ein 'from' '', das größer ist als 'to' '' im 'self_increase_int'-Argument." +warning.config.template.argument.list.invalid_type: "Problem in Datei gefunden - Das Template '' verwendet ein 'list'-Argument, das eine 'List' als Argument erwartet, während das eingegebene Argument ein(e) '' ist." +warning.config.template.argument.missing_value: "Problem in Datei gefunden - Bei der Config '' fehlt das Template-Argument für ''. Bitte verwende die 'arguments'-Option zur Konfiguration oder setze einen Standardwert für diesen Parameter." +warning.config.vanilla_loot.missing_type: "Problem in Datei gefunden - Beim Vanilla-Loot '' fehlt das erforderliche 'type'-Argument." +warning.config.vanilla_loot.invalid_type: "Problem in Datei gefunden - Der Vanilla-Loot '' verwendet einen ungültigen Typ ''. Erlaubte Typen: []." +warning.config.vanilla_loot.block.invalid_target: "Problem in Datei gefunden - Ungültiges Block-Target '' im Vanilla-Loot ''." +warning.config.sound.duplicate: "Problem in Datei gefunden - Doppelter Sound ''. Bitte prüfe, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.sound.missing_sounds: "Problem in Datei gefunden - Beim Sound '' fehlt das erforderliche 'sounds'-Argument." +warning.config.sound.missing_name: "Problem in Datei gefunden - Beim Sound '' fehlt das erforderliche 'name'-Argument." +warning.config.jukebox_song.duplicate: "Problem in Datei gefunden - Doppelter Jukebox-Song ''. Bitte prüfe, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.jukebox_song.missing_sound: "Problem in Datei gefunden - Beim Jukebox-Song '' fehlt das erforderliche 'sound'-Argument." +warning.config.furniture.duplicate: "Problem in Datei gefunden - Doppeltes Furniture ''. Bitte prüfe, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.furniture.missing_placement: "Problem in Datei gefunden - Beim Furniture '' fehlt das erforderliche 'placement'-Argument." +warning.config.furniture.element.missing_item: "Problem in Datei gefunden - Beim Furniture '' fehlt das erforderliche 'item'-Argument für eines seiner Elemente." +warning.config.furniture.settings.unknown: "Problem in Datei gefunden - Das Furniture '' verwendet einen unbekannten Einstellungs-Typ ''." +warning.config.furniture.hitbox.invalid_type: "Problem in Datei gefunden - Das Furniture '' verwendet einen ungültigen Hitbox-Typ ''." +warning.config.furniture.hitbox.custom.invalid_entity: "Problem in Datei gefunden - Das Furniture '' verwendet eine benutzerdefinierte Hitbox mit ungültigem Entity-Typ ''." +warning.config.item.duplicate: "Problem in Datei gefunden - Doppeltes Item ''. Bitte prüfe, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.item.settings.unknown: "Problem in Datei gefunden - Das Item '' verwendet einen unbekannten Einstellungs-Typ ''." +warning.config.item.settings.invulnerable.invalid_damage_source: "Problem in Datei gefunden - Das Item '' verwendet eine unbekannte Schadensquelle (damage source) ''. Erlaubte Quellen: []." +warning.config.item.settings.equipment.missing_asset_id: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'asset-id'-Argument für 'equipment'-Einstellungen." +warning.config.item.settings.equipment.invalid_asset_id: "Problem in Datei gefunden - Das Item '' verwendet ein ungültiges 'asset-id'-Argument für 'equipment'-Einstellungen. Dies könnte daran liegen, dass du diese Equipment-Konfiguration nicht erstellt oder die Asset-ID falsch geschrieben hast." +warning.config.item.settings.projectile.missing_item: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'item'-Argument für 'projectile'-Einstellungen." +warning.config.item.data.attribute_modifiers.missing_type: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'type'-Argument für 'attribute-modifiers'-Daten." +warning.config.item.data.attribute_modifiers.missing_amount: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'amount'-Argument für 'attribute-modifiers'-Daten." +warning.config.item.data.attribute_modifiers.missing_operation: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'operation'-Argument für 'attribute-modifiers'-Daten." +warning.config.item.data.attribute_modifiers.display.missing_type: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'type'-Argument für 'attribute-modifiers' Display-Daten." +warning.config.item.data.attribute_modifiers.display.missing_value: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'value'-Argument für 'attribute-modifiers' Display-Daten." +warning.config.item.data.external.missing_source: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'source'-Argument für 'external'-Daten." +warning.config.item.data.external.missing_id: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'id'-Argument für 'external'-Daten." +warning.config.item.data.external.invalid_source: "Problem in Datei gefunden - Das Item '' verwendet eine ungültige Item-Quelle '' für 'external'-Daten." +warning.config.item.missing_material: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'material'-Argument." +warning.config.item.invalid_material: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Material-Typ ''." +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.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+." +warning.config.item.behavior.missing_type: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'type'-Argument für sein Item-Behavior." +warning.config.item.behavior.invalid_type: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Item-Behavior-Typ ''." +warning.config.item.behavior.block.missing_block: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'block'-Argument für das 'block_item'-Behavior." +warning.config.item.behavior.furniture.missing_furniture: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'furniture'-Argument für das 'furniture_item'-Behavior." +warning.config.item.behavior.liquid_collision.missing_block: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'block'-Argument für das 'liquid_collision_block_item'-Behavior." +warning.config.item.behavior.double_high.missing_block: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'block'-Argument für das 'double_high_block_item'-Behavior." +warning.config.item.legacy_model.missing_path: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'path'-Argument für legacy-model." +warning.config.item.legacy_model.overrides.missing_path: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'path'-Argument für legacy-model Overrides." +warning.config.item.legacy_model.overrides.missing_predicate: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'predicate'-Argument für legacy-model Overrides." +warning.config.item.legacy_model.cannot_convert: "Problem in Datei gefunden - Items ab 1.21.4+ können für Item '' nicht in das Legacy-Format konvertiert werden. Bitte erstelle manuell einen 'legacy-model'-Abschnitt für dieses Item." +warning.config.item.model.invalid_type: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Model-Typ ''." +warning.config.item.model.tint.missing_type: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'type'-Argument für Tint." +warning.config.item.model.tint.invalid_type: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Tint-Typ ''." +warning.config.item.model.tint.constant.missing_value: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'value'-Argument für den konstanten Tint." +warning.config.item.model.tint.grass.invalid_temp: "Problem in Datei gefunden - Das Item '' verwendet eine ungültige Temperatur '' für den Gras-Tint, die zwischen 0 und 1 liegen sollte." +warning.config.item.model.tint.grass.invalid_downfall: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Niederschlag (downfall) '' für den Gras-Tint, der zwischen 0 und 1 liegen sollte." +warning.config.item.model.tint.invalid_value: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Tint ''." +warning.config.item.model.base.missing_path: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'path'-Argument für das Model 'minecraft:model'." +warning.config.item.model.base.invalid_path: "Problem in Datei gefunden - Das Item '' hat ein ungültiges 'path'-Argument '' für das Model 'minecraft:model', das ungültige Zeichen enthält. Bitte lies https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.item.model.condition.missing_property: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'property'-Argument für das Model 'minecraft:condition'." +warning.config.item.model.condition.invalid_property: "Problem in Datei gefunden - Das Item '' verwendet eine ungültige Property '' für das Model 'minecraft:condition'." +warning.config.item.model.condition.missing_on_true: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'on-true'-Argument für das Model 'minecraft:condition'." +warning.config.item.model.condition.missing_on_false: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'on-false'-Argument für das Model 'minecraft:condition'." +warning.config.item.model.condition.keybind.missing_keybind: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'keybind'-Argument für die Property 'minecraft:keybind_down'." +warning.config.item.model.condition.has_component.missing_component: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'component'-Argument für die Property 'minecraft:has_component'." +warning.config.item.model.condition.component.missing_predicate: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'predicate'-Argument für die Property 'minecraft:component'." +warning.config.item.model.condition.component.missing_value: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'value'-Argument für die Property 'minecraft:component'." +warning.config.item.model.composite.missing_models: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'models'-Argument für das 'minecraft:composite'-Model." +warning.config.item.model.range_dispatch.missing_property: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'property'-Argument für das Model 'minecraft:range_dispatch'." +warning.config.item.model.range_dispatch.invalid_property: "Problem in Datei gefunden - Das Item '' verwendet eine ungültige Property '' für das Model 'minecraft:range_dispatch'." +warning.config.item.model.range_dispatch.missing_entries: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'entries'-Argument für das Model 'minecraft:composite'." +warning.config.item.model.range_dispatch.entry.missing_model: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'model'-Argument für einen der Einträge im Model 'minecraft:composite'." +warning.config.item.model.range_dispatch.compass.missing_target: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'target'-Argument für die Property 'minecraft:compass'." +warning.config.item.model.range_dispatch.time.missing_source: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'source'-Argument für die Property 'minecraft:time'." +warning.config.item.model.select.missing_property: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'property'-Argument für das Model 'minecraft:select'." +warning.config.item.model.select.invalid_property: "Problem in Datei gefunden - Das Item '' verwendet eine ungültige Property '' für das Model 'minecraft:select'." +warning.config.item.model.select.missing_cases: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'cases'-Argument für das Model 'minecraft:select'." +warning.config.item.model.select.case.missing_when: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'when'-Argument für einen der Fälle im Model 'minecraft:select'." +warning.config.item.model.select.case.missing_model: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'model'-Argument für einen der Fälle im Model 'minecraft:select'." +warning.config.item.model.select.block_state.missing_property: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'block-state-property'-Argument für die Property 'minecraft:block_state'." +warning.config.item.model.select.local_time.missing_pattern: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'pattern'-Argument für die Property 'minecraft:local_time'." +warning.config.item.model.select.component.missing_component: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'component'-Argument für die Property 'minecraft:component'." +warning.config.item.model.special.missing_type: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'type'-Argument für das Model 'minecraft:special'." +warning.config.item.model.special.missing_path: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'path'-Argument für das Model 'minecraft:special'." +warning.config.item.model.special.invalid_path: "Problem in Datei gefunden - Das Item '' hat ein ungültiges 'path'-Argument '' für das Model 'minecraft:special', das ungültige Zeichen enthält. Bitte lies https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.item.model.special.invalid_type: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Typ '' für das Model 'minecraft:special'." +warning.config.item.model.special.banner.missing_color: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'color'-Argument für das Special-Model 'minecraft:banner'." +warning.config.item.model.special.bed.missing_texture: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'texture'-Argument für das Special-Model 'minecraft:bed'." +warning.config.item.model.special.sign.missing_wood_type: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'wood-type'-Argument für das Special-Model 'minecraft:hanging_sign'/'minecraft:standing_sign'." +warning.config.item.model.special.sign.missing_texture: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'texture'-Argument für das Special-Model 'minecraft:hanging_sign'/'minecraft:standing_sign'." +warning.config.item.model.special.chest.missing_texture: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'texture'-Argument für das Special-Model 'minecraft:chest'." +warning.config.item.model.special.chest.invalid_openness: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen 'openness'-Wert '' für das Special-Model 'minecraft:chest'. Gültiger Bereich '0~1.'" +warning.config.item.model.special.shulker_box.missing_texture: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'texture'-Argument für das Special-Model 'minecraft:shulker_box'." +warning.config.item.model.special.shulker_box.invalid_openness: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen 'openness'-Wert '' für das Special-Model 'minecraft:shulker_box'. Gültiger Bereich '0~1.'" +warning.config.item.model.special.head.missing_kind: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'kind'-Argument für das Special-Model 'minecraft:head'." +warning.config.item.updater.missing_type: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'type'-Argument für den Item-Updater." +warning.config.item.updater.invalid_type: "Problem in Datei gefunden - Das Item '' verwendet ein ungültiges 'type'-Argument '' für den Item-Updater." +warning.config.item.updater.transmute.missing_material: "Problem in Datei gefunden - Beim Item '' fehlt das erforderliche 'material'-Argument für den 'transmute'-Item-Updater." +warning.config.block.duplicate: "Problem in Datei gefunden - Doppelter Block ''. Bitte prüfe, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.block.missing_state: "Problem in Datei gefunden - Beim Block '' fehlt das erforderliche 'state'-Argument." +warning.config.block.state.property.missing_type: "Problem in Datei gefunden - Beim Block '' fehlt das erforderliche 'type'-Argument für die Property ''." +warning.config.block.state.property.invalid_type: "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.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.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." +warning.config.block.settings.unknown: "Problem in Datei gefunden - Der Block '' verwendet einen unbekannten Einstellungs-Typ ''." +warning.config.block.behavior.missing_type: "Problem in Datei gefunden - Beim Block '' fehlt das erforderliche 'type'-Argument für sein Block-Behavior." +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.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." +warning.config.block.behavior.sapling.missing_stage: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'stage'-Property für das 'sapling_block'-Behavior." +warning.config.block.behavior.sapling.missing_feature: "Problem in Datei gefunden - Beim Block '' fehlt das erforderliche 'feature'-Argument für das 'sapling_block'-Behavior." +warning.config.block.behavior.strippable.missing_stripped: "Problem in Datei gefunden - Beim Block '' fehlt das erforderliche 'stripped'-Argument für das 'strippable_block'-Behavior." +warning.config.block.behavior.door.missing_half: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'half'-Property für das 'door_block'-Behavior." +warning.config.block.behavior.door.missing_facing: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'facing'-Property für das 'door_block'-Behavior." +warning.config.block.behavior.door.missing_hinge: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'hinge'-Property für das 'door_block'-Behavior." +warning.config.block.behavior.door.missing_open: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'open'-Property für das 'door_block'-Behavior." +warning.config.block.behavior.door.missing_powered: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'powered'-Property für das 'door_block'-Behavior." +warning.config.block.behavior.trapdoor.missing_half: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'half'-Property für das 'trapdoor_block'-Behavior." +warning.config.block.behavior.trapdoor.missing_facing: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'facing'-Property für das 'trapdoor_block'-Behavior." +warning.config.block.behavior.trapdoor.missing_open: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'open'-Property für das 'trapdoor_block'-Behavior." +warning.config.block.behavior.trapdoor.missing_powered: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'powered'-Property für das 'trapdoor_block'-Behavior." +warning.config.block.behavior.stackable.missing_property: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche ''-Property für das 'stackable_block'-Behavior." +warning.config.block.behavior.stackable.missing_items: "Problem in Datei gefunden - Beim Block '' fehlt das erforderliche 'items'-Argument für das 'stackable_block'-Behavior." +warning.config.block.behavior.fence_gate.missing_facing: "Problem in Datei gefunden - Beim Block '' fehlt das erforderliche 'facing'-Argument für das 'fence_gate_block'-Behavior." +warning.config.block.behavior.fence_gate.missing_in_wall: "Problem in Datei gefunden - Beim Block '' fehlt das erforderliche 'in_wall'-Argument für das 'fence_gate_block'-Behavior." +warning.config.block.behavior.fence_gate.missing_open: "Problem in Datei gefunden - Beim Block '' fehlt das erforderliche 'powered'-Argument für das 'fence_gate_block'-Behavior." +warning.config.block.behavior.fence_gate.missing_powered: "Problem in Datei gefunden - Beim Block '' fehlt das erforderliche 'open'-Argument für das 'fence_gate_block'-Behavior." +warning.config.block.behavior.trapdoor.missing_type: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'type'-Property für das 'slab_block'-Behavior." +warning.config.block.behavior.stairs.missing_facing: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'facing'-Property für das 'stairs_block'-Behavior." +warning.config.block.behavior.stairs.missing_half: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'half'-Property für das 'stairs_block'-Behavior." +warning.config.block.behavior.stairs.missing_shape: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'shape'-Property für das 'stairs_block'-Behavior." +warning.config.block.behavior.pressure_plate.missing_powered: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'powered'-Property für das 'pressure_plate_block'-Behavior." +warning.config.block.behavior.grass.missing_feature: "Problem in Datei gefunden - Beim Block '' fehlt das erforderliche 'feature'-Argument für das 'grass_block'-Behavior." +warning.config.block.behavior.double_high.missing_half: "Problem in Datei gefunden - Beim Block '' fehlt die erforderliche 'half'-Property für das 'double_block'-Behavior." +warning.config.model.generation.missing_parent: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'parent'-Argument im 'generation'-Abschnitt." +warning.config.model.generation.invalid_display_position: "Problem in Datei gefunden - Die Config '' verwendet eine ungültige Display-Position '' im 'generation.display'-Abschnitt. Erlaubte Display-Positionen: []" +warning.config.model.generation.invalid_gui_light: "Problem in Datei gefunden - Die Config '' verwendet eine ungültige gui-light Option '' im 'generation'-Abschnitt. Erlaubte gui-light Optionen: []" +warning.config.model.generation.conflict: "Problem in Datei gefunden - Generierung des Models für '' fehlgeschlagen, da zwei oder mehr Konfigurationen versuchen, verschiedene JSON-Models mit demselben Pfad zu generieren: ''." +warning.config.model.generation.texture.invalid: "Problem in Datei gefunden - Die Config '' hat eine Textur '' mit dem Pfad '', der ungültige Zeichen enthält. Bitte lies https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.model.generation.parent.invalid: "Problem in Datei gefunden - Die Config '' hat ein parent-Argument '', das ungültige Zeichen enthält. Bitte lies https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.emoji.missing_keywords: "Problem in Datei gefunden - Beim Emoji '' fehlt das erforderliche 'keywords'-Argument." +warning.config.emoji.duplicate: "Problem in Datei gefunden - Doppeltes Emoji ''. Bitte prüfe, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." warning.config.emoji.invalid_image: "Problem in Datei gefunden - Das Emoji '' hat ein ungültiges 'image'-Argument ''." -warning.config.advancement.duplicate: "Problem in Datei gefunden - Duplizierter Fortschritt ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." -warning.config.loot_table.missing_pools: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, der das erforderliche 'pools'-Argument fehlt." -warning.config.loot_table.invalid_pools_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, 'pools' sollte eine String-/Kartenliste sein, aktueller Typ: ''." -warning.config.loot_table.invalid_conditions_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, 'conditions' sollte eine Kartenliste sein, aktueller Typ: ''." -warning.config.loot_table.invalid_functions_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, 'functions' sollte eine Kartenliste sein, aktueller Typ: ''." -warning.config.loot_table.invalid_entries_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, 'entries' sollte eine Kartenliste sein, aktueller Typ: ''." -warning.config.loot_table.function.missing_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, einer der Funktionen fehlt das erforderliche 'type'-Argument." -warning.config.loot_table.function.invalid_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, einer der Funktionen verwendet einen ungültigen Funktionstyp ''." -warning.config.loot_table.function.apply_bonus.missing_enchantment: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, der Funktion 'apply_bonus' fehlt das erforderliche 'enchantment'-Argument." -warning.config.loot_table.function.apply_bonus.missing_formula: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, der Funktion 'apply_bonus' fehlt das erforderliche 'formula'-Argument." -warning.config.loot_table.function.drop_exp.missing_count: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, der Funktion 'drop_exp' fehlt das erforderliche 'count'-Argument." -warning.config.loot_table.function.set_count.missing_count: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, der Funktion 'set_count' fehlt das erforderliche 'count'-Argument." -warning.config.loot_table.entry.missing_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, einem der Einträge fehlt das erforderliche 'type'-Argument." -warning.config.loot_table.entry.invalid_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, einem der Einträge verwendet einen ungültigen Eintragstyp ''." -warning.config.loot_table.entry.exp.missing_count: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, dem Eintrag 'exp' fehlt das erforderliche 'count'-Argument." -warning.config.loot_table.entry.item.missing_item: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, dem Eintrag 'item' fehlt das erforderliche 'item'-Argument." -warning.config.loot_table.condition.missing_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, einer der Bedingungen fehlt das erforderliche 'type'-Argument." -warning.config.loot_table.condition.invalid_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, einer der Bedingungen verwendet einen ungültigen Bedingungstyp ''." -warning.config.host.missing_type: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'type'-Argument für den Host." -warning.config.host.invalid_type: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Host-Typ '' ist ungültig. Bitte lesen Sie https://xiao-momi.github.io/craft-engine-wiki/getting_start/set_up_host." -warning.config.host.external.missing_url: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'url'-Argument für den externen Host." -warning.config.host.alist.missing_api_url: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'api-url'-Argument für den AList-Host." -warning.config.host.alist.missing_username: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'username'-Argument oder Umgebungsvariable 'CE_ALIST_USERNAME' für den AList-Host." -warning.config.host.alist.missing_password: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'password'-Argument oder Umgebungsvariable 'CE_ALIST_PASSWORD' für den AList-Host." -warning.config.host.alist.missing_upload_path: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'upload-path'-Argument für den AList-Host." -warning.config.host.dropbox.missing_app_key: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'app-key'-Argument oder Umgebungsvariable 'CE_DROPBOX_APP_KEY' für den Dropbox-Host." -warning.config.host.dropbox.missing_app_secret: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'app-secret'-Argument oder Umgebungsvariable 'CE_DROPBOX_APP_SECRET' für den Dropbox-Host." -warning.config.host.dropbox.missing_refresh_token: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'refresh-token'-Argument oder Umgebungsvariable 'CE_DROPBOX_REFRESH_TOKEN' für den Dropbox-Host." -warning.config.host.dropbox.missing_upload_path: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'upload-path'-Argument für den Dropbox-Host." -warning.config.host.lobfile.missing_api_key: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'api-key'-Argument für den Lobfile-Host." -warning.config.host.onedrive.missing_client_id: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'client-id'-Argument oder Umgebungsvariable 'CE_ONEDRIVE_CLIENT_ID' für den OneDrive-Host." -warning.config.host.onedrive.missing_client_secret: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'client-secret'-Argument oder Umgebungsvariable 'CE_ONEDRIVE_CLIENT_SECRET' für den OneDrive-Host." -warning.config.host.onedrive.missing_refresh_token: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'refresh-token'-Argument oder Umgebungsvariable 'CE_ONEDRIVE_REFRESH_TOKEN' für den OneDrive-Host." -warning.config.host.onedrive.missing_upload_path: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'upload-path'-Argument für den OneDrive-Host." -warning.config.host.s3.missing_endpoint: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'endpoint'-Argument für den S3-Host." -warning.config.host.s3.missing_bucket: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'bucket'-Argument für den S3-Host." -warning.config.host.s3.missing_access_key: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'access-key-id'-Argument oder Umgebungsvariable 'CE_S3_ACCESS_KEY_ID' für den S3-Host." -warning.config.host.s3.missing_secret: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'access-key-secret'-Argument oder Umgebungsvariable 'CE_S3_ACCESS_KEY_SECRET' für den S3-Host." -warning.config.host.s3.missing_upload_path: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'upload-path'-Argument für den S3-Host." -warning.config.host.self.missing_ip: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'ip'-Argument für den Selbst-Host." -warning.config.host.self.invalid_port: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Ungültiger Port '' für den Selbst-Host." -warning.config.host.self.invalid_url: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Ungültige URL '' für den Selbst-Host." -warning.config.host.gitlab.missing_url: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'gitlab-url'-Argument für den GitLab-Host." -warning.config.host.gitlab.missing_token: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'access-token'-Argument für den GitLab-Host." -warning.config.host.gitlab.missing_project: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'project-id'-Argument für den GitLab-Host." -warning.config.host.proxy.missing_host: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'host'-Argument für den Proxy." -warning.config.host.proxy.missing_port: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'port'-Argument für den Proxy." -warning.config.host.proxy.missing_scheme: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'scheme'-Argument für den Proxy." -warning.config.host.proxy.invalid: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Ungültiger Proxy ''." -warning.config.conflict_matcher.missing_type: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'type'-Argument für einen der Handler." -warning.config.conflict_matcher.invalid_type: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Einer der Begriffe verwendet den ungültigen Typ ''." -warning.config.conflict_matcher.exact.missing_path: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'path'-Argument für den 'exact'-Matcher." -warning.config.conflict_matcher.contains.missing_path: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'path'-Argument für den 'contains'-Matcher." -warning.config.conflict_matcher.filename.missing_name: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'path'-Argument für den 'filename'-Matcher." -warning.config.conflict_matcher.pattern.missing_pattern: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'pattern'-Argument für den 'pattern'-Matcher." -warning.config.conflict_matcher.parent_prefix.missing_prefix: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'prefix'-Argument für den 'parent_path_prefix'-Matcher." -warning.config.conflict_matcher.parent_suffix.missing_suffix: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'suffix'-Argument für den 'parent_path_suffix'-Matcher." -warning.config.conflict_matcher.inverted.missing_term: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'term'-Argument für den 'inverted'-Matcher." -warning.config.conflict_matcher.all_of.missing_terms: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'terms'-Argument für den 'all_of'-Matcher." -warning.config.conflict_matcher.any_of.missing_terms: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'terms'-Argument für den 'any_of'-Matcher." -warning.config.conflict_resolution.missing_type: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'type'-Argument für eine der Auflösungen." -warning.config.conflict_resolution.invalid_type: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Eine der Auflösungen verwendet den ungültigen Typ ''." -warning.config.event.missing_trigger: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'on'-Argument für Ereignisauslöser." -warning.config.event.invalid_trigger: "Problem in Datei gefunden - Die Konfiguration '' verwendet einen ungültigen Ereignisauslöser ''." -warning.config.event.condition.missing_type: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'type'-Argument für die Ereignisbedingung." -warning.config.event.condition.invalid_type: "Problem in Datei gefunden - Die Konfiguration '' verwendet ein ungültiges 'type'-Argument '' für die Ereignisbedingung." -warning.config.function.missing_type: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'type'-Argument für die Funktion." -warning.config.function.invalid_type: "Problem in Datei gefunden - Die Konfiguration '' verwendet einen ungültigen Funktionstyp ''." -warning.config.function.command.missing_command: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'command'-Argument für die 'command'-Funktion." -warning.config.function.actionbar.missing_actionbar: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'actionbar'-Argument für die 'actionbar'-Funktion." -warning.config.function.message.missing_message: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'message'-Argument für die 'message'-Funktion." -warning.config.function.open_window.missing_gui_type: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'gui-type'-Argument für die 'open_window'-Funktion." -warning.config.function.open_window.invalid_gui_type: "Problem in Datei gefunden - Die Konfiguration '' verwendet einen ungültigen GUI-Typ für die 'open_window'-Funktion. Erlaubte Typen: []." -warning.config.function.run.missing_functions: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'functions'-Argument für die 'run'-Funktion." -warning.config.function.place_block.missing_block_state: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'block-state'-Argument für die 'place_block'-Funktion." -warning.config.function.set_food.missing_food: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'food'-Argument für die 'set_food'-Funktion." -warning.config.function.set_saturation.missing_saturation: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'saturation'-Argument für die 'set_saturation'-Funktion." -warning.config.function.play_sound.missing_sound: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'sound'-Argument für die 'play_sound'-Funktion." -warning.config.function.particle.missing_particle: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'particle'-Argument für die 'particle'-Funktion." -warning.config.function.particle.missing_color: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'color'-Argument für die 'particle'-Funktion." -warning.config.function.particle.missing_from: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'from'-Argument für die 'particle'-Funktion." -warning.config.function.particle.missing_to: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'to'-Argument für die 'particle'-Funktion." -warning.config.function.particle.missing_item: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'item'-Argument für die 'particle'-Funktion." -warning.config.function.particle.missing_block_state: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'block-state'-Argument für die 'particle'-Funktion." -warning.config.function.leveler_exp.missing_count: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'count'-Argument für die 'leveler_exp'-Funktion." -warning.config.function.leveler_exp.missing_leveler: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'leveler'-Argument für die 'leveler_exp'-Funktion." -warning.config.function.leveler_exp.missing_plugin: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'plugin'-Argument für die 'leveler_exp'-Funktion." -warning.config.function.remove_potion_effect.missing_potion_effect: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'potion-effect'-Argument für die 'remove_potion_effect'-Funktion." -warning.config.function.potion_effect.missing_potion_effect: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'potion-effect'-Argument für die 'potion_effect'-Funktion." -warning.config.function.set_cooldown.missing_time: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'time'-Argument für die 'set_cooldown'-Funktion." -warning.config.function.set_cooldown.missing_id: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'id'-Argument für die 'set_cooldown'-Funktion." -warning.config.function.remove_cooldown.missing_id: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'id'-Argument für die 'remove_cooldown'-Funktion." -warning.config.selector.missing_type: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'type'-Argument für den Selektor." -warning.config.selector.invalid_type: "Problem in Datei gefunden - Die Konfiguration '' verwendet einen ungültigen Selektortyp ''." -warning.config.selector.invalid_target: "Problem in Datei gefunden - Die Konfiguration '' verwendet ein ungültiges Selektorziel ''." -warning.config.resource_pack.item_model.already_exist: "Fehler beim Generieren des Itemsmodells für '' da die Datei '' bereits existiert." -warning.config.resource_pack.model.generation.already_exist: "Fehler beim Generieren des Modells, da die Modelldatei '' bereits existiert." -warning.config.resource_pack.generation.missing_font_texture: "Schriftart '' fehlt die erforderliche Textur: ''" -warning.config.resource_pack.generation.missing_model_texture: "Modell '' fehlt Textur ''" -warning.config.resource_pack.generation.missing_item_model: "Item '' fehlt Modelldatei: ''" -warning.config.resource_pack.generation.missing_block_model: "Block '' fehlt Modelldatei: ''" -warning.config.resource_pack.generation.missing_parent_model: "Modell '' kann das Elternmodell nicht finden: ''" -warning.config.resource_pack.generation.malformatted_json: "Json-Datei '' ist fehlerhaft formatiert." -warning.config.resource_pack.invalid_overlay_format: "Problem in config.yml gefunden - Ungültiges Overlay-Format '' im Abschnitt 'resource-pack'. Unterstützte Overlays: []" -warning.config.type.map: "Fehler in der Datei - Das Laden von '' ist fehlgeschlagen: Kann den Typ '' nicht in den Typ 'Map' für die Option '' umwandeln." -warning.config.recipe.missing_pattern: "Fehler in der Datei - Dem geformten Rezept '' fehlt das erforderliche Argument 'pattern'." -warning.config.recipe.too_large_pattern: "Fehler in der Datei - Das geformte Rezept '' hat ein Muster mit mehr als 3 Zeilen oder Spalten." -warning.config.recipe.mismatched_shaped_pattern: "Fehler in der Datei - Das geformte Rezept '' hat ein fehlerhaftes Muster und Zutaten." -warning.config.recipe.mismatched_shulker_box: "Fehler in der Datei - Das Shulker-Box-Rezept '' hat ein fehlerhaftes Muster und Zutaten." -warning.config.recipe.mismatched_unshaped_pattern: "Fehler in der Datei - Das ungeformte Rezept '' hat ein fehlerhaftes Muster und Zutaten." -warning.config.recipe.missing_category: "Fehler in der Datei - Dem Rezept '' fehlt das erforderliche 'category'-Argument." -warning.config.recipe.missing_experience: "Fehler in der Datei - Dem Kochrezept '' fehlt das erforderliche 'experience'-Argument." -warning.config.recipe.missing_cooking_time: "Fehler in der Datei - Dem Kochrezept '' fehlt das erforderliche 'cooking-time'-Argument." -warning.config.recipe.missing_group: "Fehler in der Datei - Dem Rezept '' fehlt das erforderliche 'group'-Argument." -warning.config.recipe.missing_book: "Fehler in der Datei - Dem Rezept '' fehlt das erforderliche 'book'-Argument." -warning.config.loot_table.duplicate: "Fehler in der Datei - Doppelte Loot-Tabelle ''. Bitte prüfen Sie, ob in anderen Dateien die gleiche Konfiguration vorhanden ist." -warning.config.loot_table.missing_type: "Fehler in der Datei - Der Loot-Tabelle '' fehlt das erforderliche 'type'-Argument." -warning.config.loot_table.invalid_type: "Fehler in der Datei - Die Loot-Tabelle '' verwendet einen ungültigen Loot-Tabellen-Typ ''." -warning.config.loot_table.missing_rolls: "Fehler in der Datei - Der Loot-Tabelle '' fehlt das erforderliche 'rolls'-Argument." -warning.config.loot_table.missing_entries: "Fehler in der Datei - Der Loot-Tabelle '' fehlt das erforderliche 'entries'-Argument." -warning.config.loot_table.missing_entry_type: "Fehler in der Datei - Einer der Loot-Tabellen-Einträge in '' hat kein erforderliches 'type'-Argument." -warning.config.loot_table.invalid_entry_type: "Fehler in der Datei - Einer der Loot-Tabellen-Einträge in '' verwendet einen ungültigen Typ ''." -warning.config.loot_table.missing_entry_name: "Fehler in der Datei - Einer der Loot-Tabellen-Einträge in '' hat kein erforderliches 'name'-Argument." -warning.config.loot_table.missing_entry_weight: "Fehler in der Datei - Einer der Loot-Tabellen-Einträge in '' hat kein erforderliches 'weight'-Argument." -warning.config.loot_table.missing_function_type: "Fehler in der Datei - Eine der Loot-Tabellen-Funktionen in '' hat kein erforderliches 'type'-Argument." -warning.config.loot_table.invalid_function_type: "Fehler in der Datei - Eine der Loot-Tabellen-Funktionen in '' verwendet einen ungültigen Typ ''." -warning.config.loot_table.missing_condition_type: "Fehler in der Datei - Eine der Loot-Tabellen-Bedingungen in '' hat kein erforderliches 'type'-Argument." -warning.config.loot_table.invalid_condition_type: "Fehler in der Datei - Eine der Loot-Tabellen-Bedingungen in '' verwendet einen ungültigen Typ ''." -warning.config.resource_pack.generation.missing_equipment_texture: "Ausrüstung '' hat keine Textur ''" -warning.config.equipment.duplicate: "Problem in der Datei gefunden – Duplizierte Ausrüstung ''. Bitte prüfe, ob es dieselbe Konfiguration in anderen Dateien gibt." -warning.config.equipment.missing_type: "Problem in der Datei gefunden – Der Ausrüstung '' fehlt das erforderliche Argument 'type'." -warning.config.equipment.invalid_type: "Problem in der Datei gefunden – Die Ausrüstung '' verwendet ein ungültiges Argument 'type'." -warning.config.equipment.invalid_sacrificed_armor: "Problem in config.yml bei 'equipment.sacrificed-vanilla-armor' gefunden – Ungültiger Vanille-Rüstungstyp ''." \ No newline at end of file +warning.config.advancement.duplicate: "Problem in Datei gefunden - Doppelter Advancement ''. Bitte prüfe, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.loot_table.missing_pools: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Loot-Table, bei der das erforderliche 'pools'-Argument fehlt." +warning.config.loot_table.invalid_pools_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Loot-Table, 'pools' sollte eine String/Map-Liste sein, aktueller Typ: ''." +warning.config.loot_table.invalid_conditions_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Loot-Table, 'conditions' sollte eine Map-Liste sein, aktueller Typ: ''." +warning.config.loot_table.invalid_functions_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Loot-Table, 'functions' sollte eine Map-Liste sein, aktueller Typ: ''." +warning.config.loot_table.invalid_entries_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Loot-Table, 'entries' sollte eine Map-Liste sein, aktueller Typ: ''." +warning.config.loot_table.function.missing_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Loot-Table, bei einer der Funktionen fehlt das erforderliche 'type'-Argument." +warning.config.loot_table.function.invalid_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Loot-Table, eine der Funktionen verwendet einen ungültigen Funktionstyp ''." +warning.config.loot_table.function.apply_bonus.missing_enchantment: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Loot-Table, bei der Funktion 'apply_bonus' fehlt das erforderliche 'enchantment'-Argument." +warning.config.loot_table.function.apply_bonus.missing_formula: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Loot-Table, bei der Funktion 'apply_bonus' fehlt das erforderliche 'formula'-Argument." +warning.config.loot_table.function.drop_exp.missing_count: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Loot-Table, bei der Funktion 'drop_exp' fehlt das erforderliche 'count'-Argument." +warning.config.loot_table.function.set_count.missing_count: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Loot-Table, bei der Funktion 'set_count' fehlt das erforderliche 'count'-Argument." +warning.config.loot_table.entry.missing_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Loot-Table, bei einem der Einträge fehlt das erforderliche 'type'-Argument." +warning.config.loot_table.entry.invalid_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Loot-Table, einer der Einträge verwendet einen ungültigen Eintragstyp ''." +warning.config.loot_table.entry.exp.missing_count: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Loot-Table, beim Eintrag 'exp' fehlt das erforderliche 'count'-Argument." +warning.config.loot_table.entry.item.missing_item: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Loot-Table, beim Eintrag 'item' fehlt das erforderliche 'item'-Argument." +warning.config.loot_table.condition.missing_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Loot-Table, bei einer der Bedingungen fehlt das erforderliche 'type'-Argument." +warning.config.loot_table.condition.invalid_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Loot-Table, eine der Bedingungen verwendet einen ungültigen Bedingungstyp ''." +warning.config.host.missing_type: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'type'-Argument für Host." +warning.config.host.invalid_type: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Host-Typ '' ist ungültig. Bitte lies https://xiao-momi.github.io/craft-engine-wiki/getting_start/set_up_host." +warning.config.host.external.missing_url: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'url'-Argument für externen Host." +warning.config.host.alist.missing_api_url: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'api-url'-Argument für alist Host." +warning.config.host.alist.missing_username: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'username'-Argument oder Umgebungsvariable 'CE_ALIST_USERNAME' für alist Host." +warning.config.host.alist.missing_password: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'password'-Argument oder Umgebungsvariable 'CE_ALIST_PASSWORD' für alist Host." +warning.config.host.alist.missing_upload_path: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'upload-path'-Argument für alist Host." +warning.config.host.dropbox.missing_app_key: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'app-key'-Argument oder Umgebungsvariable 'CE_DROPBOX_APP_KEY' für dropbox Host." +warning.config.host.dropbox.missing_app_secret: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'app-secret'-Argument oder Umgebungsvariable 'CE_DROPBOX_APP_SECRET' für dropbox Host." +warning.config.host.dropbox.missing_refresh_token: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'refresh-token'-Argument oder Umgebungsvariable 'CE_DROPBOX_REFRESH_TOKEN' für dropbox Host." +warning.config.host.dropbox.missing_upload_path: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'upload-path'-Argument für dropbox Host." +warning.config.host.lobfile.missing_api_key: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'api-key'-Argument für lobfile Host." +warning.config.host.onedrive.missing_client_id: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'client-id'-Argument oder Umgebungsvariable 'CE_ONEDRIVE_CLIENT_ID' für onedrive Host." +warning.config.host.onedrive.missing_client_secret: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'client-secret'-Argument oder Umgebungsvariable 'CE_ONEDRIVE_CLIENT_SECRET' für onedrive Host." +warning.config.host.onedrive.missing_refresh_token: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'refresh-token'-Argument oder Umgebungsvariable 'CE_ONEDRIVE_REFRESH_TOKEN' für onedrive Host." +warning.config.host.onedrive.missing_upload_path: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'upload-path'-Argument für onedrive Host." +warning.config.host.s3.missing_endpoint: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'endpoint'-Argument für s3 Host." +warning.config.host.s3.missing_bucket: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'bucket'-Argument für s3 Host." +warning.config.host.s3.missing_access_key: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'access-key-id'-Argument oder Umgebungsvariable 'CE_S3_ACCESS_KEY_ID' für s3 Host." +warning.config.host.s3.missing_secret: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'access-key-secret'-Argument oder Umgebungsvariable 'CE_S3_ACCESS_KEY_SECRET' für s3 Host." +warning.config.host.s3.missing_upload_path: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'upload-path'-Argument für s3 Host." +warning.config.host.self.missing_ip: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'ip'-Argument für self Host." +warning.config.host.self.invalid_port: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Ungültiger Port '' für self Host." +warning.config.host.self.invalid_url: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Ungültige URL '' für self Host." +warning.config.host.gitlab.missing_url: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'gitlab-url'-Argument für gitlab Host." +warning.config.host.gitlab.missing_token: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'access-token'-Argument für gitlab Host." +warning.config.host.gitlab.missing_project: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'project-id'-Argument für gitlab Host." +warning.config.host.proxy.missing_host: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'host'-Argument für Proxy." +warning.config.host.proxy.missing_port: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'port'-Argument für Proxy." +warning.config.host.proxy.missing_scheme: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'scheme'-Argument für Proxy." +warning.config.host.proxy.invalid: "Problem in config.yml bei 'resource-pack.delivery.hosting' gefunden - Ungültiger Proxy ''." +warning.config.conflict_matcher.missing_type: "Problem in config.yml bei 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'type'-Argument für einen der Handler." +warning.config.conflict_matcher.invalid_type: "Problem in config.yml bei 'resource-pack.duplicated-files-handler' gefunden - Einer der Begriffe verwendet den ungültigen Typ ''." +warning.config.conflict_matcher.exact.missing_path: "Problem in config.yml bei 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'path'-Argument für 'exact'-Matcher." +warning.config.conflict_matcher.contains.missing_path: "Problem in config.yml bei 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'path'-Argument für 'contains'-Matcher." +warning.config.conflict_matcher.filename.missing_name: "Problem in config.yml bei 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'path'-Argument für 'filename'-Matcher." +warning.config.conflict_matcher.pattern.missing_pattern: "Problem in config.yml bei 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'pattern'-Argument für 'pattern'-Matcher." +warning.config.conflict_matcher.parent_prefix.missing_prefix: "Problem in config.yml bei 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'prefix'-Argument für 'parent_path_prefix'-Matcher." +warning.config.conflict_matcher.parent_suffix.missing_suffix: "Problem in config.yml bei 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'suffix'-Argument für 'parent_path_suffix'-Matcher." +warning.config.conflict_matcher.inverted.missing_term: "Problem in config.yml bei 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'term'-Argument für 'inverted'-Matcher." +warning.config.conflict_matcher.all_of.missing_terms: "Problem in config.yml bei 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'terms'-Argument für 'all_of'-Matcher." +warning.config.conflict_matcher.any_of.missing_terms: "Problem in config.yml bei 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'terms'-Argument für 'any_of'-Matcher." +warning.config.conflict_resolution.missing_type: "Problem in config.yml bei 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'type'-Argument für eine der Auflösungen." +warning.config.conflict_resolution.invalid_type: "Problem in config.yml bei 'resource-pack.duplicated-files-handler' gefunden - Eine der Auflösungen verwendet den ungültigen Typ ''." +warning.config.event.missing_trigger: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'on'-Argument für Event-Trigger." +warning.config.event.invalid_trigger: "Problem in Datei gefunden - Die Config '' verwendet einen ungültigen Event-Trigger ''." +warning.config.event.condition.missing_type: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'type'-Argument für die Event-Bedingung." +warning.config.event.condition.invalid_type: "Problem in Datei gefunden - Die Config '' verwendet ein ungültiges 'type'-Argument '' für die Event-Bedingung." +warning.config.function.missing_type: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'type'-Argument für die Funktion." +warning.config.function.invalid_type: "Problem in Datei gefunden - Die Config '' verwendet einen ungültigen Funktionstyp ''." +warning.config.function.command.missing_command: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'command'-Argument für die 'command'-Funktion." +warning.config.function.actionbar.missing_actionbar: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'actionbar'-Argument für die 'actionbar'-Funktion." +warning.config.function.message.missing_message: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'message'-Argument für die 'message'-Funktion." +warning.config.function.open_window.missing_gui_type: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'gui-type'-Argument für die 'open_window'-Funktion." +warning.config.function.open_window.invalid_gui_type: "Problem in Datei gefunden - Die Config '' verwendet einen ungültigen GUI-Typ für die 'open_window'-Funktion. Erlaubte Typen: []." +warning.config.function.run.missing_functions: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'functions'-Argument für die 'run'-Funktion." +warning.config.function.place_block.missing_block_state: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'block-state'-Argument für die 'place_block'-Funktion." +warning.config.function.set_food.missing_food: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'food'-Argument für die 'set_food'-Funktion." +warning.config.function.set_saturation.missing_saturation: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'saturation'-Argument für die 'set_saturation'-Funktion." +warning.config.function.play_sound.missing_sound: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'sound'-Argument für die 'play_sound'-Funktion." +warning.config.function.particle.missing_particle: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'particle'-Argument für die 'particle'-Funktion." +warning.config.function.particle.missing_color: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'color'-Argument für die 'particle'-Funktion." +warning.config.function.particle.missing_from: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'from'-Argument für die 'particle'-Funktion." +warning.config.function.particle.missing_to: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'to'-Argument für die 'particle'-Funktion." +warning.config.function.particle.missing_item: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'item'-Argument für die 'particle'-Funktion." +warning.config.function.particle.missing_block_state: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'block-state'-Argument für die 'particle'-Funktion." +warning.config.function.leveler_exp.missing_count: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'count'-Argument für die 'leveler_exp'-Funktion." +warning.config.function.leveler_exp.missing_leveler: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'leveler'-Argument für die 'leveler_exp'-Funktion." +warning.config.function.leveler_exp.missing_plugin: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'plugin'-Argument für die 'leveler_exp'-Funktion." +warning.config.function.remove_potion_effect.missing_potion_effect: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'potion-effect'-Argument für die 'remove_potion_effect'-Funktion." +warning.config.function.potion_effect.missing_potion_effect: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'potion-effect'-Argument für die 'potion_effect'-Funktion." +warning.config.function.set_cooldown.missing_time: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'time'-Argument für die 'set_cooldown'-Funktion." +warning.config.function.set_cooldown.missing_id: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'id'-Argument für die 'set_cooldown'-Funktion." +warning.config.function.remove_cooldown.missing_id: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'id'-Argument für die 'remove_cooldown'-Funktion." +warning.config.function.mythic_mobs_skill.missing_skill: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'skill'-Argument für die 'mythic_mobs_skill'-Funktion." +warning.config.function.spawn_furniture.missing_furniture_id: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'furniture-id'-Argument für die 'spawn_furniture'-Funktion." +warning.config.function.replace_furniture.missing_furniture_id: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'furniture-id'-Argument für die 'replace_furniture'-Funktion." +warning.config.selector.missing_type: "Problem in Datei gefunden - Bei der Config '' fehlt das erforderliche 'type'-Argument für den Selector." +warning.config.selector.invalid_type: "Problem in Datei gefunden - Die Config '' verwendet einen ungültigen Selector-Typ ''." +warning.config.selector.invalid_target: "Problem in Datei gefunden - Die Config '' verwendet ein ungültiges Selector-Target ''." +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.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: ''" +warning.config.resource_pack.generation.missing_parent_model: "Model '' kann das Parent-Model nicht finden: ''" +warning.config.resource_pack.generation.malformatted_json: "JSON-Datei '' ist fehlerhaft formatiert." +warning.config.resource_pack.generation.missing_equipment_texture: "Beim Equipment '' fehlt die Textur ''" +warning.config.resource_pack.invalid_overlay_format: "Problem in config.yml bei 'resource-pack.overlay-format' gefunden - Ungültiges Overlay-Format ''. Das Overlay-Format muss den Platzhalter '{version}' enthalten." +warning.config.equipment.duplicate: "Problem in Datei gefunden - Doppeltes Equipment ''. Bitte prüfe, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.equipment.missing_type: "Problem in Datei gefunden - Beim Equipment '' fehlt das erforderliche 'type'-Argument." +warning.config.equipment.invalid_type: "Problem in Datei gefunden - Das Equipment '' verwendet ein ungültiges 'type'-Argument." +warning.config.equipment.invalid_sacrificed_armor: "Problem in config.yml bei 'equipment.sacrificed-vanilla-armor' gefunden - Ungültiger Vanilla-Rüstungstyp ''." \ No newline at end of file diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index c15db1c61..4fa46057b 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." @@ -83,6 +85,8 @@ warning.config.number.fixed.invalid_value: "Issue found in file warning.config.number.expression.missing_expression: "Issue found in file - The config '' is missing the required 'expression' argument for 'expression' number." warning.config.number.uniform.missing_min: "Issue found in file - The config '' is missing the required 'min' argument for 'uniform' number." 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.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." @@ -92,6 +96,8 @@ warning.config.condition.inverted.invalid_term_type: "Issue found in fil warning.config.condition.enchantment.missing_predicate: "Issue found in file - The config '' is missing the required 'predicate' argument for 'enchantment' condition." warning.config.condition.enchantment.invalid_predicate: "Issue found in file - The config '' is using an invalid enchantment 'predicate' argument ''." warning.config.condition.match_block_property.missing_properties: "Issue found in file - The config '' is missing the required 'properties' argument for 'match_block_property' condition." +warning.config.condition.match_block.missing_id: "Issue found in file - The config '' is missing the required 'id' argument for 'match_block' condition." +warning.config.condition.match_entity.missing_id: "Issue found in file - The config '' is missing the required 'id' argument for 'match_entity' condition." warning.config.condition.match_item.missing_id: "Issue found in file - The config '' is missing the required 'id' argument for 'match_item' condition." warning.config.condition.table_bonus.missing_enchantment: "Issue found in file - The config '' is missing the required 'enchantment' argument for 'table_bonus' condition." warning.config.condition.table_bonus.missing_chances: "Issue found in file - The config '' is missing the required 'chances' argument for 'table_bonus' condition." @@ -191,6 +197,7 @@ warning.config.item.missing_model: "Issue found in file - The it 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." @@ -257,13 +264,18 @@ warning.config.block.state.missing_state: "Issue found in file - 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.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.missing_appearance: "Issue found in file - The block '' is missing the required 'appearance' argument for variant ''." 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_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.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.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." @@ -273,10 +285,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." @@ -291,21 +305,26 @@ 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.trapdoor.missing_type: "Issue found in file - The block '' is missing the required 'type' property for 'slab_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' argument for 'change_over_time_block' behavior." +warning.config.block.behavior.surface_attached.missing_facing: "Issue found in file - The block '' is missing the required 'facing' property for 'surface_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.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: []" warning.config.model.generation.invalid_gui_light: "Issue found in file - The config '' is using an invalid gui-light option '' in 'generation' section. Allowed gui light options: []" -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.texture.invalid: "Issue found in file - The config '' has a texture '' with path '' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." warning.config.model.generation.parent.invalid: "Issue found in file - The config '' has a parent argument '' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." warning.config.emoji.missing_keywords: "Issue found in file - The emoji '' is missing the required 'keywords' argument." @@ -383,7 +402,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." @@ -406,19 +425,27 @@ 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 ''." warning.config.resource_pack.item_model.already_exist: "Failed to generate item model for '' because the file '' already exists." warning.config.resource_pack.model.generation.already_exist: "Failed to generate model because the model file '' already exists." +warning.config.resource_pack.generation.malformatted_json: "Json file '' is malformatted." warning.config.resource_pack.generation.missing_font_texture: "Font '' is missing required 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.missing_model_texture: "Model '' is missing texture ''" warning.config.resource_pack.generation.missing_item_model: "Item '' is missing model file: ''" warning.config.resource_pack.generation.missing_block_model: "Block state '' is missing model file: ''" warning.config.resource_pack.generation.missing_parent_model: "Model '' cannot find parent model: ''" -warning.config.resource_pack.generation.malformatted_json: "Json file '' is malformatted." 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.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..77b0cc709 100644 --- a/common-files/src/main/resources/translations/es.yml +++ b/common-files/src/main/resources/translations/es.yml @@ -195,7 +195,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..209c6cf28 100644 --- a/common-files/src/main/resources/translations/ru_ru.yml +++ b/common-files/src/main/resources/translations/ru_ru.yml @@ -245,7 +245,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." diff --git a/common-files/src/main/resources/translations/tr.yml b/common-files/src/main/resources/translations/tr.yml index 0704fb7f2..d1cff3a7c 100644 --- a/common-files/src/main/resources/translations/tr.yml +++ b/common-files/src/main/resources/translations/tr.yml @@ -193,7 +193,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 348ab6b22..8a5413621 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' 参数" @@ -83,6 +85,8 @@ warning.config.number.fixed.invalid_value: "在文件 发现问 warning.config.number.expression.missing_expression: "在文件 发现问题 - 配置项 '' 缺少 'expression' 数字类型所需的 'expression' 参数" warning.config.number.uniform.missing_min: "在文件 发现问题 - 配置项 '' 缺少 'uniform' 数字类型所需的 '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.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' 参数" @@ -92,6 +96,8 @@ warning.config.condition.inverted.invalid_term_type: "在文件 warning.config.condition.enchantment.missing_predicate: "在文件 发现问题 - 配置项 '' 缺少 'enchantment' 条件所需的 'predicate' 参数" warning.config.condition.enchantment.invalid_predicate: "在文件 发现问题 - 配置项 '' 使用了无效的附魔 'predicate' 参数 ''" warning.config.condition.match_block_property.missing_properties: "在文件 发现问题 - 配置项 '' 缺少 'match_block_property' 条件所需的 'properties' 参数" +warning.config.condition.match_block.missing_id: "在文件 发现问题 - 配置项 '' 缺少 'match_block' 条件所需的 'id' 参数" +warning.config.condition.match_entity.missing_id: "在文件 发现问题 - 配置项 '' 缺少 'match_entity' 条件所需的 'id' 参数" warning.config.condition.match_item.missing_id: "在文件 发现问题 - 配置项 '' 缺少 'match_item' 条件所需的 'id' 参数" warning.config.condition.table_bonus.missing_enchantment: "在文件 发现问题 - 配置项 '' 缺少 'table_bonus' 条件所需的 'enchantment' 参数" warning.config.condition.table_bonus.missing_chances: "在文件 发现问题 - 配置项 '' 缺少 'table_bonus' 条件所需的 'chances' 参数" @@ -263,7 +269,7 @@ warning.config.block.state.invalid_vanilla: "在文件 发现问 warning.config.block.state.unavailable_vanilla: "在文件 发现问题 - 方块 '' 使用了不可用的原版方块状态 '' 请在 mappings.yml 中释放该状态" warning.config.block.state.invalid_vanilla_id: "在文件 发现问题 - 方块 '' 使用的原版方块状态 '' 超出可用槽位范围 '0~'" warning.config.block.state.conflict: "在文件 发现问题 - 方块 '' 使用的原版方块状态 '' 已被 '' 占用" -warning.config.block.state.bind_failed: "在文件 发现问题 - 方块 '' 无法为 '' 绑定真实方块状态 因该状态已被 '' 占用" +warning.config.block.state.bind_failed: "在文件 发现问题 - 方块 '' 无法为 '' 绑定真实方块状态 '' 因该状态已被 '' 占用" warning.config.block.state.invalid_real_id: "在文件 发现问题 - 方块 '' 使用的真实方块状态 '' 超出可用槽位范围 '0~' 如果槽位已用尽 请在 additional-real-blocks.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" @@ -273,10 +279,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' 参数" @@ -299,9 +307,14 @@ 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.surface_attached.missing_facing: "在文件 发现问题 - 方块 '' 的 'surface_attached_block' 行为缺少必需的 'facing' 属性" +warning.config.block.behavior.wall_torch_particle.missing_facing: "在文件 发现问题 - 配置项 '' 的 'wall_torch_particle_block' 行为缺少必需的 'facing' 属性" warning.config.model.generation.missing_parent: "在文件 发现问题 - 配置项 '' 的 'generation' 段落缺少必需的 'parent' 参数" warning.config.model.generation.conflict: "在文件 发现问题 - 无法为 '' 生成模型 存在多个配置尝试使用相同路径 '' 生成不同的 JSON 模型" warning.config.model.generation.invalid_display_position: "在文件 发现问题 - 配置项 '' 在 'generation.display' 区域使用了无效的 display 位置类型 ''. 可用展示类型: []" @@ -411,14 +424,14 @@ warning.config.selector.invalid_type: "在文件 中发现问题 warning.config.selector.invalid_target: "在文件 中发现问题 - 配置项 '' 使用了无效的选择器目标 ''" warning.config.resource_pack.item_model.already_exist: "无法为 '' 生成物品模型,因为文件 '' 已存在" warning.config.resource_pack.model.generation.already_exist: "无法生成模型,因为模型文件 '' 已存在" +warning.config.resource_pack.generation.malformatted_json: "Json文件 '' 格式错误" warning.config.resource_pack.generation.missing_font_texture: "字体''缺少必要纹理: ''" warning.config.resource_pack.generation.missing_model_texture: "模型''缺少纹理''" -warning.config.resource_pack.generation.texture_not_in_atlas: "纹理''不在图集内. 你需要将纹理路径或文件夹前缀添加到图集内,或者启用 config.yml 中的 'obfuscation' 选项" warning.config.resource_pack.generation.missing_item_model: "物品''缺少模型文件: ''" warning.config.resource_pack.generation.missing_block_model: "方块状态''缺少模型文件: ''" warning.config.resource_pack.generation.missing_parent_model: "模型''找不到父级模型文件: ''" -warning.config.resource_pack.generation.malformatted_json: "Json文件 '' 格式错误" warning.config.resource_pack.generation.missing_equipment_texture: "装备 '' 缺少纹理 ''" +warning.config.resource_pack.generation.texture_not_in_atlas: "纹理''不在图集内. 你需要将纹理路径或文件夹前缀添加到图集内,或者启用 config.yml 中的 'obfuscation' 选项" 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 df535d1cd..0694d2128 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 @@ -65,6 +65,8 @@ dependencies { compileOnly("com.mojang:brigadier:${rootProject.properties["mojang_brigadier_version"]}") // authlib compileOnly("com.mojang:authlib:${rootProject.properties["authlib_version"]}") + // concurrentutil + compileOnly("ca.spottedleaf:concurrentutil:${rootProject.properties["concurrent_util_version"]}") } java { @@ -99,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/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/advancement/network/AdvancementDisplay.java b/core/src/main/java/net/momirealms/craftengine/core/advancement/network/AdvancementDisplay.java index 93924daf9..b2e22857d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/advancement/network/AdvancementDisplay.java +++ b/core/src/main/java/net/momirealms/craftengine/core/advancement/network/AdvancementDisplay.java @@ -6,11 +6,11 @@ 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.config.Config; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.FriendlyByteBuf; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.sparrow.nbt.Tag; import java.util.Map; import java.util.Optional; @@ -51,6 +51,16 @@ public class AdvancementDisplay { public void applyClientboundData(Player player) { this.icon = CraftEngine.instance().itemManager().s2c(this.icon, player); + if (Config.interceptAdvancement()) { + Map tokens1 = CraftEngine.instance().fontManager().matchTags(AdventureHelper.componentToJson(this.title)); + if (!tokens1.isEmpty()) { + this.title = AdventureHelper.replaceText(this.title, tokens1, NetworkTextReplaceContext.of(player)); + } + Map tokens2 = CraftEngine.instance().fontManager().matchTags(AdventureHelper.componentToJson(this.description)); + if (!tokens2.isEmpty()) { + this.description = AdventureHelper.replaceText(this.description, tokens2, NetworkTextReplaceContext.of(player)); + } + } } public void write(FriendlyByteBuf buf) { @@ -75,8 +85,8 @@ public class AdvancementDisplay { } public static AdvancementDisplay read(FriendlyByteBuf buf) { - Component title = readComponent(buf); - Component description = readComponent(buf); + Component title = buf.readComponent(); + Component description = buf.readComponent(); Item icon = CraftEngine.instance().itemManager().decode(buf); AdvancementType type = AdvancementType.byId(buf.readVarInt()); int flags = buf.readInt(); @@ -88,28 +98,4 @@ public class AdvancementDisplay { float y = buf.readFloat(); return new AdvancementDisplay(title, description, icon, background, type, showToast, hidden, x, y); } - - private static Component readComponent(FriendlyByteBuf buf) { - if (Config.interceptAdvancement()) { - if (VersionHelper.isOrAbove1_20_3()) { - Tag nbt = buf.readNbt(false); - Map tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString()); - Component component = AdventureHelper.nbtToComponent(nbt); - if (!tokens.isEmpty()) { - component = AdventureHelper.replaceText(component, tokens); - } - return component; - } else { - String json = buf.readUtf(); - Component component = AdventureHelper.jsonToComponent(json); - Map tokens = CraftEngine.instance().fontManager().matchTags(json); - if (!tokens.isEmpty()) { - component = AdventureHelper.replaceText(component, tokens); - } - return component; - } - } else { - return buf.readComponent(); - } - } } 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 8b1f9e59d..458587234 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 @@ -4,6 +4,9 @@ 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 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.properties.Properties; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.loot.LootTable; @@ -84,7 +87,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem } @Override - public Map blocks() { + public Map loadedBlocks() { return Collections.unmodifiableMap(this.byId); } @@ -208,7 +211,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem BlockSettings settings = BlockSettings.fromMap(id, MiscUtils.castToMap(section.get("settings"), true)); // 读取基础外观配置 Map> properties; - Map appearances; + Map appearances; Map variants; // 读取states区域 Map stateSection = MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow( @@ -221,11 +224,13 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem // 获取原版外观的注册表id int appearanceId = pluginFormattedBlockStateToRegistryId(ResourceConfigUtils.requireNonEmptyStringOrThrow( stateSection.get("state"), "warning.config.block.state.missing_state")); + Optional[]> blockEntityRenderer = parseBlockEntityRender(stateSection.get("entity-renderer")); + // 为原版外观赋予外观模型并检查模型冲突 this.arrangeModelForStateAndVerify(appearanceId, ResourceConfigUtils.get(stateSection, "model", "models")); // 设置参数 properties = Map.of(); - appearances = Map.of("", appearanceId); + appearances = Map.of("", new BlockStateAppearance(appearanceId, blockEntityRenderer)); variants = Map.of("", new BlockStateVariant("", settings, getInternalBlockId(internalId, appearanceId))); } // 多方块状态 @@ -234,7 +239,10 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem 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 + it -> { + BlockStateAppearance blockStateAppearance = appearances.get(it); + return blockStateAppearance == null ? -1 : blockStateAppearance.stateRegistryId(); + }, settings ); } @@ -278,18 +286,26 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem return internalBlockRegistryId; } - private Map parseBlockAppearances(Map appearancesSection) { - Map appearances = new HashMap<>(); + 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); + appearances.put(entry.getKey(), new BlockStateAppearance(appearanceId, parseBlockEntityRender(appearanceSection.get("entity-renderer")))); } 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 private Map> parseBlockProperties(Map propertiesSection) { Map> properties = new HashMap<>(); @@ -324,6 +340,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem // 结合variants JsonElement combinedVariant = GsonHelper.combine(variants); Map overrideMap = AbstractBlockManager.this.blockStateOverrides.computeIfAbsent(blockId, k -> new HashMap<>()); + AbstractBlockManager.this.tempVanillaBlockStateModels.put(registryId, combinedVariant); JsonElement previous = overrideMap.get(propertyNBT); if (previous != null && !previous.equals(combinedVariant)) { throw new LocalizedResourceConfigException("warning.config.block.state.model.conflict", GsonHelper.get().toJson(combinedVariant), blockState, GsonHelper.get().toJson(previous)); @@ -397,8 +414,8 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem } } else { // 其他情况则是完整的方块 - BlockStateWrapper packedBlockState = createPackedBlockState(blockState); - if (packedBlockState == null || !packedBlockState.isVanillaBlock()) { + BlockStateWrapper packedBlockState = createBlockState(blockState); + if (packedBlockState == null) { throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState); } registryId = packedBlockState.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 e524b86c9..92593999c 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 @@ -2,6 +2,7 @@ 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.behavior.EntityBlockBehavior; import net.momirealms.craftengine.core.block.parser.BlockNbtParser; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; @@ -37,7 +38,7 @@ public abstract class AbstractCustomBlock implements CustomBlock { @NotNull Key id, @NotNull Holder.Reference holder, @NotNull Map> properties, - @NotNull Map appearances, + @NotNull Map appearances, @NotNull Map variantMapper, @NotNull BlockSettings settings, @NotNull Map>> events, @@ -58,6 +59,8 @@ public abstract class AbstractCustomBlock implements CustomBlock { placements.add(Property.createStateForPlacement(propertyEntry.getKey(), propertyEntry.getValue())); } this.placementFunction = composite(placements); + EntityBlockBehavior entityBlockBehavior = this.behavior.getEntityBehavior(); + boolean isEntityBlock = entityBlockBehavior != null; for (Map.Entry entry : variantMapper.entrySet()) { String nbtString = entry.getKey(); CompoundTag tag = BlockNbtParser.deserialize(this, nbtString); @@ -69,23 +72,32 @@ public abstract class AbstractCustomBlock implements CustomBlock { throw new LocalizedResourceConfigException("warning.config.block.state.property.invalid_format", nbtString); } BlockStateVariant blockStateVariant = entry.getValue(); - int vanillaStateRegistryId = appearances.getOrDefault(blockStateVariant.appearance(), -1); + + BlockStateAppearance blockStateAppearance = appearances.getOrDefault(blockStateVariant.appearance(), BlockStateAppearance.INVALID); + int stateId; // This should never happen - if (vanillaStateRegistryId == -1) { - vanillaStateRegistryId = appearances.values().iterator().next(); + if (blockStateAppearance.isInvalid()) { + stateId = appearances.values().iterator().next().stateRegistryId(); + } else { + stateId = blockStateAppearance.stateRegistryId(); } // Late init states ImmutableBlockState state = possibleStates.getFirst(); state.setSettings(blockStateVariant.settings()); - state.setVanillaBlockState((BlockStateWrapper.VanillaBlockState) BlockRegistryMirror.stateByRegistryId(vanillaStateRegistryId)); - state.setCustomBlockState((BlockStateWrapper.CustomBlockState) BlockRegistryMirror.stateByRegistryId(blockStateVariant.internalRegistryId())); + state.setVanillaBlockState(BlockRegistryMirror.stateByRegistryId(stateId)); + state.setCustomBlockState(BlockRegistryMirror.stateByRegistryId(blockStateVariant.internalRegistryId())); + blockStateAppearance.blockEntityRenderer().ifPresent(state::setConstantRenderers); } + // 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); } + if (isEntityBlock) { + state.setBlockEntityType(entityBlockBehavior.blockEntityType()); + } } this.applyPlatformSettings(); } 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..4f20228e3 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(); @@ -85,6 +95,21 @@ public abstract class BlockBehavior { return false; } + //BlockState state + public boolean hasAnalogOutputSignal(Object thisBlock, Object[] args) throws Exception { + return false; + } + + //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; @@ -186,4 +211,4 @@ public abstract class BlockBehavior { } public abstract CustomBlock block(); -} +} \ No newline at end of file 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 6f5daecc9..c6b9cd591 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 @@ -8,6 +8,7 @@ public final class BlockKeys { public static final Key NOTE_BLOCK = Key.of("minecraft:note_block"); public static final Key TRIPWIRE = Key.of("minecraft:tripwire"); public static final Key CRAFTING_TABLE = Key.of("minecraft:crafting_table"); + public static final Key CARTOGRAPHY_TABLE = Key.of("minecraft:cartography_table"); public static final Key STONECUTTER = Key.of("minecraft:stonecutter"); public static final Key BELL = Key.of("minecraft:bell"); public static final Key SMITHING_TABLE = Key.of("minecraft:smithing_table"); @@ -176,8 +177,6 @@ public final class BlockKeys { public static final Key WAXED_OXIDIZED_COPPER_TRAPDOOR = Key.of("minecraft:waxed_oxidized_copper_trapdoor"); public static final Key WAXED_WEATHERED_COPPER_TRAPDOOR = Key.of("minecraft:waxed_weathered_copper_trapdoor"); - - public static final Key OAK_FENCE_GATE = Key.of("minecraft:oak_fence_gate"); public static final Key SPRUCE_FENCE_GATE = Key.of("minecraft:spruce_fence_gate"); public static final Key BIRCH_FENCE_GATE = Key.of("minecraft:birch_fence_gate"); 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 6b69f04ee..785d13b17 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 @@ -24,7 +24,12 @@ public interface BlockManager extends Manageable, ModelGenerator { Map modBlockStates(); - Map blocks(); + Map loadedBlocks(); + + @Deprecated(forRemoval = true) + default Map blocks() { + return loadedBlocks(); + } Optional blockById(Key key); @@ -43,5 +48,5 @@ public interface BlockManager extends Manageable, ModelGenerator { ImmutableBlockState getImmutableBlockState(int stateId); @Nullable - BlockStateWrapper createPackedBlockState(String blockState); + BlockStateWrapper createBlockState(String blockState); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java index 8abe2a871..e2e78a9a4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java @@ -38,6 +38,9 @@ public class BlockSettings { LazyReference> correctTools = LazyReference.lazyReference(Set::of); String name; String supportShapeBlockState; + float friction = 0.6f; + float speedFactor = 1f; + float jumpFactor = 1f; private BlockSettings() {} @@ -101,6 +104,9 @@ public class BlockSettings { newSettings.supportShapeBlockState = settings.supportShapeBlockState; newSettings.propagatesSkylightDown = settings.propagatesSkylightDown; newSettings.useShapeForLightOcclusion = settings.useShapeForLightOcclusion; + newSettings.speedFactor = settings.speedFactor; + newSettings.jumpFactor = settings.jumpFactor; + newSettings.friction = settings.friction; return newSettings; } @@ -140,6 +146,18 @@ public class BlockSettings { return hardness; } + public float friction() { + return friction; + } + + public float jumpFactor() { + return jumpFactor; + } + + public float speedFactor() { + return speedFactor; + } + public Tristate canOcclude() { return canOcclude; } @@ -236,6 +254,21 @@ public class BlockSettings { return this; } + public BlockSettings friction(float friction) { + this.friction = friction; + return this; + } + + public BlockSettings speedFactor(float speedFactor) { + this.speedFactor = speedFactor; + return this; + } + + public BlockSettings jumpFactor(float jumpFactor) { + this.jumpFactor = jumpFactor; + return this; + } + public BlockSettings tags(Set tags) { this.tags = tags; return this; @@ -384,6 +417,18 @@ public class BlockSettings { float floatValue = ResourceConfigUtils.getAsFloat(value, "hardness"); return settings -> settings.hardness(floatValue); })); + registerFactory("friction", (value -> { + float floatValue = ResourceConfigUtils.getAsFloat(value, "friction"); + return settings -> settings.friction(floatValue); + })); + registerFactory("speed-factor", (value -> { + float floatValue = ResourceConfigUtils.getAsFloat(value, "speed-factor"); + return settings -> settings.speedFactor(floatValue); + })); + registerFactory("jump-factor", (value -> { + float floatValue = ResourceConfigUtils.getAsFloat(value, "jump-factor"); + return settings -> settings.jumpFactor(floatValue); + })); registerFactory("resistance", (value -> { float floatValue = ResourceConfigUtils.getAsFloat(value, "resistance"); return settings -> settings.resistance(floatValue); 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..0e2c7acc6 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateAppearance.java @@ -0,0 +1,14 @@ +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(int stateRegistryId, Optional[]> blockEntityRenderer) { + public static final BlockStateAppearance INVALID = new BlockStateAppearance(-1, Optional.empty()); + + public boolean isInvalid() { + return this.stateRegistryId < 0; + } +} 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 f019602f6..fb880292e 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 @@ -2,71 +2,7 @@ package net.momirealms.craftengine.core.block; public interface BlockStateWrapper { - Object handle(); + Object literalObject(); int registryId(); - - boolean isVanillaBlock(); - - static BlockStateWrapper vanilla(Object handle, int registryId) { - return new VanillaBlockState(handle, registryId); - } - - static BlockStateWrapper custom(Object handle, int registryId) { - return new CustomBlockState(handle, registryId); - } - - static BlockStateWrapper create(Object handle, int registryId, boolean isVanillaBlock) { - if (isVanillaBlock) return new VanillaBlockState(handle, registryId); - else return new CustomBlockState(handle, registryId); - } - - abstract class AbstractBlockState implements BlockStateWrapper { - protected final Object handle; - protected final int registryId; - - public AbstractBlockState(Object handle, int registryId) { - this.handle = handle; - this.registryId = registryId; - } - - @Override - public Object handle() { - return this.handle; - } - - @Override - public int registryId() { - return this.registryId; - } - } - - class VanillaBlockState extends AbstractBlockState { - - public VanillaBlockState(Object handle, int registryId) { - super(handle, registryId); - } - - @Override - public boolean isVanillaBlock() { - return true; - } - } - - class CustomBlockState extends AbstractBlockState { - - public CustomBlockState(Object handle, int registryId) { - super(handle, registryId); - } - - @Override - public DelegatingBlockState handle() { - return (DelegatingBlockState) super.handle(); - } - - @Override - public boolean isVanillaBlock() { - return false; - } - } } 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..744d0f4c9 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 @@ -19,11 +19,13 @@ 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); @@ -43,7 +45,7 @@ public interface CustomBlock { Builder events(Map>> events); - Builder appearances(Map appearances); + Builder appearances(Map appearances); Builder behavior(List> behavior); 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 5c27421bf..8bdeb7115 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; @@ -19,11 +26,13 @@ import java.util.List; public final class ImmutableBlockState extends BlockStateHolder { private CompoundTag tag; - private BlockStateWrapper.CustomBlockState customBlockState; - private BlockStateWrapper.VanillaBlockState vanillaBlockState; + 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, @@ -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() { @@ -75,11 +93,11 @@ public final class ImmutableBlockState extends BlockStateHolder { return this.vanillaBlockState; } - public void setCustomBlockState(@NotNull BlockStateWrapper.CustomBlockState customBlockState) { + public void setCustomBlockState(@NotNull BlockStateWrapper customBlockState) { this.customBlockState = customBlockState; } - public void setVanillaBlockState(@NotNull BlockStateWrapper.VanillaBlockState vanillaBlockState) { + public void setVanillaBlockState(@NotNull BlockStateWrapper vanillaBlockState) { this.vanillaBlockState = vanillaBlockState; } @@ -132,4 +150,11 @@ 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 createBlockEntityTicker(CEWorld world, BlockEntityType type) { + EntityBlockBehavior blockBehavior = this.behavior.getEntityBehavior(); + if (blockBehavior == null) return null; + return (BlockEntityTicker) blockBehavior.createBlockEntityTicker(world, this, type); + } } 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..f92b0e59a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/EntityBlockBehavior.java @@ -0,0 +1,31 @@ +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 createBlockEntityTicker(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/properties/Properties.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java index a54c56afa..e5a143264 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 @@ -21,6 +21,7 @@ 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"); static { register(BOOLEAN, BooleanProperty.FACTORY); @@ -36,6 +37,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)); + } public static void register(Key key, PropertyFactory factory) { 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/state/StatePropertyAccessor.java new file mode 100644 index 000000000..a69d4e24e --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/state/StatePropertyAccessor.java @@ -0,0 +1,14 @@ +package net.momirealms.craftengine.core.block.state; + +import java.util.Collection; + +public interface StatePropertyAccessor { + + String getPropertyValueAsString(String property); + + Collection getPropertyNames(); + + boolean hasProperty(String property); + + T getPropertyValue(String property); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/state/properties/SofaShape.java b/core/src/main/java/net/momirealms/craftengine/core/block/state/properties/SofaShape.java new file mode 100644 index 000000000..a9d717dcb --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/state/properties/SofaShape.java @@ -0,0 +1,7 @@ +package net.momirealms.craftengine.core.block.state.properties; + +public enum SofaShape { + STRAIGHT, + INNER_LEFT, + INNER_RIGHT, +} 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..2e5b56471 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 @@ -58,6 +58,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(); @@ -98,7 +103,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 +113,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 956a55571..9a33c2dbc 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,6 +1,7 @@ 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; @@ -8,10 +9,9 @@ import net.momirealms.craftengine.core.plugin.network.NetWorkUser; 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.WorldPosition; import org.jetbrains.annotations.NotNull; -import java.util.List; - public abstract class Player extends AbstractEntity implements NetWorkUser { private static final Key TYPE = Key.of("minecraft:player"); @@ -26,10 +26,6 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { @Override public abstract Object serverPlayer(); - public abstract void sendPackets(List packet, boolean immediately); - - public abstract void sendPackets(List packet, boolean immediately, Runnable sendListener); - public abstract float getDestroyProgress(Object blockState, BlockPos pos); public abstract void setClientSideCanBreakBlock(boolean canBreak); @@ -52,6 +48,14 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract boolean isSneaking(); + public abstract boolean isSwimming(); + + public abstract boolean isClimbing(); + + public abstract boolean isGliding(); + + public abstract boolean isFlying(); + public abstract GameMode gameMode(); public abstract void setGameMode(GameMode gameMode); @@ -60,6 +64,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); @@ -104,12 +110,12 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void unloadCurrentResourcePack(); - public abstract void performCommand(String command); + public abstract void performCommand(String command, boolean asOp); + + public abstract void performCommandAsEvent(String command); public abstract double luck(); - public abstract boolean isFlying(); - @Override public Key type() { return TYPE; @@ -146,4 +152,6 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void clearPotionEffects(); public abstract CooldownData cooldown(); + + public abstract void teleport(WorldPosition worldPosition); } 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 bb4c46dd2..cc5c6cdfb 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 @@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.*; import org.ahocorasick.trie.Token; import org.ahocorasick.trie.Trie; @@ -39,9 +40,9 @@ public abstract class AbstractFontManager implements FontManager { private final EmojiParser emojiParser; private OffsetFont offsetFont; - protected Trie imageTagTrie; + protected Trie networkTagTrie; protected Trie emojiKeywordTrie; - protected Map tagMapper; + protected Map networkTagMapper; protected Map emojiMapper; protected List emojiList; protected List allEmojiSuggestions; @@ -57,6 +58,7 @@ public abstract class AbstractFontManager implements FontManager { this.offsetFont = Optional.ofNullable(plugin.config().settings().getSection("image.offset-characters")) .map(OffsetFont::new) .orElse(null); + this.networkTagMapper = new HashMap<>(1024); } @Override @@ -65,6 +67,14 @@ public abstract class AbstractFontManager implements FontManager { 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 @@ -80,7 +90,10 @@ public abstract class AbstractFontManager implements FontManager { @Override public void delayedLoad() { Optional.ofNullable(this.fonts.get(DEFAULT_FONT)).ifPresent(font -> this.illegalChars.addAll(font.codepointsInUse())); - this.buildImageTagTrie(); + this.registerImageTags(); + this.registerShiftTags(); + this.registerGlobalTags(); + this.buildNetworkTagTrie(); this.buildEmojiKeywordsTrie(); this.emojiList = new ArrayList<>(this.emojis.values()); this.allEmojiSuggestions = this.emojis.values().stream() @@ -88,15 +101,48 @@ public abstract class AbstractFontManager implements FontManager { .collect(Collectors.toList()); } + private void registerGlobalTags() { + for (Map.Entry entry : this.plugin.globalVariableManager().globalVariables().entrySet()) { + String globalTag = globalTag(entry.getKey()); + this.networkTagMapper.put(globalTag, ComponentProvider.miniMessageOrConstant(entry.getValue())); + this.networkTagMapper.put("\\" + globalTag, ComponentProvider.constant(Component.text(entry.getValue()))); + } + } + + private void registerShiftTags() { + for (int i = -256; i <= 256; i++) { + String shiftTag = ""; + this.networkTagMapper.put(shiftTag, ComponentProvider.constant(this.offsetFont.createOffset(i))); + this.networkTagMapper.put("\\" + shiftTag, ComponentProvider.constant(Component.text(shiftTag))); + } + } + + private void registerImageTags() { + for (BitmapImage image : this.images.values()) { + String id = image.id().toString(); + String simpleImageTag = imageTag(id); + this.networkTagMapper.put(simpleImageTag, ComponentProvider.constant(image.componentAt(0, 0))); + this.networkTagMapper.put("\\" + simpleImageTag, ComponentProvider.constant(Component.text(simpleImageTag))); + for (int i = 0; i < image.rows(); i++) { + for (int j = 0; j < image.columns(); j++) { + String imageArgs = id + ":" + i + ":" + j; + String imageTag = imageTag(imageArgs); + this.networkTagMapper.put(imageTag, ComponentProvider.constant(image.componentAt(i, j))); + this.networkTagMapper.put("\\" + imageTag, ComponentProvider.constant(Component.text(imageTag))); + } + } + } + } + @Override - public Map matchTags(String json) { - if (this.imageTagTrie == null) { + public Map matchTags(String json) { + if (this.networkTagTrie == null) { return Collections.emptyMap(); } - Map tags = new HashMap<>(); - for (Token token : this.imageTagTrie.tokenize(json)) { + Map tags = new HashMap<>(); + for (Token token : this.networkTagTrie.tokenize(json)) { if (token.isMatch()) { - tags.put(token.getFragment(), this.tagMapper.get(token.getFragment())); + tags.put(token.getFragment(), this.networkTagMapper.get(token.getFragment())); } } return tags; @@ -218,9 +264,9 @@ public abstract class AbstractFontManager implements FontManager { public IllegalCharacterProcessResult processIllegalCharacters(String raw, char replacement) { boolean hasIllegal = false; // replace illegal image usage - Map tokens = matchTags(raw); + Map tokens = matchTags(raw); if (!tokens.isEmpty()) { - for (Map.Entry entry : tokens.entrySet()) { + for (Map.Entry entry : tokens.entrySet()) { raw = raw.replace(entry.getKey(), String.valueOf(replacement)); hasIllegal = true; } @@ -264,30 +310,10 @@ public abstract class AbstractFontManager implements FontManager { .build(); } - private void buildImageTagTrie() { - this.tagMapper = new HashMap<>(1024); - for (BitmapImage image : this.images.values()) { - String id = image.id().toString(); - String simpleImageTag = imageTag(id); - this.tagMapper.put(simpleImageTag, image.componentAt(0, 0)); - this.tagMapper.put("\\" + simpleImageTag, Component.text(simpleImageTag)); - for (int i = 0; i < image.rows(); i++) { - for (int j = 0; j < image.columns(); j++) { - String imageArgs = id + ":" + i + ":" + j; - String imageTag = imageTag(imageArgs); - this.tagMapper.put(imageTag, image.componentAt(i, j)); - this.tagMapper.put("\\" + imageTag, Component.text(imageTag)); - } - } - } - for (int i = -256; i <= 256; i++) { - String shiftTag = ""; - this.tagMapper.put(shiftTag, this.offsetFont.createOffset(i)); - this.tagMapper.put("\\" + shiftTag, Component.text(shiftTag)); - } - this.imageTagTrie = Trie.builder() + private void buildNetworkTagTrie() { + this.networkTagTrie = Trie.builder() .ignoreOverlaps() - .addKeywords(this.tagMapper.keySet()) + .addKeywords(this.networkTagMapper.keySet()) .build(); } @@ -295,6 +321,10 @@ public abstract class AbstractFontManager implements FontManager { return ""; } + private static String globalTag(String text) { + return ""; + } + @Override public boolean isDefaultFontInUse() { return !this.illegalChars.isEmpty(); 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 307350eea..aca5c9dd6 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 @@ -5,6 +5,7 @@ import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.Manageable; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.CharacterUtils; import net.momirealms.craftengine.core.util.FormatUtils; @@ -103,7 +104,7 @@ public interface FontManager extends Manageable { return createOffsets(offset, (raw, font) -> raw); } - Map matchTags(String json); + Map matchTags(String json); void refreshEmojiSuggestions(UUID uuid); } 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..61a06df52 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 @@ -229,8 +229,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 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/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..03c355356 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 @@ -25,7 +25,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 +89,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 +128,8 @@ public class ItemSettings { return canPlaceRelatedVanillaBlock; } - public Tristate canRepair() { - return canRepair; + public Repairable repairable() { + return repairable; } public int fuelTime() { @@ -233,8 +233,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 +315,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,9 +405,9 @@ 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"); + 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"); ProjectileType type = Optional.ofNullable(args.get("type")).map(String::valueOf).map(it -> ProjectileType.valueOf(it.toUpperCase(Locale.ENGLISH))).orElse(null); double range = ResourceConfigUtils.getAsDouble(args.getOrDefault("range", 1), "range"); return settings -> settings.projectileMeta(new ProjectileMeta(customTridentItemId, displayType, billboard, scale, translation, rotation, range, type)); @@ -426,14 +432,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/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/AttributeModifiersModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/AttributeModifiersModifier.java index fc1ed038b..63298239f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/AttributeModifiersModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/AttributeModifiersModifier.java @@ -105,7 +105,11 @@ public class AttributeModifiersModifier implements SimpleNetworkItemDataModif @Override public Item apply(Item item, ItemBuildContext context) { - return item.attributeModifiers(this.modifiers.stream().map(it -> it.toAttributeModifier(context)).toList()); + List results = new ArrayList<>(this.modifiers.size()); + for (PreModifier modifier : this.modifiers) { + results.add(modifier.toAttributeModifier(item, context)); + } + return item.attributeModifiers(results); } @Override @@ -125,12 +129,12 @@ public class AttributeModifiersModifier implements SimpleNetworkItemDataModif public record PreModifier(String type, AttributeModifier.Slot slot, - Key id, + Optional id, NumberProvider amount, AttributeModifier.Operation operation, AttributeModifiersModifier.PreModifier.@Nullable PreDisplay display) { - public PreModifier(String type, AttributeModifier.Slot slot, Key id, NumberProvider amount, AttributeModifier.Operation operation, @Nullable PreDisplay display) { + public PreModifier(String type, AttributeModifier.Slot slot, Optional id, NumberProvider amount, AttributeModifier.Operation operation, @Nullable PreDisplay display) { this.amount = amount; this.type = type; this.slot = slot; @@ -139,8 +143,9 @@ public class AttributeModifiersModifier implements SimpleNetworkItemDataModif this.display = display; } - public AttributeModifier toAttributeModifier(ItemBuildContext context) { - return new AttributeModifier(type, slot, id, amount.getDouble(context), operation, display == null ? null : display.toDisplay(context)); + public AttributeModifier toAttributeModifier(Item item, ItemBuildContext context) { + return new AttributeModifier(this.type, this.slot, this.id.orElseGet(() -> Key.of("craftengine", UUID.randomUUID().toString())), + this.amount.getDouble(context), this.operation, this.display == null ? null : this.display.toDisplay(context)); } public record PreDisplay(AttributeModifier.Display.Type type, String value) { @@ -155,15 +160,11 @@ public class AttributeModifiersModifier implements SimpleNetworkItemDataModif @Override public ItemDataModifier create(Object arg) { - MutableInt mutableInt = new MutableInt(0); List attributeModifiers = ResourceConfigUtils.parseConfigAsList(arg, (map) -> { String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.item.data.attribute_modifiers.missing_type"); Key nativeType = AttributeModifiersModifier.getNativeAttributeName(Key.of(type)); AttributeModifier.Slot slot = AttributeModifier.Slot.valueOf(map.getOrDefault("slot", "any").toString().toUpperCase(Locale.ENGLISH)); - Key id = Optional.ofNullable(map.get("id")).map(String::valueOf).map(Key::of).orElseGet(() -> { - mutableInt.add(1); - return Key.of("craftengine", "modifier_" + mutableInt.intValue()); - }); + Optional id = Optional.ofNullable(map.get("id")).map(String::valueOf).map(Key::of); NumberProvider amount = NumberProviders.fromObject(ResourceConfigUtils.requireNonNullOrThrow(map.get("amount"), "warning.config.item.data.attribute_modifiers.missing_amount")); AttributeModifier.Operation operation = AttributeModifier.Operation.valueOf( ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("operation"), "warning.config.item.data.attribute_modifiers.missing_operation").toUpperCase(Locale.ENGLISH) 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/modifier/ItemDataModifiers.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifiers.java index 2b6672de8..82eafdef4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifiers.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifiers.java @@ -45,6 +45,7 @@ public final class ItemDataModifiers { public static final Key UNBREAKABLE = Key.of("craftengine:unbreakable"); public static final Key DYNAMIC_LORE = Key.of("craftengine:dynamic-lore"); public static final Key OVERWRITABLE_LORE = Key.of("craftengine:overwritable-lore"); + public static final Key MAX_DAMAGE = Key.of("craftengine:max-damage"); public static void register(Key key, ItemDataModifierFactory factory) { ((WritableRegistry>) BuiltInRegistries.ITEM_DATA_MODIFIER_FACTORY) @@ -83,6 +84,7 @@ public final class ItemDataModifiers { register(COMPONENTS, ComponentsModifier.FACTORY); register(REMOVE_COMPONENTS, RemoveComponentModifier.FACTORY); register(FOOD, FoodModifier.FACTORY); + register(MAX_DAMAGE, MaxDamageModifier.FACTORY); } else { register(CUSTOM_NAME, CustomNameModifier.FACTORY); register(ITEM_NAME, CustomNameModifier.FACTORY); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/MaxDamageModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/MaxDamageModifier.java new file mode 100644 index 000000000..842e2892b --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/MaxDamageModifier.java @@ -0,0 +1,44 @@ +package net.momirealms.craftengine.core.item.modifier; + +import net.momirealms.craftengine.core.item.ComponentKeys; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; +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; + +public class MaxDamageModifier implements SimpleNetworkItemDataModifier { + public static final Factory FACTORY = new Factory<>(); + private final NumberProvider argument; + + public MaxDamageModifier(NumberProvider argument) { + this.argument = argument; + } + + @Override + public Key type() { + return ItemDataModifiers.MAX_DAMAGE; + } + + @Override + public Item apply(Item item, ItemBuildContext context) { + item.maxDamage(argument.getInt(context)); + return item; + } + + @Override + public @Nullable Key componentType(Item item, ItemBuildContext context) { + return ComponentKeys.MAX_DAMAGE; + } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + NumberProvider numberProvider = NumberProviders.fromObject(arg); + return new MaxDamageModifier<>(numberProvider); + } + } +} 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 3087306b1..d6a3460f5 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 @@ -11,6 +11,7 @@ import net.momirealms.craftengine.core.item.recipe.result.PostProcessors; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.*; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -61,9 +62,10 @@ public abstract class AbstractRecipeSerializer> implement return recipeCategory; } + @NotNull @SuppressWarnings({"unchecked"}) protected CustomRecipeResult parseResult(Map arguments) { - Map resultMap = MiscUtils.castToMap(arguments.get("result"), true); + Map resultMap = ResourceConfigUtils.getAsMapOrNull(arguments.get("result"), "result"); if (resultMap == null) { throw new LocalizedResourceConfigException("warning.config.recipe.missing_result"); } @@ -81,6 +83,27 @@ public abstract class AbstractRecipeSerializer> implement ); } + @Nullable + @SuppressWarnings({"unchecked"}) + protected CustomRecipeResult parseVisualResult(Map arguments) { + Map resultMap = ResourceConfigUtils.getAsMapOrNull(arguments.get("visual-result"), "visual-result"); + if (resultMap == null) { + return null; + } + String id = ResourceConfigUtils.requireNonEmptyStringOrThrow(resultMap.get("id"), "warning.config.recipe.result.missing_id"); + int count = ResourceConfigUtils.getAsInt(resultMap.getOrDefault("count", 1), "count"); + BuildableItem resultItem = (BuildableItem) CraftEngine.instance().itemManager().getBuildableItem(Key.of(id)).orElseThrow(() -> new LocalizedResourceConfigException("warning.config.recipe.invalid_result", id)); + if (resultItem.isEmpty()) { + throw new LocalizedResourceConfigException("warning.config.recipe.invalid_result", id); + } + List> processors = ResourceConfigUtils.parseConfigAsList(resultMap.get("post-processors"), PostProcessors::fromMap); + return new CustomRecipeResult<>( + resultItem, + count, + processors.isEmpty() ? null : processors.toArray(new PostProcessor[0]) + ); + } + @SuppressWarnings("unchecked") protected CustomRecipeResult parseResult(DatapackRecipeResult recipeResult) { Item result = (Item) CraftEngine.instance().itemManager().build(recipeResult); 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 2adf21dc3..655f40ea4 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 @@ -1,14 +1,20 @@ package net.momirealms.craftengine.core.item.recipe; +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.util.Key; +import org.jetbrains.annotations.Nullable; public abstract class CustomCraftingTableRecipe extends AbstractGroupedRecipe { protected final CraftingRecipeCategory category; + private final CustomRecipeResult visualResult; - protected CustomCraftingTableRecipe(Key id, boolean showNotification, CustomRecipeResult result, String group, CraftingRecipeCategory category) { + protected CustomCraftingTableRecipe(Key id, boolean showNotification, CustomRecipeResult result, @Nullable CustomRecipeResult visualResult, String group, CraftingRecipeCategory category) { super(id, showNotification, result, group); this.category = category == null ? CraftingRecipeCategory.MISC : category; + this.visualResult = visualResult; } public CraftingRecipeCategory category() { @@ -19,4 +25,28 @@ public abstract class CustomCraftingTableRecipe extends AbstractGroupedRecipe public RecipeType type() { return RecipeType.CRAFTING; } + + public CustomRecipeResult visualResult() { + return visualResult; + } + + public boolean hasVisualResult() { + return visualResult != null; + } + + public T assembleVisual(RecipeInput input, ItemBuildContext context) { + if (this.visualResult != null) { + return this.visualResult.buildItemStack(context); + } else { + throw new IllegalStateException("No visual result available"); + } + } + + public Item buildVisualOrActualResult(ItemBuildContext context) { + if (this.visualResult != null) { + return this.visualResult.buildItem(context); + } else { + return super.result.buildItem(context); + } + } } 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 70faa1450..40af56a0f 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 @@ -21,10 +21,11 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { public CustomShapedRecipe(Key id, boolean showNotification, CustomRecipeResult result, + CustomRecipeResult visualResult, String group, CraftingRecipeCategory category, Pattern pattern) { - super(id, showNotification, result, group, category); + super(id, showNotification, result, visualResult, group, category); this.pattern = pattern; this.parsedPattern = pattern.parse(); } @@ -165,7 +166,9 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { } return new CustomShapedRecipe(id, showNotification(arguments), - parseResult(arguments), arguments.containsKey("group") ? arguments.get("group").toString() : null, craftingRecipeCategory(arguments), + parseResult(arguments), + parseVisualResult(arguments), + arguments.containsKey("group") ? arguments.get("group").toString() : null, craftingRecipeCategory(arguments), new Pattern<>(pattern.toArray(new String[0]), ingredients) ); } @@ -175,7 +178,10 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { Map> ingredients = Maps.transformValues(VANILLA_RECIPE_HELPER.shapedIngredientMap(json.getAsJsonObject("key")), this::toIngredient); return new CustomShapedRecipe<>(id, true, - parseResult(VANILLA_RECIPE_HELPER.craftingResult(json.getAsJsonObject("result"))), VANILLA_RECIPE_HELPER.readGroup(json), VANILLA_RECIPE_HELPER.craftingCategory(json), + parseResult(VANILLA_RECIPE_HELPER.craftingResult(json.getAsJsonObject("result"))), + null, + VANILLA_RECIPE_HELPER.readGroup(json), + VANILLA_RECIPE_HELPER.craftingCategory(json), new Pattern<>(VANILLA_RECIPE_HELPER.craftingShapedPattern(json), ingredients) ); } 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 d8ca669ef..678c75543 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 @@ -20,10 +20,11 @@ public class CustomShapelessRecipe extends CustomCraftingTableRecipe { public CustomShapelessRecipe(Key id, boolean showNotification, CustomRecipeResult result, + CustomRecipeResult visualResult, String group, CraftingRecipeCategory category, List> ingredients) { - super(id, showNotification, result, group, category); + super(id, showNotification, result, visualResult, group, category); this.ingredients = ingredients; this.placementInfo = PlacementInfo.create(ingredients); } @@ -84,7 +85,9 @@ public class CustomShapelessRecipe extends CustomCraftingTableRecipe { } return new CustomShapelessRecipe(id, showNotification(arguments), - parseResult(arguments), arguments.containsKey("group") ? arguments.get("group").toString() : null, craftingRecipeCategory(arguments), + parseResult(arguments), + parseVisualResult(arguments), + arguments.containsKey("group") ? arguments.get("group").toString() : null, craftingRecipeCategory(arguments), ingredients ); } @@ -93,7 +96,9 @@ public class CustomShapelessRecipe extends CustomCraftingTableRecipe { public CustomShapelessRecipe readJson(Key id, JsonObject json) { return new CustomShapelessRecipe<>(id, true, - parseResult(VANILLA_RECIPE_HELPER.craftingResult(json.getAsJsonObject("result"))), VANILLA_RECIPE_HELPER.readGroup(json), VANILLA_RECIPE_HELPER.craftingCategory(json), + 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() ); } 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 bbb5aed75..9ebe583a7 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 @@ -214,7 +214,7 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip for (Key component : this.components) { Object componentObj = item1.getExactComponent(component); if (componentObj != null) { - item3.setComponent(component, componentObj); + item3.setExactComponent(component, componentObj); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/UniqueIdItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/UniqueIdItem.java index 7e60308d1..9594a764e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/UniqueIdItem.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/UniqueIdItem.java @@ -19,7 +19,7 @@ public class UniqueIdItem { @NotNull public UniqueKey id() { - return uniqueId; + return this.uniqueId; } @NotNull @@ -37,6 +37,6 @@ public class UniqueIdItem { @Override public String toString() { - return "UniqueIdItem[" + "uniqueId=" + uniqueId + ", item=" + rawItem.getItem() + ']'; + return "UniqueIdItem[" + "uniqueId=" + this.uniqueId + ", item=" + this.rawItem.getItem() + ']'; } } 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/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index c8c09139c..f053a6102 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 @@ -24,7 +24,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 +41,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,7 +84,8 @@ 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 JsonObject vanillaAtlas; @@ -93,9 +94,10 @@ public abstract class AbstractPackManager implements PackManager { 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,9 +124,10 @@ 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")); for (int i = 0; i < 256; i++) { VANILLA_TEXTURES.add(Key.of("minecraft", "font/unicode_page_" + String.format("%02x", i))); } @@ -167,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"); @@ -244,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"); } @@ -265,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); } @@ -344,25 +354,27 @@ 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/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"); @@ -384,29 +396,43 @@ 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"); + // 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"); @@ -420,8 +446,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"); @@ -452,17 +481,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"); @@ -474,13 +502,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"); @@ -492,14 +519,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"); @@ -507,7 +533,6 @@ 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"); @@ -542,6 +567,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()); @@ -638,11 +676,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; @@ -697,7 +739,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); } } @@ -1823,9 +1865,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; @@ -1833,13 +1879,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()); @@ -2116,7 +2180,7 @@ public abstract class AbstractPackManager implements PackManager { } } - private List>> updateCachedAssets(@Nullable FileSystem fs) throws IOException { + private List>> updateCachedAssets(@NotNull PackCacheData cacheData, @Nullable FileSystem fs) throws IOException { Map> conflictChecker = new Object2ObjectOpenHashMap<>(Math.max(128, this.cachedAssetFiles.size())); Map previousFiles = this.cachedAssetFiles; this.cachedAssetFiles = new Object2ObjectOpenHashMap<>(Math.max(128, this.cachedAssetFiles.size())); @@ -2126,10 +2190,7 @@ public abstract class AbstractPackManager implements PackManager { .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<>() { @@ -2141,13 +2202,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/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/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/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/pack/obfuscation/ResourcePackGenerationException.java b/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ResourcePackGenerationException.java index adebeba89..9056ed3cd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ResourcePackGenerationException.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ResourcePackGenerationException.java @@ -5,4 +5,8 @@ public class ResourcePackGenerationException extends RuntimeException { public ResourcePackGenerationException(String message) { super(message); } + + public ResourcePackGenerationException(String message, Throwable cause) { + super(message, cause); + } } 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..25bb1acf6 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; @@ -101,11 +102,12 @@ public abstract class CraftEngine implements Plugin { } protected void onPluginLoad() { - RecipeDisplayTypes.register(); - SlotDisplayTypes.register(); - LegacyRecipeTypes.register(); + RecipeDisplayTypes.init(); + SlotDisplayTypes.init(); + LegacyRecipeTypes.init(); ((Logger) LogManager.getRootLogger()).addFilter(new LogFilter()); ((Logger) LogManager.getRootLogger()).addFilter(new DisconnectLogFilter()); + this.config.load(); } public record ReloadResult(boolean success, long asyncTime, long syncTime) { @@ -338,8 +340,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/Config.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java index 6c1fe4b11..ffdc1cacf 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 @@ -93,26 +93,26 @@ 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 int performance$max_note_block_chain_update_limit; - protected int performance$max_tripwire_chain_update_limit; - protected int performance$max_emojis_per_parse; - 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; + protected Map chunk_system$process_invalid_furniture$mapping; + protected boolean chunk_system$process_invalid_blocks$enable; + protected Map chunk_system$process_invalid_blocks$mapping; - protected boolean furniture$handle_invalid_furniture_on_chunk_load$enable; - protected Map furniture$handle_invalid_furniture_on_chunk_load$mapping; protected boolean furniture$hide_base_entity; protected ColliderType furniture$collision_entity_type; @@ -122,6 +122,7 @@ 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 boolean recipe$enable; protected boolean recipe$disable_vanilla_recipes$all; @@ -161,10 +162,11 @@ public class Config { protected Key equipment$sacrificed_vanilla_armor$humanoid; protected Key equipment$sacrificed_vanilla_armor$humanoid_leggings; - protected boolean emoji$chat; - protected boolean emoji$book; - protected boolean emoji$anvil; - protected boolean emoji$sign; + protected boolean emoji$contexts$chat; + protected boolean emoji$contexts$book; + protected boolean emoji$contexts$anvil; + protected boolean emoji$contexts$sign; + protected int emoji$max_emojis_per_parse; public Config(CraftEngine plugin) { this.plugin = plugin; @@ -225,6 +227,8 @@ 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", '.') .build()); } try { @@ -268,6 +272,7 @@ 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); @@ -285,7 +290,7 @@ public class Config { resource_pack$protection$obfuscation$resource_location$random_path$source = config.getString("resource-pack.protection.obfuscation.resource-location.random-path.source", "obf"); resource_pack$protection$obfuscation$resource_location$random_path$anti_unzip = config.getBoolean("resource-pack.protection.obfuscation.resource-location.random-path.anti-unzip", false); resource_pack$protection$obfuscation$resource_location$random_atlas$images_per_canvas = config.getInt("resource-pack.protection.obfuscation.resource-location.random-atlas.images-per-canvas", 16); - resource_pack$protection$obfuscation$resource_location$random_atlas$use_double = config.getBoolean("resource-pack.protection.obfuscation.resource-location.random-atlas.use-double", true); + resource_pack$protection$obfuscation$resource_location$random_atlas$use_double = config.getBoolean("resource-pack.protection.obfuscation.resource-location.random-atlas.use-double", false); resource_pack$protection$obfuscation$resource_location$bypass_textures = config.getStringList("resource-pack.protection.obfuscation.resource-location.bypass-textures"); 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"); @@ -310,13 +315,9 @@ public class Config { resource_pack$duplicated_files_handler = List.of(); } - // performance - performance$max_note_block_chain_update_limit = config.getInt("performance.max-note-block-chain-update-limit", 64); - performance$max_tripwire_chain_update_limit = config.getInt("performance.max-tripwire-chain-update-limit", 128); - performance$max_emojis_per_parse = config.getInt("performance.max-emojis-per-parse", 32); - // 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 @@ -330,22 +331,37 @@ public class Config { chunk_system$injection$target = config.getEnum("chunk-system.injection.target", InjectionTarget.class, InjectionTarget.PALETTE) == InjectionTarget.PALETTE; } - // furniture - furniture$handle_invalid_furniture_on_chunk_load$enable = config.getBoolean("furniture.handle-invalid-furniture-on-chunk-load.enable", false); - ImmutableMap.Builder builder = ImmutableMap.builder(); - for (String furniture : config.getStringList("furniture.handle-invalid-furniture-on-chunk-load.remove")) { - builder.put(furniture, ""); + chunk_system$process_invalid_furniture$enable = config.getBoolean("chunk-system.process-invalid-furniture.enable", false); + ImmutableMap.Builder furnitureBuilder = ImmutableMap.builder(); + for (String furniture : config.getStringList("chunk-system.process-invalid-furniture.remove")) { + furnitureBuilder.put(furniture, ""); } - if (config.contains("furniture.handle-invalid-furniture-on-chunk-load.convert")) { - Section section = config.getSection("furniture.handle-invalid-furniture-on-chunk-load.convert"); + if (config.contains("chunk-system.process-invalid-furniture.convert")) { + Section section = config.getSection("chunk-system.process-invalid-furniture.convert"); if (section != null) { for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { - builder.put(entry.getKey(), entry.getValue().toString()); + furnitureBuilder.put(entry.getKey(), entry.getValue().toString()); } } } + chunk_system$process_invalid_furniture$mapping = furnitureBuilder.build(); - furniture$handle_invalid_furniture_on_chunk_load$mapping = builder.build(); + chunk_system$process_invalid_blocks$enable = config.getBoolean("chunk-system.process-invalid-blocks.enable", false); + ImmutableMap.Builder blockBuilder = ImmutableMap.builder(); + for (String furniture : config.getStringList("chunk-system.process-invalid-blocks.remove")) { + blockBuilder.put(furniture, ""); + } + if (config.contains("chunk-system.process-invalid-blocks.convert")) { + Section section = config.getSection("chunk-system.process-invalid-blocks.convert"); + if (section != null) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + blockBuilder.put(entry.getKey(), entry.getValue().toString()); + } + } + } + chunk_system$process_invalid_blocks$mapping = blockBuilder.build(); + + // furniture furniture$hide_base_entity = config.getBoolean("furniture.hide-base-entity", true); furniture$collision_entity_type = ColliderType.valueOf(config.getString("furniture.collision-entity-type", "interaction").toUpperCase(Locale.ENGLISH)); @@ -375,6 +391,7 @@ 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); // recipe recipe$enable = config.getBoolean("recipe.enable", true); @@ -405,10 +422,11 @@ public class Config { image$intercept_packets$advancement = config.getBoolean("image.intercept-packets.advancement", true); // emoji - emoji$chat = config.getBoolean("emoji.chat", true); - emoji$anvil = config.getBoolean("emoji.anvil", true); - emoji$book = config.getBoolean("emoji.book", true); - emoji$sign = config.getBoolean("emoji.sign", true); + emoji$contexts$chat = config.getBoolean("emoji.contexts.chat", true); + emoji$contexts$anvil = config.getBoolean("emoji.contexts.anvil", true); + emoji$contexts$book = config.getBoolean("emoji.contexts.book", true); + emoji$contexts$sign = config.getBoolean("emoji.contexts.sign", true); + emoji$max_emojis_per_parse = config.getInt("emoji.max-emojis-per-parse", 32); firstTime = false; } @@ -439,6 +457,10 @@ public class Config { return instance.debug$item; } + public static boolean debugBlockEntity() { + return false; + } + public static boolean debugFurniture() { return instance.debug$furniture; } @@ -464,24 +486,28 @@ public class Config { } public static int maxNoteBlockChainUpdate() { - return instance.performance$max_note_block_chain_update_limit; + return 64; } public static int maxEmojisPerParse() { - return instance.performance$max_emojis_per_parse; + return instance.emoji$max_emojis_per_parse; } public static boolean handleInvalidFurniture() { - return instance.furniture$handle_invalid_furniture_on_chunk_load$enable; + return instance.chunk_system$process_invalid_furniture$enable; + } + + public static boolean handleInvalidBlock() { + return instance.chunk_system$process_invalid_blocks$enable; } public static Map furnitureMappings() { - return instance.furniture$handle_invalid_furniture_on_chunk_load$mapping; + return instance.chunk_system$process_invalid_furniture$mapping; } -// public static boolean forceUpdateLight() { -// return instance.light_system$force_update_light; -// } + public static Map blockMappings() { + return instance.chunk_system$process_invalid_blocks$mapping; + } public static boolean enableLightSystem() { return instance.light_system$enable; @@ -566,6 +592,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; @@ -772,19 +801,19 @@ public class Config { } public static boolean allowEmojiSign() { - return instance.emoji$sign; + return instance.emoji$contexts$sign; } public static boolean allowEmojiChat() { - return instance.emoji$chat; + return instance.emoji$contexts$chat; } public static boolean allowEmojiAnvil() { - return instance.emoji$anvil; + return instance.emoji$contexts$anvil; } public static boolean allowEmojiBook() { - return instance.emoji$book; + return instance.emoji$contexts$book; } public static ColliderType colliderType() { @@ -859,6 +888,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/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 5c29100a9..bb2ab886d 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 @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import org.jetbrains.annotations.Nullable; import java.nio.file.Path; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -25,6 +26,10 @@ public class GlobalVariableManager implements Manageable { this.globalVariables.clear(); } + public Map globalVariables() { + return Collections.unmodifiableMap(this.globalVariables); + } + public ConfigParser parser() { return this.parser; } @@ -50,7 +55,7 @@ public class GlobalVariableManager implements Manageable { @Override public void parseObject(Pack pack, Path path, net.momirealms.craftengine.core.util.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 new file mode 100644 index 000000000..1b80021c5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/NetworkTextReplaceContext.java @@ -0,0 +1,35 @@ +package net.momirealms.craftengine.core.plugin.context; + +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.plugin.text.minimessage.*; + +import java.util.Map; + +public final class NetworkTextReplaceContext extends AbstractChainParameterContext implements PlayerContext { + private final Player player; + + public NetworkTextReplaceContext(Player player) { + super(new ContextHolder(Map.of(DirectContextParameters.PLAYER, () -> player))); + this.player = player; + } + + public static NetworkTextReplaceContext of(Player player) { + return new NetworkTextReplaceContext(player); + } + + @Override + public Player player() { + return player; + } + + @Override + public TagResolver[] tagResolvers() { + if (this.tagResolvers == null) { + this.tagResolvers = new TagResolver[]{ShiftTag.INSTANCE, ImageTag.INSTANCE, new PlaceholderTag(this), new I18NTag(this), + new NamedArgumentTag(this), new ExpressionTag(this), new GlobalVariableTag(this)}; + } + return this.tagResolvers; + } +} 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/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/CommonConditions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/CommonConditions.java index a5521169c..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,11 +5,14 @@ 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"); public static final Key MATCH_ITEM = Key.of("craftengine:match_item"); + public static final Key MATCH_ENTITY = Key.of("craftengine:match_entity"); + public static final Key MATCH_BLOCK = Key.of("craftengine:match_block"); public static final Key MATCH_BLOCK_PROPERTY = Key.from("craftengine:match_block_property"); public static final Key TABLE_BONUS = Key.from("craftengine:table_bonus"); public static final Key SURVIVES_EXPLOSION = Key.from("craftengine:survives_explosion"); 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/MatchBlockCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchBlockCondition.java new file mode 100644 index 000000000..769e849fa --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchBlockCondition.java @@ -0,0 +1,46 @@ +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.plugin.context.parameter.DirectContextParameters; +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.world.ExistingBlock; + +import java.util.*; + +public class MatchBlockCondition implements Condition { + private final Set ids; + private final boolean regexMatch; + + public MatchBlockCondition(Collection ids, boolean regexMatch) { + this.ids = new HashSet<>(ids); + this.regexMatch = regexMatch; + } + + @Override + public Key type() { + return CommonConditions.MATCH_BLOCK; + } + + @Override + public boolean test(CTX ctx) { + Optional block = ctx.getOptionalParameter(DirectContextParameters.BLOCK); + return block.filter(blockInWorld -> MiscUtils.matchRegex(blockInWorld.id().asString(), this.ids, this.regexMatch)).isPresent(); + } + + public static class FactoryImpl implements ConditionFactory { + + @Override + public Condition create(Map arguments) { + List ids = MiscUtils.getAsStringList(arguments.get("id")); + if (ids.isEmpty()) { + throw new LocalizedResourceConfigException("warning.config.condition.match_block.missing_id"); + } + boolean regex = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("regex", false), "regex"); + return new MatchBlockCondition<>(ids, regex); + } + } +} 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 b8a213d27..e5ce7b198 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 @@ -1,7 +1,9 @@ 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.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; @@ -9,11 +11,9 @@ import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.Pair; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.ExistingBlock; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Map; +import java.util.*; public class MatchBlockPropertyCondition implements Condition { private final List> properties; @@ -29,19 +29,49 @@ public class MatchBlockPropertyCondition implements Conditi @Override public boolean test(CTX ctx) { - return ctx.getOptionalParameter(DirectContextParameters.CUSTOM_BLOCK_STATE).map(state -> { - CustomBlock block = state.owner().value(); + ImmutableBlockState customBlockState = null; + StatePropertyAccessor vanillaStatePropertyAccessor = null; + // 优先使用自定义状态,其主要应用于自定义方块掉落物 + Optional optionalCustomState = ctx.getOptionalParameter(DirectContextParameters.CUSTOM_BLOCK_STATE); + if (optionalCustomState.isPresent()) { + customBlockState = optionalCustomState.get(); + } else { + // 其次再判断block,这个过程会更慢,因为每次获取都是全新的方块状态,适用于物品等事件 + Optional optionalExistingBlock = ctx.getOptionalParameter(DirectContextParameters.BLOCK); + if (optionalExistingBlock.isPresent()) { + ExistingBlock existingBlock = optionalExistingBlock.get(); + customBlockState = existingBlock.customBlockState(); + if (customBlockState == null) { + vanillaStatePropertyAccessor = existingBlock.createStatePropertyAccessor(); + } + } else { + // 都没有则条件不过 + return false; + } + } + if (customBlockState != null) { + CustomBlock block = customBlockState.owner().value(); for (Pair property : this.properties) { Property propertyIns = block.getProperty(property.left()); if (propertyIns == null) { return false; } - if (!state.get(propertyIns).toString().toLowerCase(Locale.ENGLISH).equals(property.right())) { + if (!customBlockState.get(propertyIns).toString().toLowerCase(Locale.ENGLISH).equals(property.right())) { return false; } } - return true; - }).orElse(false); + } else { + for (Pair property : this.properties) { + String value = vanillaStatePropertyAccessor.getPropertyValueAsString(property.left()); + if (value == null) { + return false; + } + if (!value.equals(property.right())) { + return false; + } + } + } + return true; } public static class FactoryImpl implements ConditionFactory { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchEntityCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchEntityCondition.java new file mode 100644 index 000000000..c1a92cedc --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchEntityCondition.java @@ -0,0 +1,46 @@ +package net.momirealms.craftengine.core.plugin.context.condition; + +import net.momirealms.craftengine.core.entity.Entity; +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; +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 java.util.*; + +public class MatchEntityCondition implements Condition { + private final Set ids; + private final boolean regexMatch; + + public MatchEntityCondition(Collection ids, boolean regexMatch) { + this.ids = new HashSet<>(ids); + this.regexMatch = regexMatch; + } + + @Override + public Key type() { + return CommonConditions.MATCH_ENTITY; + } + + @Override + public boolean test(CTX ctx) { + Optional entity = ctx.getOptionalParameter(DirectContextParameters.ENTITY); + return entity.filter(value -> MiscUtils.matchRegex(value.type().asString(), this.ids, this.regexMatch)).isPresent(); + } + + public static class FactoryImpl implements ConditionFactory { + + @Override + public Condition create(Map arguments) { + List ids = MiscUtils.getAsStringList(arguments.get("id")); + if (ids.isEmpty()) { + throw new LocalizedResourceConfigException("warning.config.condition.match_entity.missing_id"); + } + boolean regex = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("regex", false), "regex"); + return new MatchEntityCondition<>(ids, regex); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchItemCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchItemCondition.java index 45d14beac..f6c7f78a8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchItemCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchItemCondition.java @@ -28,19 +28,7 @@ public class MatchItemCondition implements Condition { @Override public boolean test(CTX ctx) { Optional> item = ctx.getOptionalParameter(DirectContextParameters.ITEM_IN_HAND); - if (item.isEmpty()) return false; - Key key = item.get().id(); - String itemId = key.toString(); - if (this.regexMatch) { - for (String regex : ids) { - if (itemId.matches(regex)) { - return true; - } - } - } else { - return this.ids.contains(itemId); - } - return false; + return item.filter(value -> MiscUtils.matchRegex(value.id().asString(), this.ids, this.regexMatch)).isPresent(); } public static class FactoryImpl implements ConditionFactory { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventConditions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventConditions.java index e5fc50979..c5e0e3c71 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventConditions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventConditions.java @@ -17,6 +17,8 @@ public class EventConditions { static { register(CommonConditions.MATCH_ITEM, new MatchItemCondition.FactoryImpl<>()); + register(CommonConditions.MATCH_ENTITY, new MatchEntityCondition.FactoryImpl<>()); + register(CommonConditions.MATCH_BLOCK, new MatchBlockCondition.FactoryImpl<>()); register(CommonConditions.MATCH_BLOCK_PROPERTY, new MatchBlockPropertyCondition.FactoryImpl<>()); register(CommonConditions.TABLE_BONUS, new TableBonusCondition.FactoryImpl<>()); register(CommonConditions.SURVIVES_EXPLOSION, new SurvivesExplosionCondition.FactoryImpl<>()); 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..8c36f5af6 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,9 @@ 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)); } public static void register(Key key, FunctionFactory factory) { 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..86aeebfa5 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 @@ -33,7 +33,7 @@ public class ActionBarFunction extends AbstractConditionalF }); } 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.sendActionBar(AdventureHelper.miniMessage().deserialize(this.message.get(relationalContext), relationalContext.tagResolvers())); } } 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 c27b2177f..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 @@ -16,34 +16,37 @@ import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Map; +import java.util.function.Consumer; public class CommandFunction extends AbstractConditionalFunction { private final List command; - private final boolean asPlayer; private final PlayerSelector selector; + private final boolean asPlayer; + private final boolean asEvent; + private final boolean asOp; - public CommandFunction(List> predicates, @Nullable PlayerSelector selector, List command, boolean asPlayer) { + public CommandFunction(List> predicates, @Nullable PlayerSelector selector, List command, + boolean asPlayer, boolean asEvent, boolean asOp) { super(predicates); - this.asPlayer = asPlayer; this.command = command; this.selector = selector; + this.asPlayer = asPlayer; + this.asEvent = asEvent; + this.asOp = asOp; } @Override public void runInternal(CTX ctx) { - if (this.asPlayer) { + if (this.asPlayer || this.asOp) { if (this.selector == null) { - ctx.getOptionalParameter(DirectContextParameters.PLAYER).ifPresent(it -> { - for (TextProvider c : this.command) { - it.performCommand(c.get(ctx)); - } - }); + ctx.getOptionalParameter(DirectContextParameters.PLAYER) + .ifPresent(player -> executeCommands( + ctx, this.asEvent ? player::performCommandAsEvent : command1 -> player.performCommand(command1, this.asOp) + )); } else { for (Player viewer : this.selector.get(ctx)) { - RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer, ContextHolder.EMPTY)); - for (TextProvider c : this.command) { - viewer.performCommand(c.get(relationalContext)); - } + RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer)); + executeCommands(relationalContext, this.asEvent ? viewer::performCommandAsEvent : command1 -> viewer.performCommand(command1, this.asOp)); } } } else { @@ -54,6 +57,12 @@ public class CommandFunction extends AbstractConditionalFun } } + private void executeCommands(Context ctx, Consumer executor) { + for (TextProvider c : this.command) { + executor.accept(c.get(ctx)); + } + } + @Override public Key type() { return CommonFunctions.COMMAND; @@ -70,7 +79,9 @@ public class CommandFunction extends AbstractConditionalFun Object command = ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(arguments, "command", "commands"), "warning.config.function.command.missing_command"); List commands = MiscUtils.getAsStringList(command).stream().map(TextProviders::fromString).toList(); boolean asPlayer = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("as-player", false), "as-player"); - return new CommandFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), commands, asPlayer); + boolean asEvent = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("as-event", false), "as-event"); + boolean asOp = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("as-op", false), "as-op"); + return new CommandFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), commands, asPlayer, asEvent, 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..143b02f1d 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,7 @@ 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"); } 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..ffedc3333 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 @@ -38,7 +38,7 @@ public class MessageFunction 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)); for (TextProvider c : this.messages) { viewer.sendMessage(AdventureHelper.miniMessage().deserialize(c.get(relationalContext), relationalContext.tagResolvers()), this.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..3cda10b9b 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 @@ -46,7 +46,7 @@ public class OpenWindowFunction extends AbstractConditional 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)); + RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer)); CraftEngine.instance().guiManager().updateInventoryTitle(viewer, AdventureHelper.miniMessage().deserialize(this.optionalTitle.get(relationalContext), relationalContext.tagResolvers())); } } 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 2ab73db03..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().createPackedBlockState(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 2efaf8877..15167ef06 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 @@ -62,7 +62,7 @@ public class PlaceBlockFunction extends AbstractConditional NumberProvider y = NumberProviders.fromObject(arguments.getOrDefault("y", "")); NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "")); NumberProvider flags = Optional.ofNullable(arguments.get("update-flags")).map(NumberProviders::fromObject).orElse(NumberProviders.direct(UpdateOption.UPDATE_ALL.flags())); - return new PlaceBlockFunction<>(LazyReference.lazyReference(() -> CraftEngine.instance().blockManager().createPackedBlockState(state)), x, y, z, flags, getPredicates(arguments)); + return new PlaceBlockFunction<>(LazyReference.lazyReference(() -> CraftEngine.instance().blockManager().createBlockState(state)), x, y, z, flags, getPredicates(arguments)); } } } 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/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..cbae62005 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 @@ -45,7 +45,7 @@ public class TitleFunction extends AbstractConditionalFunct )); } 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()), 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/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 new file mode 100644 index 000000000..77de046d5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/GaussianNumberProvider.java @@ -0,0 +1,103 @@ +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.ResourceConfigUtils; + +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +public class GaussianNumberProvider implements NumberProvider { + public static final Factory FACTORY = new Factory(); + private final double min; + private final double max; + private final double mean; + private final double stdDev; + private final int maxAttempts; + + public GaussianNumberProvider(double min, double max, double mean, double stdDev, int maxAttempts) { + this.min = min; + this.max = max; + this.mean = mean; + this.stdDev = stdDev; + this.maxAttempts = maxAttempts; + validateParameters(); + } + + private void validateParameters() { + if (this.min >= this.max) { + throw new IllegalArgumentException("min must be less than max"); + } + if (this.stdDev <= 0) { + throw new IllegalArgumentException("std-dev must be greater than 0"); + } + if (this.maxAttempts <= 0) { + throw new IllegalArgumentException("max-attempts must be greater than 0"); + } + } + + @Override + public float getFloat(Context context) { + return (float) getDouble(context); + } + + @Override + public double getDouble(Context context) { + Random random = ThreadLocalRandom.current(); + int attempts = 0; + while (attempts < maxAttempts) { + double value = random.nextGaussian() * stdDev + mean; + if (value >= min && value <= max) { + return value; + } + attempts++; + } + return MCUtils.clamp(this.mean, this.min, this.max); + } + + @Override + public Key type() { + return NumberProviders.GAUSSIAN; + } + + public double min() { + return min; + } + + public double max() { + return max; + } + + public int maxAttempts() { + return maxAttempts; + } + + public double mean() { + return mean; + } + + public double stdDev() { + return stdDev; + } + + public static class Factory implements NumberProviderFactory { + + @Override + public NumberProvider create(Map arguments) { + double min = ResourceConfigUtils.getAsDouble(ResourceConfigUtils.requireNonNullOrThrow(arguments.get("min"), "warning.config.number.gaussian.missing_min"), "min"); + double max = ResourceConfigUtils.getAsDouble(ResourceConfigUtils.requireNonNullOrThrow(arguments.get("max"), "warning.config.number.gaussian.missing_max"), "max"); + double mean = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("mean", (min + max) / 2.0), "mean"); + double stdDev = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("std-dev", (max - min) / 6.0), "std-dev"); + int maxAttempts = ResourceConfigUtils.getAsInt(arguments.getOrDefault("max-attempts", 128), "max-attempts"); + return new GaussianNumberProvider(min, max, mean, stdDev, maxAttempts); + } + } + + @Override + public String toString() { + return String.format("GaussianNumberProvider{min=%.2f, max=%.2f, mean=%.2f, stdDev=%.2f, maxAttempts=%d}", + min, max, mean, stdDev, maxAttempts); + } +} \ No newline at end of file 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 22b698c86..49ce77b1a 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 @@ -17,11 +17,13 @@ public class NumberProviders { public static final Key CONSTANT = Key.of("craftengine:constant"); 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"); static { register(FIXED, FixedNumberProvider.FACTORY); register(CONSTANT, FixedNumberProvider.FACTORY); register(UNIFORM, UniformNumberProvider.FACTORY); + register(GAUSSIAN, GaussianNumberProvider.FACTORY); register(EXPRESSION, ExpressionNumberProvider.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/BlockParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/BlockParameterProvider.java index 1aac69f9c..dc1cff926 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/BlockParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/BlockParameterProvider.java @@ -2,31 +2,31 @@ 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.world.BlockInWorld; +import net.momirealms.craftengine.core.world.ExistingBlock; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.function.Function; -public class BlockParameterProvider implements ChainParameterProvider { - private static final Map, Function> CONTEXT_FUNCTIONS = new HashMap<>(); +public class BlockParameterProvider implements ChainParameterProvider { + private static final Map, Function> CONTEXT_FUNCTIONS = new HashMap<>(); static { - CONTEXT_FUNCTIONS.put(DirectContextParameters.X, BlockInWorld::x); - CONTEXT_FUNCTIONS.put(DirectContextParameters.Y, BlockInWorld::y); - CONTEXT_FUNCTIONS.put(DirectContextParameters.Z, BlockInWorld::z); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, BlockInWorld::x); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, BlockInWorld::y); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, BlockInWorld::z); - CONTEXT_FUNCTIONS.put(DirectContextParameters.CUSTOM_BLOCK, BlockInWorld::customBlock); - CONTEXT_FUNCTIONS.put(DirectContextParameters.CUSTOM_BLOCK_STATE, BlockInWorld::customBlockState); - CONTEXT_FUNCTIONS.put(DirectContextParameters.WORLD, BlockInWorld::world); - CONTEXT_FUNCTIONS.put(DirectContextParameters.POSITION, BlockInWorld::position); + CONTEXT_FUNCTIONS.put(DirectContextParameters.X, ExistingBlock::x); + CONTEXT_FUNCTIONS.put(DirectContextParameters.Y, ExistingBlock::y); + CONTEXT_FUNCTIONS.put(DirectContextParameters.Z, ExistingBlock::z); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, ExistingBlock::x); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, ExistingBlock::y); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, ExistingBlock::z); + CONTEXT_FUNCTIONS.put(DirectContextParameters.CUSTOM_BLOCK, ExistingBlock::customBlock); + CONTEXT_FUNCTIONS.put(DirectContextParameters.CUSTOM_BLOCK_STATE, ExistingBlock::customBlockState); + CONTEXT_FUNCTIONS.put(DirectContextParameters.WORLD, ExistingBlock::world); + CONTEXT_FUNCTIONS.put(DirectContextParameters.POSITION, ExistingBlock::position); } @SuppressWarnings("unchecked") @Override - public Optional getOptionalParameter(ContextKey parameter, BlockInWorld block) { + public Optional getOptionalParameter(ContextKey parameter, ExistingBlock block) { return (Optional) Optional.ofNullable(CONTEXT_FUNCTIONS.get(parameter)).map(f -> f.apply(block)); } } \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/DirectContextParameters.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/DirectContextParameters.java index cf98e3774..a3d0b5335 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/DirectContextParameters.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/DirectContextParameters.java @@ -12,7 +12,7 @@ import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.context.ContextKey; import net.momirealms.craftengine.core.util.Cancellable; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.BlockInWorld; +import net.momirealms.craftengine.core.world.ExistingBlock; import net.momirealms.craftengine.core.world.Position; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldPosition; @@ -49,7 +49,7 @@ public final class DirectContextParameters { public static final ContextKey> MAIN_HAND_ITEM = ContextKey.direct("main_hand_item"); public static final ContextKey> OFF_HAND_ITEM = ContextKey.direct("off_hand_item"); public static final ContextKey CUSTOM_BLOCK = ContextKey.direct("custom_block"); - public static final ContextKey BLOCK = ContextKey.direct("block"); + public static final ContextKey BLOCK = ContextKey.direct("block"); public static final ContextKey TIME = ContextKey.direct("time"); public static final ContextKey ID = ContextKey.direct("id"); public static final ContextKey CUSTOM_MODEL_DATA = ContextKey.direct("custom_model_data"); @@ -57,8 +57,11 @@ public final class DirectContextParameters { public static final ContextKey ANCHOR_TYPE = ContextKey.direct("anchor_type"); public static final ContextKey HAND = ContextKey.direct("hand"); public static final ContextKey EVENT = ContextKey.direct("event"); - public static final ContextKey IS_FLYING = ContextKey.direct("is_flying"); public static final ContextKey IS_SNEAKING = ContextKey.direct("is_sneaking"); + public static final ContextKey IS_SWIMMING = ContextKey.direct("is_swimming"); + public static final ContextKey IS_CLIMBING = ContextKey.direct("is_climbing"); + public static final ContextKey IS_GLIDING = ContextKey.direct("is_gliding"); + public static final ContextKey IS_FLYING = ContextKey.direct("is_flying"); public static final ContextKey IS_CUSTOM = ContextKey.direct("is_custom"); public static final ContextKey IS_BLOCK_ITEM = ContextKey.direct("is_block_item"); public static final ContextKey GAMEMODE = ContextKey.direct("gamemode"); 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 7ed66aff2..25c3df127 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 @@ -29,8 +29,11 @@ public class PlayerParameterProvider implements ChainParameterProvider { CONTEXT_FUNCTIONS.put(DirectContextParameters.NAME, Player::name); CONTEXT_FUNCTIONS.put(DirectContextParameters.UUID, Player::uuid); CONTEXT_FUNCTIONS.put(DirectContextParameters.WORLD, Entity::world); - CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_FLYING, Player::isFlying); CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_SNEAKING, Player::isSneaking); + CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_SWIMMING, Player::isSwimming); + CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_CLIMBING, Player::isClimbing); + CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_GLIDING, Player::isGliding); + CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_FLYING, Player::isFlying); CONTEXT_FUNCTIONS.put(DirectContextParameters.GAMEMODE, Player::gameMode); CONTEXT_FUNCTIONS.put(DirectContextParameters.MAIN_HAND_ITEM, p -> p.getItemInHand(InteractionHand.MAIN_HAND)); CONTEXT_FUNCTIONS.put(DirectContextParameters.OFF_HAND_ITEM, p -> p.getItemInHand(InteractionHand.OFF_HAND)); 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 2eb14be4f..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,27 +372,6 @@ public class Dependencies { List.of(Relocation.of("evalex", "com{}ezylang{}evalex")) ); - public static final Dependency NETTY_HTTP = new Dependency( - "netty-codec-http", - "io{}netty", - "netty-codec-http", - Collections.emptyList() - ); - - public static final Dependency NETTY_HTTP2 = new Dependency( - "netty-codec-http2", - "io{}netty", - "netty-codec-http2", - Collections.emptyList() - ); - - 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", @@ -420,6 +379,36 @@ public class Dependencies { 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", + 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", + 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"), + 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( "amazon-sdk-s3", @@ -427,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") ) ); @@ -437,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 @@ -452,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 @@ -467,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 @@ -482,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 @@ -497,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 @@ -512,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 @@ -527,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 @@ -542,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 @@ -557,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 @@ -572,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 @@ -587,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 @@ -602,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 @@ -617,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 @@ -632,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 @@ -647,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") ) ); @@ -657,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 @@ -672,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 @@ -687,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 @@ -702,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 @@ -717,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 @@ -732,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 @@ -747,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 @@ -762,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 @@ -777,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 @@ -792,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 @@ -807,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 @@ -822,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 @@ -837,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..44930a9fc 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 @@ -114,7 +114,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { 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 +161,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 +240,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 836731d7e..a87b62c10 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 @@ -92,7 +92,7 @@ public class I18NData { } private static String stateToRealBlockId(ImmutableBlockState state) { - String id = state.customBlockState().handle().toString(); + String id = state.customBlockState().literalObject().toString(); int first = -1, last = -1; for (int i = 0; i < id.length(); i++) { char c = id.charAt(i); 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 4ca696c84..9ac5d509d 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 @@ -9,8 +9,9 @@ public enum Debugger { COMMON(Config::debugCommon), PACKET(Config::debugPacket), FURNITURE(Config::debugFurniture), - RESOURCE_PACK(Config::debugFurniture), - ITEM(Config::debugItem); + RESOURCE_PACK(Config::debugResourcePack), + ITEM(Config::debugItem), + BLOCK_ENTITY(Config::debugBlockEntity); private final Supplier condition; @@ -26,7 +27,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/EntityPacketHandler.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java index 15e0aa919..57fb85de9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.plugin.network; import it.unimi.dsi.fastutil.ints.IntList; +import net.momirealms.craftengine.core.entity.player.Player; public interface EntityPacketHandler { @@ -8,7 +9,7 @@ public interface EntityPacketHandler { return false; } - default void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + default void handleSetEntityData(Player user, ByteBufPacketEvent event) { } default void handleSyncEntityPosition(NetWorkUser user, NMSPacketEvent event, Object packet) { 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 453b8e055..baa955b1a 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,10 +4,13 @@ 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.chunk.ChunkStatus; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -28,16 +31,28 @@ 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); void sendPacket(Object packet, boolean immediately, Runnable sendListener); + void sendPackets(List packet, boolean immediately); + + void sendPackets(List packet, boolean immediately, Runnable sendListener); + void sendCustomPayload(Key channel, byte[] data); void kick(Component message); @@ -71,4 +86,18 @@ public interface NetWorkUser { void setShouldProcessFinishConfiguration(boolean shouldProcess); boolean shouldProcessFinishConfiguration(); + + boolean isChunkTracked(long chunkPos); + + ChunkStatus getTrackedChunk(long chunkPos); + + void addTrackedChunk(long chunkPos, ChunkStatus chunkStatus); + + void clearTrackedChunks(); + + void removeTrackedChunk(long chunkPos); + + IntIdentityList clientBlockList(); + + void setClientBlockList(IntIdentityList integers); } 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..0589a2df5 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; @@ -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(MCUtils::unpackDegrees, MCUtils::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/component/ComponentProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/component/ComponentProvider.java new file mode 100644 index 000000000..87b021160 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/component/ComponentProvider.java @@ -0,0 +1,51 @@ +package net.momirealms.craftengine.core.plugin.text.component; + +import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.util.AdventureHelper; + +import java.util.function.Function; + +import static net.momirealms.craftengine.core.plugin.text.minimessage.FormattedLine.CUSTOM_RESOLVERS; + +public sealed interface ComponentProvider extends Function + permits ComponentProvider.Constant, ComponentProvider.MiniMessage { + + static ComponentProvider constant(Component component) { + return new Constant(component); + } + + static ComponentProvider miniMessageOrConstant(String line) { + if (line.equals(AdventureHelper.customMiniMessage().stripTags(line, CUSTOM_RESOLVERS))) { + return constant(AdventureHelper.miniMessage().deserialize(line)); + } else { + return new MiniMessage(line); + } + } + + non-sealed class Constant implements ComponentProvider { + private final Component value; + + public Constant(final Component value) { + this.value = value; + } + + @Override + public Component apply(Context context) { + return this.value; + } + } + + non-sealed class MiniMessage implements ComponentProvider { + private final String value; + + public MiniMessage(final String value) { + this.value = value; + } + + @Override + public Component apply(Context context) { + return AdventureHelper.miniMessage().deserialize(this.value, context.tagResolvers()); + } + } +} \ No newline at end of file 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..1c24d56fc 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 @@ -12,10 +12,11 @@ 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 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/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/SoundSource.java b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundSource.java index e48a6c429..a3174292d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundSource.java +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundSource.java @@ -12,7 +12,8 @@ public enum SoundSource { NEUTRAL("neutral"), PLAYER("player"), AMBIENT("ambient"), - VOICE("voice"); + VOICE("voice"), + UI("ui"); private final String id; 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 e988d1f68..a8ad7ed18 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 @@ -13,6 +13,8 @@ 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.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.sparrow.nbt.Tag; import net.momirealms.sparrow.nbt.adventure.NBTComponentSerializer; import net.momirealms.sparrow.nbt.adventure.NBTSerializerOptions; @@ -356,12 +358,15 @@ public class AdventureHelper { return AdventureHelper.plainTextContent(resultComponent); } - public static Component replaceText(Component text, Map replacements) { + public static Component replaceText(Component text, Map replacements, Context context) { String patternString = replacements.keySet().stream() .map(Pattern::quote) .collect(Collectors.joining("|")); return text.replaceText(builder -> builder.match(Pattern.compile(patternString)) - .replacement((result, b) -> replacements.get(result.group()))); + .replacement((result, b) -> + 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/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/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/Int2ObjectBiMap.java b/core/src/main/java/net/momirealms/craftengine/core/util/Int2ObjectBiMap.java index 297d49600..76cbad82e 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 @@ -126,7 +126,7 @@ public class Int2ObjectBiMap implements IndexedIterable { 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/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 index 95e1621f7..38cf3acdd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MCUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MCUtils.java @@ -31,6 +31,11 @@ public class MCUtils { 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; 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 54195dff8..8e5a2864a 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,13 +1,6 @@ package net.momirealms.craftengine.core.util; -import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import org.joml.Quaternionf; -import org.joml.Vector3f; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; public class MiscUtils { @@ -81,42 +74,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()) { @@ -153,4 +110,17 @@ public class MiscUtils { return o; } } + + public static boolean matchRegex(String id, Set ids, boolean regexMatch) { + if (regexMatch) { + for (String regex : ids) { + if (id.matches(regex)) { + return true; + } + } + } else { + return ids.contains(id); + } + return false; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MutableBoolean.java b/core/src/main/java/net/momirealms/craftengine/core/util/MutableBoolean.java new file mode 100644 index 000000000..dfe5b977a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MutableBoolean.java @@ -0,0 +1,17 @@ +package net.momirealms.craftengine.core.util; + +public class MutableBoolean { + private boolean value; + + public MutableBoolean(boolean value) { + this.value = value; + } + + public boolean booleanValue() { + return value; + } + + public void set(boolean value) { + this.value = value; + } +} 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..58c09669e 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 @@ -3,7 +3,10 @@ package net.momirealms.craftengine.core.util; import com.mojang.datafixers.util.Either; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.world.Vec3d; import org.jetbrains.annotations.Nullable; +import org.joml.Quaternionf; +import org.joml.Vector3f; import java.util.*; import java.util.function.Function; @@ -223,4 +226,58 @@ 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); + } + } + } } 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/world/BlockPos.java b/core/src/main/java/net/momirealms/craftengine/core/world/BlockPos.java index 686b5e95d..0bc751b85 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 @@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.MCUtils; 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); 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 2a636bd69..ac187bb3a 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 @@ -1,108 +1,110 @@ package net.momirealms.craftengine.core.world; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +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.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.locks.ReentrantReadWriteLock; public abstract class CEWorld { public static final String REGION_DIRECTORY = "craftengine"; - protected final World world; - protected final Map loadedChunkMap; + public final World world; + protected final ConcurrentLong2ReferenceChainedHashTable loadedChunkMap; protected final WorldDataStorage worldDataStorage; - protected final ReentrantReadWriteLock loadedChunkMapLock = new ReentrantReadWriteLock(); 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 tickingBlockEntities = new BlockEntityTickersList(); + protected final List pendingTickingBlockEntities = new ArrayList<>(); + protected volatile boolean isTickingBlockEntities = false; + protected volatile boolean isUpdatingLights = false; + protected SchedulerTask syncTickTask; + protected SchedulerTask asyncTickTask; public CEWorld(World world, StorageAdaptor adaptor) { this.world = world; - this.loadedChunkMap = new Long2ObjectOpenHashMap<>(1024, 0.5f); + 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) { this.world = world; - this.loadedChunkMap = new Long2ObjectOpenHashMap<>(1024, 0.5f); + 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() { - this.loadedChunkMapLock.readLock().lock(); try { - for (Map.Entry entry : this.loadedChunkMap.entrySet()) { + 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); } } } catch (IOException e) { CraftEngine.instance().logger().warn("Failed to save world chunks", e); - } finally { - this.loadedChunkMapLock.readLock().unlock(); } } public World world() { - return world; + return this.world; } public boolean isChunkLoaded(final long chunkPos) { - this.loadedChunkMapLock.readLock().lock(); - try { - return loadedChunkMap.containsKey(chunkPos); - } finally { - this.loadedChunkMapLock.readLock().unlock(); - } + return loadedChunkMap.containsKey(chunkPos); } public void addLoadedChunk(CEChunk chunk) { - this.loadedChunkMapLock.writeLock().lock(); - try { - this.loadedChunkMap.put(chunk.chunkPos().longKey(), chunk); - } finally { - this.loadedChunkMapLock.writeLock().unlock(); - } + this.loadedChunkMap.put(chunk.chunkPos().longKey(), chunk); } public void removeLoadedChunk(CEChunk chunk) { - this.loadedChunkMapLock.writeLock().lock(); - try { - this.loadedChunkMap.remove(chunk.chunkPos().longKey()); - if (this.lastChunk == chunk) { - this.lastChunk = null; - this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS; - } - } finally { - this.loadedChunkMapLock.writeLock().unlock(); - } + this.loadedChunkMap.remove(chunk.chunkPos().longKey()); } @Nullable public CEChunk getChunkAtIfLoaded(long chunkPos) { - this.loadedChunkMapLock.readLock().lock(); - try { - return getChunkAtIfLoadedMainThread(chunkPos); - } finally { - this.loadedChunkMapLock.readLock().unlock(); - } + return this.loadedChunkMap.get(chunkPos); } @Nullable @@ -111,27 +113,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) { @@ -140,6 +126,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) { @@ -149,7 +136,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); @@ -160,17 +147,75 @@ 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.tickBlockEntities(); + if (!Config.asyncLightUpdate()) { + this.updateLight(); + } + } + + public void asyncTick() { + if (Config.asyncLightUpdate()) { + this.updateLight(); + } + } + + public abstract void updateLight(); + + public void addBlockEntityTicker(TickingBlockEntity ticker) { + if (this.isTickingBlockEntities) { + this.pendingTickingBlockEntities.add(ticker); + } else { + this.tickingBlockEntities.add(ticker); + } + } + + protected void tickBlockEntities() { + this.isTickingBlockEntities = true; + if (!this.pendingTickingBlockEntities.isEmpty()) { + this.tickingBlockEntities.addAll(this.pendingTickingBlockEntities); + this.pendingTickingBlockEntities.clear(); + } + if (!this.tickingBlockEntities.isEmpty()) { + Object[] entities = this.tickingBlockEntities.elements(); + for (int i = 0, size = this.tickingBlockEntities.size(); i < size; i++) { + TickingBlockEntity entity = (TickingBlockEntity) entities[i]; + if (entity.isValid()) { + entity.tick(); + } else { + this.tickingBlockEntities.markAsRemoved(i); + } + } + this.tickingBlockEntities.removeMarkedEntries(); + } + this.isTickingBlockEntities = 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/BlockInWorld.java b/core/src/main/java/net/momirealms/craftengine/core/world/ExistingBlock.java similarity index 64% rename from core/src/main/java/net/momirealms/craftengine/core/world/BlockInWorld.java rename to core/src/main/java/net/momirealms/craftengine/core/world/ExistingBlock.java index 5dac8cf13..947bc664d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/BlockInWorld.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/ExistingBlock.java @@ -1,11 +1,15 @@ 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.item.context.BlockPlaceContext; +import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public interface BlockInWorld { +public interface ExistingBlock { default boolean canBeReplaced(BlockPlaceContext blockPlaceContext) { return false; @@ -18,15 +22,25 @@ public interface BlockInWorld { @Nullable CustomBlock customBlock(); + boolean isCustom(); + @Nullable ImmutableBlockState customBlockState(); + @NotNull + BlockStateWrapper blockState(); + + @NotNull + StatePropertyAccessor createStatePropertyAccessor(); + default WorldPosition position() { return new WorldPosition(world(), x(), y(), z()); } World world(); + Key id(); + int x(); int y(); 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..aeb8b3df5 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 @@ -45,6 +45,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..9413aff03 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 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 fb4923698..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,34 +1,44 @@ 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(); WorldHeight worldHeight(); - BlockInWorld getBlockAt(int x, int y, int z); + ExistingBlock getBlockAt(int x, int y, int z); - default BlockInWorld getBlockAt(final BlockPos pos) { + default ExistingBlock getBlockAt(final BlockPos pos) { return getBlockAt(pos.x(), pos.y(), pos.z()); } 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..442ac8999 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,57 @@ 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.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 tickingBlockEntitiesByPos; // 从区域线程上访问,安全 + 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.tickingBlockEntitiesByPos = 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.tickingBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); int sectionCount = this.worldHeightAccessor.getSectionsCount(); this.sections = new CESection[sectionCount]; if (sections != null) { @@ -44,10 +63,255 @@ 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.tickingBlockEntitiesByPos.values().forEach((ticker) -> ticker.setTicker(DummyTickingBlockEntity.INSTANCE)); + this.tickingBlockEntitiesByPos.clear(); + } + + public void replaceOrCreateTickingBlockEntity(T blockEntity) { + ImmutableBlockState blockState = blockEntity.blockState(); + BlockEntityTicker ticker = blockState.createBlockEntityTicker(this.world, blockEntity.type()); + if (ticker != null) { + this.tickingBlockEntitiesByPos.compute(blockEntity.pos(), ((pos, previousTicker) -> { + TickingBlockEntity newTicker = new TickingBlockEntityImpl<>(this, blockEntity, ticker); + if (previousTicker != null) { + previousTicker.setTicker(newTicker); + return previousTicker; + } else { + ReplaceableTickingBlockEntity replaceableTicker = new ReplaceableTickingBlockEntity(newTicker); + this.world.addBlockEntityTicker(replaceableTicker); + return replaceableTicker; + } + })); + } else { + this.removeBlockEntityTicker(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 removeBlockEntityTicker(BlockPos pos) { + ReplaceableTickingBlockEntity blockEntity = this.tickingBlockEntitiesByPos.remove(pos); + if (blockEntity != null) { + blockEntity.setTicker(DummyTickingBlockEntity.INSTANCE); + } + } + + 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 +333,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 +357,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 +392,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/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..6dfc48961 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,48 @@ 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); + 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 e8947d094..36d420260 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 @@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.InactiveCustomBlock; +import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.WritableRegistry; @@ -58,7 +59,20 @@ public final class DefaultSectionSerializer { CompoundTag palette = (CompoundTag) tag; String id = palette.getString("id"); CompoundTag data = palette.getCompound("properties"); - Key key = Key.of(id); + Key key; + if (Config.handleInvalidBlock()) { + String converted = Config.blockMappings().get(id); + if (converted == null) { + key = Key.of(id); + } else if (converted.isEmpty()) { + paletteEntries.add(EmptyBlock.STATE); + continue; + } else { + key = Key.of(converted); + } + } else { + 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)); 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 3ce89d0df..69959143c 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 -config_version=44 -lang_version=24 +project_version=0.0.63 +config_version=45 +lang_version=29 project_group=net.momirealms latest_supported_version=1.21.8 @@ -40,23 +40,24 @@ commons_io_version=2.18.0 commons_imaging_version=1.0.0-alpha6 commons_lang3_version=3.17.0 sparrow_nbt_version=0.9.4 -sparrow_util_version=0.50.9 +sparrow_util_version=0.51 fastutil_version=8.5.15 -netty_version=4.1.121.Final +netty_version=4.1.124.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.57 +snake_yaml_version=2.5 +anti_grief_version=0.20 +nms_helper_version=1.0.87 evalex_version=3.5.0 reactive_streams_version=1.0.4 -amazon_awssdk_version=2.31.23 +amazon_awssdk_version=2.33.1 amazon_awssdk_eventstream_version=1.0.1 jimfs_version=1.3.0 authlib_version=6.0.58 +concurrent_util_version=0.0.3 # Proxy settings #systemProp.socks.proxyHost=127.0.0.1 diff --git a/libs/boosted-yaml-1.3.7.jar b/libs/boosted-yaml-1.3.7.jar index ea489cb34..b6481f3fa 100644 Binary files a/libs/boosted-yaml-1.3.7.jar and b/libs/boosted-yaml-1.3.7.jar differ diff --git a/libs/concurrentutil-0.0.3.jar b/libs/concurrentutil-0.0.3.jar new file mode 100644 index 000000000..27b20749b Binary files /dev/null and b/libs/concurrentutil-0.0.3.jar differ 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